Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
ff04649
fix(native): disable CapacitorHttp proxy, move native auth to Authori…
innolope-dev Jul 15, 2026
200fcf9
chore(deps): add next-intl and @capacitor/device
innolope-dev Jul 16, 2026
687117f
fix(lint): resolve no-require-imports
innolope-dev Jul 16, 2026
1eb7c3c
fix(lint): resolve no-unused-vars
innolope-dev Jul 16, 2026
6e7445a
fix(lint): use next/link for internal links
innolope-dev Jul 16, 2026
1649927
feat(i18n): client-side next-intl foundation for the product UI
innolope-dev Jul 16, 2026
b6fb3cc
feat(i18n): language picker
innolope-dev Jul 16, 2026
0b0b5d8
chore: move request-pot status fix out of this branch
innolope-dev Jul 16, 2026
690ce23
feat(i18n): extract setup flow strings
innolope-dev Jul 16, 2026
5abd72a
feat(i18n): extract home and navigation strings
innolope-dev Jul 16, 2026
6708f77
feat(i18n): extract profile, settings, limits, exchange-rate strings
innolope-dev Jul 16, 2026
e99cf41
feat(i18n): extract send, request, payment and qr-pay strings
innolope-dev Jul 16, 2026
3b7db87
fix(i18n): translate limits warning card and passkey help content
innolope-dev Jul 16, 2026
348415e
feat(i18n): extract claim flow strings
innolope-dev Jul 16, 2026
2e24a6b
fix(claim): show real copy for IBAN cashout limit errors
innolope-dev Jul 16, 2026
3e90b88
feat(i18n): extract card strings
innolope-dev Jul 16, 2026
0cffa3e
feat(i18n): extract add-money and withdraw strings
innolope-dev Jul 16, 2026
3e3ec7f
feat(i18n): extract transaction details and history strings
innolope-dev Jul 16, 2026
f9d7b93
feat(i18n): extract rewards, invites, badges and notifications strings
innolope-dev Jul 16, 2026
be372d3
feat(i18n): extract kyc, identity and recover-funds strings
innolope-dev Jul 16, 2026
95e046d
feat(i18n): extract global, common and force-pwa-install strings
innolope-dev Jul 16, 2026
f6e50c8
fix(i18n): keep English apostrophes straight, as the source had them
innolope-dev Jul 16, 2026
f6c44f9
feat(i18n): extract error-handler copy to the errors namespace
innolope-dev Jul 16, 2026
bd1d2d8
feat(i18n): extract payments feature strings
innolope-dev Jul 16, 2026
83dab0d
feat(i18n): add jsx-no-literals guard for product-UI copy
innolope-dev Jul 17, 2026
60aa994
fix(i18n): hoist AppIntlProvider above ContextProvider
innolope-dev Jul 17, 2026
428c39a
fix(qr): replace developer jargon in the camera start failure
innolope-dev Jul 17, 2026
5323da8
chore(eslint): clear the 17-error tail of broken suppressions
innolope-dev Jul 17, 2026
6a21caa
chore(eslint): deep-import @/interfaces, drop the barrel
innolope-dev Jul 17, 2026
17c723b
chore(eslint): deep-import @/assets
innolope-dev Jul 17, 2026
5f2c82b
chore(eslint): deep-import @/context, @/components, @/config
innolope-dev Jul 17, 2026
743c097
chore(eslint): exempt the img sites where next/image cannot work
innolope-dev Jul 17, 2026
9d2f528
fix(layout): unobserve the footer node captured at effect setup
innolope-dev Jul 17, 2026
9f88af9
chore(eslint): add the exhaustive-deps that are provably no-ops
innolope-dev Jul 17, 2026
a2c07dc
fix(amount-input): recompute the conversion rate when a price changes
innolope-dev Jul 17, 2026
4bdbb18
chore(eslint): resolve the qr-pay exhaustive-deps
innolope-dev Jul 17, 2026
c32cc7a
chore(eslint): resolve the safe half of Initial.view's deps
innolope-dev Jul 17, 2026
1fd7429
chore(types): drop the react-force-graph shim, use the real types
innolope-dev Jul 17, 2026
b735294
chore(types): delete the dead ILinkDetails interface
innolope-dev Jul 17, 2026
6c61d8c
chore(types): declare window.Capacitor and navigator.standalone
innolope-dev Jul 17, 2026
de15d59
fix: resolve @next/next/no-img-element lint errors
innolope-dev Jul 17, 2026
6bf8adf
test: stop demo-api suite leaking viem RPC-ranking timers
innolope-dev Jul 17, 2026
996c9e0
Merge origin/main into chore/eslint-cleanup
innolope-dev Jul 17, 2026
e2d2631
chore(eslint): clear jsx-no-literals and scope no-explicit-any suppre…
innolope-dev Jul 17, 2026
58d5153
chore(types): eliminate the remaining no-explicit-any errors in produ…
innolope-dev Jul 17, 2026
c9b1c11
Merge origin/dev into chore/eslint-cleanup
innolope-dev Jul 17, 2026
437efef
merge dev: localize the copy dev added while this branch was in flight
innolope-dev Jul 22, 2026
73d90e2
Merge remote-tracking branch 'origin/dev' into chore/eslint-cleanup
kushagrasarathe Jul 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-app')
implementation project(':capacitor-browser')
implementation project(':capacitor-camera')
implementation project(':capacitor-clipboard')
implementation project(':capacitor-haptics')
implementation project(':capacitor-keyboard')
implementation project(':capacitor-preferences')
implementation project(':capacitor-splash-screen')
implementation project(':capacitor-status-bar')
implementation project(':capgo-capacitor-crisp')
Expand Down
6 changes: 6 additions & 0 deletions android/capacitor.settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacito
include ':capacitor-browser'
project(':capacitor-browser').projectDir = new File('../node_modules/.pnpm/@capacitor+browser@8.0.3_@capacitor+core@8.2.0/node_modules/@capacitor/browser/android')

include ':capacitor-camera'
project(':capacitor-camera').projectDir = new File('../node_modules/.pnpm/@capacitor+camera@8.2.0_@capacitor+core@8.2.0/node_modules/@capacitor/camera/android')

include ':capacitor-clipboard'
project(':capacitor-clipboard').projectDir = new File('../node_modules/.pnpm/@capacitor+clipboard@8.0.1_@capacitor+core@8.2.0/node_modules/@capacitor/clipboard/android')

Expand All @@ -17,6 +20,9 @@ project(':capacitor-haptics').projectDir = new File('../node_modules/.pnpm/@capa
include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../node_modules/.pnpm/@capacitor+keyboard@8.0.3_@capacitor+core@8.2.0/node_modules/@capacitor/keyboard/android')

include ':capacitor-preferences'
project(':capacitor-preferences').projectDir = new File('../node_modules/.pnpm/@capacitor+preferences@8.0.1_@capacitor+core@8.2.0/node_modules/@capacitor/preferences/android')

include ':capacitor-splash-screen'
project(':capacitor-splash-screen').projectDir = new File('../node_modules/.pnpm/@capacitor+splash-screen@8.0.1_@capacitor+core@8.2.0/node_modules/@capacitor/splash-screen/android')

Expand Down
7 changes: 6 additions & 1 deletion capacitor.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,13 @@ const config: CapacitorConfig = {
publicKey:
'-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAr0HzEca/1vuvWcJ8/xYB6tx0j4uJMzw/kT34GnjyMlRmLLUIO9sj\nroXaUGaNoqlOCx73b7Qgp10TLPOAVxmoHV9ZJ4BS9cMCl5mvzB4qIdl6FZLcl3g5\nk5Nkj4w22nskqbBqL7eqMXpk4DD9oWRclnaZC/lCpok1n2AWy4EMZrshemBQ6iXr\ncppo+WByPbqmh/GbHvJyRvkx4Rgt2LSBJBI3laP3eEDkujCq1ZH9qgcIE4MXO5xq\n7c6LsLjN5wkQiNPSPI81zAbqBThhqodKzwav0FwIE1pyiJeGk1nV5Ji5kUgpFNwY\nY78iDVq4OP2jPfWO4jXnnJtnGN7aeKDMEQIDAQAB\n-----END RSA PUBLIC KEY-----\n',
},
// CapacitorHttp OFF: its Android GET proxy (_capacitor_http_interceptor_)
// stalls under load, timing out every in-flight request (PEANUT-UI-R44).
// Requests go direct from the webview — CORS allows https://localhost and
// capacitor://, and auth rides the Authorization header from native
// Preferences (src/utils/auth-token.ts) instead of the plugin's cookie jar.
CapacitorHttp: {
enabled: true,
enabled: false,
},
CapacitorPasskey: {
// shim patches navigator.credentials.create/get so browser WebAuthn code works natively.
Expand Down
122 changes: 122 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ module.exports = [
'public/**',
'src/content/**',
'android/**',
'ios/**',
'build/**',
'src/types/api.generated.ts',
'coverage/**',
'playwright-report/**',
Expand Down Expand Up @@ -83,6 +85,8 @@ module.exports = [
'react/prop-types': 'off',
// Allow unescaped quotes — too noisy and prettier handles spacing
'react/no-unescaped-entities': 'off',
// `jsx`/`global` are styled-jsx's <style> attributes (built into Next), not DOM props.
'react/no-unknown-property': ['error', { ignore: ['jsx', 'global'] }],

// Ban barrel imports — see BANNED_BARREL_PATHS above.
'no-restricted-imports': [
Expand Down Expand Up @@ -157,4 +161,122 @@ module.exports = [
files: ['src/components/Profile/components/PublicProfile.tsx'],
rules: { 'no-restricted-syntax': 'off' },
},
{
// require() inside test bodies is the Jest idiom for reading mocks after
// jest.mock()/resetModules(); hoisting them to imports changes semantics.
// no-img-element: these files mock next/image down to a raw <img>.
// no-explicit-any: mocks and partial fixtures legitimately cast through
// `any` — production code keeps the ban.
files: ['src/**/__tests__/**/*.{ts,tsx}', 'src/**/*.test.{ts,tsx}', 'src/**/__mocks__/**/*.{ts,tsx}'],
rules: {
'@typescript-eslint/no-require-imports': 'off',
'@next/next/no-img-element': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
},
{
// Dev-only tooling: /dev pages, the window.debug console cheats, and the
// InvitesGraph debug visualization. The cheat API is intrinsically dynamic
// and d3/force-graph mutate node objects at runtime — typing them buys no
// user-facing safety. Production code keeps the any ban.
files: ['src/app/(mobile-ui)/dev/**', 'src/context/PeanutDebug.tsx', 'src/components/Global/InvitesGraph/**'],
rules: { '@typescript-eslint/no-explicit-any': 'off' },
},
{
// OG images render through Satori (next/og ImageResponse), which supports
// only a subset of HTML/CSS and cannot render next/image — raw <img> with
// explicit width/height is the required form here, not an oversight.
files: ['src/components/og/**', 'src/app/api/og/**'],
rules: { '@next/next/no-img-element': 'off' },
},
{
// Rasterized to PNG by html-to-image (see share-asset/captureShareAsset.ts).
// next/image's lazy loading and wrapper markup break the capture — the same
// class of bug as the runtime <canvas> that file already documents.
files: ['src/components/Card/share-asset/**', 'src/components/Global/ImageGeneration/**'],
rules: { '@next/next/no-img-element': 'off' },
},
{
// Localization guard: product-UI copy must come from next-intl, not JSX
// literals. Scoped to the translated surface — marketing (its own i18n),
// the /dev design-system catalog, and shared primitives that receive copy
// as props are excluded. allowedStrings covers the symbols/masks that are
// not translatable copy (card masks, %, currency glyphs, arrows).
files: [
'src/app/(mobile-ui)/**/*.tsx',
'src/app/(setup)/**/*.tsx',
'src/components/{Home,Send,Request,Profile,Setup,Settings,Card,AddMoney,AddWithdraw,Withdraw,Claim,Payment,Points,Badges,Notifications,Invites,TransactionDetails,Kyc,IdentityVerification,ExchangeRate,Common,ForceIOSPWAInstall,User}/**/*.tsx',
'src/components/Global/**/*.tsx',
'src/features/**/*.tsx',
],
ignores: [
'src/app/(mobile-ui)/dev/**',
'src/**/__tests__/**',
'src/**/*.test.tsx',
// Marketing-shared Global components render on marketing pages (whose
// locale comes from the URL, not the app context) — they keep English
// and take any product-UI copy as props. FAQs/ExchangeRateWidget are
// imported by LandingPage and Marketing/mdx; Loading is a spinner
// fallback reached through the shared 0_Bruddle/Button.
'src/components/Global/{Layout,AnimateOnView,MarqueeWrapper,FAQs,FooterVisibilityObserver,ExchangeRateWidget,Modal,Loading}/**',
'src/components/Global/{Layout,AnimateOnView,MarqueeWrapper,FAQs,FooterVisibilityObserver,ExchangeRateWidget,Modal,Loading}.tsx',
'src/components/Global/{PeanutLoading,Icons,Badges}/**',
// InvitesGraph is a /dev-only debug visualization, not user-facing UI.
'src/components/Global/InvitesGraph/**',
// Hidden support tool — never linked in-app; support DMs the URL to
// affected users, so the copy stays English-only.
'src/app/(mobile-ui)/fix-card-signature/**',
],
rules: {
'react/jsx-no-literals': [
'error',
{
noStrings: false,
ignoreProps: true,
allowedStrings: [
'•',
'·',
'%',
'$',
'(',
')',
'-',
'/',
':',
'#',
'+',
'×',
'→',
'←',
',',
'.',
'*',
'≈',
'≈ $',
'USD',
'R$',
'EVM',
'Solana',
'Tron',
// non-copy glyphs: card-number masks, percentages, decorative
// emoji, amount prefixes, and the brand URL stem
'****',
'••••',
'????',
'???? ???? ???? ????',
'??/??',
'100%',
'0%',
'120%',
'+$',
'✨',
'⭐',
'peanut.me/',
'i',
'version:',
],
},
],
},
},
]
8 changes: 8 additions & 0 deletions ios/App/CapApp-SPM/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ let package = Package(
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "8.2.0"),
.package(name: "CapacitorApp", path: "../../../node_modules/.pnpm/@capacitor+app@8.1.0_@capacitor+core@8.2.0/node_modules/@capacitor/app"),
.package(name: "CapacitorBrowser", path: "../../../node_modules/.pnpm/@capacitor+browser@8.0.3_@capacitor+core@8.2.0/node_modules/@capacitor/browser"),
.package(name: "CapacitorCamera", path: "../../../node_modules/.pnpm/@capacitor+camera@8.2.0_@capacitor+core@8.2.0/node_modules/@capacitor/camera"),
.package(name: "CapacitorClipboard", path: "../../../node_modules/.pnpm/@capacitor+clipboard@8.0.1_@capacitor+core@8.2.0/node_modules/@capacitor/clipboard"),
.package(name: "CapacitorDevice", path: "../../../node_modules/.pnpm/@capacitor+device@8.0.2_@capacitor+core@8.2.0/node_modules/@capacitor/device"),
.package(name: "CapacitorHaptics", path: "../../../node_modules/.pnpm/@capacitor+haptics@8.0.2_@capacitor+core@8.2.0/node_modules/@capacitor/haptics"),
.package(name: "CapacitorKeyboard", path: "../../../node_modules/.pnpm/@capacitor+keyboard@8.0.3_@capacitor+core@8.2.0/node_modules/@capacitor/keyboard"),
.package(name: "CapacitorPreferences", path: "../../../node_modules/.pnpm/@capacitor+preferences@8.0.1_@capacitor+core@8.2.0/node_modules/@capacitor/preferences"),
.package(name: "CapacitorSplashScreen", path: "../../../node_modules/.pnpm/@capacitor+splash-screen@8.0.1_@capacitor+core@8.2.0/node_modules/@capacitor/splash-screen"),
.package(name: "CapacitorStatusBar", path: "../../../node_modules/.pnpm/@capacitor+status-bar@8.0.2_@capacitor+core@8.2.0/node_modules/@capacitor/status-bar"),
.package(name: "CapgoCapacitorCrisp", path: "../../../node_modules/.pnpm/@capgo+capacitor-crisp@8.0.27_@capacitor+core@8.2.0/node_modules/@capgo/capacitor-crisp"),
Expand All @@ -32,8 +36,12 @@ let package = Package(
.product(name: "Cordova", package: "capacitor-swift-pm"),
.product(name: "CapacitorApp", package: "CapacitorApp"),
.product(name: "CapacitorBrowser", package: "CapacitorBrowser"),
.product(name: "CapacitorCamera", package: "CapacitorCamera"),
.product(name: "CapacitorClipboard", package: "CapacitorClipboard"),
.product(name: "CapacitorDevice", package: "CapacitorDevice"),
.product(name: "CapacitorHaptics", package: "CapacitorHaptics"),
.product(name: "CapacitorKeyboard", package: "CapacitorKeyboard"),
.product(name: "CapacitorPreferences", package: "CapacitorPreferences"),
.product(name: "CapacitorSplashScreen", package: "CapacitorSplashScreen"),
.product(name: "CapacitorStatusBar", package: "CapacitorStatusBar"),
.product(name: "CapgoCapacitorCrisp", package: "CapgoCapacitorCrisp"),
Expand Down
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@
"@capacitor/cli": "8.2.0",
"@capacitor/clipboard": "^8.0.1",
"@capacitor/core": "8.2.0",
"@capacitor/device": "^8.0.2",
"@capacitor/haptics": "^8.0.2",
"@capacitor/ios": "8.2.0",
"@capacitor/keyboard": "^8.0.3",
"@capacitor/preferences": "^8.0.1",
"@capacitor/splash-screen": "^8.0.1",
"@capacitor/status-bar": "^8.0.2",
"@capgo/capacitor-crisp": "^8.0.27",
Expand Down Expand Up @@ -98,6 +100,7 @@
"js-cookie": "^3.0.5",
"lucide-react": "^1.8.0",
"next": "16.2.3",
"next-intl": "^4.13.1",
"next-mdx-remote": "^6.0.0",
"nuqs": "^2.8.6",
"p2-es": "1.2.3",
Expand Down Expand Up @@ -136,6 +139,7 @@
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^16.1.0",
"@types/canvas-confetti": "^1.9.0",
"@types/d3-force": "^3.0.10",
"@types/jest": "^29.5.12",
"@types/js-cookie": "^3.0.6",
"@types/node": "20.4.2",
Expand Down Expand Up @@ -178,10 +182,17 @@
"tsconfig": "tsconfig.test.json",
"useESM": true
}
],
"^.+\\.(js|mjs)$": [
"ts-jest",
{
"tsconfig": "tsconfig.test.json",
"isolatedModules": true
}
]
},
"transformIgnorePatterns": [
"node_modules/(?!(@wagmi|wagmi|viem|@viem|@walletconnect|@justaname\\.id|@zerodev|permissionless)/)"
"node_modules/(?!(\\.pnpm/)?(next-intl|use-intl|@formatjs|intl-messageformat)([@+/]|$))"
],
"moduleNameMapper": {
"\\.(svg|png|jpg|jpeg|gif|webp)$": "<rootDir>/src/utils/__mocks__/static-image.ts",
Expand Down
Loading
Loading