@shapeshiftoss/swap-widget 0.0.2

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 (157) hide show
  1. package/Dockerfile +87 -0
  2. package/README.md +519 -0
  3. package/dist/assets/PhArrowCircleDown-B305t-Sh.js +14 -0
  4. package/dist/assets/PhArrowClockwise-CJPxQhr_.js +14 -0
  5. package/dist/assets/PhArrowDown-C4hT9BVu.js +14 -0
  6. package/dist/assets/PhArrowLeft-Way4Cu-r.js +14 -0
  7. package/dist/assets/PhArrowRight-Cp5ZME0F.js +14 -0
  8. package/dist/assets/PhArrowSquareOut-NnrK69c5.js +14 -0
  9. package/dist/assets/PhArrowUp-Dph6j8rm.js +14 -0
  10. package/dist/assets/PhArrowUpRight-BpJjRRRh.js +14 -0
  11. package/dist/assets/PhArrowsClockwise-CFUT8X7R.js +14 -0
  12. package/dist/assets/PhArrowsDownUp-BjBI4ckC.js +14 -0
  13. package/dist/assets/PhArrowsLeftRight-CuLpbKRk.js +14 -0
  14. package/dist/assets/PhBank-Dchr4XQJ.js +14 -0
  15. package/dist/assets/PhBrowser-dUu2TrC1.js +14 -0
  16. package/dist/assets/PhCaretDown-78IuKU3V.js +14 -0
  17. package/dist/assets/PhCaretLeft-_aiyH7n1.js +14 -0
  18. package/dist/assets/PhCaretRight-8zJQEuDX.js +14 -0
  19. package/dist/assets/PhCaretUp-CiCxKWwN.js +14 -0
  20. package/dist/assets/PhCheck-CcADx8br.js +14 -0
  21. package/dist/assets/PhCircleHalf-BYzSzd2h.js +14 -0
  22. package/dist/assets/PhClock-UItiXeJq.js +14 -0
  23. package/dist/assets/PhCompass-CUy3uA7t.js +14 -0
  24. package/dist/assets/PhCopy-C-PDgSlZ.js +14 -0
  25. package/dist/assets/PhCreditCard-CWRaUT6F.js +14 -0
  26. package/dist/assets/PhCurrencyDollar-Cqla3AXT.js +14 -0
  27. package/dist/assets/PhDesktop-BBIPrYLl.js +14 -0
  28. package/dist/assets/PhDeviceMobile-BdsPaATg.js +14 -0
  29. package/dist/assets/PhDotsThree-BMXCQl-0.js +14 -0
  30. package/dist/assets/PhEnvelope-yUsis6yU.js +14 -0
  31. package/dist/assets/PhFunnelSimple-Ba7BbWTp.js +14 -0
  32. package/dist/assets/PhGlobe-DgLxBk5U.js +14 -0
  33. package/dist/assets/PhIdentificationCard-Czjmq9Z3.js +14 -0
  34. package/dist/assets/PhImage-BAJ_erz6.js +14 -0
  35. package/dist/assets/PhInfo-Cr4DhVUE.js +14 -0
  36. package/dist/assets/PhLightbulb-BX_7duTr.js +14 -0
  37. package/dist/assets/PhMagnifyingGlass-Mygfryvz.js +14 -0
  38. package/dist/assets/PhPaperPlaneRight-BeuCA660.js +14 -0
  39. package/dist/assets/PhPlus-BMjcHjsT.js +14 -0
  40. package/dist/assets/PhPower-CGffGmdb.js +14 -0
  41. package/dist/assets/PhPuzzlePiece-wm_GpVSi.js +14 -0
  42. package/dist/assets/PhQrCode-DRwRzXCH.js +14 -0
  43. package/dist/assets/PhQuestion-M09j2U0T.js +14 -0
  44. package/dist/assets/PhQuestionMark-BP9EMB6h.js +14 -0
  45. package/dist/assets/PhSealCheck-BfG80yKn.js +14 -0
  46. package/dist/assets/PhSignOut-C5YOWlPT.js +14 -0
  47. package/dist/assets/PhSpinner-BXooDYzU.js +14 -0
  48. package/dist/assets/PhTrash-Biq6rmmL.js +14 -0
  49. package/dist/assets/PhUser-CAgZSpGe.js +14 -0
  50. package/dist/assets/PhVault-l2PHF38a.js +14 -0
  51. package/dist/assets/PhWallet-w25D16It.js +14 -0
  52. package/dist/assets/PhWarning-DPEydIPw.js +14 -0
  53. package/dist/assets/PhWarningCircle-BqjT745H.js +14 -0
  54. package/dist/assets/PhX-s-SmVExa.js +14 -0
  55. package/dist/assets/SwapController-If0bFrNc.js +1 -0
  56. package/dist/assets/ccip-BBB7uCdL.js +1 -0
  57. package/dist/assets/ccip-CkkdfKKI.js +1 -0
  58. package/dist/assets/data-capture-D4gAy-Yc.js +242 -0
  59. package/dist/assets/email-B1AJehis.js +90 -0
  60. package/dist/assets/embedded-wallet-Q6DQopDk.js +410 -0
  61. package/dist/assets/features-B6JAPAFb.js +5 -0
  62. package/dist/assets/index-5jd8RPN9.js +7428 -0
  63. package/dist/assets/index-B4kNtSTk.js +1080 -0
  64. package/dist/assets/index-B753eAob.js +1 -0
  65. package/dist/assets/index-BZoLTv77.js +3 -0
  66. package/dist/assets/index-Bcw9UURX.js +3 -0
  67. package/dist/assets/index-BkrV-ahK.js +2 -0
  68. package/dist/assets/index-CRQzQ_up.js +9 -0
  69. package/dist/assets/index-CSW0Gt5y.js +132 -0
  70. package/dist/assets/index-CZ4jTIsk.js +138 -0
  71. package/dist/assets/index-Cwa8SB3h.js +61 -0
  72. package/dist/assets/index-DEwQgT4r.js +78 -0
  73. package/dist/assets/index-DVfQiQ1d.js +1 -0
  74. package/dist/assets/index-cV3UdaMs.css +1 -0
  75. package/dist/assets/index-hAmu622O.js +43 -0
  76. package/dist/assets/onramp-XcPNCLRU.js +422 -0
  77. package/dist/assets/pay-with-exchange-BeHt4OF4.js +280 -0
  78. package/dist/assets/property-C9JG5tag.js +27 -0
  79. package/dist/assets/receive-7ff4he2S.js +131 -0
  80. package/dist/assets/send-CkMBIRXT.js +554 -0
  81. package/dist/assets/socials-DmmxuLrP.js +250 -0
  82. package/dist/assets/swaps-IHAYRQ0x.js +1050 -0
  83. package/dist/assets/transactions-9Cxdk7o9.js +16 -0
  84. package/dist/assets/v4-BOvFkHkt.js +1 -0
  85. package/dist/assets/w3m-modal-kJzFnBfB.js +891 -0
  86. package/dist/assets/wallet-standard-Dv8LMoUB.js +1 -0
  87. package/dist/favicon.png +0 -0
  88. package/dist/favicon.svg +11 -0
  89. package/dist/icon-192x192.png +0 -0
  90. package/dist/icon-512x512.png +0 -0
  91. package/dist/index.html +33 -0
  92. package/index.html +32 -0
  93. package/package.json +59 -0
  94. package/public/favicon.png +0 -0
  95. package/public/favicon.svg +11 -0
  96. package/public/icon-192x192.png +0 -0
  97. package/public/icon-512x512.png +0 -0
  98. package/railway.toml +10 -0
  99. package/src/api/client.ts +95 -0
  100. package/src/components/AddressInputModal.css +173 -0
  101. package/src/components/AddressInputModal.tsx +250 -0
  102. package/src/components/ApprovalStep.tsx +92 -0
  103. package/src/components/ExecutionStep.tsx +27 -0
  104. package/src/components/InputStep.tsx +431 -0
  105. package/src/components/QuoteSelector.css +146 -0
  106. package/src/components/QuoteSelector.tsx +137 -0
  107. package/src/components/QuotesModal.css +250 -0
  108. package/src/components/QuotesModal.tsx +182 -0
  109. package/src/components/SettingsModal.css +145 -0
  110. package/src/components/SettingsModal.tsx +180 -0
  111. package/src/components/StatusStep.tsx +139 -0
  112. package/src/components/SwapWidget.css +713 -0
  113. package/src/components/SwapWidget.tsx +607 -0
  114. package/src/components/TokenSelectModal.css +438 -0
  115. package/src/components/TokenSelectModal.tsx +487 -0
  116. package/src/components/WalletProvider.tsx +92 -0
  117. package/src/config/appkit.ts +104 -0
  118. package/src/config/wagmi.ts +27 -0
  119. package/src/constants/chains.ts +72 -0
  120. package/src/constants/defaults.ts +28 -0
  121. package/src/constants/swappers.ts +48 -0
  122. package/src/constants/viemChains.ts +69 -0
  123. package/src/contexts/SwapWalletContext.tsx +25 -0
  124. package/src/demo/App.css +458 -0
  125. package/src/demo/App.tsx +481 -0
  126. package/src/demo/main.tsx +25 -0
  127. package/src/hooks/useAssets.ts +180 -0
  128. package/src/hooks/useBalances.ts +822 -0
  129. package/src/hooks/useBitcoinSigning.ts +276 -0
  130. package/src/hooks/useMarketData.ts +169 -0
  131. package/src/hooks/useSolanaSigning.ts +275 -0
  132. package/src/hooks/useStatusPolling.ts +137 -0
  133. package/src/hooks/useSwapApproval.ts +106 -0
  134. package/src/hooks/useSwapDisplayValues.ts +157 -0
  135. package/src/hooks/useSwapExecution.ts +199 -0
  136. package/src/hooks/useSwapHandlers.ts +119 -0
  137. package/src/hooks/useSwapQuote.ts +71 -0
  138. package/src/hooks/useSwapQuoting.ts +102 -0
  139. package/src/hooks/useSwapRates.ts +61 -0
  140. package/src/index.ts +43 -0
  141. package/src/machines/SwapMachineContext.ts +5 -0
  142. package/src/machines/__tests__/guards.test.ts +228 -0
  143. package/src/machines/__tests__/setup.test.ts +7 -0
  144. package/src/machines/__tests__/swapMachine.test.ts +544 -0
  145. package/src/machines/__tests__/types.test.ts +124 -0
  146. package/src/machines/guards.ts +33 -0
  147. package/src/machines/swapMachine.ts +288 -0
  148. package/src/machines/types.ts +54 -0
  149. package/src/services/transactionStatus.ts +322 -0
  150. package/src/types/index.ts +361 -0
  151. package/src/utils/addressValidation.ts +201 -0
  152. package/src/utils/redirect.ts +45 -0
  153. package/src/vite-env.d.ts +10 -0
  154. package/tsconfig.json +23 -0
  155. package/tsconfig.node.json +10 -0
  156. package/vite.config.ts +101 -0
  157. package/vitest.config.ts +18 -0
@@ -0,0 +1,72 @@
1
+ import type { Asset } from '@shapeshiftoss/types'
2
+ import {
3
+ arbitrum,
4
+ atom,
5
+ avax,
6
+ base,
7
+ bitcoin,
8
+ bitcoincash,
9
+ bnbsmartchain,
10
+ dogecoin,
11
+ ethereum,
12
+ gnosis,
13
+ hyperevm,
14
+ katana,
15
+ litecoin,
16
+ mayachain,
17
+ monad,
18
+ optimism,
19
+ plasma,
20
+ polygon,
21
+ solana,
22
+ thorchain,
23
+ worldchain,
24
+ } from '@shapeshiftoss/utils'
25
+
26
+ import type { ChainId } from '../types'
27
+
28
+ const BASE_ASSETS_BY_CHAIN_ID: Record<ChainId, Asset> = {
29
+ [ethereum.chainId]: ethereum,
30
+ [arbitrum.chainId]: arbitrum,
31
+ [optimism.chainId]: optimism,
32
+ [polygon.chainId]: polygon,
33
+ [base.chainId]: base,
34
+ [avax.chainId]: avax,
35
+ [bnbsmartchain.chainId]: bnbsmartchain,
36
+ [gnosis.chainId]: gnosis,
37
+ [monad.chainId]: monad,
38
+ [hyperevm.chainId]: hyperevm,
39
+ [plasma.chainId]: plasma,
40
+ [worldchain.chainId]: worldchain,
41
+ [katana.chainId]: katana,
42
+ [bitcoin.chainId]: bitcoin,
43
+ [bitcoincash.chainId]: bitcoincash,
44
+ [dogecoin.chainId]: dogecoin,
45
+ [litecoin.chainId]: litecoin,
46
+ [atom.chainId]: atom,
47
+ [thorchain.chainId]: thorchain,
48
+ [mayachain.chainId]: mayachain,
49
+ [solana.chainId]: solana,
50
+ }
51
+
52
+ export const getBaseAsset = (chainId: ChainId): Asset | undefined => {
53
+ return BASE_ASSETS_BY_CHAIN_ID[chainId]
54
+ }
55
+
56
+ export const getChainName = (chainId: ChainId): string => {
57
+ return BASE_ASSETS_BY_CHAIN_ID[chainId]?.networkName ?? chainId
58
+ }
59
+
60
+ export const getChainIcon = (chainId: ChainId): string | undefined => {
61
+ const asset = BASE_ASSETS_BY_CHAIN_ID[chainId]
62
+ return asset?.networkIcon ?? asset?.icon
63
+ }
64
+
65
+ export const getChainColor = (chainId: ChainId): string => {
66
+ const asset = BASE_ASSETS_BY_CHAIN_ID[chainId]
67
+ return (asset as Asset & { networkColor?: string })?.networkColor ?? asset?.color ?? '#888888'
68
+ }
69
+
70
+ export const getExplorerTxLink = (chainId: ChainId): string => {
71
+ return BASE_ASSETS_BY_CHAIN_ID[chainId]?.explorerTxLink ?? 'https://etherscan.io/tx/'
72
+ }
@@ -0,0 +1,28 @@
1
+ import { ethChainId, usdcAssetId } from '@shapeshiftoss/caip'
2
+ import { ethereum } from '@shapeshiftoss/utils'
3
+
4
+ import type { Asset } from '../types'
5
+
6
+ export const DEFAULT_SELL_ASSET: Asset = {
7
+ assetId: ethereum.assetId,
8
+ chainId: ethereum.chainId,
9
+ symbol: ethereum.symbol,
10
+ name: ethereum.name,
11
+ precision: ethereum.precision,
12
+ icon: ethereum.icon,
13
+ networkName: ethereum.networkName,
14
+ explorer: ethereum.explorer,
15
+ explorerTxLink: ethereum.explorerTxLink,
16
+ explorerAddressLink: ethereum.explorerAddressLink,
17
+ }
18
+
19
+ export const DEFAULT_BUY_ASSET: Asset = {
20
+ assetId: usdcAssetId,
21
+ chainId: ethChainId,
22
+ symbol: 'USDC',
23
+ name: 'USD Coin',
24
+ precision: 6,
25
+ icon: 'https://assets.coingecko.com/coins/images/6319/standard/usdc.png',
26
+ }
27
+
28
+ export const POLL_INTERVAL_MS = 5000
@@ -0,0 +1,48 @@
1
+ import { SwapperName } from '../types'
2
+
3
+ export const SWAPPER_ICONS: Partial<Record<SwapperName, string>> = {
4
+ [SwapperName.Thorchain]:
5
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/thorchain-icon.png',
6
+ [SwapperName.Mayachain]:
7
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/maya_logo.png',
8
+ [SwapperName.CowSwap]:
9
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/cow-icon.png',
10
+ [SwapperName.Zrx]:
11
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/0x-icon.png',
12
+ [SwapperName.Portals]:
13
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/portals-icon.png',
14
+ [SwapperName.Chainflip]:
15
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/chainflip-icon.png',
16
+ [SwapperName.Relay]:
17
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/relay-icon.svg',
18
+ [SwapperName.Bebop]:
19
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/bebop-icon.png',
20
+ [SwapperName.Jupiter]:
21
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/jupiter-icon.svg',
22
+ [SwapperName.ButterSwap]:
23
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/butterswap.png',
24
+ [SwapperName.ArbitrumBridge]:
25
+ 'https://raw.githubusercontent.com/shapeshift/web/develop/src/components/MultiHopTrade/components/TradeInput/components/SwapperIcon/arbitrum-bridge-icon.png',
26
+ }
27
+
28
+ export const SWAPPER_COLORS: Partial<Record<SwapperName, string>> = {
29
+ [SwapperName.Thorchain]: '#00CCFF',
30
+ [SwapperName.Mayachain]: '#4169E1',
31
+ [SwapperName.CowSwap]: '#012d73',
32
+ [SwapperName.Zrx]: '#000000',
33
+ [SwapperName.Portals]: '#8B5CF6',
34
+ [SwapperName.Chainflip]: '#FF4081',
35
+ [SwapperName.Relay]: '#6366F1',
36
+ [SwapperName.Bebop]: '#E91E63',
37
+ [SwapperName.Jupiter]: '#C4A962',
38
+ [SwapperName.ButterSwap]: '#FFD700',
39
+ [SwapperName.ArbitrumBridge]: '#28A0F0',
40
+ }
41
+
42
+ export const getSwapperIcon = (swapperName: SwapperName): string | undefined => {
43
+ return SWAPPER_ICONS[swapperName]
44
+ }
45
+
46
+ export const getSwapperColor = (swapperName: SwapperName): string => {
47
+ return SWAPPER_COLORS[swapperName] ?? '#6366F1'
48
+ }
@@ -0,0 +1,69 @@
1
+ import type { Chain, WalletClient } from 'viem'
2
+ import {
3
+ arbitrum,
4
+ avalanche,
5
+ base,
6
+ bsc,
7
+ gnosis,
8
+ hyperEvm,
9
+ katana,
10
+ mainnet,
11
+ monad,
12
+ optimism,
13
+ plasma,
14
+ polygon,
15
+ } from 'viem/chains'
16
+
17
+ export const VIEM_CHAINS_BY_ID: Record<number, Chain> = {
18
+ 1: mainnet,
19
+ 10: optimism,
20
+ 56: bsc,
21
+ 100: gnosis,
22
+ 137: polygon,
23
+ 143: monad,
24
+ 999: hyperEvm,
25
+ 8453: base,
26
+ 9745: plasma,
27
+ 42161: arbitrum,
28
+ 43114: avalanche,
29
+ 747474: katana,
30
+ }
31
+
32
+ export const addChainToWallet = async (client: WalletClient, chain: Chain): Promise<void> => {
33
+ const { id, name, nativeCurrency, rpcUrls, blockExplorers } = chain
34
+
35
+ await client.request({
36
+ method: 'wallet_addEthereumChain',
37
+ params: [
38
+ {
39
+ chainId: `0x${id.toString(16)}`,
40
+ chainName: name,
41
+ nativeCurrency,
42
+ rpcUrls: rpcUrls.default.http,
43
+ blockExplorerUrls: blockExplorers?.default ? [blockExplorers.default.url] : undefined,
44
+ },
45
+ ],
46
+ })
47
+ }
48
+
49
+ export const switchOrAddChain = async (client: WalletClient, chainId: number): Promise<void> => {
50
+ const chain = VIEM_CHAINS_BY_ID[chainId]
51
+
52
+ try {
53
+ await client.switchChain({ id: chainId })
54
+ } catch (error) {
55
+ const switchError = error as { code?: number; message?: string }
56
+ const isChainNotAddedError =
57
+ switchError.code === 4902 ||
58
+ switchError.message?.toLowerCase().includes('unrecognized chain') ||
59
+ switchError.message?.toLowerCase().includes('chain not added') ||
60
+ switchError.message?.toLowerCase().includes('try adding the chain')
61
+
62
+ if (isChainNotAddedError && chain) {
63
+ await addChainToWallet(client, chain)
64
+ await client.switchChain({ id: chainId })
65
+ } else {
66
+ throw error
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,25 @@
1
+ import { createContext, useContext } from 'react'
2
+
3
+ import type { UseBitcoinSigningResult } from '../hooks/useBitcoinSigning'
4
+ import type { UseSolanaSigningResult } from '../hooks/useSolanaSigning'
5
+
6
+ export type SwapWalletContextValue = {
7
+ walletClient: unknown
8
+ walletAddress: string | undefined
9
+ effectiveReceiveAddress: string
10
+ isCustomAddress: boolean
11
+ customReceiveAddress: string
12
+ setCustomReceiveAddress: (address: string) => void
13
+ bitcoin: UseBitcoinSigningResult
14
+ solana: UseSolanaSigningResult
15
+ }
16
+
17
+ const SwapWalletContext = createContext<SwapWalletContextValue | null>(null)
18
+
19
+ export const SwapWalletProvider = SwapWalletContext.Provider
20
+
21
+ export const useSwapWallet = (): SwapWalletContextValue => {
22
+ const ctx = useContext(SwapWalletContext)
23
+ if (!ctx) throw new Error('useSwapWallet must be used within SwapWalletProvider')
24
+ return ctx
25
+ }
@@ -0,0 +1,458 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ html,
8
+ body,
9
+ #root {
10
+ width: 100%;
11
+ min-height: 100vh;
12
+ }
13
+
14
+ .demo-app {
15
+ width: 100%;
16
+ min-height: 100vh;
17
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
18
+ "Helvetica Neue", Arial, sans-serif;
19
+ display: flex;
20
+ flex-direction: column;
21
+ }
22
+
23
+ .demo-app.dark {
24
+ --demo-bg: var(--demo-page-bg, #09090f);
25
+ --demo-bg-secondary: color-mix(
26
+ in srgb,
27
+ var(--demo-page-bg, #09090f),
28
+ white 5%
29
+ );
30
+ --demo-text: #ffffff;
31
+ --demo-text-secondary: #a0a0b0;
32
+ --demo-text-muted: #6b6b80;
33
+ --demo-border: rgba(255, 255, 255, 0.08);
34
+ --demo-accent: var(--demo-page-accent, #3861fb);
35
+
36
+ background: var(--demo-bg);
37
+ background-image: radial-gradient(
38
+ ellipse 80% 50% at 50% -20%,
39
+ color-mix(in srgb, var(--demo-page-accent, #3861fb), transparent 88%),
40
+ transparent
41
+ ),
42
+ radial-gradient(
43
+ ellipse 60% 40% at 80% 100%,
44
+ color-mix(in srgb, var(--demo-page-accent, #6366f1), transparent 92%),
45
+ transparent
46
+ );
47
+ color: var(--demo-text);
48
+ }
49
+
50
+ .demo-app.light {
51
+ --demo-bg: #f5f5f7;
52
+ --demo-bg-secondary: #ffffff;
53
+ --demo-text: #1a1a2e;
54
+ --demo-text-secondary: #5c5c70;
55
+ --demo-text-muted: #9090a0;
56
+ --demo-border: rgba(0, 0, 0, 0.08);
57
+ --demo-accent: #3861fb;
58
+
59
+ background: var(--demo-bg);
60
+ color: var(--demo-text);
61
+ }
62
+
63
+ .demo-header {
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: space-between;
67
+ padding: 16px 24px;
68
+ border-bottom: 1px solid var(--demo-border);
69
+ background: var(--demo-bg-secondary);
70
+ position: sticky;
71
+ top: 0;
72
+ z-index: 100;
73
+ }
74
+
75
+ .demo-logo {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 10px;
79
+ color: var(--demo-text);
80
+ text-decoration: none;
81
+ transition: opacity 0.15s ease;
82
+ }
83
+
84
+ .demo-logo:hover {
85
+ opacity: 0.8;
86
+ }
87
+
88
+ .demo-logo-text {
89
+ font-size: 17px;
90
+ font-weight: 700;
91
+ letter-spacing: -0.02em;
92
+ }
93
+
94
+ .demo-header-actions {
95
+ display: flex;
96
+ align-items: center;
97
+ gap: 12px;
98
+ }
99
+
100
+ .demo-customize-btn {
101
+ display: flex;
102
+ align-items: center;
103
+ gap: 6px;
104
+ padding: 10px 14px;
105
+ border: 1px solid var(--demo-border);
106
+ border-radius: 12px;
107
+ background: transparent;
108
+ color: var(--demo-text-secondary);
109
+ font-size: 13px;
110
+ font-weight: 500;
111
+ cursor: pointer;
112
+ transition: all 0.15s ease;
113
+ }
114
+
115
+ .demo-customize-btn:hover {
116
+ border-color: var(--demo-accent);
117
+ color: var(--demo-text);
118
+ }
119
+
120
+ .demo-main {
121
+ flex: 1;
122
+ display: flex;
123
+ flex-direction: column;
124
+ align-items: center;
125
+ padding: 48px 24px;
126
+ }
127
+
128
+ .demo-content {
129
+ width: 100%;
130
+ max-width: 1100px;
131
+ display: flex;
132
+ flex-direction: column;
133
+ align-items: center;
134
+ gap: 40px;
135
+ }
136
+
137
+ .demo-hero {
138
+ text-align: center;
139
+ max-width: 500px;
140
+ }
141
+
142
+ .demo-title {
143
+ font-size: 42px;
144
+ font-weight: 700;
145
+ letter-spacing: -0.03em;
146
+ margin-bottom: 12px;
147
+ color: var(--demo-accent);
148
+ }
149
+
150
+ .demo-subtitle {
151
+ font-size: 16px;
152
+ color: var(--demo-text-secondary);
153
+ line-height: 1.5;
154
+ }
155
+
156
+ .demo-layout {
157
+ display: flex;
158
+ gap: 24px;
159
+ align-items: flex-start;
160
+ justify-content: center;
161
+ width: 100%;
162
+ }
163
+
164
+ .demo-customizer {
165
+ width: 280px;
166
+ padding: 20px;
167
+ background: var(--demo-bg-secondary);
168
+ border: 1px solid var(--demo-border);
169
+ border-radius: 16px;
170
+ flex-shrink: 0;
171
+ }
172
+
173
+ .demo-customizer-title {
174
+ font-size: 15px;
175
+ font-weight: 600;
176
+ color: var(--demo-text);
177
+ margin-bottom: 20px;
178
+ }
179
+
180
+ .demo-customizer-section {
181
+ margin-bottom: 20px;
182
+ }
183
+
184
+ .demo-customizer-section:last-child {
185
+ margin-bottom: 0;
186
+ }
187
+
188
+ .demo-customizer-label {
189
+ display: block;
190
+ font-size: 12px;
191
+ font-weight: 500;
192
+ color: var(--demo-text-muted);
193
+ text-transform: uppercase;
194
+ letter-spacing: 0.05em;
195
+ margin-bottom: 10px;
196
+ }
197
+
198
+ .demo-theme-toggle {
199
+ display: flex;
200
+ gap: 8px;
201
+ }
202
+
203
+ .demo-theme-btn {
204
+ flex: 1;
205
+ display: flex;
206
+ align-items: center;
207
+ justify-content: center;
208
+ gap: 6px;
209
+ padding: 10px;
210
+ border: 1px solid var(--demo-border);
211
+ border-radius: 10px;
212
+ background: transparent;
213
+ color: var(--demo-text-secondary);
214
+ font-size: 13px;
215
+ font-weight: 500;
216
+ cursor: pointer;
217
+ transition: all 0.15s ease;
218
+ }
219
+
220
+ .demo-theme-btn:hover {
221
+ border-color: var(--demo-text-muted);
222
+ }
223
+
224
+ .demo-theme-btn.active {
225
+ border-color: var(--demo-accent);
226
+ background: color-mix(in srgb, var(--demo-accent), transparent 90%);
227
+ color: var(--demo-accent);
228
+ }
229
+
230
+ .demo-color-grid {
231
+ display: grid;
232
+ grid-template-columns: repeat(6, 1fr);
233
+ gap: 8px;
234
+ }
235
+
236
+ .demo-color-btn {
237
+ width: 100%;
238
+ aspect-ratio: 1;
239
+ border: 2px solid transparent;
240
+ border-radius: 8px;
241
+ cursor: pointer;
242
+ transition: all 0.15s ease;
243
+ }
244
+
245
+ .demo-color-btn:hover {
246
+ transform: scale(1.1);
247
+ }
248
+
249
+ .demo-color-btn.active {
250
+ border-color: var(--demo-text);
251
+ box-shadow: 0 0 0 2px var(--demo-bg);
252
+ }
253
+
254
+ .demo-preset-grid {
255
+ display: grid;
256
+ grid-template-columns: repeat(3, 1fr);
257
+ gap: 8px;
258
+ }
259
+
260
+ .demo-preset-btn {
261
+ display: flex;
262
+ flex-direction: column;
263
+ align-items: center;
264
+ gap: 6px;
265
+ padding: 8px;
266
+ border: 1px solid var(--demo-border);
267
+ border-radius: 10px;
268
+ background: transparent;
269
+ cursor: pointer;
270
+ transition: all 0.15s ease;
271
+ }
272
+
273
+ .demo-preset-btn:hover {
274
+ border-color: var(--demo-accent);
275
+ transform: translateY(-1px);
276
+ }
277
+
278
+ .demo-preset-preview {
279
+ width: 100%;
280
+ aspect-ratio: 1.2;
281
+ border-radius: 6px;
282
+ position: relative;
283
+ overflow: hidden;
284
+ }
285
+
286
+ .demo-preset-card {
287
+ position: absolute;
288
+ inset: 20%;
289
+ border-radius: 4px;
290
+ }
291
+
292
+ .demo-preset-accent {
293
+ position: absolute;
294
+ bottom: 15%;
295
+ left: 25%;
296
+ right: 25%;
297
+ height: 12%;
298
+ border-radius: 2px;
299
+ }
300
+
301
+ .demo-preset-name {
302
+ font-size: 10px;
303
+ font-weight: 500;
304
+ color: var(--demo-text-secondary);
305
+ }
306
+
307
+ .demo-color-input-row {
308
+ display: flex;
309
+ gap: 8px;
310
+ align-items: center;
311
+ }
312
+
313
+ .demo-color-picker {
314
+ width: 40px;
315
+ height: 40px;
316
+ padding: 0;
317
+ border: 1px solid var(--demo-border);
318
+ border-radius: 8px;
319
+ cursor: pointer;
320
+ background: transparent;
321
+ }
322
+
323
+ .demo-color-picker::-webkit-color-swatch-wrapper {
324
+ padding: 2px;
325
+ }
326
+
327
+ .demo-color-picker::-webkit-color-swatch {
328
+ border-radius: 6px;
329
+ border: none;
330
+ }
331
+
332
+ .demo-color-text {
333
+ flex: 1;
334
+ padding: 10px 12px;
335
+ border: 1px solid var(--demo-border);
336
+ border-radius: 8px;
337
+ background: var(--demo-bg);
338
+ color: var(--demo-text);
339
+ font-size: 13px;
340
+ font-family: monospace;
341
+ }
342
+
343
+ .demo-color-text:focus {
344
+ outline: none;
345
+ border-color: var(--demo-accent);
346
+ }
347
+
348
+ .demo-range-input {
349
+ flex: 1;
350
+ height: 4px;
351
+ accent-color: var(--demo-accent);
352
+ cursor: pointer;
353
+ }
354
+
355
+ .demo-copy-btn {
356
+ display: flex;
357
+ align-items: center;
358
+ justify-content: center;
359
+ gap: 8px;
360
+ width: 100%;
361
+ padding: 12px 16px;
362
+ border: 1px solid var(--demo-accent);
363
+ border-radius: 10px;
364
+ background: color-mix(in srgb, var(--demo-accent), transparent 90%);
365
+ color: var(--demo-accent);
366
+ font-size: 13px;
367
+ font-weight: 600;
368
+ cursor: pointer;
369
+ transition: all 0.15s ease;
370
+ }
371
+
372
+ .demo-copy-btn:hover {
373
+ background: color-mix(in srgb, var(--demo-accent), transparent 80%);
374
+ }
375
+
376
+ .demo-copy-btn:active {
377
+ transform: scale(0.98);
378
+ }
379
+
380
+ .demo-connection-info {
381
+ display: flex;
382
+ flex-direction: column;
383
+ gap: 6px;
384
+ }
385
+
386
+ .demo-connected-badge {
387
+ display: inline-flex;
388
+ align-items: center;
389
+ padding: 4px 8px;
390
+ background: rgba(16, 185, 129, 0.1);
391
+ color: #10b981;
392
+ border-radius: 6px;
393
+ font-size: 12px;
394
+ font-weight: 500;
395
+ width: fit-content;
396
+ }
397
+
398
+ .demo-address {
399
+ font-size: 13px;
400
+ color: var(--demo-text-secondary);
401
+ font-family: monospace;
402
+ }
403
+
404
+ .demo-disconnected {
405
+ font-size: 13px;
406
+ color: var(--demo-text-muted);
407
+ }
408
+
409
+ .demo-widget-container {
410
+ display: flex;
411
+ justify-content: center;
412
+ }
413
+
414
+ .demo-footer {
415
+ padding: 20px;
416
+ text-align: center;
417
+ border-top: 1px solid var(--demo-border);
418
+ color: var(--demo-text-muted);
419
+ font-size: 13px;
420
+ }
421
+
422
+ @media (max-width: 768px) {
423
+ .demo-header {
424
+ padding: 12px 16px;
425
+ flex-wrap: wrap;
426
+ gap: 12px;
427
+ }
428
+
429
+ .demo-customize-btn span {
430
+ display: none;
431
+ }
432
+
433
+ .demo-customize-btn {
434
+ padding: 10px;
435
+ }
436
+
437
+ .demo-title {
438
+ font-size: 32px;
439
+ }
440
+
441
+ .demo-subtitle {
442
+ font-size: 14px;
443
+ }
444
+
445
+ .demo-layout {
446
+ flex-direction: column;
447
+ align-items: center;
448
+ }
449
+
450
+ .demo-customizer {
451
+ width: 100%;
452
+ max-width: 420px;
453
+ }
454
+
455
+ .demo-main {
456
+ padding: 32px 16px;
457
+ }
458
+ }