@taskon/widget-react 0.0.1

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 (37) hide show
  1. package/README.md +1064 -0
  2. package/dist/CommunityTaskList.css +5010 -0
  3. package/dist/EligibilityInfo.css +1966 -0
  4. package/dist/LeaderboardWidget.css +815 -0
  5. package/dist/Quest.css +4584 -0
  6. package/dist/Table.css +389 -0
  7. package/dist/TaskOnProvider.css +163 -0
  8. package/dist/WidgetShell.css +182 -0
  9. package/dist/chunks/CommunityTaskList-CrH6r4Av.js +6886 -0
  10. package/dist/chunks/EligibilityInfo-DesW9-k9.js +24900 -0
  11. package/dist/chunks/LeaderboardWidget-BSGpHKTk.js +1156 -0
  12. package/dist/chunks/Quest-uSIVq78I.js +8581 -0
  13. package/dist/chunks/Table-CWGf2FKV.js +449 -0
  14. package/dist/chunks/TaskOnProvider-QMwxGL44.js +1435 -0
  15. package/dist/chunks/ThemeProvider-Cs8IUVQj.js +1118 -0
  16. package/dist/chunks/WidgetShell-NlOgn1x5.js +1517 -0
  17. package/dist/chunks/common-ja-DWhTaFHb.js +23 -0
  18. package/dist/chunks/common-ko-80ezXsMG.js +23 -0
  19. package/dist/chunks/index-CwMvO_wZ.js +777 -0
  20. package/dist/chunks/leaderboardwidget-ja-Bj6gz6y1.js +119 -0
  21. package/dist/chunks/leaderboardwidget-ko-f1cLO9ic.js +119 -0
  22. package/dist/chunks/useToast-BGJhd3BX.js +93 -0
  23. package/dist/chunks/useWidgetLocale-BVcopbZS.js +74 -0
  24. package/dist/chunks/usercenter-ja-DBj_dtuz.js +329 -0
  25. package/dist/chunks/usercenter-ko-DYTkHAld.js +329 -0
  26. package/dist/community-task.d.ts +451 -0
  27. package/dist/community-task.js +9 -0
  28. package/dist/core.d.ts +803 -0
  29. package/dist/core.js +22 -0
  30. package/dist/index.css +3662 -0
  31. package/dist/index.d.ts +1627 -0
  32. package/dist/index.js +7372 -0
  33. package/dist/leaderboard.d.ts +547 -0
  34. package/dist/leaderboard.js +17 -0
  35. package/dist/quest.d.ts +389 -0
  36. package/dist/quest.js +8 -0
  37. package/package.json +97 -0
package/README.md ADDED
@@ -0,0 +1,1064 @@
1
+ # @taskon/widget-react
2
+
3
+ TaskOn React Widget - Embeddable white-label components for integrating TaskOn quest/task functionality into your application.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @taskon/widget-react @taskon/core
9
+ # or
10
+ pnpm add @taskon/widget-react @taskon/core
11
+ # or
12
+ yarn add @taskon/widget-react @taskon/core
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```tsx
18
+ // Providers from core (minimal CSS ~3KB)
19
+ import { TaskOnProvider } from "@taskon/widget-react/core";
20
+ // Widgets from their sub-paths (isolated CSS)
21
+ import { QuestWidget } from "@taskon/widget-react/quest";
22
+
23
+ const App = () => (
24
+ <TaskOnProvider
25
+ config={{
26
+ apiKey: "your-api-key",
27
+ }}
28
+ >
29
+ <YourApp />
30
+ </TaskOnProvider>
31
+ );
32
+ ```
33
+
34
+ ## Import Methods
35
+
36
+ ### Recommended: Sub-path Imports (Optimal CSS)
37
+
38
+ ```tsx
39
+ // Core: Providers and hooks only (~3KB CSS for Toast)
40
+ import { TaskOnProvider, ThemeProvider, useTaskOnAuth } from "@taskon/widget-react/core";
41
+
42
+ // Widgets: Each loads only its own CSS
43
+ import { QuestWidget } from "@taskon/widget-react/quest"; // ~26KB CSS
44
+ import { CommunityTaskList } from "@taskon/widget-react/community-task"; // ~51KB CSS
45
+ import { LeaderboardWidget } from "@taskon/widget-react/leaderboard"; // ~17KB CSS
46
+ ```
47
+
48
+ ### Alternative: Main Entry Import (Loads All CSS)
49
+
50
+ ```tsx
51
+ // Loads ALL widget CSS (~92KB) - not recommended for production
52
+ import { TaskOnProvider, QuestWidget, CommunityTaskList } from "@taskon/widget-react";
53
+ ```
54
+
55
+ ## Usage Example
56
+
57
+ ```tsx
58
+ import { TaskOnProvider, useTaskOnAuth } from "@taskon/widget-react/core";
59
+ import { QuestWidget } from "@taskon/widget-react/quest";
60
+
61
+ const App = () => (
62
+ <TaskOnProvider
63
+ config={{
64
+ apiKey: "your-api-key",
65
+ }}
66
+ >
67
+ <YourApp />
68
+ </TaskOnProvider>
69
+ );
70
+
71
+ // Your app controls the login UI
72
+ const YourApp = () => {
73
+ const { userId, login, logout } = useTaskOnAuth();
74
+ const { user, getSignature } = useYourAuth(); // Your auth hook
75
+
76
+ // When user logs in to your app, login to TaskOn
77
+ useEffect(() => {
78
+ if (user?.email && !userId) {
79
+ const { sign, timestamp } = getSignature(); // Get signature from your backend
80
+ login({ method: "email", value: user.email, sign, timestamp });
81
+ }
82
+ }, [user, userId]);
83
+
84
+ // When user logs out from your app, logout from TaskOn
85
+ useEffect(() => {
86
+ if (!user && userId) {
87
+ logout();
88
+ }
89
+ }, [user, userId]);
90
+
91
+ return (
92
+ <div>
93
+ {userId && <p>TaskOn User ID: {userId}</p>}
94
+ {/* Theme configured in TaskOn Dashboard */}
95
+ <QuestWidget configId="cfg_abc123" />
96
+ </div>
97
+ );
98
+ };
99
+ ```
100
+
101
+ ## Architecture
102
+
103
+ ```
104
+ ┌─ TaskOnProvider ─────────────────────────────────────────┐
105
+ │ config: { apiKey } │
106
+ │ Purpose: Authentication only │
107
+ │ │
108
+ │ ┌─ Mode A: Cloud Config ──────────────────────────────┐ │
109
+ │ │ <QuestWidget configId="cfg_abc123" /> │ │
110
+ │ │ → Theme from TaskOn cloud │ │
111
+ │ └─────────────────────────────────────────────────────┘ │
112
+ │ │
113
+ │ ┌─ Mode B: Local Theme ───────────────────────────────┐ │
114
+ │ │ <ThemeProvider theme={{ mode: 'dark' }}> │ │
115
+ │ │ <QuestWidget /> /* no configId */ │ │
116
+ │ │ </ThemeProvider> │ │
117
+ │ └─────────────────────────────────────────────────────┘ │
118
+ └──────────────────────────────────────────────────────────┘
119
+ ```
120
+
121
+ ### Theme Source (mutually exclusive)
122
+
123
+ | Mode | Theme Source |
124
+ | --------------------- | ------------------------------------ |
125
+ | `configId` provided | Cloud config (ThemeProvider ignored) |
126
+ | No `configId` | ThemeProvider or default theme |
127
+
128
+ ## Security
129
+
130
+ TaskOn uses **API Key** authentication to verify that widget requests come from authorized projects.
131
+
132
+ ### Step 1: Get API Key from TaskOn Dashboard
133
+
134
+ ```
135
+ API Key: /KDiqEFNCaGTVTdTpCFrZOsUj5vDi5uGLSFmwyHeboE= (for X-API-Key header)
136
+ ```
137
+
138
+ ### Step 2: Configure TaskOnProvider
139
+
140
+ ```tsx
141
+ <TaskOnProvider
142
+ config={{
143
+ apiKey: "your-api-key",
144
+ }}
145
+ >
146
+ <YourApp />
147
+ </TaskOnProvider>;
148
+ ```
149
+
150
+ ### HTTP Headers
151
+
152
+ All API requests include:
153
+
154
+ ```
155
+ X-API-Key: your-api-key # Project authorization
156
+ Authorization: Bearer xxx # User authorization (after login)
157
+ ```
158
+
159
+ ### Security Best Practices
160
+
161
+ 1. **Keep API Key secure** - Don't expose in public repositories
162
+ 2. **Use HTTPS** - All communication must be encrypted
163
+
164
+ ## TaskOnProvider
165
+
166
+ The root provider component for authentication. Must wrap your application.
167
+
168
+ ### Props
169
+
170
+ | Prop | Type | Required | Default | Description |
171
+ | ------------ | ------------------------ | -------- | ------- | -------------------- |
172
+ | `config` | `TaskOnProviderConfig` | Yes | - | Configuration object |
173
+ | `children` | `ReactNode` | Yes | - | Child components |
174
+
175
+ ### TaskOnProviderConfig
176
+
177
+ ```typescript
178
+ interface TaskOnProviderConfig {
179
+ // Required: API Key for authentication (X-API-Key header)
180
+ apiKey: string;
181
+
182
+ // Locale setting
183
+ locale?: "en" | "ko" | "ja" | "ru" | "es"; // default: auto-detect
184
+
185
+ // Wallet configuration (only needed if using wallet login)
186
+ walletConfig?: {
187
+ evmAdapter?: WalletAdapter; // Custom EVM wallet adapter
188
+ solanaAdapter?: WalletAdapter; // Custom Solana wallet adapter
189
+ disableAutoDetect?: boolean; // Disable auto-detection
190
+ };
191
+
192
+ // WalletConnect Project ID (required for WalletConnect support)
193
+ // Get your project ID at https://cloud.walletconnect.com
194
+ walletConnectProjectId?: string;
195
+
196
+ // Callback when user needs to login (e.g., clicks login overlay)
197
+ onRequestLogin?: () => void;
198
+ }
199
+ ```
200
+
201
+ ## Internationalization
202
+
203
+ ### Supported Locales
204
+
205
+ | Locale | Language |
206
+ | ------ | ----------------- |
207
+ | `en` | English (default) |
208
+ | `ko` | Korean |
209
+ | `ja` | Japanese |
210
+ | `ru` | Russian |
211
+ | `es` | Spanish |
212
+
213
+ ### Configuration
214
+
215
+ ```tsx
216
+ <TaskOnProvider
217
+ config={{
218
+ apiKey: "your-api-key",
219
+ locale: "ko",
220
+ }}
221
+ >
222
+ <App />
223
+ </TaskOnProvider>
224
+ ```
225
+
226
+ ### Dynamic Locale Switching
227
+
228
+ Control locale via your own state:
229
+
230
+ ```tsx
231
+ const App = () => {
232
+ const [locale, setLocale] = useState("en");
233
+
234
+ return (
235
+ <TaskOnProvider config={{ apiKey: "your-api-key", locale }}>
236
+ <select value={locale} onChange={(e) => setLocale(e.target.value)}>
237
+ <option value="en">English</option>
238
+ <option value="ko">한국어</option>
239
+ <option value="ja">日本語</option>
240
+ </select>
241
+ <QuestWidget configId="cfg_abc123" />
242
+ </TaskOnProvider>
243
+ );
244
+ };
245
+ ```
246
+
247
+ ### Locale Auto-Detection
248
+
249
+ When `locale` is not specified, TaskOn detects from browser language, falling back to `en`.
250
+
251
+ ## ThemeProvider
252
+
253
+ Optional provider for theme configuration. Supports nesting for different theme zones.
254
+
255
+ ### Props
256
+
257
+ | Prop | Type | Required | Default | Description |
258
+ | ------------ | --------------------- | -------- | -------- | -------------------- |
259
+ | `theme` | `TaskOnThemeConfig` | No | - | Theme configuration |
260
+ | `children` | `ReactNode` | Yes | - | Child components |
261
+ | `inherit` | `boolean` | No | `true` | Inherit parent theme |
262
+
263
+ ### TaskOnThemeConfig
264
+
265
+ ```typescript
266
+ interface TaskOnThemeConfig {
267
+ // Theme mode
268
+ mode?: "light" | "dark" | "auto"; // default: 'light'
269
+
270
+ // Compact mode
271
+ compact?: boolean; // default: false
272
+
273
+ // Seed tokens - algorithm input, auto-derives other values
274
+ seed?: SeedToken;
275
+
276
+ // Map tokens - override derived values
277
+ map?: MapToken;
278
+
279
+ // Optional: separate config for light/dark mode
280
+ light?: {
281
+ seed?: SeedToken;
282
+ map?: MapToken;
283
+ };
284
+ dark?: {
285
+ seed?: SeedToken;
286
+ map?: MapToken;
287
+ };
288
+ }
289
+
290
+ interface SeedToken {
291
+ colorPrimary?: string; // e.g., '#6366f1'
292
+ colorSecondary?: string;
293
+ colorSuccess?: string;
294
+ colorWarning?: string;
295
+ colorError?: string;
296
+ borderRadius?: number; // e.g., 8
297
+ fontSize?: number; // e.g., 14
298
+ fontFamily?: string;
299
+ }
300
+
301
+ interface MapToken {
302
+ // Primary colors (derived from seed.colorPrimary)
303
+ colorPrimary?: string;
304
+ colorPrimaryHover?: string;
305
+ colorPrimaryActive?: string;
306
+ colorPrimaryBg?: string;
307
+
308
+ // Background
309
+ colorBg?: string;
310
+ colorBgElevated?: string;
311
+ colorBgSpotlight?: string;
312
+
313
+ // Text
314
+ colorText?: string;
315
+ colorTextSecondary?: string;
316
+ colorTextTertiary?: string;
317
+ colorTextDisabled?: string;
318
+
319
+ // Border
320
+ colorBorder?: string;
321
+ colorBorderSecondary?: string;
322
+
323
+ // Layout
324
+ borderRadius?: number;
325
+ borderRadiusSm?: number;
326
+ borderRadiusLg?: number;
327
+
328
+ // Typography
329
+ fontSize?: number;
330
+ fontSizeSm?: number;
331
+ fontSizeLg?: number;
332
+
333
+ // Spacing
334
+ spacing?: number; // base spacing unit, e.g., 8
335
+ spacingXs?: number; // e.g., 4
336
+ spacingSm?: number; // e.g., 8
337
+ spacingMd?: number; // e.g., 16
338
+ spacingLg?: number; // e.g., 24
339
+ spacingXl?: number; // e.g., 32
340
+ }
341
+ ```
342
+
343
+ ### Token Priority
344
+
345
+ ```
346
+ light/dark.map > light/dark.seed (derived) > map > seed (derived) > default
347
+ ```
348
+
349
+ ### Theme Inheritance
350
+
351
+ Nested ThemeProviders inherit from parent and can override specific values:
352
+
353
+ ```tsx
354
+ <TaskOnProvider config={{ apiKey: "your-api-key" }}>
355
+ <ThemeProvider theme={{ mode: "light", seed: { colorPrimary: "#6366f1" } }}>
356
+ <Header /> {/* light + primary #6366f1 */}
357
+ <ThemeProvider theme={{ mode: "dark" }}>
358
+ <Sidebar /> {/* dark + inherits primary #6366f1 */}
359
+ </ThemeProvider>
360
+ <ThemeProvider theme={{ seed: { colorPrimary: "#ef4444" } }}>
361
+ <DangerZone /> {/* light + primary #ef4444 */}
362
+ </ThemeProvider>
363
+ </ThemeProvider>
364
+ </TaskOnProvider>
365
+ ```
366
+
367
+ ### Light/Dark Separate Config
368
+
369
+ Configure different themes for light and dark modes:
370
+
371
+ ```tsx
372
+ // Different primary colors for each mode
373
+ <ThemeProvider
374
+ theme={{
375
+ mode: 'auto',
376
+ light: { seed: { colorPrimary: '#6366f1' } },
377
+ dark: { seed: { colorPrimary: '#818cf8' } },
378
+ }}
379
+ >
380
+ <App />
381
+ </ThemeProvider>
382
+
383
+ // Override specific derived values
384
+ <ThemeProvider
385
+ theme={{
386
+ mode: 'auto',
387
+ light: {
388
+ seed: { colorPrimary: '#6366f1' },
389
+ map: { colorPrimaryHover: '#4f46e5' },
390
+ },
391
+ dark: {
392
+ seed: { colorPrimary: '#818cf8' },
393
+ map: { colorBg: '#0a0a0a' },
394
+ },
395
+ }}
396
+ >
397
+ <App />
398
+ </ThemeProvider>
399
+ ```
400
+
401
+ ### Disable Inheritance
402
+
403
+ Use `inherit={false}` for completely independent themes:
404
+
405
+ ```tsx
406
+ <ThemeProvider theme={{ mode: "dark" }}>
407
+ <DarkContent />
408
+
409
+ <ThemeProvider theme={{ mode: "light" }} inherit={false}>
410
+ <LightPopup /> {/* Fully independent light theme */}
411
+ </ThemeProvider>
412
+ </ThemeProvider>
413
+ ```
414
+
415
+ ## Widgets
416
+
417
+ ### Cloud Configuration
418
+
419
+ Widgets support cloud configuration via `configId`. Configure in TaskOn Dashboard and load at runtime:
420
+
421
+ ```tsx
422
+ // Cloud config includes: theme, feature flags, custom texts
423
+ <QuestWidget configId="cfg_abc123" />
424
+ ```
425
+
426
+ ### Widget Props
427
+
428
+ ```typescript
429
+ interface WidgetProps {
430
+ // Cloud config ID from TaskOn Dashboard
431
+ configId?: string;
432
+
433
+ // Custom class names for widget parts
434
+ classNames?: {
435
+ root?: string;
436
+ header?: string;
437
+ body?: string;
438
+ footer?: string;
439
+ // ... widget-specific parts
440
+ };
441
+
442
+ // Custom inline styles for widget parts
443
+ styles?: {
444
+ root?: React.CSSProperties;
445
+ header?: React.CSSProperties;
446
+ body?: React.CSSProperties;
447
+ footer?: React.CSSProperties;
448
+ // ... widget-specific parts
449
+ };
450
+ }
451
+ ```
452
+
453
+ ### Styling with classNames and styles
454
+
455
+ Widgets support fine-grained styling via `classNames` and `styles` props:
456
+
457
+ ```tsx
458
+ // Using classNames
459
+ <QuestWidget
460
+ configId="cfg_abc123"
461
+ classNames={{
462
+ root: 'my-quest-widget',
463
+ header: 'my-quest-header',
464
+ body: 'my-quest-body',
465
+ }}
466
+ />
467
+
468
+ // Using inline styles
469
+ <QuestWidget
470
+ configId="cfg_abc123"
471
+ styles={{
472
+ root: { maxWidth: 400 },
473
+ header: { borderBottom: '1px solid #eee' },
474
+ body: { padding: 24 },
475
+ }}
476
+ />
477
+
478
+ // Combining both
479
+ <QuestWidget
480
+ configId="cfg_abc123"
481
+ classNames={{ root: 'custom-widget' }}
482
+ styles={{ header: { backgroundColor: 'transparent' } }}
483
+ />
484
+ ```
485
+
486
+ Each widget documents its available parts in its own API reference.
487
+
488
+ ### Usage Examples
489
+
490
+ ```tsx
491
+ // Example 1: Using cloud config (theme from Dashboard)
492
+ <TaskOnProvider config={{ apiKey: 'your-api-key' }}>
493
+ <QuestWidget configId="cfg_abc123" />
494
+ <TaskWidget configId="cfg_xyz789" />
495
+ </TaskOnProvider>
496
+
497
+ // Example 2: Using local theme (no cloud config)
498
+ <TaskOnProvider config={{ apiKey: 'your-api-key' }}>
499
+ <ThemeProvider theme={{ mode: 'dark', seed: { colorPrimary: '#6366f1' } }}>
500
+ <QuestWidget />
501
+ <TaskWidget />
502
+ </ThemeProvider>
503
+ </TaskOnProvider>
504
+
505
+ // Example 3: Different local themes for different areas
506
+ <TaskOnProvider config={{ apiKey: 'your-api-key' }}>
507
+ <ThemeProvider theme={{ mode: 'light' }}>
508
+ <TaskWidget />
509
+ </ThemeProvider>
510
+
511
+ <ThemeProvider theme={{ mode: 'dark', compact: true }}>
512
+ <QuestWidget />
513
+ </ThemeProvider>
514
+ </TaskOnProvider>
515
+
516
+ // Example 4: Mixed - some with cloud config, some with local theme
517
+ <TaskOnProvider config={{ apiKey: 'your-api-key' }}>
518
+ {/* Cloud config */}
519
+ <QuestWidget configId="cfg_abc123" />
520
+
521
+ {/* Local theme */}
522
+ <ThemeProvider theme={{ mode: 'dark' }}>
523
+ <TaskWidget />
524
+ </ThemeProvider>
525
+ </TaskOnProvider>
526
+ ```
527
+
528
+ ## Login Methods
529
+
530
+ TaskOn uses a unified `login` method with signature verification. All login methods require a backend signature for security.
531
+
532
+ ### Unified Login API
533
+
534
+ ```typescript
535
+ import { useTaskOnAuth } from "@taskon/widget-react";
536
+
537
+ const { login } = useTaskOnAuth();
538
+
539
+ // Login with any method
540
+ await login({
541
+ method: "evm_wallet", // Login method type
542
+ value: "0x1234...", // Address / email / OAuth token
543
+ sign: signatureFromBackend, // Backend signature
544
+ timestamp: 1234567890, // Signature timestamp (seconds)
545
+ });
546
+ ```
547
+
548
+ ### Supported Methods
549
+
550
+ | Method | `method` value | `value` parameter |
551
+ | ------------- | ----------------- | ----------------------- |
552
+ | EVM Wallet | `evm_wallet` | Wallet address (0x...) |
553
+ | Solana Wallet | `solana_wallet` | Wallet address (base58) |
554
+ | Email | `email` | Email address |
555
+ | Discord | `discord` | OAuth token |
556
+ | Twitter | `twitter` | OAuth token |
557
+ | Telegram | `telegram` | OAuth token |
558
+
559
+ ### LoginParams Type
560
+
561
+ ```typescript
562
+ interface LoginParams {
563
+ method: LoginMethod; // Login method type
564
+ value: string; // Address / email / OAuth token
565
+ sign: string; // Backend signature
566
+ timestamp: number; // Signature timestamp (seconds)
567
+ }
568
+
569
+ type LoginMethod =
570
+ | "evm_wallet"
571
+ | "solana_wallet"
572
+ | "email"
573
+ | "discord"
574
+ | "twitter"
575
+ | "telegram";
576
+ ```
577
+
578
+ ## Wallet Integration
579
+
580
+ When tasks or rewards involve blockchain operations (e.g., token rewards, NFT minting, on-chain verification), widgets need to interact with the user's wallet for:
581
+
582
+ - **Connecting wallet** - Get user's wallet address
583
+ - **Signing messages** - Verify wallet ownership
584
+ - **Calling contracts** - Claim on-chain rewards, execute transactions
585
+
586
+ ### Overview
587
+
588
+ TaskOn widgets include built-in wallet management, but also support external wallet providers for seamless integration with existing dApps. When wrapped in an external provider, the widget automatically detects and reuses your wallet management.
589
+
590
+ ### Wallet Management
591
+
592
+ TaskOn provides flexible wallet integration options:
593
+
594
+ | Setup | Behavior |
595
+ | -------------- | ------------------------------------- |
596
+ | Custom adapter | Uses your provided `WalletAdapter` |
597
+ | Default | Uses built-in window.ethereum adapter |
598
+
599
+ ### Custom Wallet Adapter (Recommended)
600
+
601
+ If you want full control over wallet connection (e.g., using RainbowKit, Web3Modal), provide a custom adapter:
602
+
603
+ ```tsx
604
+ import { createWalletAdapter } from "./my-wallet-adapter";
605
+
606
+ <TaskOnProvider
607
+ config={{
608
+ apiKey: "your-api-key",
609
+ walletConfig: {
610
+ evmAdapter: createWalletAdapter(),
611
+ },
612
+ }}
613
+ >
614
+ <App />
615
+ </TaskOnProvider>;
616
+ ```
617
+
618
+ ### Built-in Wallet Support
619
+
620
+ If no custom adapter is provided, TaskOn automatically uses `window.ethereum` to connect to browser wallets like MetaMask:
621
+
622
+ ```tsx
623
+ // No wallet config needed - uses window.ethereum by default
624
+ <TaskOnProvider config={{ apiKey: "your-api-key" }}>
625
+ <App />
626
+ </TaskOnProvider>
627
+ ```
628
+
629
+ ### Built-in Wallet Binding Dialog
630
+
631
+ When tasks require wallet binding (e.g., on-chain verification), TaskOn shows a built-in wallet selection dialog:
632
+
633
+ **Desktop (without adapter):**
634
+ - MetaMask
635
+ - ONTO Wallet
636
+ - Bitget Wallet
637
+ - OKX Wallet
638
+ - WalletConnect (requires `walletConnectProjectId`)
639
+
640
+ **Mobile (non-Dapp browser):**
641
+ - WalletConnect only (requires `walletConnectProjectId`)
642
+
643
+ **Mobile (Dapp browser / wallet app):**
644
+ - Uses injected provider directly
645
+
646
+ To enable WalletConnect in the dialog:
647
+
648
+ ```tsx
649
+ <TaskOnProvider
650
+ config={{
651
+ apiKey: "your-api-key",
652
+ walletConnectProjectId: "your-project-id", // Get from cloud.walletconnect.com
653
+ }}
654
+ >
655
+ <App />
656
+ </TaskOnProvider>
657
+ ```
658
+
659
+ ### Built-in Wallet Management
660
+
661
+ If no external provider is detected, the widget uses its built-in wallet management. No configuration needed:
662
+
663
+ ```tsx
664
+ <TaskOnProvider config={{ apiKey: "your-api-key" }}>
665
+ <App /> {/* Widget handles wallet connection internally */}
666
+ </TaskOnProvider>
667
+ ```
668
+
669
+ ### Configuration Options
670
+
671
+ ```typescript
672
+ interface WalletConfig {
673
+ // EVM wallet adapter (highest priority)
674
+ evmAdapter?: WalletAdapter;
675
+
676
+ // Solana wallet adapter (highest priority)
677
+ solanaAdapter?: WalletAdapter;
678
+
679
+ // Disable auto-detection of external providers
680
+ disableAutoDetect?: boolean;
681
+ }
682
+ ```
683
+
684
+ ### Custom Wallet Adapter
685
+
686
+ For projects with their own wallet management:
687
+
688
+ ```tsx
689
+ // Example: Custom wallet management
690
+ const useCustomWalletAdapter = (): WalletAdapter => {
691
+ const { openWalletModal, connectedAddress, chainId } = useYourWalletManager();
692
+
693
+ return {
694
+ connect: async () => {
695
+ // Open your wallet selection modal, return selected address
696
+ const address = await openWalletModal();
697
+ return address;
698
+ },
699
+ disconnect: async () => {
700
+ await yourDisconnectLogic();
701
+ },
702
+ signMessage: async (message) => {
703
+ return await yourSignMessageLogic(message);
704
+ },
705
+ getAddress: () => connectedAddress,
706
+ getChainId: () => chainId,
707
+ switchNetwork: async (chainId) => {
708
+ await yourSwitchNetworkLogic(chainId);
709
+ },
710
+ };
711
+ };
712
+
713
+ const App = () => {
714
+ const evmAdapter = useCustomWalletAdapter();
715
+
716
+ return (
717
+ <TaskOnProvider
718
+ config={{
719
+ apiKey: "your-api-key",
720
+ walletConfig: { evmAdapter },
721
+ }}
722
+ >
723
+ <YourApp />
724
+ </TaskOnProvider>
725
+ );
726
+ };
727
+ ```
728
+
729
+ ### Priority Order
730
+
731
+ When multiple options are available:
732
+
733
+ 1. **Custom Adapter** - `walletConfig.evmAdapter` / `solanaAdapter` (highest)
734
+ 2. **Built-in Adapter** - window.ethereum adapter for EVM wallets (lowest)
735
+
736
+ ## Hooks
737
+
738
+ ### useTaskOnAuth
739
+
740
+ Access TaskOn authentication with unified login method.
741
+
742
+ ```tsx
743
+ import { useTaskOnAuth } from "@taskon/widget-react";
744
+
745
+ const Component = () => {
746
+ const {
747
+ // State
748
+ userId, // TaskOn user ID (number | null)
749
+ isLoggedIn, // Whether user is logged in
750
+ isInitializing, // Whether provider is initializing
751
+
752
+ // Unified login method
753
+ login, // (params: LoginParams) => Promise<void>
754
+
755
+ // Logout
756
+ logout, // () => void
757
+ } = useTaskOnAuth();
758
+
759
+ return <div>TaskOn ID: {userId}</div>;
760
+ };
761
+ ```
762
+
763
+ ### Login Function
764
+
765
+ ```typescript
766
+ // Unified login method
767
+ login: (params: LoginParams) => Promise<void>;
768
+
769
+ // LoginParams
770
+ interface LoginParams {
771
+ method: LoginMethod; // Login method type
772
+ value: string; // Address / email / OAuth token
773
+ sign: string; // Backend signature
774
+ timestamp: number; // Signature timestamp (seconds)
775
+ }
776
+
777
+ // Logout
778
+ logout: () => void;
779
+ ```
780
+
781
+ ### useTaskOnTheme
782
+
783
+ Access current theme. Must be used within ThemeProvider.
784
+
785
+ ```tsx
786
+ import { useTaskOnTheme } from "@taskon/widget-react";
787
+
788
+ const Component = () => {
789
+ const theme = useTaskOnTheme();
790
+
791
+ return (
792
+ <div
793
+ style={{
794
+ background: theme.tokens.colorBg,
795
+ color: theme.tokens.colorText,
796
+ padding: theme.tokens.spacingMd,
797
+ borderRadius: theme.tokens.borderRadius,
798
+ }}
799
+ >
800
+ Current mode: {theme.mode}
801
+ </div>
802
+ );
803
+ };
804
+ ```
805
+
806
+ ### Integration Examples
807
+
808
+ ```tsx
809
+ // Example 1: With EVM Wallet
810
+ const EVMWalletIntegration = () => {
811
+ const { evmAddress } = useWallet(); // TaskOn wallet hook
812
+ const { userId, login } = useTaskOnAuth();
813
+
814
+ const handleLogin = async () => {
815
+ if (evmAddress && !userId) {
816
+ // Get signature from your backend
817
+ const { sign, timestamp } = await fetchSignatureFromBackend(evmAddress);
818
+ await login({
819
+ method: "evm_wallet",
820
+ value: evmAddress,
821
+ sign,
822
+ timestamp,
823
+ });
824
+ }
825
+ };
826
+
827
+ useEffect(() => {
828
+ handleLogin();
829
+ }, [evmAddress, userId]);
830
+
831
+ return <div>{userId ? `TaskOn: ${userId}` : "Not logged in"}</div>;
832
+ };
833
+
834
+ // Example 2: With Custom Wallet Adapter
835
+ const CustomWalletIntegration = () => {
836
+ const { evmAddress, connectEvm } = useWallet();
837
+ const { userId, login } = useTaskOnAuth();
838
+
839
+ const handleLogin = async () => {
840
+ if (publicKey && !userId) {
841
+ const address = publicKey.toBase58();
842
+ const { sign, timestamp } = await fetchSignatureFromBackend(address);
843
+ await login({
844
+ method: "solana_wallet",
845
+ value: address,
846
+ sign,
847
+ timestamp,
848
+ });
849
+ }
850
+ };
851
+
852
+ useEffect(() => {
853
+ handleLogin();
854
+ }, [publicKey, userId]);
855
+
856
+ return <div>{userId ? `TaskOn: ${userId}` : "Not logged in"}</div>;
857
+ };
858
+
859
+ // Example 3: Email login
860
+ const EmailLogin = () => {
861
+ const { login } = useTaskOnAuth();
862
+
863
+ const handleEmailLogin = async (email: string) => {
864
+ const { sign, timestamp } = await fetchSignatureFromBackend(email);
865
+ await login({
866
+ method: "email",
867
+ value: email,
868
+ sign,
869
+ timestamp,
870
+ });
871
+ };
872
+
873
+ return (
874
+ <button onClick={() => handleEmailLogin("user@example.com")}>Login</button>
875
+ );
876
+ };
877
+
878
+ // Example 4: Discord OAuth callback
879
+ const DiscordCallback = () => {
880
+ const { login } = useTaskOnAuth();
881
+
882
+ useEffect(() => {
883
+ // After your Discord OAuth flow
884
+ const processOAuth = async () => {
885
+ const oauthToken = getDiscordTokenFromOAuth();
886
+ const { sign, timestamp } = await fetchSignatureFromBackend(oauthToken);
887
+ await login({
888
+ method: "discord",
889
+ value: oauthToken,
890
+ sign,
891
+ timestamp,
892
+ });
893
+ };
894
+ processOAuth();
895
+ }, []);
896
+
897
+ return <div>Processing...</div>;
898
+ };
899
+ ```
900
+
901
+ ## WalletAdapter Interface
902
+
903
+ For custom wallet integration:
904
+
905
+ ```typescript
906
+ interface WalletAdapter {
907
+ // Required
908
+ connect: () => Promise<string>;
909
+ disconnect: () => Promise<void>;
910
+ signMessage: (message: string) => Promise<string>;
911
+ getAddress: () => string | null;
912
+
913
+ // Optional (EVM only)
914
+ getChainId?: () => number | null;
915
+ switchNetwork?: (chainId: number) => Promise<void>;
916
+
917
+ // Optional (Event subscriptions)
918
+ onAccountChange?: (callback: (address: string | null) => void) => () => void;
919
+ onChainChange?: (callback: (chainId: number) => void) => () => void;
920
+ }
921
+ ```
922
+
923
+ ## Types
924
+
925
+ ### TaskOnAuthState
926
+
927
+ ```typescript
928
+ interface TaskOnAuthState {
929
+ userId: number | null; // TaskOn user ID (null if not logged in)
930
+ isLoggedIn: boolean; // Whether user is logged in
931
+ isInitializing: boolean; // Whether provider is initializing
932
+ }
933
+ ```
934
+
935
+ ### LoginParams
936
+
937
+ ```typescript
938
+ interface LoginParams {
939
+ method: LoginMethod; // Login method type
940
+ value: string; // Address / email / OAuth token
941
+ sign: string; // Backend signature
942
+ timestamp: number; // Signature timestamp (seconds)
943
+ }
944
+
945
+ type LoginMethod =
946
+ | "evm_wallet"
947
+ | "solana_wallet"
948
+ | "email"
949
+ | "discord"
950
+ | "twitter"
951
+ | "telegram";
952
+ ```
953
+
954
+ ### TaskOnTheme
955
+
956
+ The resolved theme object returned by `useTaskOnTheme()`:
957
+
958
+ ```typescript
959
+ interface TaskOnTheme {
960
+ mode: "light" | "dark";
961
+ compact: boolean;
962
+
963
+ // All tokens are resolved (seed + derived + overrides)
964
+ tokens: {
965
+ // Primary colors
966
+ colorPrimary: string;
967
+ colorPrimaryHover: string;
968
+ colorPrimaryActive: string;
969
+ colorPrimaryBg: string;
970
+
971
+ // Secondary colors
972
+ colorSecondary: string;
973
+
974
+ // Status colors
975
+ colorSuccess: string;
976
+ colorWarning: string;
977
+ colorError: string;
978
+
979
+ // Background
980
+ colorBg: string;
981
+ colorBgElevated: string;
982
+ colorBgSpotlight: string;
983
+
984
+ // Text
985
+ colorText: string;
986
+ colorTextSecondary: string;
987
+ colorTextTertiary: string;
988
+ colorTextDisabled: string;
989
+
990
+ // Border
991
+ colorBorder: string;
992
+ colorBorderSecondary: string;
993
+
994
+ // Layout
995
+ borderRadius: number;
996
+ borderRadiusSm: number;
997
+ borderRadiusLg: number;
998
+
999
+ // Typography
1000
+ fontSize: number;
1001
+ fontSizeSm: number;
1002
+ fontSizeLg: number;
1003
+ fontFamily: string;
1004
+
1005
+ // Spacing
1006
+ spacing: number;
1007
+ spacingXs: number;
1008
+ spacingSm: number;
1009
+ spacingMd: number;
1010
+ spacingLg: number;
1011
+ spacingXl: number;
1012
+ };
1013
+ }
1014
+ ```
1015
+
1016
+ ## SSR Compatibility
1017
+
1018
+ This package is SSR-compatible:
1019
+
1020
+ - All components are marked with `'use client'`
1021
+ - Browser APIs are safely wrapped
1022
+ - No hydration mismatches
1023
+
1024
+ Works with:
1025
+
1026
+ - Next.js App Router
1027
+ - Next.js Pages Router
1028
+ - Remix
1029
+ - Other React SSR frameworks
1030
+
1031
+ ## Peer Dependencies
1032
+
1033
+ - `react` >= 18.0.0
1034
+ - `react-dom` >= 18.0.0
1035
+ - `@taskon/core` >= 0.0.0
1036
+
1037
+ ### Optional Peer Dependencies
1038
+
1039
+ For WalletConnect support in the built-in wallet binding dialog:
1040
+
1041
+ ```bash
1042
+ npm install @walletconnect/ethereum-provider @walletconnect/modal
1043
+ ```
1044
+
1045
+ Then configure your project ID:
1046
+
1047
+ ```tsx
1048
+ <TaskOnProvider
1049
+ config={{
1050
+ apiKey: "your-api-key",
1051
+ walletConnectProjectId: "your-walletconnect-project-id",
1052
+ }}
1053
+ >
1054
+ <App />
1055
+ </TaskOnProvider>
1056
+ ```
1057
+
1058
+ Get your WalletConnect Project ID at https://cloud.walletconnect.com
1059
+
1060
+ If not configured, the WalletConnect option will be disabled in the wallet binding dialog.
1061
+
1062
+ ## License
1063
+
1064
+ MIT