@hfunlabs/hypurr-connect 0.1.14 → 0.1.16
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 +86 -3
- package/dist/index.js +1827 -333
- package/dist/index.js.map +1 -1
- package/package.json +21 -6
- package/src/AddWalletModal.tsx +744 -0
- package/src/AgentExpiryWarning.tsx +129 -0
- package/src/DeleteWalletModal.tsx +2 -1
- package/src/HypurrConnectProvider.tsx +190 -0
- package/src/LoginModal.tsx +11 -11
- package/src/RenameWalletModal.tsx +2 -1
- package/src/RenewAgentModal.tsx +380 -0
- package/src/UserProfileModal.tsx +151 -23
- package/src/WalletSelectorDropdown.tsx +138 -4
- 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,36 @@ const formatCompactAddress = (addr: string | undefined) => {
|
|
|
171
201
|
return `${addr.slice(0, 4)}...${addr.slice(-2)}`;
|
|
172
202
|
};
|
|
173
203
|
|
|
204
|
+
type WalletTypeMeta = {
|
|
205
|
+
label: string;
|
|
206
|
+
color: string;
|
|
207
|
+
icon: ReactNode;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
function getWalletTypeMeta(wallet: DropdownWallet): WalletTypeMeta {
|
|
211
|
+
if (wallet.isAgent) {
|
|
212
|
+
return {
|
|
213
|
+
label: "Agent wallet",
|
|
214
|
+
color: "#38bdf8",
|
|
215
|
+
icon: <Bot size={12} />,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (wallet.isReadOnly) {
|
|
220
|
+
return {
|
|
221
|
+
label: "Read-only wallet",
|
|
222
|
+
color: "#a78bfa",
|
|
223
|
+
icon: <Eye size={12} />,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
label: "Private-key wallet",
|
|
229
|
+
color: "#34d399",
|
|
230
|
+
icon: <KeyRound size={12} />,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
174
234
|
const rootStyle: CSSProperties = {
|
|
175
235
|
position: "absolute",
|
|
176
236
|
right: 0,
|
|
@@ -217,6 +277,7 @@ export function WalletSelectorDropdown({
|
|
|
217
277
|
onClose,
|
|
218
278
|
onAddWallet,
|
|
219
279
|
onShowPortfolio,
|
|
280
|
+
onRenewAgentWallet,
|
|
220
281
|
onLogout,
|
|
221
282
|
onNotify,
|
|
222
283
|
vipThreshold = 10,
|
|
@@ -230,6 +291,7 @@ export function WalletSelectorDropdown({
|
|
|
230
291
|
accentColor,
|
|
231
292
|
principalColors,
|
|
232
293
|
backgroundColor = DEFAULT_BACKGROUND_COLOR,
|
|
294
|
+
extraFooterItems,
|
|
233
295
|
}: WalletSelectorDropdownProps): ReactNode {
|
|
234
296
|
const { user, wallets, selectedWalletId, selectWallet, logout, authMethod } =
|
|
235
297
|
useHypurrConnectInternal();
|
|
@@ -270,12 +332,17 @@ export function WalletSelectorDropdown({
|
|
|
270
332
|
const { wallet, label } = item;
|
|
271
333
|
const isSelected = wallet.id === selectedWalletId;
|
|
272
334
|
const compactAddress = formatCompactAddress(wallet.ethereumAddress);
|
|
335
|
+
const agentExpiryTitle = getAgentExpiryTitle({
|
|
336
|
+
isAgent: !!wallet.isAgent,
|
|
337
|
+
agentExpiresAt: wallet.agentExpiresAt,
|
|
338
|
+
});
|
|
273
339
|
return (
|
|
274
340
|
<WalletRow
|
|
275
341
|
key={wallet.id}
|
|
276
342
|
depth={depth}
|
|
277
343
|
isSelected={isSelected}
|
|
278
344
|
label={label}
|
|
345
|
+
walletType={getWalletTypeMeta(wallet)}
|
|
279
346
|
compactAddress={compactAddress}
|
|
280
347
|
onSelect={() => {
|
|
281
348
|
selectWallet(wallet.id);
|
|
@@ -290,6 +357,15 @@ export function WalletSelectorDropdown({
|
|
|
290
357
|
: undefined
|
|
291
358
|
}
|
|
292
359
|
onCopy={() => handleCopyAddress(wallet.ethereumAddress)}
|
|
360
|
+
agentExpiryTitle={agentExpiryTitle}
|
|
361
|
+
onRenewAgentWallet={
|
|
362
|
+
wallet.isAgent && onRenewAgentWallet
|
|
363
|
+
? () => {
|
|
364
|
+
onRenewAgentWallet(wallet as HyperliquidWallet);
|
|
365
|
+
onClose();
|
|
366
|
+
}
|
|
367
|
+
: undefined
|
|
368
|
+
}
|
|
293
369
|
colors={colors}
|
|
294
370
|
/>
|
|
295
371
|
);
|
|
@@ -557,6 +633,15 @@ export function WalletSelectorDropdown({
|
|
|
557
633
|
icon={<User size={14} />}
|
|
558
634
|
label="Profile & Settings"
|
|
559
635
|
/>
|
|
636
|
+
{extraFooterItems?.map((item) => (
|
|
637
|
+
<FooterBtn
|
|
638
|
+
key={item.key}
|
|
639
|
+
onClick={item.onClick}
|
|
640
|
+
icon={item.icon}
|
|
641
|
+
label={item.label}
|
|
642
|
+
hoverColor={item.hoverColor}
|
|
643
|
+
/>
|
|
644
|
+
))}
|
|
560
645
|
<FooterBtn
|
|
561
646
|
onClick={handleLogout}
|
|
562
647
|
icon={<LogOut size={14} />}
|
|
@@ -580,6 +665,7 @@ export function WalletSelectorDropdown({
|
|
|
580
665
|
principalColors={principalColors}
|
|
581
666
|
onWalletDeleted={onWalletDeleted}
|
|
582
667
|
onWalletRenamed={onWalletRenamed}
|
|
668
|
+
onRenewAgentWallet={onRenewAgentWallet}
|
|
583
669
|
onShowPortfolio={
|
|
584
670
|
onShowPortfolio
|
|
585
671
|
? (wallet) => {
|
|
@@ -628,21 +714,30 @@ function WalletRow({
|
|
|
628
714
|
depth,
|
|
629
715
|
isSelected,
|
|
630
716
|
label,
|
|
717
|
+
walletType,
|
|
631
718
|
compactAddress,
|
|
632
719
|
onSelect,
|
|
633
720
|
onShowPortfolio,
|
|
634
721
|
onCopy,
|
|
722
|
+
agentExpiryTitle,
|
|
723
|
+
onRenewAgentWallet,
|
|
635
724
|
colors,
|
|
636
725
|
}: {
|
|
637
726
|
depth: number;
|
|
638
727
|
isSelected: boolean;
|
|
639
728
|
label: string | null;
|
|
729
|
+
walletType: WalletTypeMeta;
|
|
640
730
|
compactAddress: string;
|
|
641
731
|
onSelect: () => void;
|
|
642
732
|
onShowPortfolio?: () => void;
|
|
643
733
|
onCopy: () => void;
|
|
734
|
+
agentExpiryTitle?: string | null;
|
|
735
|
+
onRenewAgentWallet?: () => void;
|
|
644
736
|
colors: PrincipalColors;
|
|
645
737
|
}) {
|
|
738
|
+
const isAgentExpired = !!agentExpiryTitle;
|
|
739
|
+
const walletTextColor = isAgentExpired ? EXPIRED_AGENT_COLOR : "#d1d5db";
|
|
740
|
+
const mutedTextColor = isAgentExpired ? "rgba(245,158,11,0.78)" : "#6b7280";
|
|
646
741
|
return (
|
|
647
742
|
<div
|
|
648
743
|
style={{
|
|
@@ -652,7 +747,7 @@ function WalletRow({
|
|
|
652
747
|
paddingTop: 6,
|
|
653
748
|
paddingBottom: 6,
|
|
654
749
|
fontSize: 14,
|
|
655
|
-
color:
|
|
750
|
+
color: walletTextColor,
|
|
656
751
|
display: "flex",
|
|
657
752
|
alignItems: "center",
|
|
658
753
|
background: isSelected ? "rgba(255,255,255,0.06)" : "transparent",
|
|
@@ -689,6 +784,19 @@ function WalletRow({
|
|
|
689
784
|
padding: 0,
|
|
690
785
|
}}
|
|
691
786
|
>
|
|
787
|
+
<span
|
|
788
|
+
title={walletType.label}
|
|
789
|
+
aria-label={walletType.label}
|
|
790
|
+
style={{
|
|
791
|
+
display: "inline-flex",
|
|
792
|
+
alignItems: "center",
|
|
793
|
+
justifyContent: "center",
|
|
794
|
+
flexShrink: 0,
|
|
795
|
+
color: isAgentExpired ? walletTextColor : walletType.color,
|
|
796
|
+
}}
|
|
797
|
+
>
|
|
798
|
+
{walletType.icon}
|
|
799
|
+
</span>
|
|
692
800
|
{label ? (
|
|
693
801
|
<>
|
|
694
802
|
<span
|
|
@@ -696,7 +804,11 @@ function WalletRow({
|
|
|
696
804
|
overflow: "hidden",
|
|
697
805
|
textOverflow: "ellipsis",
|
|
698
806
|
whiteSpace: "nowrap",
|
|
699
|
-
color:
|
|
807
|
+
color: isAgentExpired
|
|
808
|
+
? walletTextColor
|
|
809
|
+
: isSelected
|
|
810
|
+
? "#fff"
|
|
811
|
+
: undefined,
|
|
700
812
|
fontWeight: isSelected ? 500 : undefined,
|
|
701
813
|
}}
|
|
702
814
|
>
|
|
@@ -707,7 +819,7 @@ function WalletRow({
|
|
|
707
819
|
fontSize: 12,
|
|
708
820
|
fontWeight: 400,
|
|
709
821
|
flexShrink: 0,
|
|
710
|
-
color:
|
|
822
|
+
color: mutedTextColor,
|
|
711
823
|
}}
|
|
712
824
|
>
|
|
713
825
|
({compactAddress})
|
|
@@ -717,7 +829,11 @@ function WalletRow({
|
|
|
717
829
|
<span
|
|
718
830
|
style={{
|
|
719
831
|
fontSize: 12,
|
|
720
|
-
color:
|
|
832
|
+
color: isAgentExpired
|
|
833
|
+
? walletTextColor
|
|
834
|
+
: isSelected
|
|
835
|
+
? "#fff"
|
|
836
|
+
: "#9ca3af",
|
|
721
837
|
fontWeight: isSelected ? 500 : undefined,
|
|
722
838
|
}}
|
|
723
839
|
>
|
|
@@ -725,6 +841,12 @@ function WalletRow({
|
|
|
725
841
|
</span>
|
|
726
842
|
)}
|
|
727
843
|
</button>
|
|
844
|
+
{agentExpiryTitle && (
|
|
845
|
+
<AgentExpiryWarningIcon
|
|
846
|
+
message={agentExpiryTitle}
|
|
847
|
+
onClick={onRenewAgentWallet}
|
|
848
|
+
/>
|
|
849
|
+
)}
|
|
728
850
|
<div
|
|
729
851
|
data-row-actions
|
|
730
852
|
style={{
|
|
@@ -734,6 +856,18 @@ function WalletRow({
|
|
|
734
856
|
transition: "opacity 120ms",
|
|
735
857
|
}}
|
|
736
858
|
>
|
|
859
|
+
{onRenewAgentWallet && (
|
|
860
|
+
<RowIconBtn
|
|
861
|
+
title="Renew agent wallet"
|
|
862
|
+
hoverColor={isAgentExpired ? EXPIRED_AGENT_COLOR : colors.accent}
|
|
863
|
+
onClick={(e) => {
|
|
864
|
+
e.stopPropagation();
|
|
865
|
+
onRenewAgentWallet();
|
|
866
|
+
}}
|
|
867
|
+
>
|
|
868
|
+
<RefreshCw size={12} />
|
|
869
|
+
</RowIconBtn>
|
|
870
|
+
)}
|
|
737
871
|
{onShowPortfolio && (
|
|
738
872
|
<RowIconBtn
|
|
739
873
|
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}
|