Feat/new home + switch wallet + ux change#547
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughAdds 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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas needing extra attention:
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
|
Size Change: +87.3 kB (+1.59%) Total Size: 5.58 MB
ℹ️ View Unchanged
|
🚀 Preview environment deployed!Preview URL: https://preview.vechainkit.vechain.org/featnew-home |
🚀 Preview environment deployed!Preview URL: https://preview.vechainkit.vechain.org/featnew-home |
🚀 Preview environment deployed!Preview URL: https://preview.vechainkit.vechain.org/featnew-home |
1 similar comment
🚀 Preview environment deployed!Preview URL: https://preview.vechainkit.vechain.org/featnew-home |
🚀 Preview environment deployed!Preview URL: https://preview.vechainkit.vechain.org/featnew-home |
🚀 Preview environment deployed!Preview URL: https://preview.vechainkit.vechain.org/featnew-home |
There was a problem hiding this comment.
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) andonClose(line 33) are declared in thePropstype 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
handleLogoutfunction 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
📒 Files selected for processing (13)
packages/vechain-kit/src/components/AccountModal/AccountModal.tsxpackages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsxpackages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsxpackages/vechain-kit/src/components/ConnectModal/Contents/MainContent.tsxpackages/vechain-kit/src/hooks/api/wallet/index.tspackages/vechain-kit/src/hooks/api/wallet/useSwitchWallet.tspackages/vechain-kit/src/languages/de.jsonpackages/vechain-kit/src/languages/en.jsonpackages/vechain-kit/src/languages/es.jsonpackages/vechain-kit/src/languages/fr.jsonpackages/vechain-kit/src/languages/it.jsonpackages/vechain-kit/src/languages/ja.jsonpackages/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, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook 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 theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption 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: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions
Files:
packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsxpackages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsxpackages/vechain-kit/src/components/ConnectModal/Contents/MainContent.tsxpackages/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.tsxpackages/vechain-kit/src/components/ConnectModal/Contents/MainContent.tsxpackages/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
useEffectanduseWalletimports are necessary for the new auto-close functionality.
20-27: LGTM! Props updated correctly.The addition of the
onCloseprop 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
connectionobject 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
useSwitchWalletexport 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
connectionobject providesisInAppBrowserdetection, andswitchWallet/isSwitchingenable 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
switchWallethook 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 thesetCurrentContentprop in multiple ways:
- To conditionally apply cursor styling (line 83)
- Directly in an onClick handler to navigate to account-customization (lines 84-96)
- To conditionally render the edit pencil icon (line 112)
- To pass to the AddressDisplay component (line 204)
Passing
setCurrentContentto ProfileCard is not redundant; it is necessary for the component to function correctly. TheonEditClickcallback in ProfileContent independently handles edit button clicks, while thesetCurrentContentprop 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 (
LuArrowLeftRightfor switch,LuCopy/LuCheckfor 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", |
There was a problem hiding this comment.
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.
| "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.
There was a problem hiding this comment.
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-labelattribute 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
⛔ Files ignored due to path filters (6)
examples/homepage/public/images/favicon/apple-touch-icon.pngis excluded by!**/*.pngexamples/homepage/public/images/favicon/favicon-96x96.pngis excluded by!**/*.pngexamples/homepage/public/images/favicon/favicon.icois excluded by!**/*.icoexamples/homepage/public/images/favicon/favicon.svgis excluded by!**/*.svgexamples/homepage/public/images/favicon/web-app-manifest-192x192.pngis excluded by!**/*.pngexamples/homepage/public/images/favicon/web-app-manifest-512x512.pngis excluded by!**/*.png
📒 Files selected for processing (10)
examples/homepage/src/app/components/features/HeroSection/HeroSection.tsxexamples/homepage/src/app/components/features/InfoSection/InfoSection.tsxexamples/homepage/src/app/components/features/ScrollableInfoSections/ScrollableInfoSections.tsxexamples/homepage/src/app/languages/de.jsonexamples/homepage/src/app/languages/en.jsonexamples/homepage/src/app/languages/es.jsonexamples/homepage/src/app/languages/fr.jsonexamples/homepage/src/app/languages/it.jsonexamples/homepage/src/app/languages/ja.jsonexamples/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, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook 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 theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption 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: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${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.
| "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.", |
There was a problem hiding this comment.
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.
| "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.
| "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.", |
There was a problem hiding this comment.
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.
| "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.
| "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.", |
There was a problem hiding this comment.
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.
| "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.
| "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.", |
There was a problem hiding this comment.
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.
| "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.
| "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 設定を使用します。", |
There was a problem hiding this comment.
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.
| "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.
| "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 配置。", |
There was a problem hiding this comment.
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.
| "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.
There was a problem hiding this comment.
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
onLogoutcallback and the Logout button'sonClickhandler). 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.
initOAuthis 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.typeandconnection.networkwithout 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.0prevents 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:dangerouslySetInnerHTMLusage.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, andNEXT_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 beforeNEXT_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
../../../../../i18nmakes the code harder to maintain and refactor. Consider configuring a path alias (e.g.,@/i18nor~/i18n) in your TypeScript/Next.js configuration for cleaner imports.
22-31: Add missing 'it' entry to maintain consistency.The
languageToCountryCodemapping is missing an entry for'it'(Italian), even thoughsupportedLanguagesincludes it. While the fallback ingetFlagUrlprevents 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 ofas constfor 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 inTransactionExamples.tsxwithIB3TR__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
SettingsModalProvidercomponent 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
SettingsModalProvideris 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 -C3packages/vechain-kit/src/hooks/modals/useWalletModal.tsx (1)
38-40: Consider removing the no-opWalletModalProvidercomponent.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
onErrorhandler 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"orbg="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 themeAlso 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
useSwitchWallethook 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
openaccepts 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 thetestVariantfor production.The variant name
testVariantsuggests 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
../../../../../i18nis 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
../../../../../i18nis 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
useCurrentAllocationsRoundIdanduseIsPersonhooks. 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
useColorModehook and provides a clear toggle interface.💡 Optional: Remove unnecessary Box wrapper
The
Boxwrapper doesn't add any functionality or styling. Consider returning theButtondirectly: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
!importanton 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
!importantflags are needed to override Chakra UI's inline styles, consider:
- Using Chakra's theme configuration to set these colors at the theme level
- Increasing selector specificity without
!important- Using Chakra's
ColorModeProviderconfig to control default backgroundsExample of increasing specificity:
html[data-chakra-ui-color-mode='dark'] body { background-color: #121213; color: #F7FAFC; }
45-53: Use theme-aware border color.The
.containerclass 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.
lightTertiaryuses numeric keys (100, 200, etc.) whilelightPrimaryandlightSecondaryuse 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 fromreact-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 betweenLanguageSyncandVechainKitProviderWrapper.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 memoizingplaygroundTranslations.This object is recomputed on every render. Since
resourcesis static, memoize withuseMemoto 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, andlanguageNamesobjects 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: Unnecessarye.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
Boxlacks keyboard support and ARIA semantics. Consider addingrole,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
⛔ Files ignored due to path filters (18)
examples/playground/public/images/favicon.pngis excluded by!**/*.pngexamples/playground/public/images/favicon/apple-touch-icon.pngis excluded by!**/*.pngexamples/playground/public/images/favicon/favicon-96x96.pngis excluded by!**/*.pngexamples/playground/public/images/favicon/favicon.icois excluded by!**/*.icoexamples/playground/public/images/favicon/favicon.svgis excluded by!**/*.svgexamples/playground/public/images/favicon/web-app-manifest-192x192.pngis excluded by!**/*.pngexamples/playground/public/images/favicon/web-app-manifest-512x512.pngis excluded by!**/*.pngexamples/playground/public/images/logo.pngis excluded by!**/*.pngexamples/playground/public/images/metamask-logo.pngis excluded by!**/*.pngexamples/playground/public/images/rabby-logo.pngis excluded by!**/*.pngexamples/playground/public/images/spider-web-element-onblack-background-long.pngis excluded by!**/*.pngexamples/playground/public/images/spider-web-element-onblack-background.jpgis excluded by!**/*.jpgexamples/playground/public/images/vechain-kit-long.pngis excluded by!**/*.pngexamples/playground/public/images/veworld-logo.pngis excluded by!**/*.pngexamples/playground/public/images/wallet-connect-logo.pngis excluded by!**/*.pngexamples/playground/src/app/fonts/GeistMonoVF.woffis excluded by!**/*.woffexamples/playground/src/app/fonts/GeistVF.woffis excluded by!**/*.woffyarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (71)
examples/homepage/.env.exampleexamples/homepage/src/app/components/features/LanguagesSection/LanguagesSection.tsxexamples/homepage/src/app/components/features/LoginMethodsSection/index.tsexamples/homepage/src/app/layout.tsxexamples/playground/.env.exampleexamples/playground/.eslintrc.jsexamples/playground/.gitignoreexamples/playground/README.mdexamples/playground/i18n.tsexamples/playground/next.config.jsexamples/playground/package.jsonexamples/playground/public/images/coinbase-wallet-logo.webpexamples/playground/public/images/favicon/site.webmanifestexamples/playground/public/images/rainbow-logo.webpexamples/playground/src/app/components/features/AccountInfo/AccountInfo.tsxexamples/playground/src/app/components/features/AccountInfo/index.tsexamples/playground/src/app/components/features/ConnectionInfo/ConnectionInfo.tsxexamples/playground/src/app/components/features/ConnectionInfo/index.tsexamples/playground/src/app/components/features/DaoInfo/DaoInfo.tsxexamples/playground/src/app/components/features/DaoInfo/index.tsexamples/playground/src/app/components/features/DataReading/DataReadingExample.tsxexamples/playground/src/app/components/features/DataReading/index.tsexamples/playground/src/app/components/features/FeaturesToTry/FeatureCard.tsxexamples/playground/src/app/components/features/FeaturesToTry/FeaturesToTry.tsxexamples/playground/src/app/components/features/FeaturesToTry/GithubCard.tsxexamples/playground/src/app/components/features/FeaturesToTry/LanguageCard.tsxexamples/playground/src/app/components/features/FeaturesToTry/ThemeCard.tsxexamples/playground/src/app/components/features/FeaturesToTry/index.tsexamples/playground/src/app/components/features/LanguageSelector/LanguageSelector.tsxexamples/playground/src/app/components/features/LanguageSelector/index.tsexamples/playground/src/app/components/features/LoginToContinueBox/LoginToContinueBox.tsxexamples/playground/src/app/components/features/LoginToContinueBox/index.tsexamples/playground/src/app/components/features/LoginUIControl/LoginUIControl.tsxexamples/playground/src/app/components/features/LoginUIControl/index.tsexamples/playground/src/app/components/features/Signing/SigningExample.tsxexamples/playground/src/app/components/features/Signing/index.tsexamples/playground/src/app/components/features/SmartAccountInfo/SmartAccountInfo.tsxexamples/playground/src/app/components/features/SmartAccountInfo/index.tsexamples/playground/src/app/components/features/ThemeToggle/ThemeToggle.tsxexamples/playground/src/app/components/features/ThemeToggle/index.tsexamples/playground/src/app/components/features/TransactionExamples/TransactionExamples.tsxexamples/playground/src/app/components/features/TransactionExamples/index.tsexamples/playground/src/app/components/layout/Header/LanguageDropdown.tsxexamples/playground/src/app/components/layout/Header/index.tsexamples/playground/src/app/components/ui/CollapsibleCard/CollapsibleCard.tsxexamples/playground/src/app/components/ui/CollapsibleCard/index.tsexamples/playground/src/app/constants.tsexamples/playground/src/app/globals.cssexamples/playground/src/app/languages/de.jsonexamples/playground/src/app/languages/en.jsonexamples/playground/src/app/languages/es.jsonexamples/playground/src/app/languages/fr.jsonexamples/playground/src/app/languages/it.jsonexamples/playground/src/app/languages/ja.jsonexamples/playground/src/app/languages/zh.jsonexamples/playground/src/app/layout.tsxexamples/playground/src/app/page.tsxexamples/playground/src/app/pages/Home.tsxexamples/playground/src/app/providers/VechainKitProviderWrapper.tsxexamples/playground/src/app/theme/button.tsexamples/playground/src/app/theme/card.tsexamples/playground/src/app/theme/colors.tsexamples/playground/src/app/theme/index.tsexamples/playground/src/app/theme/modal.tsexamples/playground/src/app/theme/theme.tsxexamples/playground/translo.config.jsonexamples/playground/tsconfig.jsonpackages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsxpackages/vechain-kit/src/hooks/modals/index.tspackages/vechain-kit/src/hooks/modals/useSettingsModal.tsxpackages/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, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook 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 theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption 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: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions
Files:
examples/playground/src/app/components/features/ConnectionInfo/index.tsexamples/playground/src/app/components/features/FeaturesToTry/LanguageCard.tsxpackages/vechain-kit/src/hooks/modals/index.tsexamples/playground/src/app/theme/theme.tsxexamples/playground/src/app/components/features/AccountInfo/index.tsexamples/playground/src/app/components/features/ThemeToggle/ThemeToggle.tsxexamples/playground/src/app/theme/colors.tsexamples/playground/src/app/components/features/DaoInfo/DaoInfo.tsxexamples/playground/src/app/components/features/DataReading/index.tsexamples/playground/src/app/components/features/ConnectionInfo/ConnectionInfo.tsxexamples/playground/src/app/components/features/FeaturesToTry/ThemeCard.tsxexamples/playground/src/app/theme/modal.tsexamples/playground/src/app/components/features/AccountInfo/AccountInfo.tsxexamples/playground/src/app/components/features/SmartAccountInfo/SmartAccountInfo.tsxexamples/playground/src/app/components/ui/CollapsibleCard/index.tsexamples/playground/src/app/components/features/LanguageSelector/LanguageSelector.tsxexamples/playground/src/app/components/features/LoginUIControl/LoginUIControl.tsxexamples/playground/src/app/components/features/FeaturesToTry/FeatureCard.tsxexamples/playground/src/app/components/layout/Header/LanguageDropdown.tsxexamples/playground/src/app/layout.tsxexamples/playground/src/app/theme/button.tsexamples/playground/src/app/components/features/Signing/SigningExample.tsxexamples/playground/src/app/components/features/LoginToContinueBox/LoginToContinueBox.tsxexamples/playground/src/app/components/features/FeaturesToTry/FeaturesToTry.tsxexamples/playground/src/app/theme/index.tsexamples/playground/src/app/theme/card.tsexamples/playground/src/app/components/features/DaoInfo/index.tsexamples/playground/src/app/constants.tsexamples/playground/src/app/components/features/DataReading/DataReadingExample.tsxexamples/playground/src/app/page.tsxpackages/vechain-kit/src/hooks/modals/useWalletModal.tsxexamples/playground/src/app/components/features/FeaturesToTry/GithubCard.tsxexamples/playground/src/app/components/features/Signing/index.tsexamples/playground/src/app/components/features/TransactionExamples/TransactionExamples.tsxexamples/playground/src/app/components/features/ThemeToggle/index.tsexamples/playground/src/app/components/ui/CollapsibleCard/CollapsibleCard.tsxexamples/playground/src/app/components/features/FeaturesToTry/index.tsexamples/playground/src/app/pages/Home.tsxpackages/vechain-kit/src/hooks/modals/useSettingsModal.tsxpackages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsxexamples/playground/i18n.tsexamples/homepage/src/app/components/features/LanguagesSection/LanguagesSection.tsxexamples/homepage/src/app/layout.tsxexamples/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.tsexamples/playground/src/app/components/features/ConnectionInfo/ConnectionInfo.tsxexamples/playground/src/app/components/features/TransactionExamples/TransactionExamples.tsxexamples/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.tsexamples/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
| #For translations | ||
| OPENAI_API_KEY= No newline at end of file |
There was a problem hiding this comment.
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.
| #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).
| "react": "^18", | ||
| "react-dom": "^18", |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
| <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)', | ||
| }, | ||
| }, | ||
| }} |
There was a problem hiding this comment.
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.
| <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.
| { | ||
| title: t('Upgrade Smart Account'), | ||
| description: t('Upgrade your smart account to the latest version'), | ||
| icon: LuRefreshCw, | ||
| content: openUpgradeSmartAccountModal, | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find the FeaturesToTry component and examine the surrounding context
find . -type f -name "FeaturesToTry.tsx" | head -5Repository: 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 -160Repository: 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 -10Repository: vechain/vechain-kit
Length of output: 177
🏁 Script executed:
# Read the hook definition
cat -n ./packages/vechain-kit/src/hooks/modals/useUpgradeSmartAccountModal.tsxRepository: vechain/vechain-kit
Length of output: 796
🏁 Script executed:
# Check what UpgradeSmartAccountModalStyle is
rg -n "UpgradeSmartAccountModalStyle" --type ts -B 5 -A 5 | head -40Repository: vechain/vechain-kit
Length of output: 3478
🏁 Script executed:
# Find the UpgradeSmartAccountModalStyle definition
rg -n "type UpgradeSmartAccountModalStyle|interface UpgradeSmartAccountModalStyle" --type ts -A 10Repository: 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 -60Repository: 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 -80Repository: 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.tsxRepository: 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.tsxRepository: 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).
| <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> |
There was a problem hiding this comment.
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.
| <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.", |
There was a problem hiding this comment.
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.
| "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").
| 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]); |
There was a problem hiding this comment.
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.
| 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', | ||
| }, | ||
| }); |
There was a problem hiding this comment.
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.
| 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.
| //@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`, | ||
| }, |
There was a problem hiding this comment.
🛠️ 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.
There was a problem hiding this comment.
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.
| } catch { | ||
| // Silently handle errors - wallet state will update automatically on success | ||
| // Errors are handled by dapp-kit internally |
There was a problem hiding this comment.
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.
| } 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); |
| const handleDomainEdit = () => { | ||
| if (!setCurrentContent) return; |
There was a problem hiding this comment.
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.
Co-authored-by: Victor Emanuel <[email protected]>
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
examples/homepage/src/app/layout.tsx (2)
124-146: Safe usage ofdangerouslySetInnerHTML, but part of redundant enforcement.While static analysis flags the
dangerouslySetInnerHTMLusage, 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
ForceLightModecomponent,ColorModeScript(line 124), and the inline localStorage-clearing script (lines 125-146) represents three separate mechanisms to enforce light mode. The theme configuration already setsinitialColorMode: 'light'anduseSystemColorMode: false, which should be sufficient.Consider removing the
ForceLightModecomponent and the inline script, relying solely on the theme config andColorModeScript.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
examples/homepage/i18n.tsexamples/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, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook 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 theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption 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: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions
Files:
examples/homepage/src/app/layout.tsxexamples/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
customLanguageDetectoris 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 updatedtheme, 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
darkThemetothemealigns with the import changes and the theme configuration shown in the relevant snippets, which properly sets light mode defaults.
| // 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); | ||
| } | ||
| }, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
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.
| // 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.
New homepage + back up old homepage, will be useful later + changed vechainkit navigation + implemented switch wallet functionality
Summary by CodeRabbit
New Features
Improvements
Chores
✏️ Tip: You can customize this high-level summary in your review settings.