@hfunlabs/hypurr-connect 0.1.12 → 0.1.13

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.
@@ -0,0 +1,197 @@
1
+ import type { CSSProperties, SVGProps } from "react";
2
+
3
+ interface IconProps extends Omit<SVGProps<SVGSVGElement>, "fill"> {
4
+ size?: number;
5
+ color?: string;
6
+ fill?: string;
7
+ style?: CSSProperties;
8
+ }
9
+
10
+ function svgBase({
11
+ size = 16,
12
+ color = "currentColor",
13
+ fill = "none",
14
+ ...rest
15
+ }: IconProps) {
16
+ return {
17
+ width: size,
18
+ height: size,
19
+ viewBox: "0 0 24 24",
20
+ fill,
21
+ stroke: color,
22
+ strokeWidth: 2,
23
+ strokeLinecap: "round" as const,
24
+ strokeLinejoin: "round" as const,
25
+ ...rest,
26
+ };
27
+ }
28
+
29
+ export function Copy(props: IconProps) {
30
+ return (
31
+ <svg {...svgBase(props)}>
32
+ <rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
33
+ <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
34
+ </svg>
35
+ );
36
+ }
37
+
38
+ export function Star(props: IconProps) {
39
+ return (
40
+ <svg {...svgBase(props)}>
41
+ <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
42
+ </svg>
43
+ );
44
+ }
45
+
46
+ export function User(props: IconProps) {
47
+ return (
48
+ <svg {...svgBase(props)}>
49
+ <path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
50
+ <circle cx="12" cy="7" r="4" />
51
+ </svg>
52
+ );
53
+ }
54
+
55
+ export function Zap(props: IconProps) {
56
+ return (
57
+ <svg {...svgBase(props)}>
58
+ <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
59
+ </svg>
60
+ );
61
+ }
62
+
63
+ export function Wallet(props: IconProps) {
64
+ return (
65
+ <svg {...svgBase(props)}>
66
+ <path d="M20 12V8H6a2 2 0 0 1-2-2c0-1.1.9-2 2-2h12v4" />
67
+ <path d="M4 6v12c0 1.1.9 2 2 2h14v-4" />
68
+ <path d="M18 12a2 2 0 0 0-2 2c0 1.1.9 2 2 2h4v-4z" />
69
+ </svg>
70
+ );
71
+ }
72
+
73
+ export function TrendingUp(props: IconProps) {
74
+ return (
75
+ <svg {...svgBase(props)}>
76
+ <polyline points="22 7 13.5 15.5 8.5 10.5 2 17" />
77
+ <polyline points="16 7 22 7 22 13" />
78
+ </svg>
79
+ );
80
+ }
81
+
82
+ export function Trash2(props: IconProps) {
83
+ return (
84
+ <svg {...svgBase(props)}>
85
+ <path d="M3 6h18" />
86
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
87
+ <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
88
+ <line x1="10" x2="10" y1="11" y2="17" />
89
+ <line x1="14" x2="14" y1="11" y2="17" />
90
+ </svg>
91
+ );
92
+ }
93
+
94
+ export function Pencil(props: IconProps) {
95
+ return (
96
+ <svg {...svgBase(props)}>
97
+ <path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
98
+ <path d="m15 5 4 4" />
99
+ </svg>
100
+ );
101
+ }
102
+
103
+ export function LayoutDashboard(props: IconProps) {
104
+ return (
105
+ <svg {...svgBase(props)}>
106
+ <rect width="7" height="9" x="3" y="3" rx="1" />
107
+ <rect width="7" height="5" x="14" y="3" rx="1" />
108
+ <rect width="7" height="9" x="14" y="12" rx="1" />
109
+ <rect width="7" height="5" x="3" y="16" rx="1" />
110
+ </svg>
111
+ );
112
+ }
113
+
114
+ export function AlertTriangle(props: IconProps) {
115
+ return (
116
+ <svg {...svgBase(props)}>
117
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
118
+ <line x1="12" x2="12" y1="9" y2="13" />
119
+ <line x1="12" x2="12.01" y1="17" y2="17" />
120
+ </svg>
121
+ );
122
+ }
123
+
124
+ export function Loader2(props: IconProps) {
125
+ const { size = 16, color = "currentColor", style, ...rest } = props;
126
+ return (
127
+ <svg
128
+ width={size}
129
+ height={size}
130
+ viewBox="0 0 24 24"
131
+ fill="none"
132
+ stroke={color}
133
+ strokeWidth={2}
134
+ strokeLinecap="round"
135
+ strokeLinejoin="round"
136
+ style={{
137
+ animation: "hypurr-spin 1s linear infinite",
138
+ ...style,
139
+ }}
140
+ {...rest}
141
+ >
142
+ <path d="M21 12a9 9 0 1 1-6.219-8.56" />
143
+ </svg>
144
+ );
145
+ }
146
+
147
+ export function Crown(props: IconProps) {
148
+ return (
149
+ <svg {...svgBase(props)}>
150
+ <path d="M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z" />
151
+ <path d="M5 21h14" />
152
+ </svg>
153
+ );
154
+ }
155
+
156
+ export function Folder(props: IconProps) {
157
+ return (
158
+ <svg {...svgBase(props)}>
159
+ <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
160
+ </svg>
161
+ );
162
+ }
163
+
164
+ export function LogOut(props: IconProps) {
165
+ return (
166
+ <svg {...svgBase(props)}>
167
+ <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
168
+ <polyline points="16 17 21 12 16 7" />
169
+ <line x1="21" x2="9" y1="12" y2="12" />
170
+ </svg>
171
+ );
172
+ }
173
+
174
+ export function Plus(props: IconProps) {
175
+ return (
176
+ <svg {...svgBase(props)}>
177
+ <path d="M5 12h14" />
178
+ <path d="M12 5v14" />
179
+ </svg>
180
+ );
181
+ }
182
+
183
+ export function X(props: IconProps) {
184
+ return (
185
+ <svg {...svgBase(props)}>
186
+ <path d="M18 6 6 18" />
187
+ <path d="m6 6 12 12" />
188
+ </svg>
189
+ );
190
+ }
191
+
192
+ /** Inject the keyframes used by {@link Loader2}. Mount once at the top of any modal that spins. */
193
+ export function SpinKeyframes() {
194
+ return (
195
+ <style>{`@keyframes hypurr-spin { to { transform: rotate(360deg); } }`}</style>
196
+ );
197
+ }
package/src/index.ts CHANGED
@@ -4,6 +4,15 @@ export {
4
4
  } from "./HypurrConnectProvider";
5
5
  export { LoginModal } from "./LoginModal";
6
6
  export type { LoginModalProps } from "./LoginModal";
7
+ export { WalletSelectorDropdown } from "./WalletSelectorDropdown";
8
+ export type { WalletSelectorDropdownProps } from "./WalletSelectorDropdown";
9
+ export { UserProfileModal } from "./UserProfileModal";
10
+ export type { UserProfileModalProps, SlippageOption } from "./UserProfileModal";
11
+ export { DeleteWalletModal } from "./DeleteWalletModal";
12
+ export type { DeleteWalletModalProps } from "./DeleteWalletModal";
13
+ export { RenameWalletModal } from "./RenameWalletModal";
14
+ export type { RenameWalletModalProps } from "./RenameWalletModal";
15
+ export type { PrincipalColors, PrincipalColorOverrides } from "./profileStyles";
7
16
  export { GrpcExchangeTransport } from "./GrpcExchangeTransport";
8
17
  export type { GrpcExchangeTransportConfig } from "./GrpcExchangeTransport";
9
18
  export { createTelegramClient, createStaticClient } from "./grpc";
@@ -0,0 +1,213 @@
1
+ import type { CSSProperties } from "react";
2
+
3
+ export const profileColors = {
4
+ accent: "#a855f7",
5
+ panel: "rgba(14,18,24,0.8)",
6
+ panelSolid: "#0e1218",
7
+ backdrop: "rgba(0,0,0,0.7)",
8
+ border: "#1f2937",
9
+ surfaceBtn: "#0D1219",
10
+ surfaceBtnHover: "#15171A",
11
+ surfaceBtnActive: "#1E2124",
12
+ surfaceBd: "#262A30",
13
+ surfaceBdHover: "#444548",
14
+ surfaceBdActive: "#4B4D50",
15
+ text: "#ffffff",
16
+ muted: "#aab1c1",
17
+ subdued: "#7d8597",
18
+ disabled: "#4b5563",
19
+ danger: "#f87171",
20
+ dangerStrong: "#ef4444",
21
+ purple: "#d8b4fe",
22
+ yellow: "#eab308",
23
+ };
24
+
25
+ export const fontFamily = {
26
+ sans: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif",
27
+ mono: "Google Sans Code, Roboto Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
28
+ };
29
+
30
+ export interface PrincipalColors {
31
+ accent: string;
32
+ accentText: string;
33
+ accentBackground: string;
34
+ accentBorder: string;
35
+ accentHoverBackground: string;
36
+ }
37
+
38
+ export type PrincipalColorOverrides = Partial<PrincipalColors>;
39
+
40
+ const colorWithAlpha = (color: string, alpha: number): string => {
41
+ const hex = color.trim();
42
+ const shortHex = /^#([0-9a-f]{3})$/i.exec(hex);
43
+ const longHex = /^#([0-9a-f]{6})$/i.exec(hex);
44
+
45
+ if (shortHex) {
46
+ const [r, g, b] = shortHex[1].split("").map((value) => value + value);
47
+ return `rgba(${parseInt(r, 16)},${parseInt(g, 16)},${parseInt(b, 16)},${alpha})`;
48
+ }
49
+
50
+ if (longHex) {
51
+ const value = longHex[1];
52
+ return `rgba(${parseInt(value.slice(0, 2), 16)},${parseInt(
53
+ value.slice(2, 4),
54
+ 16,
55
+ )},${parseInt(value.slice(4, 6), 16)},${alpha})`;
56
+ }
57
+
58
+ return `color-mix(in srgb, ${color} ${Math.round(alpha * 100)}%, transparent)`;
59
+ };
60
+
61
+ export const resolvePrincipalColors = (
62
+ accentColor?: string,
63
+ overrides: PrincipalColorOverrides = {},
64
+ ): PrincipalColors => {
65
+ const hasCustomAccent = !!accentColor || !!overrides.accent;
66
+ const accent = overrides.accent ?? accentColor ?? profileColors.accent;
67
+ const accentText =
68
+ overrides.accentText ?? (hasCustomAccent ? accent : profileColors.purple);
69
+
70
+ return {
71
+ accent,
72
+ accentText,
73
+ accentBackground: overrides.accentBackground ?? colorWithAlpha(accent, 0.1),
74
+ accentBorder: overrides.accentBorder ?? colorWithAlpha(accent, 0.25),
75
+ accentHoverBackground:
76
+ overrides.accentHoverBackground ?? colorWithAlpha(accent, 0.1),
77
+ };
78
+ };
79
+
80
+ export const modalBackdropStyle = (zIndex: number): CSSProperties => ({
81
+ position: "fixed",
82
+ inset: 0,
83
+ zIndex,
84
+ background: profileColors.backdrop,
85
+ backdropFilter: "blur(4px)",
86
+ WebkitBackdropFilter: "blur(4px)",
87
+ });
88
+
89
+ export const modalWrapperStyle = (
90
+ zIndex: number,
91
+ padding = 0,
92
+ ): CSSProperties => ({
93
+ position: "fixed",
94
+ inset: 0,
95
+ zIndex,
96
+ display: "flex",
97
+ alignItems: "center",
98
+ justifyContent: "center",
99
+ padding,
100
+ });
101
+
102
+ export const modalPanelStyle = (solid = false): CSSProperties => ({
103
+ position: "relative",
104
+ width: "100%",
105
+ maxWidth: 448,
106
+ background: solid ? profileColors.panelSolid : profileColors.panel,
107
+ backdropFilter: solid ? undefined : "blur(10px)",
108
+ WebkitBackdropFilter: solid ? undefined : "blur(10px)",
109
+ border: `1px solid ${profileColors.border}`,
110
+ borderRadius: 8,
111
+ boxShadow: "0 25px 50px -12px rgba(0,0,0,0.6)",
112
+ overflow: "hidden",
113
+ fontFamily: fontFamily.sans,
114
+ });
115
+
116
+ export const modalHeaderStyle: CSSProperties = {
117
+ position: "relative",
118
+ display: "flex",
119
+ alignItems: "center",
120
+ justifyContent: "center",
121
+ padding: "24px 24px 20px",
122
+ borderBottom: `1px solid ${profileColors.border}`,
123
+ };
124
+
125
+ export const titleStyle: CSSProperties = {
126
+ margin: 0,
127
+ fontSize: 14,
128
+ lineHeight: "1.25rem",
129
+ fontWeight: 600,
130
+ color: profileColors.text,
131
+ };
132
+
133
+ export const closeBtnStyle = (disabled = false): CSSProperties => ({
134
+ position: "absolute",
135
+ right: 24,
136
+ background: "transparent",
137
+ border: "none",
138
+ color: profileColors.muted,
139
+ cursor: disabled ? "not-allowed" : "pointer",
140
+ padding: 0,
141
+ display: "flex",
142
+ opacity: disabled ? 0.4 : 1,
143
+ transition: "color 150ms",
144
+ });
145
+
146
+ export const upperLabelStyle: CSSProperties = {
147
+ fontSize: 11,
148
+ lineHeight: "1rem",
149
+ fontWeight: 500,
150
+ textTransform: "uppercase",
151
+ letterSpacing: "0.1em",
152
+ };
153
+
154
+ export const raisedButtonStyle = (
155
+ state: "default" | "hover" | "active" | "disabled" = "default",
156
+ ): CSSProperties => {
157
+ if (state === "active") {
158
+ return {
159
+ backgroundColor: profileColors.surfaceBtnActive,
160
+ backgroundImage:
161
+ "linear-gradient(to bottom, rgba(255,255,255,0.05), transparent)",
162
+ boxShadow:
163
+ "inset 0 1px 0 rgba(255,255,255,0.18), inset 0 -1px 0 rgba(0,0,0,0.45), inset 0 0 0 1px #4B4D50, 0 1px 2px rgba(0,0,0,0.5)",
164
+ color: profileColors.text,
165
+ border: "none",
166
+ };
167
+ }
168
+
169
+ if (state === "disabled") {
170
+ return {
171
+ backgroundColor: profileColors.surfaceBtn,
172
+ backgroundImage:
173
+ "linear-gradient(to bottom, rgba(255,255,255,0.02), transparent)",
174
+ boxShadow:
175
+ "inset 0 1px 0 rgba(255,255,255,0.05), inset 0 -1px 0 rgba(0,0,0,0.25), inset 0 0 0 1px #1c2026",
176
+ color: profileColors.subdued,
177
+ border: "none",
178
+ cursor: "not-allowed",
179
+ };
180
+ }
181
+
182
+ if (state === "hover") {
183
+ return {
184
+ backgroundColor: profileColors.surfaceBtnHover,
185
+ backgroundImage:
186
+ "linear-gradient(to bottom, rgba(255,255,255,0.04), transparent)",
187
+ boxShadow:
188
+ "inset 0 1px 0 rgba(255,255,255,0.14), inset 0 -1px 0 rgba(0,0,0,0.40), inset 0 0 0 1px #444548",
189
+ color: "#d1d5db",
190
+ border: "none",
191
+ };
192
+ }
193
+
194
+ return {
195
+ backgroundColor: profileColors.surfaceBtn,
196
+ backgroundImage:
197
+ "linear-gradient(to bottom, rgba(255,255,255,0.03), transparent)",
198
+ boxShadow:
199
+ "inset 0 1px 0 rgba(255,255,255,0.10), inset 0 -1px 0 rgba(0,0,0,0.35), inset 0 0 0 1px #262A30",
200
+ color: profileColors.muted,
201
+ border: "none",
202
+ };
203
+ };
204
+
205
+ export const dangerOutlineButtonStyle = (
206
+ enabled: boolean,
207
+ hovered = false,
208
+ ): CSSProperties => ({
209
+ background: enabled && hovered ? "rgba(248,113,113,0.1)" : "transparent",
210
+ border: `1px solid ${enabled ? profileColors.danger : "#374151"}`,
211
+ color: enabled ? profileColors.danger : profileColors.disabled,
212
+ cursor: enabled ? "pointer" : "not-allowed",
213
+ });
package/src/types.ts CHANGED
@@ -25,19 +25,15 @@ export interface HypurrConnectConfig {
25
25
  isTestnet?: boolean;
26
26
  /** Polling interval in ms for TWAP/Scale session updates. Default 5000. Set 0 to disable. */
27
27
  sessionPollInterval?: number;
28
- telegram: {
29
- /** Deprecated for the hub flow; retained for older consumers. */
30
- botUsername?: string;
31
- /** Deprecated for the hub flow; retained for older consumers. */
32
- botId?: string;
33
- /** Deprecated: Telegram login is now handled by the auth hub. */
34
- useWidget?: boolean;
28
+ telegram?: {
35
29
  /** Auth hub login URL. Defaults to https://auth.hypurr.fun/login. */
36
30
  authHubUrl?: string;
37
31
  /** Optional callback URL. Defaults to the current page without auth query params. */
38
32
  returnTo?: string | (() => string);
39
33
  /** Requested hub scopes. Defaults to the scopes required by this SDK. */
40
34
  scope?: string | string[];
35
+ /** @deprecated Telegram login is handled by the auth hub; this option is ignored. */
36
+ useWidget?: boolean;
41
37
  };
42
38
  }
43
39
 
@@ -217,6 +213,7 @@ export interface HypurrConnectState {
217
213
  // Wallet management (Telegram only)
218
214
  createWallet: (name: string) => Promise<HyperliquidWallet>;
219
215
  deleteWallet: (walletId: number) => Promise<void>;
216
+ renameWallet: (walletId: number, name: string) => Promise<void>;
220
217
  refreshWallets: () => void;
221
218
 
222
219
  // Wallet packs & labels (Telegram only)
@@ -271,9 +268,6 @@ export interface HypurrConnectState {
271
268
  agentReady: boolean;
272
269
  clearAgent: () => void;
273
270
 
274
- // Telegram config
275
- botId: string;
276
-
277
271
  // Low-level access
278
272
  /** Deprecated: JWT auth leaves authData empty; use `telegramRpcOptions` for low-level calls. */
279
273
  authDataMap: Record<string, string>;
@@ -1,62 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
- import type { TelegramLoginData } from "./types";
3
-
4
- const WIDGET_SCRIPT_URL = "https://telegram.org/js/telegram-widget.js?22";
5
- const CALLBACK_NAME = "__hypurrConnectTelegramAuth";
6
-
7
- export interface TelegramLoginWidgetProps {
8
- botUsername: string;
9
- onAuth: (data: TelegramLoginData) => void;
10
- buttonSize?: "large" | "medium" | "small";
11
- cornerRadius?: number;
12
- showUserPhoto?: boolean;
13
- requestAccess?: boolean;
14
- }
15
-
16
- export function TelegramLoginWidget({
17
- botUsername,
18
- onAuth,
19
- buttonSize = "large",
20
- cornerRadius,
21
- showUserPhoto = true,
22
- requestAccess = true,
23
- }: TelegramLoginWidgetProps) {
24
- const containerRef = useRef<HTMLDivElement>(null);
25
- const onAuthRef = useRef(onAuth);
26
- onAuthRef.current = onAuth;
27
-
28
- useEffect(() => {
29
- const container = containerRef.current;
30
- if (!container) return;
31
-
32
- (window as unknown as Record<string, unknown>)[CALLBACK_NAME] = (
33
- user: TelegramLoginData,
34
- ) => {
35
- onAuthRef.current(user);
36
- };
37
-
38
- const script = document.createElement("script");
39
- script.src = WIDGET_SCRIPT_URL;
40
- script.async = true;
41
- script.setAttribute("data-telegram-login", botUsername);
42
- script.setAttribute("data-size", buttonSize);
43
- script.setAttribute("data-onauth", `${CALLBACK_NAME}(user)`);
44
- script.setAttribute("data-userpic", String(showUserPhoto));
45
- if (requestAccess) {
46
- script.setAttribute("data-request-access", "write");
47
- }
48
- if (cornerRadius !== undefined) {
49
- script.setAttribute("data-radius", String(cornerRadius));
50
- }
51
-
52
- container.innerHTML = "";
53
- container.appendChild(script);
54
-
55
- return () => {
56
- container.innerHTML = "";
57
- delete (window as unknown as Record<string, unknown>)[CALLBACK_NAME];
58
- };
59
- }, [botUsername, buttonSize, cornerRadius, showUserPhoto, requestAccess]);
60
-
61
- return <div ref={containerRef} />;
62
- }