@b3dotfun/sdk 0.0.65-test.3 → 0.0.65-test.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.js +1 -1
  2. package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +1 -1
  3. package/dist/cjs/anyspend/react/components/common/PanelOnrampPayment.js +1 -1
  4. package/dist/cjs/global-account/react/components/AvatarEditor/AvatarEditor.d.ts +1 -0
  5. package/dist/cjs/global-account/react/components/AvatarEditor/AvatarEditor.js +71 -4
  6. package/dist/cjs/global-account/react/components/B3DynamicModal.js +1 -1
  7. package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.js +1 -1
  8. package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +1 -1
  9. package/dist/cjs/global-account/react/components/ManageAccount/Header.js +2 -2
  10. package/dist/cjs/global-account/react/components/ManageAccount/HomeActions.js +2 -2
  11. package/dist/cjs/global-account/react/components/ManageAccount/HomeContent.js +1 -1
  12. package/dist/cjs/global-account/react/components/ManageAccount/ProfileSection.js +1 -1
  13. package/dist/cjs/global-account/react/components/ManageAccount/SettingsContent.js +1 -1
  14. package/dist/cjs/global-account/react/components/ManageAccount/SettingsMenuItem.js +1 -1
  15. package/dist/cjs/global-account/react/components/ManageAccount/SettingsProfileCard.js +1 -1
  16. package/dist/cjs/global-account/react/components/ModalHeader/ModalHeader.d.ts +2 -1
  17. package/dist/cjs/global-account/react/components/ModalHeader/ModalHeader.js +2 -2
  18. package/dist/cjs/global-account/react/components/ui/dialog.js +1 -1
  19. package/dist/cjs/global-account/react/components/ui/drawer.js +1 -1
  20. package/dist/esm/anyspend/react/components/AnySpend.js +1 -1
  21. package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +1 -1
  22. package/dist/esm/anyspend/react/components/common/PanelOnrampPayment.js +1 -1
  23. package/dist/esm/global-account/react/components/AvatarEditor/AvatarEditor.d.ts +1 -0
  24. package/dist/esm/global-account/react/components/AvatarEditor/AvatarEditor.js +72 -5
  25. package/dist/esm/global-account/react/components/B3DynamicModal.js +1 -1
  26. package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.js +1 -1
  27. package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +1 -1
  28. package/dist/esm/global-account/react/components/ManageAccount/Header.js +2 -2
  29. package/dist/esm/global-account/react/components/ManageAccount/HomeActions.js +2 -2
  30. package/dist/esm/global-account/react/components/ManageAccount/HomeContent.js +1 -1
  31. package/dist/esm/global-account/react/components/ManageAccount/ProfileSection.js +1 -1
  32. package/dist/esm/global-account/react/components/ManageAccount/SettingsContent.js +1 -1
  33. package/dist/esm/global-account/react/components/ManageAccount/SettingsMenuItem.js +1 -1
  34. package/dist/esm/global-account/react/components/ManageAccount/SettingsProfileCard.js +1 -1
  35. package/dist/esm/global-account/react/components/ModalHeader/ModalHeader.d.ts +2 -1
  36. package/dist/esm/global-account/react/components/ModalHeader/ModalHeader.js +3 -3
  37. package/dist/esm/global-account/react/components/ui/dialog.js +1 -1
  38. package/dist/esm/global-account/react/components/ui/drawer.js +1 -1
  39. package/dist/styles/index.css +1 -1
  40. package/dist/types/global-account/react/components/AvatarEditor/AvatarEditor.d.ts +1 -0
  41. package/dist/types/global-account/react/components/ModalHeader/ModalHeader.d.ts +2 -1
  42. package/package.json +2 -1
  43. package/src/anyspend/react/components/AnySpend.tsx +1 -1
  44. package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +1 -1
  45. package/src/anyspend/react/components/common/PanelOnrampPayment.tsx +1 -1
  46. package/src/global-account/react/components/AvatarEditor/AvatarEditor.tsx +123 -6
  47. package/src/global-account/react/components/B3DynamicModal.tsx +1 -1
  48. package/src/global-account/react/components/LinkAccount/LinkAccount.tsx +1 -1
  49. package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +1 -1
  50. package/src/global-account/react/components/ManageAccount/Header.tsx +9 -9
  51. package/src/global-account/react/components/ManageAccount/HomeActions.tsx +2 -2
  52. package/src/global-account/react/components/ManageAccount/HomeContent.tsx +2 -2
  53. package/src/global-account/react/components/ManageAccount/ProfileSection.tsx +9 -2
  54. package/src/global-account/react/components/ManageAccount/SettingsContent.tsx +2 -2
  55. package/src/global-account/react/components/ManageAccount/SettingsMenuItem.tsx +1 -1
  56. package/src/global-account/react/components/ManageAccount/SettingsProfileCard.tsx +3 -1
  57. package/src/global-account/react/components/ModalHeader/ModalHeader.tsx +21 -10
  58. package/src/global-account/react/components/ui/dialog.tsx +2 -2
  59. package/src/global-account/react/components/ui/drawer.tsx +1 -1
@@ -7,7 +7,10 @@ import { cn } from "@b3dotfun/sdk/shared/utils/cn";
7
7
  import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
8
8
  import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
9
9
  import { Loader2, Upload, X } from "lucide-react";
10
- import { useRef, useState } from "react";
10
+ import { useCallback, useRef, useState } from "react";
11
+ import type { Area } from "react-easy-crop";
12
+ import Cropper from "react-easy-crop";
13
+ import "react-easy-crop/react-easy-crop.css";
11
14
  import { toast } from "sonner";
12
15
  import { useActiveAccount } from "thirdweb/react";
13
16
  import { upload } from "thirdweb/storage";
@@ -17,6 +20,16 @@ import ModalHeader from "../ModalHeader/ModalHeader";
17
20
 
18
21
  const debug = debugB3React("AvatarEditor");
19
22
 
23
+ // Helper function to create an image element from a URL
24
+ const createImage = (url: string): Promise<HTMLImageElement> =>
25
+ new Promise((resolve, reject) => {
26
+ const image = new Image();
27
+ image.addEventListener("load", () => resolve(image));
28
+ image.addEventListener("error", error => reject(error));
29
+ image.setAttribute("crossOrigin", "anonymous");
30
+ image.src = url;
31
+ });
32
+
20
33
  interface AvatarEditorProps {
21
34
  onSetAvatar?: () => void;
22
35
  className?: string;
@@ -33,6 +46,9 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
33
46
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
34
47
  const [isSaving, setIsSaving] = useState(false);
35
48
  const [isDragging, setIsDragging] = useState(false);
49
+ const [crop, setCrop] = useState({ x: 0, y: 0 });
50
+ const [zoom, setZoom] = useState(1);
51
+ const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
36
52
  const fileInputRef = useRef<HTMLInputElement>(null);
37
53
  const { setUser, user, partnerId } = useB3();
38
54
  const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
@@ -50,6 +66,48 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
50
66
  const currentAvatar = validateImageUrl(rawCurrentAvatar);
51
67
  const safePreviewUrl = validateImageUrl(previewUrl);
52
68
 
69
+ const onCropComplete = useCallback((_croppedArea: Area, croppedAreaPixels: Area) => {
70
+ setCroppedAreaPixels(croppedAreaPixels);
71
+ }, []);
72
+
73
+ const createCroppedImage = async (imageSrc: string, pixelCrop: Area): Promise<Blob> => {
74
+ const image = await createImage(imageSrc);
75
+ const canvas = document.createElement("canvas");
76
+ const ctx = canvas.getContext("2d");
77
+
78
+ if (!ctx) {
79
+ throw new Error("Failed to get canvas context");
80
+ }
81
+
82
+ // Set canvas size to the crop area
83
+ canvas.width = pixelCrop.width;
84
+ canvas.height = pixelCrop.height;
85
+
86
+ // Draw the cropped image
87
+ ctx.drawImage(
88
+ image,
89
+ pixelCrop.x,
90
+ pixelCrop.y,
91
+ pixelCrop.width,
92
+ pixelCrop.height,
93
+ 0,
94
+ 0,
95
+ pixelCrop.width,
96
+ pixelCrop.height,
97
+ );
98
+
99
+ // Return as blob
100
+ return new Promise((resolve, reject) => {
101
+ canvas.toBlob(blob => {
102
+ if (!blob) {
103
+ reject(new Error("Canvas is empty"));
104
+ return;
105
+ }
106
+ resolve(blob);
107
+ }, "image/jpeg");
108
+ });
109
+ };
110
+
53
111
  const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
54
112
  const file = event.target.files?.[0];
55
113
  if (file) {
@@ -87,6 +145,10 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
87
145
  if (fileInputRef.current) {
88
146
  fileInputRef.current.value = "";
89
147
  }
148
+ // Reset crop state
149
+ setCrop({ x: 0, y: 0 });
150
+ setZoom(1);
151
+ setCroppedAreaPixels(null);
90
152
  };
91
153
 
92
154
  const handleSaveChanges = async () => {
@@ -99,8 +161,20 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
99
161
  try {
100
162
  let fileToUpload: File | null = null;
101
163
 
102
- // If user uploaded a new file
103
- if (selectedFile) {
164
+ // If user uploaded a new file and cropped it
165
+ if (selectedFile && previewUrl && croppedAreaPixels) {
166
+ try {
167
+ const croppedBlob = await createCroppedImage(previewUrl, croppedAreaPixels);
168
+ const extension = selectedFile.name.split(".").pop() || "jpg";
169
+ fileToUpload = new File([croppedBlob], `avatar-cropped.${extension}`, { type: "image/jpeg" });
170
+ } catch (error) {
171
+ debug("Error cropping image:", error);
172
+ toast.error("Failed to crop image. Please try again.");
173
+ setIsSaving(false);
174
+ return;
175
+ }
176
+ } else if (selectedFile) {
177
+ // Fallback if no crop was made
104
178
  fileToUpload = selectedFile;
105
179
  } else if (selectedProfileType && selectedAvatar) {
106
180
  // User selected from existing profile avatars
@@ -319,7 +393,7 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
319
393
  {/* Upload Image Button */}
320
394
  <button
321
395
  onClick={handleUploadImageClick}
322
- className="font-inter shadow-xs mb-6 flex w-full items-center justify-center gap-2 rounded-xl border border-[#e4e4e7] bg-white px-4 py-3 text-sm font-semibold text-[#18181b] transition-colors hover:bg-[#f4f4f5]"
396
+ className="font-inter mb-6 flex w-full items-center justify-center gap-2 rounded-xl border border-[#e4e4e7] bg-white px-4 py-3 text-sm font-semibold text-[#18181b] shadow-sm transition-colors hover:bg-[#f4f4f5]"
323
397
  >
324
398
  <Upload className="h-4 w-4" />
325
399
  Upload image
@@ -419,13 +493,56 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
419
493
  </div>
420
494
  ) : (
421
495
  <div className="mb-6 w-full">
422
- <div className="aspect-square w-full overflow-hidden rounded-xl bg-[#f4f4f5]">
496
+ <div className="relative aspect-square w-full overflow-hidden rounded-xl bg-[#f4f4f5]">
423
497
  {safePreviewUrl ? (
424
- <img src={safePreviewUrl} alt="Preview" className="h-full w-full object-cover" />
498
+ <>
499
+ <Cropper
500
+ image={safePreviewUrl}
501
+ crop={crop}
502
+ zoom={zoom}
503
+ aspect={1}
504
+ onCropChange={setCrop}
505
+ onCropComplete={onCropComplete}
506
+ onZoomChange={setZoom}
507
+ cropShape="rect"
508
+ showGrid={false}
509
+ style={{
510
+ containerStyle: {
511
+ width: "100%",
512
+ height: "100%",
513
+ backgroundColor: "#f4f4f5",
514
+ },
515
+ cropAreaStyle: {
516
+ border: "2px solid #3368ef",
517
+ borderRadius: "0px",
518
+ },
519
+ }}
520
+ />
521
+ <button
522
+ onClick={handleRemovePreview}
523
+ className="absolute right-4 top-4 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-[#51525c] text-white transition-colors hover:bg-[#71717a]"
524
+ >
525
+ <X className="h-4 w-4" />
526
+ </button>
527
+ </>
425
528
  ) : (
426
529
  <div className="bg-b3-primary-wash h-full w-full" />
427
530
  )}
428
531
  </div>
532
+ {safePreviewUrl && (
533
+ <div className="mt-4 flex items-center gap-3">
534
+ <label className="flex-shrink-0 text-sm font-semibold text-[#475467]">Zoom</label>
535
+ <input
536
+ type="range"
537
+ min={1}
538
+ max={3}
539
+ step={0.1}
540
+ value={zoom}
541
+ onChange={e => setZoom(Number(e.target.value))}
542
+ className="flex-1 accent-[#3368ef]"
543
+ />
544
+ </div>
545
+ )}
429
546
  </div>
430
547
  )}
431
548
  </>
@@ -186,7 +186,7 @@ export function B3DynamicModal() {
186
186
  <ModalDescription className="sr-only hidden">{contentType?.type || "Modal Body"}</ModalDescription>
187
187
 
188
188
  <div className={cn("no-scrollbar max-h-[90dvh] overflow-auto sm:max-h-[80dvh]")}>
189
- {(!hideCloseButton || contentType?.showBackButton) && (
189
+ {!hideCloseButton && (
190
190
  <button
191
191
  onClick={navigateBack}
192
192
  className="flex items-center gap-2 px-6 py-4 text-gray-600 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
@@ -249,7 +249,7 @@ export const LinkAccount = ({
249
249
  <Button
250
250
  onClick={handleOpenLinkModal}
251
251
  disabled={isLinking}
252
- className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 border-white/12 group relative h-12 w-full rounded-xl border-2 px-[18px] text-base font-semibold text-white shadow-[inset_0px_0px_0px_1px_rgba(10,13,18,0.18),inset_0px_-2px_0px_0px_rgba(10,13,18,0.05)] transition-all"
252
+ className="b3-modal-link-new-account-button bg-b3-primary-blue hover:bg-b3-primary-blue/90 border-white/12 group relative h-12 w-full rounded-xl border-2 px-[18px] text-base font-semibold text-white shadow-[inset_0px_0px_0px_1px_rgba(10,13,18,0.18),inset_0px_-2px_0px_0px_rgba(10,13,18,0.05)] transition-all"
253
253
  >
254
254
  <div className="flex items-center justify-center gap-1.5">
255
255
  <svg
@@ -44,7 +44,7 @@ const BottomNavigation = () => {
44
44
  const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
45
45
 
46
46
  return (
47
- <div className="sticky bottom-0 left-0 w-full rounded-b-xl border-t border-gray-200 bg-[#FAFAFA]">
47
+ <div className="b3-modal-bottom-navigation sticky bottom-0 left-0 w-full rounded-b-xl border-t border-gray-200 bg-[#FAFAFA]">
48
48
  <TabsListPrimitive className="flex h-[68px] w-full items-center justify-center gap-4 border-none bg-transparent">
49
49
  <TabTriggerPrimitive
50
50
  value="home"
@@ -58,9 +58,9 @@ function WalletItem({ wallet, isActive, onClick }: { wallet: Wallet; isActive: b
58
58
 
59
59
  return (
60
60
  <div
61
- className={`box-border flex cursor-pointer items-center gap-2 rounded-xl px-3 py-2 transition-colors ${
62
- isActive ? "bg-[#F4F4F5]" : "hover:bg-b3-line/50"
63
- }`}
61
+ className={`b3-modal-wallet-item ${
62
+ isActive ? "b3-modal-wallet-item-active bg-[#F4F4F5]" : "hover:bg-b3-line/50"
63
+ } box-border flex cursor-pointer items-center gap-2 rounded-xl px-3 py-2 transition-colors`}
64
64
  onClick={onClick}
65
65
  >
66
66
  <div className="relative size-10 shrink-0 overflow-clip rounded-full">
@@ -137,11 +137,11 @@ export function Header({ onLogout }: { onLogout?: () => void }) {
137
137
  type="single"
138
138
  collapsible
139
139
  value={isExpanded ? "wallet-switcher" : ""}
140
- className="bg-b3-background border-b3-line relative border-b"
140
+ className="b3-modal-wallet-switcher bg-b3-background border-b3-line relative border-b"
141
141
  onValueChange={(value: string) => setIsExpanded(value === "wallet-switcher")}
142
142
  >
143
143
  <AccordionPrimitive.Item value="wallet-switcher" className="border-none">
144
- <AccordionPrimitive.Trigger className="group flex w-full items-center justify-between border-none bg-transparent px-5 py-3 outline-none">
144
+ <AccordionPrimitive.Trigger className="b3-modal-wallet-switcher-trigger group flex w-full items-center justify-between border-none bg-transparent px-5 py-3 outline-none">
145
145
  <div className="flex items-center gap-2">
146
146
  <div className="relative size-10 shrink-0 overflow-clip rounded-full">
147
147
  {isActiveGlobalAccount ? (
@@ -166,7 +166,7 @@ export function Header({ onLogout }: { onLogout?: () => void }) {
166
166
  </div>
167
167
  </div>
168
168
  </div>
169
- <ChevronDownIcon className="text-b3-grey transition-transform duration-200 group-data-[state=open]:rotate-180" />
169
+ <ChevronDownIcon className="b3-modal-wallet-switcher-chevron text-b3-grey transition-transform duration-200 group-data-[state=open]:rotate-180" />
170
170
  </AccordionPrimitive.Trigger>
171
171
 
172
172
  <AnimatePresence>
@@ -177,7 +177,7 @@ export function Header({ onLogout }: { onLogout?: () => void }) {
177
177
  animate={{ opacity: 1, y: 0 }}
178
178
  exit={{ opacity: 0, y: -10 }}
179
179
  transition={{ duration: 0.2, ease: "easeInOut" }}
180
- className="flex flex-col gap-3 rounded-bl-3xl rounded-br-3xl bg-white px-2 pb-5 pt-3 shadow-[0px_32px_64px_-12px_rgba(10,13,18,0.14),0px_5px_5px_-2.5px_rgba(10,13,18,0.04)]"
180
+ className="b3-modal-wallet-switcher-content flex flex-col gap-3 rounded-bl-3xl rounded-br-3xl bg-white px-2 pb-5 pt-3 shadow-[0px_32px_64px_-12px_rgba(10,13,18,0.14),0px_5px_5px_-2.5px_rgba(10,13,18,0.04)]"
181
181
  >
182
182
  {/* Connected Wallets */}
183
183
  <div className="flex flex-col gap-3">
@@ -192,7 +192,7 @@ export function Header({ onLogout }: { onLogout?: () => void }) {
192
192
 
193
193
  {/* Link Another Wallet */}
194
194
  <div
195
- className="hover:bg-b3-line/50 box-border flex cursor-pointer items-center gap-2 rounded-xl px-3 py-2 transition-colors"
195
+ className="b3-modal-link-another-wallet hover:bg-b3-line/50 box-border flex cursor-pointer items-center gap-2 rounded-xl px-3 py-2 transition-colors"
196
196
  onClick={handleLinkWallet}
197
197
  >
198
198
  <div className="bg-b3-line flex size-10 shrink-0 items-center justify-center rounded-full">
@@ -206,7 +206,7 @@ export function Header({ onLogout }: { onLogout?: () => void }) {
206
206
 
207
207
  {/* Sign Out Button */}
208
208
  <button
209
- className="border-b3-line hover:bg-b3-line bg-b3-background flex items-center justify-center gap-1.5 rounded-xl border border-solid p-2.5 transition-colors"
209
+ className="b3-modal-sign-out-button border-b3-line hover:bg-b3-line bg-b3-background flex items-center justify-center gap-1.5 rounded-xl border border-solid p-2.5 transition-colors"
210
210
  onClick={onLogoutEnhanced}
211
211
  disabled={logoutLoading}
212
212
  style={{
@@ -41,7 +41,7 @@ const HomeActionButton = ({
41
41
  return (
42
42
  <Button
43
43
  className={cn(
44
- "border-b3-line hover:border-b3-primary-blue shadow-xs flex h-[84px] w-full flex-col items-center justify-center gap-2 rounded-2xl border-[1.5px] bg-[#FAFAFA] hover:bg-[#FAFAFA]",
44
+ "border-b3-line hover:border-b3-primary-blue flex h-[84px] w-full flex-col items-center justify-center gap-2 rounded-2xl border-[1.5px] bg-[#FAFAFA] shadow-[0_0_0_1px_rgba(10,13,18,0.18)_inset,0_-2px_0_0_rgba(10,13,18,0.05)_inset,0_1px_2px_0_rgba(10,13,18,0.05)] hover:bg-[#FAFAFA]",
45
45
  customClass,
46
46
  )}
47
47
  onClick={onClick}
@@ -57,7 +57,7 @@ const HomeActions = ({ showDeposit, showSwap }: { showDeposit: boolean; showSwap
57
57
  const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
58
58
 
59
59
  return (
60
- <div className="border-b3-line grid grid-cols-4 gap-3 border-b px-5 pb-6">
60
+ <div className="b3-modal-home-actions border-b3-line grid grid-cols-4 gap-3 border-b px-5 pb-6">
61
61
  {showDeposit && (
62
62
  <HomeActionButton
63
63
  customClass="manage-account-deposit"
@@ -18,9 +18,9 @@ export function HomeContent({ showDeposit = false, showSwap = true }: HomeConten
18
18
  <ProfileSection />
19
19
 
20
20
  <HomeActions showDeposit={showDeposit} showSwap={showSwap} />
21
- <div className="space-y-2 p-5">
21
+ <div className="b3-modal-balance-content space-y-2 p-5">
22
22
  <Tabs defaultValue={"balance"}>
23
- <TabsList>
23
+ <TabsList className="b3-modal-balance-tabs-list">
24
24
  <TabTrigger value="balance" className="font-neue-montreal-semibold p-0 pr-3">
25
25
  Balance
26
26
  </TabTrigger>
@@ -49,7 +49,12 @@ const ProfileSection = () => {
49
49
  <div className="flex items-center justify-between px-5 py-6">
50
50
  <div className="global-account-profile flex items-center gap-4">
51
51
  <div className="global-account-profile-avatar relative">
52
- <IPFSMediaRenderer src={avatarSrc} alt="Profile Avatar" className="size-14 rounded-full" />
52
+ <IPFSMediaRenderer
53
+ src={avatarSrc}
54
+ alt="Profile Avatar"
55
+ className="border-b3-line border-1 bg-b3-primary-wash size-14 rounded-full border"
56
+ />
57
+
53
58
  <button
54
59
  onClick={handleEditAvatar}
55
60
  className="border-b3-background hover:bg-b3-grey/80 absolute -bottom-1 -right-1 flex size-6 items-center justify-center rounded-full border-4 bg-[#a0a0ab] transition-colors"
@@ -62,7 +67,9 @@ const ProfileSection = () => {
62
67
  <div className="text-b3-foreground-muted"> $</div>
63
68
  <div className="text-[30px]">{formatDisplayNumber(totalBalanceUsd, { fractionDigits: 2 })}</div>
64
69
  </h2>
65
- <div className="font-neue-montreal-semibold text-base leading-none text-[#0B57C2]">{currentUsername}</div>
70
+ <div className="b3-modal-username font-neue-montreal-semibold text-base leading-none text-[#0B57C2]">
71
+ {currentUsername}
72
+ </div>
66
73
  </div>
67
74
  </div>
68
75
  </div>
@@ -50,11 +50,11 @@ const SettingsContent = ({
50
50
 
51
51
  return (
52
52
  <div className="flex h-[470px] flex-col">
53
- <ModalHeader title="Settings" />
53
+ <ModalHeader showBackButton={false} showCloseButton={false} title="Settings" />
54
54
 
55
55
  {/* Profile Section */}
56
56
  <div className="p-5">
57
- <div className="flex items-center rounded-xl border border-[#e4e4e7] bg-[#f4f4f5] p-4">
57
+ <div className="b3-modal-settings-profile-card flex items-center rounded-xl border border-[#e4e4e7] bg-[#f4f4f5] p-4">
58
58
  <SettingsProfileCard />
59
59
  </div>
60
60
  </div>
@@ -12,7 +12,7 @@ const SettingsMenuItem = ({ icon, title, subtitle, onClick }: SettingsMenuItemPr
12
12
  return (
13
13
  <button
14
14
  onClick={onClick}
15
- className="flex w-full items-center gap-2 rounded-xl px-3 py-2 transition-colors hover:bg-[#f4f4f5]"
15
+ className="b3-modal-settings-menu-item flex w-full items-center gap-2 rounded-xl px-3 py-2 transition-colors hover:bg-[#f4f4f5]"
16
16
  >
17
17
  <div className="flex size-10 items-center justify-center rounded-full bg-[#f4f4f5]">{icon}</div>
18
18
  <div className="flex flex-1 flex-col items-start gap-1 text-left">
@@ -162,7 +162,9 @@ const SettingsProfileCard = () => {
162
162
  /* Display mode */
163
163
  <>
164
164
  <div className="flex items-center gap-1">
165
- <p className="font-neue-montreal-semibold text-lg leading-none text-[#0B57C2]">{currentUsername}</p>
165
+ <p className="b3-modal-username font-neue-montreal-semibold text-lg leading-none text-[#0B57C2]">
166
+ {currentUsername}
167
+ </p>
166
168
  </div>
167
169
  <button
168
170
  onClick={handleEditUsername}
@@ -1,8 +1,9 @@
1
1
  import { cn } from "@b3dotfun/sdk/shared/utils";
2
- import { ChevronDown, X } from "lucide-react";
2
+ import { ChevronLeft, X } from "lucide-react";
3
3
  import { useModalStore } from "../../stores";
4
4
 
5
5
  const ModalHeader = ({
6
+ showBackButton = true,
6
7
  handleBack,
7
8
  handleClose,
8
9
  title,
@@ -11,6 +12,7 @@ const ModalHeader = ({
11
12
  className,
12
13
  showBackWord = false,
13
14
  }: {
15
+ showBackButton?: boolean;
14
16
  handleBack?: () => void;
15
17
  handleClose?: () => void;
16
18
  title: string;
@@ -24,23 +26,32 @@ const ModalHeader = ({
24
26
 
25
27
  return (
26
28
  <div
27
- className={cn("flex h-16 items-center justify-between border-b border-[#e4e4e7] bg-white px-5 py-3", className)}
29
+ className={cn(
30
+ "b3-modal-header flex h-16 items-center justify-between border-b border-[#e4e4e7] bg-white px-5 py-3",
31
+ className,
32
+ )}
28
33
  >
29
- <button
30
- onClick={handleBack || navigateBack}
31
- className="flex h-6 w-6 items-center justify-center transition-opacity hover:opacity-70"
32
- >
33
- <ChevronDown className="h-6 w-6 rotate-90 text-[#51525c]" />
34
- {showBackWord && <span className="text-sm font-medium">Back</span>}
35
- </button>
34
+ {showBackButton ? (
35
+ <button
36
+ onClick={handleBack || navigateBack}
37
+ className="flex h-6 w-6 items-center justify-center transition-opacity hover:opacity-70"
38
+ >
39
+ <ChevronLeft className="h-6 w-6 text-[#51525c]" />
40
+ {showBackWord && <span className="text-sm font-medium">Back</span>}
41
+ </button>
42
+ ) : (
43
+ <div className="w-2" />
44
+ )}
36
45
  <p className="font-inter text-lg font-semibold leading-7 text-[#18181b]">{title}</p>
37
- {showCloseButton && (
46
+ {showCloseButton ? (
38
47
  <button
39
48
  onClick={handleClose || (() => setB3ModalOpen(false))}
40
49
  className="flex h-6 w-6 items-center justify-center transition-opacity hover:opacity-70"
41
50
  >
42
51
  <X className="h-6 w-6 text-[#51525c]" />
43
52
  </button>
53
+ ) : (
54
+ <div className="w-2" />
44
55
  )}
45
56
  {children}
46
57
  </div>
@@ -60,7 +60,7 @@ const DialogContent: React.ForwardRefExoticComponent<DialogContentProps & React.
60
60
  )}
61
61
  {...props}
62
62
  >
63
- <div className="flex flex-1 flex-col overflow-hidden rounded-xl border border-[#D1D1D6] bg-white shadow-[0_20px_24px_-4px_rgba(10,13,18,0.08),0_8px_8px_-4px_rgba(10,13,18,0.03),0_3px_3px_-1.5px_rgba(10,13,18,0.04)]">
63
+ <div className="modal-inner-content flex flex-1 flex-col overflow-hidden rounded-xl border border-[#D1D1D6] bg-white shadow-[0_20px_24px_-4px_rgba(10,13,18,0.08),0_8px_8px_-4px_rgba(10,13,18,0.03),0_3px_3px_-1.5px_rgba(10,13,18,0.04)]">
64
64
  {children}
65
65
  {!hideCloseButton && (
66
66
  <DialogPrimitive.Close
@@ -75,7 +75,7 @@ const DialogContent: React.ForwardRefExoticComponent<DialogContentProps & React.
75
75
  )}
76
76
  </div>
77
77
  {/* Global Account Footer */}
78
- <div className="flex items-center justify-center gap-1.5 pt-[10px]">
78
+ <div className="b3-modal-ga-branding flex items-center justify-center gap-1.5 pt-[10px]">
79
79
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
80
80
  <path
81
81
  d="M2 4.66667C2 3.19391 3.19391 2 4.66667 2H11.3333C12.8061 2 14 3.19391 14 4.66667V11.3333C14 12.8061 12.8061 14 11.3333 14H4.66667C3.19391 14 2 12.8061 2 11.3333V4.66667Z"
@@ -35,7 +35,7 @@ const DrawerContent = React.forwardRef<
35
35
  <DrawerPrimitive.Content
36
36
  ref={ref}
37
37
  className={cn(
38
- "bg-b3-react-background fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border py-6",
38
+ "bg-b3-react-background fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border py-6 pt-5",
39
39
  className,
40
40
  )}
41
41
  {...props}