@hfunlabs/hypurr-connect 0.1.13 → 0.1.15
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.
- package/README.md +12 -0
- package/dist/index.d.ts +89 -4
- package/dist/index.js +1858 -348
- package/dist/index.js.map +1 -1
- package/package.json +17 -6
- package/src/AddWalletModal.tsx +744 -0
- package/src/AgentExpiryWarning.tsx +129 -0
- package/src/DeleteWalletModal.tsx +5 -1
- package/src/HypurrConnectProvider.tsx +193 -1
- package/src/LoginModal.tsx +30 -19
- package/src/RenameWalletModal.tsx +5 -1
- package/src/RenewAgentModal.tsx +380 -0
- package/src/UserProfileModal.tsx +157 -25
- package/src/WalletSelectorDropdown.tsx +146 -6
- package/src/agent.ts +38 -12
- package/src/agentWallet.ts +86 -0
- package/src/css.d.ts +1 -0
- package/src/icons/lucide.tsx +61 -0
- package/src/index.ts +17 -1
- package/src/profileStyles.ts +58 -0
- package/src/styles.css +1 -0
- package/src/tailwind.css +77 -0
- package/src/types.ts +13 -0
|
@@ -9,15 +9,24 @@ import {
|
|
|
9
9
|
type MouseEvent as ReactMouseEvent,
|
|
10
10
|
type ReactNode,
|
|
11
11
|
} from "react";
|
|
12
|
+
import {
|
|
13
|
+
AgentExpiryWarningIcon,
|
|
14
|
+
EXPIRED_AGENT_COLOR,
|
|
15
|
+
} from "./AgentExpiryWarning";
|
|
12
16
|
import { useHypurrConnectInternal } from "./HypurrConnectProvider";
|
|
13
17
|
import { UserProfileModal, type SlippageOption } from "./UserProfileModal";
|
|
18
|
+
import { getAgentExpiryTitle } from "./agentWallet";
|
|
14
19
|
import {
|
|
20
|
+
Bot,
|
|
15
21
|
Copy,
|
|
16
22
|
Crown,
|
|
23
|
+
Eye,
|
|
17
24
|
Folder,
|
|
25
|
+
KeyRound,
|
|
18
26
|
LayoutDashboard,
|
|
19
27
|
LogOut,
|
|
20
28
|
Plus,
|
|
29
|
+
RefreshCw,
|
|
21
30
|
Star,
|
|
22
31
|
User,
|
|
23
32
|
Wallet,
|
|
@@ -36,6 +45,8 @@ export interface WalletSelectorDropdownProps {
|
|
|
36
45
|
onAddWallet?: () => void;
|
|
37
46
|
/** Called when the user clicks the portfolio icon on a wallet row. */
|
|
38
47
|
onShowPortfolio?: (wallet: HyperliquidWallet) => void;
|
|
48
|
+
/** Called when the user clicks an agent wallet renew action. Host should connect the owner wallet and call `renewAgentWallet`. */
|
|
49
|
+
onRenewAgentWallet?: (wallet: HyperliquidWallet) => void;
|
|
39
50
|
/**
|
|
40
51
|
* Called before the SDK's `logout()` runs. Host can do extra cleanup
|
|
41
52
|
* (e.g. wagmi `disconnect()`).
|
|
@@ -67,6 +78,21 @@ export interface WalletSelectorDropdownProps {
|
|
|
67
78
|
principalColors?: PrincipalColorOverrides;
|
|
68
79
|
/** CSS color used as the dropdown panel background. Defaults to `rgba(20,20,20,0.95)`. */
|
|
69
80
|
backgroundColor?: string;
|
|
81
|
+
/** Extra footer rows rendered between "Profile & Settings" and "Logout". */
|
|
82
|
+
extraFooterItems?: ExtraFooterItem[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ExtraFooterItem {
|
|
86
|
+
/** Stable key for React reconciliation. */
|
|
87
|
+
key: string;
|
|
88
|
+
/** Leading icon (e.g. an icon from `./icons/lucide`). */
|
|
89
|
+
icon: ReactNode;
|
|
90
|
+
/** Row label. */
|
|
91
|
+
label: string;
|
|
92
|
+
/** Click handler. The dropdown is NOT auto-closed — call `onClose` yourself if desired. */
|
|
93
|
+
onClick: () => void;
|
|
94
|
+
/** Optional hover text color (matches the "Logout" red-on-hover pattern). */
|
|
95
|
+
hoverColor?: string;
|
|
70
96
|
}
|
|
71
97
|
|
|
72
98
|
const DEFAULT_BACKGROUND_COLOR = "rgba(20,20,20,0.95)";
|
|
@@ -75,6 +101,10 @@ interface DropdownWallet {
|
|
|
75
101
|
id: number;
|
|
76
102
|
name?: string | null;
|
|
77
103
|
ethereumAddress?: string;
|
|
104
|
+
agentEthereumAddress?: HyperliquidWallet["agentEthereumAddress"];
|
|
105
|
+
isAgent?: boolean;
|
|
106
|
+
isReadOnly?: boolean;
|
|
107
|
+
agentExpiresAt?: HyperliquidWallet["agentExpiresAt"];
|
|
78
108
|
}
|
|
79
109
|
|
|
80
110
|
interface WalletListItem {
|
|
@@ -171,6 +201,41 @@ const formatCompactAddress = (addr: string | undefined) => {
|
|
|
171
201
|
return `${addr.slice(0, 4)}...${addr.slice(-2)}`;
|
|
172
202
|
};
|
|
173
203
|
|
|
204
|
+
const getDisplayAddress = (wallet: DropdownWallet) =>
|
|
205
|
+
wallet.isAgent && wallet.agentEthereumAddress?.value
|
|
206
|
+
? wallet.agentEthereumAddress.value
|
|
207
|
+
: wallet.ethereumAddress;
|
|
208
|
+
|
|
209
|
+
type WalletTypeMeta = {
|
|
210
|
+
label: string;
|
|
211
|
+
color: string;
|
|
212
|
+
icon: ReactNode;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
function getWalletTypeMeta(wallet: DropdownWallet): WalletTypeMeta {
|
|
216
|
+
if (wallet.isAgent) {
|
|
217
|
+
return {
|
|
218
|
+
label: "Agent wallet",
|
|
219
|
+
color: "#38bdf8",
|
|
220
|
+
icon: <Bot size={12} />,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (wallet.isReadOnly) {
|
|
225
|
+
return {
|
|
226
|
+
label: "Read-only wallet",
|
|
227
|
+
color: "#a78bfa",
|
|
228
|
+
icon: <Eye size={12} />,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
label: "Private-key wallet",
|
|
234
|
+
color: "#34d399",
|
|
235
|
+
icon: <KeyRound size={12} />,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
174
239
|
const rootStyle: CSSProperties = {
|
|
175
240
|
position: "absolute",
|
|
176
241
|
right: 0,
|
|
@@ -217,6 +282,7 @@ export function WalletSelectorDropdown({
|
|
|
217
282
|
onClose,
|
|
218
283
|
onAddWallet,
|
|
219
284
|
onShowPortfolio,
|
|
285
|
+
onRenewAgentWallet,
|
|
220
286
|
onLogout,
|
|
221
287
|
onNotify,
|
|
222
288
|
vipThreshold = 10,
|
|
@@ -230,6 +296,7 @@ export function WalletSelectorDropdown({
|
|
|
230
296
|
accentColor,
|
|
231
297
|
principalColors,
|
|
232
298
|
backgroundColor = DEFAULT_BACKGROUND_COLOR,
|
|
299
|
+
extraFooterItems,
|
|
233
300
|
}: WalletSelectorDropdownProps): ReactNode {
|
|
234
301
|
const { user, wallets, selectedWalletId, selectWallet, logout, authMethod } =
|
|
235
302
|
useHypurrConnectInternal();
|
|
@@ -269,13 +336,19 @@ export function WalletSelectorDropdown({
|
|
|
269
336
|
const renderWalletRow = (item: WalletListItem, depth: number): ReactNode => {
|
|
270
337
|
const { wallet, label } = item;
|
|
271
338
|
const isSelected = wallet.id === selectedWalletId;
|
|
272
|
-
const
|
|
339
|
+
const displayAddress = getDisplayAddress(wallet);
|
|
340
|
+
const compactAddress = formatCompactAddress(displayAddress);
|
|
341
|
+
const agentExpiryTitle = getAgentExpiryTitle({
|
|
342
|
+
isAgent: !!wallet.isAgent,
|
|
343
|
+
agentExpiresAt: wallet.agentExpiresAt,
|
|
344
|
+
});
|
|
273
345
|
return (
|
|
274
346
|
<WalletRow
|
|
275
347
|
key={wallet.id}
|
|
276
348
|
depth={depth}
|
|
277
349
|
isSelected={isSelected}
|
|
278
350
|
label={label}
|
|
351
|
+
walletType={getWalletTypeMeta(wallet)}
|
|
279
352
|
compactAddress={compactAddress}
|
|
280
353
|
onSelect={() => {
|
|
281
354
|
selectWallet(wallet.id);
|
|
@@ -289,7 +362,16 @@ export function WalletSelectorDropdown({
|
|
|
289
362
|
}
|
|
290
363
|
: undefined
|
|
291
364
|
}
|
|
292
|
-
onCopy={() => handleCopyAddress(
|
|
365
|
+
onCopy={() => handleCopyAddress(displayAddress)}
|
|
366
|
+
agentExpiryTitle={agentExpiryTitle}
|
|
367
|
+
onRenewAgentWallet={
|
|
368
|
+
wallet.isAgent && onRenewAgentWallet
|
|
369
|
+
? () => {
|
|
370
|
+
onRenewAgentWallet(wallet as HyperliquidWallet);
|
|
371
|
+
onClose();
|
|
372
|
+
}
|
|
373
|
+
: undefined
|
|
374
|
+
}
|
|
293
375
|
colors={colors}
|
|
294
376
|
/>
|
|
295
377
|
);
|
|
@@ -557,6 +639,15 @@ export function WalletSelectorDropdown({
|
|
|
557
639
|
icon={<User size={14} />}
|
|
558
640
|
label="Profile & Settings"
|
|
559
641
|
/>
|
|
642
|
+
{extraFooterItems?.map((item) => (
|
|
643
|
+
<FooterBtn
|
|
644
|
+
key={item.key}
|
|
645
|
+
onClick={item.onClick}
|
|
646
|
+
icon={item.icon}
|
|
647
|
+
label={item.label}
|
|
648
|
+
hoverColor={item.hoverColor}
|
|
649
|
+
/>
|
|
650
|
+
))}
|
|
560
651
|
<FooterBtn
|
|
561
652
|
onClick={handleLogout}
|
|
562
653
|
icon={<LogOut size={14} />}
|
|
@@ -580,6 +671,7 @@ export function WalletSelectorDropdown({
|
|
|
580
671
|
principalColors={principalColors}
|
|
581
672
|
onWalletDeleted={onWalletDeleted}
|
|
582
673
|
onWalletRenamed={onWalletRenamed}
|
|
674
|
+
onRenewAgentWallet={onRenewAgentWallet}
|
|
583
675
|
onShowPortfolio={
|
|
584
676
|
onShowPortfolio
|
|
585
677
|
? (wallet) => {
|
|
@@ -628,21 +720,30 @@ function WalletRow({
|
|
|
628
720
|
depth,
|
|
629
721
|
isSelected,
|
|
630
722
|
label,
|
|
723
|
+
walletType,
|
|
631
724
|
compactAddress,
|
|
632
725
|
onSelect,
|
|
633
726
|
onShowPortfolio,
|
|
634
727
|
onCopy,
|
|
728
|
+
agentExpiryTitle,
|
|
729
|
+
onRenewAgentWallet,
|
|
635
730
|
colors,
|
|
636
731
|
}: {
|
|
637
732
|
depth: number;
|
|
638
733
|
isSelected: boolean;
|
|
639
734
|
label: string | null;
|
|
735
|
+
walletType: WalletTypeMeta;
|
|
640
736
|
compactAddress: string;
|
|
641
737
|
onSelect: () => void;
|
|
642
738
|
onShowPortfolio?: () => void;
|
|
643
739
|
onCopy: () => void;
|
|
740
|
+
agentExpiryTitle?: string | null;
|
|
741
|
+
onRenewAgentWallet?: () => void;
|
|
644
742
|
colors: PrincipalColors;
|
|
645
743
|
}) {
|
|
744
|
+
const isAgentExpired = !!agentExpiryTitle;
|
|
745
|
+
const walletTextColor = isAgentExpired ? EXPIRED_AGENT_COLOR : "#d1d5db";
|
|
746
|
+
const mutedTextColor = isAgentExpired ? "rgba(245,158,11,0.78)" : "#6b7280";
|
|
646
747
|
return (
|
|
647
748
|
<div
|
|
648
749
|
style={{
|
|
@@ -652,7 +753,7 @@ function WalletRow({
|
|
|
652
753
|
paddingTop: 6,
|
|
653
754
|
paddingBottom: 6,
|
|
654
755
|
fontSize: 14,
|
|
655
|
-
color:
|
|
756
|
+
color: walletTextColor,
|
|
656
757
|
display: "flex",
|
|
657
758
|
alignItems: "center",
|
|
658
759
|
background: isSelected ? "rgba(255,255,255,0.06)" : "transparent",
|
|
@@ -689,6 +790,19 @@ function WalletRow({
|
|
|
689
790
|
padding: 0,
|
|
690
791
|
}}
|
|
691
792
|
>
|
|
793
|
+
<span
|
|
794
|
+
title={walletType.label}
|
|
795
|
+
aria-label={walletType.label}
|
|
796
|
+
style={{
|
|
797
|
+
display: "inline-flex",
|
|
798
|
+
alignItems: "center",
|
|
799
|
+
justifyContent: "center",
|
|
800
|
+
flexShrink: 0,
|
|
801
|
+
color: isAgentExpired ? walletTextColor : walletType.color,
|
|
802
|
+
}}
|
|
803
|
+
>
|
|
804
|
+
{walletType.icon}
|
|
805
|
+
</span>
|
|
692
806
|
{label ? (
|
|
693
807
|
<>
|
|
694
808
|
<span
|
|
@@ -696,7 +810,11 @@ function WalletRow({
|
|
|
696
810
|
overflow: "hidden",
|
|
697
811
|
textOverflow: "ellipsis",
|
|
698
812
|
whiteSpace: "nowrap",
|
|
699
|
-
color:
|
|
813
|
+
color: isAgentExpired
|
|
814
|
+
? walletTextColor
|
|
815
|
+
: isSelected
|
|
816
|
+
? "#fff"
|
|
817
|
+
: undefined,
|
|
700
818
|
fontWeight: isSelected ? 500 : undefined,
|
|
701
819
|
}}
|
|
702
820
|
>
|
|
@@ -707,7 +825,7 @@ function WalletRow({
|
|
|
707
825
|
fontSize: 12,
|
|
708
826
|
fontWeight: 400,
|
|
709
827
|
flexShrink: 0,
|
|
710
|
-
color:
|
|
828
|
+
color: mutedTextColor,
|
|
711
829
|
}}
|
|
712
830
|
>
|
|
713
831
|
({compactAddress})
|
|
@@ -717,7 +835,11 @@ function WalletRow({
|
|
|
717
835
|
<span
|
|
718
836
|
style={{
|
|
719
837
|
fontSize: 12,
|
|
720
|
-
color:
|
|
838
|
+
color: isAgentExpired
|
|
839
|
+
? walletTextColor
|
|
840
|
+
: isSelected
|
|
841
|
+
? "#fff"
|
|
842
|
+
: "#9ca3af",
|
|
721
843
|
fontWeight: isSelected ? 500 : undefined,
|
|
722
844
|
}}
|
|
723
845
|
>
|
|
@@ -725,6 +847,12 @@ function WalletRow({
|
|
|
725
847
|
</span>
|
|
726
848
|
)}
|
|
727
849
|
</button>
|
|
850
|
+
{agentExpiryTitle && (
|
|
851
|
+
<AgentExpiryWarningIcon
|
|
852
|
+
message={agentExpiryTitle}
|
|
853
|
+
onClick={onRenewAgentWallet}
|
|
854
|
+
/>
|
|
855
|
+
)}
|
|
728
856
|
<div
|
|
729
857
|
data-row-actions
|
|
730
858
|
style={{
|
|
@@ -734,6 +862,18 @@ function WalletRow({
|
|
|
734
862
|
transition: "opacity 120ms",
|
|
735
863
|
}}
|
|
736
864
|
>
|
|
865
|
+
{onRenewAgentWallet && (
|
|
866
|
+
<RowIconBtn
|
|
867
|
+
title="Renew agent wallet"
|
|
868
|
+
hoverColor={isAgentExpired ? EXPIRED_AGENT_COLOR : colors.accent}
|
|
869
|
+
onClick={(e) => {
|
|
870
|
+
e.stopPropagation();
|
|
871
|
+
onRenewAgentWallet();
|
|
872
|
+
}}
|
|
873
|
+
>
|
|
874
|
+
<RefreshCw size={12} />
|
|
875
|
+
</RowIconBtn>
|
|
876
|
+
)}
|
|
737
877
|
{onShowPortfolio && (
|
|
738
878
|
<RowIconBtn
|
|
739
879
|
title="View portfolio"
|
package/src/agent.ts
CHANGED
|
@@ -50,14 +50,14 @@ interface ExtraAgent {
|
|
|
50
50
|
validUntil: number;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
function isNamedAgent(agent: ExtraAgent): boolean {
|
|
54
|
+
return agent.name.replace(/ valid_until \d+$/, "") === AGENT_NAME;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function fetchExtraAgents(
|
|
58
58
|
userAddress: string,
|
|
59
59
|
isTestnet: boolean,
|
|
60
|
-
): Promise<ExtraAgent
|
|
60
|
+
): Promise<ExtraAgent[]> {
|
|
61
61
|
const url = isTestnet
|
|
62
62
|
? "https://api.hyperliquid-testnet.xyz/info"
|
|
63
63
|
: "https://api.hyperliquid.xyz/info";
|
|
@@ -68,17 +68,43 @@ export async function fetchActiveAgent(
|
|
|
68
68
|
body: JSON.stringify({ type: "extraAgents", user: userAddress }),
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
if (!res.ok) return
|
|
71
|
+
if (!res.ok) return [];
|
|
72
72
|
|
|
73
73
|
const agents: unknown = await res.json();
|
|
74
|
-
if (!Array.isArray(agents)) return
|
|
74
|
+
if (!Array.isArray(agents)) return [];
|
|
75
|
+
|
|
76
|
+
return (agents as ExtraAgent[]).map((agent) => ({
|
|
77
|
+
...agent,
|
|
78
|
+
validUntil: agent.validUntil * 1000,
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
75
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Query the Hyperliquid info API for the named agents registered to a user.
|
|
84
|
+
* Returns the matching entry for AGENT_NAME if it exists and is still valid.
|
|
85
|
+
*/
|
|
86
|
+
export async function fetchActiveAgent(
|
|
87
|
+
userAddress: string,
|
|
88
|
+
isTestnet: boolean,
|
|
89
|
+
): Promise<ExtraAgent | null> {
|
|
76
90
|
const nowMs = Date.now();
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
);
|
|
91
|
+
const agents = await fetchExtraAgents(userAddress, isTestnet);
|
|
92
|
+
const match = agents.find((a) => isNamedAgent(a) && a.validUntil > nowMs);
|
|
80
93
|
if (!match) return null;
|
|
81
|
-
return
|
|
94
|
+
return match;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function fetchAgentByAddress(
|
|
98
|
+
userAddress: string,
|
|
99
|
+
agentAddress: string,
|
|
100
|
+
isTestnet: boolean,
|
|
101
|
+
): Promise<ExtraAgent | null> {
|
|
102
|
+
const agents = await fetchExtraAgents(userAddress, isTestnet);
|
|
103
|
+
return (
|
|
104
|
+
agents.find(
|
|
105
|
+
(agent) => agent.address.toLowerCase() === agentAddress.toLowerCase(),
|
|
106
|
+
) ?? null
|
|
107
|
+
);
|
|
82
108
|
}
|
|
83
109
|
|
|
84
110
|
/**
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { HyperliquidWallet } from "hypurr-grpc/ts/hypurr/wallet";
|
|
2
|
+
|
|
3
|
+
const MS_PER_SECOND = 1_000;
|
|
4
|
+
const NS_PER_MS = 1_000_000;
|
|
5
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1_000;
|
|
6
|
+
const TELEGRAM_AGENT_APPROVAL_NAME = "xyz";
|
|
7
|
+
|
|
8
|
+
export interface AgentApprovalDurationOption {
|
|
9
|
+
label: string;
|
|
10
|
+
durationMs: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const DEFAULT_AGENT_APPROVAL_DURATION_MS = MS_PER_DAY;
|
|
14
|
+
|
|
15
|
+
export const AGENT_APPROVAL_DURATION_OPTIONS: AgentApprovalDurationOption[] = [
|
|
16
|
+
{ label: "1 day", durationMs: MS_PER_DAY },
|
|
17
|
+
{ label: "7 days", durationMs: 7 * MS_PER_DAY },
|
|
18
|
+
{ label: "30 days", durationMs: 30 * MS_PER_DAY },
|
|
19
|
+
{ label: "90 days", durationMs: 90 * MS_PER_DAY },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
type TimestampLike =
|
|
23
|
+
| {
|
|
24
|
+
seconds?: number | string | bigint;
|
|
25
|
+
nanos?: number | string | bigint;
|
|
26
|
+
}
|
|
27
|
+
| string
|
|
28
|
+
| number
|
|
29
|
+
| Date;
|
|
30
|
+
|
|
31
|
+
function toFiniteNumber(value: number | string | bigint | undefined): number {
|
|
32
|
+
if (value === undefined) return 0;
|
|
33
|
+
const numeric = typeof value === "bigint" ? Number(value) : Number(value);
|
|
34
|
+
return Number.isFinite(numeric) ? numeric : 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function normalizeAgentApprovalDurationMs(durationMs?: number): number {
|
|
38
|
+
return typeof durationMs === "number" &&
|
|
39
|
+
Number.isFinite(durationMs) &&
|
|
40
|
+
durationMs > 0
|
|
41
|
+
? durationMs
|
|
42
|
+
: DEFAULT_AGENT_APPROVAL_DURATION_MS;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function createTelegramAgentApprovalName(durationMs?: number): string {
|
|
46
|
+
const expiresAt = Date.now() + normalizeAgentApprovalDurationMs(durationMs);
|
|
47
|
+
return `${TELEGRAM_AGENT_APPROVAL_NAME} valid_until ${expiresAt}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type WalletWithAgentExpiry = Pick<HyperliquidWallet, "isAgent"> & {
|
|
51
|
+
agentExpiresAt?: unknown;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export function agentExpiresAtMs(wallet: WalletWithAgentExpiry): number | null {
|
|
55
|
+
if (!wallet.isAgent || !wallet.agentExpiresAt) return null;
|
|
56
|
+
|
|
57
|
+
const timestamp = wallet.agentExpiresAt as TimestampLike;
|
|
58
|
+
if (timestamp instanceof Date) return timestamp.getTime();
|
|
59
|
+
if (typeof timestamp === "number") return timestamp;
|
|
60
|
+
if (typeof timestamp === "string") {
|
|
61
|
+
const parsed = Date.parse(timestamp);
|
|
62
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const seconds = toFiniteNumber(timestamp.seconds);
|
|
66
|
+
const nanos = toFiniteNumber(timestamp.nanos);
|
|
67
|
+
const ms = seconds * MS_PER_SECOND + Math.floor(nanos / NS_PER_MS);
|
|
68
|
+
return Number.isFinite(ms) && ms > 0 ? ms : null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function formatAgentExpiry(expiresAtMs: number): string {
|
|
72
|
+
return new Intl.DateTimeFormat(undefined, {
|
|
73
|
+
dateStyle: "medium",
|
|
74
|
+
timeStyle: "short",
|
|
75
|
+
}).format(new Date(expiresAtMs));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getAgentExpiryTitle(
|
|
79
|
+
wallet: WalletWithAgentExpiry,
|
|
80
|
+
): string | null {
|
|
81
|
+
const expiresAtMs = agentExpiresAtMs(wallet);
|
|
82
|
+
if (!expiresAtMs || expiresAtMs > Date.now()) return null;
|
|
83
|
+
|
|
84
|
+
const expiresAt = formatAgentExpiry(expiresAtMs);
|
|
85
|
+
return `Agent approval expired ${expiresAt}. Connect the owner wallet to renew this agent.`;
|
|
86
|
+
}
|
package/src/css.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module "*.css";
|
package/src/icons/lucide.tsx
CHANGED
|
@@ -70,6 +70,67 @@ export function Wallet(props: IconProps) {
|
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export function Bot(props: IconProps) {
|
|
74
|
+
return (
|
|
75
|
+
<svg {...svgBase(props)}>
|
|
76
|
+
<path d="M12 8V4H8" />
|
|
77
|
+
<rect width="16" height="12" x="4" y="8" rx="2" />
|
|
78
|
+
<path d="M2 14h2" />
|
|
79
|
+
<path d="M20 14h2" />
|
|
80
|
+
<path d="M15 13v2" />
|
|
81
|
+
<path d="M9 13v2" />
|
|
82
|
+
</svg>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function Eye(props: IconProps) {
|
|
87
|
+
return (
|
|
88
|
+
<svg {...svgBase(props)}>
|
|
89
|
+
<path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" />
|
|
90
|
+
<circle cx="12" cy="12" r="3" />
|
|
91
|
+
</svg>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function EyeOff(props: IconProps) {
|
|
96
|
+
return (
|
|
97
|
+
<svg {...svgBase(props)}>
|
|
98
|
+
<path d="M10.733 5.076a10.74 10.74 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.8 10.8 0 0 1-1.444 2.49" />
|
|
99
|
+
<path d="M14.084 14.158a3 3 0 0 1-4.242-4.242" />
|
|
100
|
+
<path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" />
|
|
101
|
+
<path d="m2 2 20 20" />
|
|
102
|
+
</svg>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function Check(props: IconProps) {
|
|
107
|
+
return (
|
|
108
|
+
<svg {...svgBase(props)}>
|
|
109
|
+
<path d="M20 6 9 17l-5-5" />
|
|
110
|
+
</svg>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function KeyRound(props: IconProps) {
|
|
115
|
+
return (
|
|
116
|
+
<svg {...svgBase(props)}>
|
|
117
|
+
<path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z" />
|
|
118
|
+
<circle cx="16.5" cy="7.5" r=".5" fill="currentColor" />
|
|
119
|
+
</svg>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function RefreshCw(props: IconProps) {
|
|
124
|
+
return (
|
|
125
|
+
<svg {...svgBase(props)}>
|
|
126
|
+
<path d="M3 12a9 9 0 0 1 15.18-6.49" />
|
|
127
|
+
<path d="M21 3v6h-6" />
|
|
128
|
+
<path d="M21 12a9 9 0 0 1-15.18 6.49" />
|
|
129
|
+
<path d="M3 21v-6h6" />
|
|
130
|
+
</svg>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
73
134
|
export function TrendingUp(props: IconProps) {
|
|
74
135
|
return (
|
|
75
136
|
<svg {...svgBase(props)}>
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
import "./styles.css";
|
|
2
|
+
|
|
1
3
|
export {
|
|
2
4
|
HypurrConnectProvider,
|
|
3
5
|
useHypurrConnect,
|
|
4
6
|
} from "./HypurrConnectProvider";
|
|
5
7
|
export { LoginModal } from "./LoginModal";
|
|
6
8
|
export type { LoginModalProps } from "./LoginModal";
|
|
9
|
+
export { AddWalletModal } from "./AddWalletModal";
|
|
10
|
+
export type { AddWalletModalProps } from "./AddWalletModal";
|
|
11
|
+
export { RenewAgentModal } from "./RenewAgentModal";
|
|
12
|
+
export type { RenewAgentModalProps } from "./RenewAgentModal";
|
|
7
13
|
export { WalletSelectorDropdown } from "./WalletSelectorDropdown";
|
|
8
|
-
export type {
|
|
14
|
+
export type {
|
|
15
|
+
WalletSelectorDropdownProps,
|
|
16
|
+
ExtraFooterItem,
|
|
17
|
+
} from "./WalletSelectorDropdown";
|
|
9
18
|
export { UserProfileModal } from "./UserProfileModal";
|
|
10
19
|
export type { UserProfileModalProps, SlippageOption } from "./UserProfileModal";
|
|
11
20
|
export { DeleteWalletModal } from "./DeleteWalletModal";
|
|
@@ -17,6 +26,12 @@ export { GrpcExchangeTransport } from "./GrpcExchangeTransport";
|
|
|
17
26
|
export type { GrpcExchangeTransportConfig } from "./GrpcExchangeTransport";
|
|
18
27
|
export { createTelegramClient, createStaticClient } from "./grpc";
|
|
19
28
|
export { PrivateKeySigner } from "./privateKeySigner";
|
|
29
|
+
export {
|
|
30
|
+
AGENT_APPROVAL_DURATION_OPTIONS,
|
|
31
|
+
DEFAULT_AGENT_APPROVAL_DURATION_MS,
|
|
32
|
+
createTelegramAgentApprovalName,
|
|
33
|
+
} from "./agentWallet";
|
|
34
|
+
export type { AgentApprovalDurationOption } from "./agentWallet";
|
|
20
35
|
export { createEoaSigner } from "./types";
|
|
21
36
|
export type {
|
|
22
37
|
AuthMethod,
|
|
@@ -29,6 +44,7 @@ export type {
|
|
|
29
44
|
HypurrConnectConfig,
|
|
30
45
|
HypurrConnectState,
|
|
31
46
|
HypurrUser,
|
|
47
|
+
RenewAgentWalletParams,
|
|
32
48
|
ScaleCreateParams,
|
|
33
49
|
SignEvmTransactionFn,
|
|
34
50
|
SignTypedDataFn,
|
package/src/profileStyles.ts
CHANGED
|
@@ -37,6 +37,64 @@ export interface PrincipalColors {
|
|
|
37
37
|
|
|
38
38
|
export type PrincipalColorOverrides = Partial<PrincipalColors>;
|
|
39
39
|
|
|
40
|
+
const parseSrgb = (color: string): [number, number, number] | null => {
|
|
41
|
+
const value = color.trim();
|
|
42
|
+
const shortHex = /^#([0-9a-f]{3})$/i.exec(value);
|
|
43
|
+
if (shortHex) {
|
|
44
|
+
const [r, g, b] = shortHex[1].split("").map((c) => parseInt(c + c, 16)) as [
|
|
45
|
+
number,
|
|
46
|
+
number,
|
|
47
|
+
number,
|
|
48
|
+
];
|
|
49
|
+
return [r, g, b];
|
|
50
|
+
}
|
|
51
|
+
const longHex = /^#([0-9a-f]{6})$/i.exec(value);
|
|
52
|
+
if (longHex) {
|
|
53
|
+
const v = longHex[1];
|
|
54
|
+
return [
|
|
55
|
+
parseInt(v.slice(0, 2), 16),
|
|
56
|
+
parseInt(v.slice(2, 4), 16),
|
|
57
|
+
parseInt(v.slice(4, 6), 16),
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
const rgb =
|
|
61
|
+
/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*[\d.]+\s*)?\)$/i.exec(
|
|
62
|
+
value,
|
|
63
|
+
);
|
|
64
|
+
if (rgb) {
|
|
65
|
+
return [parseInt(rgb[1], 10), parseInt(rgb[2], 10), parseInt(rgb[3], 10)];
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* WCAG-style relative luminance in [0, 1]. Returns `null` for color formats
|
|
72
|
+
* the parser doesn't understand (named colors, hsl, color-mix, …).
|
|
73
|
+
*/
|
|
74
|
+
export const relativeLuminance = (color: string): number | null => {
|
|
75
|
+
const rgb = parseSrgb(color);
|
|
76
|
+
if (!rgb) return null;
|
|
77
|
+
const [r, g, b] = rgb.map((c) => {
|
|
78
|
+
const v = c / 255;
|
|
79
|
+
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
80
|
+
}) as [number, number, number];
|
|
81
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Picks a readable foreground for `background`. Falls back to `lightFg` when
|
|
86
|
+
* the background can't be parsed.
|
|
87
|
+
*/
|
|
88
|
+
export const pickContrastColor = (
|
|
89
|
+
background: string,
|
|
90
|
+
lightFg: string = profileColors.text,
|
|
91
|
+
darkFg: string = "#0a0a0a",
|
|
92
|
+
): string => {
|
|
93
|
+
const lum = relativeLuminance(background);
|
|
94
|
+
if (lum === null) return lightFg;
|
|
95
|
+
return lum > 0.5 ? darkFg : lightFg;
|
|
96
|
+
};
|
|
97
|
+
|
|
40
98
|
const colorWithAlpha = (color: string, alpha: number): string => {
|
|
41
99
|
const hex = color.trim();
|
|
42
100
|
const shortHex = /^#([0-9a-f]{3})$/i.exec(hex);
|
package/src/styles.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.hypurr-connect .btn-raised{background-color:#0d1219;background-image:linear-gradient(180deg,hsla(0,0%,100%,.03),transparent);box-shadow:inset 0 1px 0 hsla(0,0%,100%,.1),inset 0 -1px 0 rgba(0,0,0,.35),inset 0 0 0 1px #262a30;color:#aab1c1;transition:background-color .15s,color .15s,background-image .15s,box-shadow .15s}.hypurr-connect .btn-raised:hover:not(:disabled){background-color:#15171a;background-image:linear-gradient(180deg,hsla(0,0%,100%,.04),transparent);box-shadow:inset 0 1px 0 hsla(0,0%,100%,.14),inset 0 -1px 0 rgba(0,0,0,.4),inset 0 0 0 1px #444548;color:#d1d5db}.hypurr-connect .btn-raised-active{background-color:#1e2124;background-image:linear-gradient(180deg,hsla(0,0%,100%,.05),transparent);box-shadow:inset 0 1px 0 hsla(0,0%,100%,.18),inset 0 -1px 0 rgba(0,0,0,.45),inset 0 0 0 1px #4b4d50,0 1px 2px rgba(0,0,0,.5);color:#fff}.hypurr-connect .btn-raised-active,.hypurr-connect .btn-raised-disabled{transition:background-color .15s,color .15s,background-image .15s,box-shadow .15s}.hypurr-connect .btn-raised-disabled{background-color:#0d1219;background-image:linear-gradient(180deg,hsla(0,0%,100%,.02),transparent);box-shadow:inset 0 1px 0 hsla(0,0%,100%,.05),inset 0 -1px 0 rgba(0,0,0,.25),inset 0 0 0 1px #1c2026;color:#7d8597;cursor:not-allowed}.hypurr-connect .\!visible{visibility:visible!important}.hypurr-connect .visible{visibility:visible}.hypurr-connect .fixed{position:fixed}.hypurr-connect .absolute{position:absolute}.hypurr-connect .relative{position:relative}.hypurr-connect .inset-0{inset:0}.hypurr-connect .right-2{right:.5rem}.hypurr-connect .right-6{right:1.5rem}.hypurr-connect .top-1\/2{top:50%}.hypurr-connect .z-\[100\]{z-index:100}.hypurr-connect .z-\[101\]{z-index:101}.hypurr-connect .z-\[110\]{z-index:110}.hypurr-connect .z-\[111\]{z-index:111}.hypurr-connect .mb-1\.5{margin-bottom:.375rem}.hypurr-connect .mb-2{margin-bottom:.5rem}.hypurr-connect .mt-0\.5{margin-top:.125rem}.hypurr-connect .mt-1{margin-top:.25rem}.hypurr-connect .mt-1\.5{margin-top:.375rem}.hypurr-connect .block{display:block}.hypurr-connect .inline-block{display:inline-block}.hypurr-connect .inline{display:inline}.hypurr-connect .flex{display:flex}.hypurr-connect .inline-flex{display:inline-flex}.hypurr-connect .grid{display:grid}.hypurr-connect .contents{display:contents}.hypurr-connect .hidden{display:none}.hypurr-connect .h-8{height:2rem}.hypurr-connect .w-8{width:2rem}.hypurr-connect .w-full{width:100%}.hypurr-connect .min-w-0{min-width:0}.hypurr-connect .max-w-md{max-width:28rem}.hypurr-connect .flex-1{flex:1 1 0%}.hypurr-connect .flex-shrink-0{flex-shrink:0}.hypurr-connect .-translate-y-1\/2{--tw-translate-y:-50%}.hypurr-connect .-translate-y-1\/2,.hypurr-connect .transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hypurr-connect .cursor-not-allowed{cursor:not-allowed}.hypurr-connect .grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.hypurr-connect .items-start{align-items:flex-start}.hypurr-connect .items-center{align-items:center}.hypurr-connect .justify-center{justify-content:center}.hypurr-connect .justify-between{justify-content:space-between}.hypurr-connect .gap-1\.5{gap:.375rem}.hypurr-connect .gap-2{gap:.5rem}.hypurr-connect .gap-3{gap:.75rem}.hypurr-connect :is(.space-y-2>:not([hidden])~:not([hidden])){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.hypurr-connect :is(.space-y-3>:not([hidden])~:not([hidden])){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.hypurr-connect :is(.space-y-4>:not([hidden])~:not([hidden])){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.hypurr-connect .overflow-hidden{overflow:hidden}.hypurr-connect .truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hypurr-connect .break-all{word-break:break-all}.hypurr-connect .rounded{border-radius:.25rem}.hypurr-connect .rounded-lg{border-radius:.5rem}.hypurr-connect .rounded-md{border-radius:.375rem}.hypurr-connect .border{border-width:1px}.hypurr-connect .border-b{border-bottom-width:1px}.hypurr-connect .border-amber-500\/25{border-color:rgba(245,158,11,.25)}.hypurr-connect .border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.hypurr-connect .border-purple-500{--tw-border-opacity:1;border-color:rgb(185 125 241/var(--tw-border-opacity,1))}.hypurr-connect .border-surface-bd{--tw-border-opacity:1;border-color:rgb(38 42 48/var(--tw-border-opacity,1))}.hypurr-connect .border-trade-down\/20{border-color:hsla(0,91%,71%,.2)}.hypurr-connect .border-trade-down\/50{border-color:hsla(0,91%,71%,.5)}.hypurr-connect .border-white\/\[0\.06\]{border-color:hsla(0,0%,100%,.06)}.hypurr-connect .border-white\/\[0\.08\]{border-color:hsla(0,0%,100%,.08)}.hypurr-connect .bg-amber-500\/\[0\.08\]{background-color:rgba(245,158,11,.08)}.hypurr-connect .bg-black\/70{background-color:rgba(0,0,0,.7)}.hypurr-connect .bg-surface-btn\/90{background-color:rgba(13,18,25,.9)}.hypurr-connect .bg-surface-modal{--tw-bg-opacity:1;background-color:rgb(14 18 24/var(--tw-bg-opacity,1))}.hypurr-connect .bg-trade-down\/\[0\.08\]{background-color:hsla(0,91%,71%,.08)}.hypurr-connect .bg-transparent{background-color:transparent}.hypurr-connect .bg-white\/\[0\.03\]{background-color:hsla(0,0%,100%,.03)}.hypurr-connect .bg-white\/\[0\.06\]{background-color:hsla(0,0%,100%,.06)}.hypurr-connect .p-1\.5{padding:.375rem}.hypurr-connect .p-3{padding:.75rem}.hypurr-connect .p-4{padding:1rem}.hypurr-connect .px-3{padding-left:.75rem;padding-right:.75rem}.hypurr-connect .px-6{padding-left:1.5rem;padding-right:1.5rem}.hypurr-connect .py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.hypurr-connect .py-2{padding-top:.5rem;padding-bottom:.5rem}.hypurr-connect .py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.hypurr-connect .py-5{padding-top:1.25rem}.hypurr-connect .pb-5,.hypurr-connect .py-5{padding-bottom:1.25rem}.hypurr-connect .pb-6{padding-bottom:1.5rem}.hypurr-connect .pr-11{padding-right:2.75rem}.hypurr-connect .pt-5{padding-top:1.25rem}.hypurr-connect .pt-6{padding-top:1.5rem}.hypurr-connect .font-mono{font-family:Google Sans Code,Roboto Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.hypurr-connect .font-sans{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.hypurr-connect .text-base{font-size:12.5px;line-height:1rem}.hypurr-connect .text-lg{font-size:14px;line-height:1.25rem}.hypurr-connect .font-medium{font-weight:500}.hypurr-connect .font-semibold{font-weight:600}.hypurr-connect .uppercase{text-transform:uppercase}.hypurr-connect .tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.hypurr-connect .tracking-\[0\.1em\]{letter-spacing:.1em}.hypurr-connect .text-amber-200{--tw-text-opacity:1;color:rgb(253 230 138/var(--tw-text-opacity,1))}.hypurr-connect .text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.hypurr-connect .text-gray-400{--tw-text-opacity:1;color:rgb(170 177 193/var(--tw-text-opacity,1))}.hypurr-connect .text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.hypurr-connect .text-gray-600{--tw-text-opacity:1;color:rgb(125 133 151/var(--tw-text-opacity,1))}.hypurr-connect .text-purple-400{--tw-text-opacity:1;color:rgb(216 180 254/var(--tw-text-opacity,1))}.hypurr-connect .text-trade-down{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.hypurr-connect .text-trade-up{--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.hypurr-connect .text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hypurr-connect .placeholder-gray-600::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(125 133 151/var(--tw-placeholder-opacity,1))}.hypurr-connect .placeholder-gray-600::placeholder{--tw-placeholder-opacity:1;color:rgb(125 133 151/var(--tw-placeholder-opacity,1))}.hypurr-connect .shadow-modal{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.6);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hypurr-connect .outline{outline-style:solid}.hypurr-connect .blur{--tw-blur:blur(8px)}.hypurr-connect .blur,.hypurr-connect .drop-shadow{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.hypurr-connect .drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))}.hypurr-connect .filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.hypurr-connect .backdrop-blur-sm{--tw-backdrop-blur:blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.hypurr-connect .transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hypurr-connect .transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hypurr-connect .hover\:bg-purple-500\/10:hover{background-color:rgba(185,125,241,.1)}.hypurr-connect .hover\:text-gray-300:hover{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.hypurr-connect .hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hypurr-connect .focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.hypurr-connect .disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.hypurr-connect .disabled\:opacity-40:disabled{opacity:.4}.hypurr-connect .disabled\:opacity-50:disabled{opacity:.5}
|