@reclack/walletkit 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +341 -2
- package/assets/backpack.webp +0 -0
- package/assets/berasig.webp +0 -0
- package/assets/bestwallet.webp +0 -0
- package/assets/bifrostwallet.webp +0 -0
- package/assets/binance.webp +0 -0
- package/assets/bitcoincom.webp +0 -0
- package/assets/bitget.webp +0 -0
- package/assets/bitpay.webp +0 -0
- package/assets/bitski.webp +0 -0
- package/assets/bitverse.webp +0 -0
- package/assets/blockchaincom.webp +0 -0
- package/assets/brave.webp +0 -0
- package/assets/bybit.webp +0 -0
- package/assets/clv.webp +0 -0
- package/assets/coin98.webp +0 -0
- package/assets/coinbase.webp +0 -0
- package/assets/compass.webp +0 -0
- package/assets/core.webp +0 -0
- package/assets/crypto_com.webp +0 -0
- package/assets/ctrl.webp +0 -0
- package/assets/dawn.webp +0 -0
- package/assets/desig.webp +0 -0
- package/assets/enkrypt.webp +0 -0
- package/assets/exodus.webp +0 -0
- package/assets/foxwallet.webp +0 -0
- package/assets/frame.webp +0 -0
- package/assets/frontier.webp +0 -0
- package/assets/gate.webp +0 -0
- package/assets/gemini.webp +0 -0
- package/assets/imtoken.webp +0 -0
- package/assets/iopay.webp +0 -0
- package/assets/kaia.webp +0 -0
- package/assets/kaikas.webp +0 -0
- package/assets/kraken.webp +0 -0
- package/assets/kresuswallet.webp +0 -0
- package/assets/ledger.webp +0 -0
- package/assets/magiceden.webp +0 -0
- package/assets/meco.webp +0 -0
- package/assets/metamask.webp +0 -0
- package/assets/mew.webp +0 -0
- package/assets/nest.webp +0 -0
- package/assets/nova.webp +0 -0
- package/assets/okto.webp +0 -0
- package/assets/okx.webp +0 -0
- package/assets/oneinch.webp +0 -0
- package/assets/onekey.webp +0 -0
- package/assets/phantom.webp +0 -0
- package/assets/rabby.webp +0 -0
- package/assets/rainbow.webp +0 -0
- package/assets/ramper.webp +0 -0
- package/assets/ready.webp +0 -0
- package/assets/ronin.webp +0 -0
- package/assets/safe.webp +0 -0
- package/assets/safepal.webp +0 -0
- package/assets/solflare.webp +0 -0
- package/assets/subwallet.webp +0 -0
- package/assets/taho.webp +0 -0
- package/assets/talisman.webp +0 -0
- package/assets/tokenary.webp +0 -0
- package/assets/tokenpocket.webp +0 -0
- package/assets/trust-wallet.webp +0 -0
- package/assets/uniswap.webp +0 -0
- package/assets/universalprofiles.webp +0 -0
- package/assets/valora.webp +0 -0
- package/assets/wigwam.webp +0 -0
- package/assets/xportal.webp +0 -0
- package/assets/zeal.webp +0 -0
- package/assets/zerion.webp +0 -0
- package/custom-elements.json +207 -0
- package/dist/abi.d.ts +21 -0
- package/dist/abi.d.ts.map +1 -0
- package/dist/abi.js +50 -0
- package/dist/abi.js.map +1 -0
- package/dist/address.d.ts +15 -0
- package/dist/address.d.ts.map +1 -0
- package/dist/address.js +25 -0
- package/dist/address.js.map +1 -0
- package/dist/balance.d.ts +78 -0
- package/dist/balance.d.ts.map +1 -0
- package/dist/balance.js +146 -0
- package/dist/balance.js.map +1 -0
- package/dist/base58.d.ts +15 -0
- package/dist/base58.d.ts.map +1 -0
- package/dist/base58.js +52 -0
- package/dist/base58.js.map +1 -0
- package/dist/catalog-fetch.d.ts +18 -0
- package/dist/catalog-fetch.d.ts.map +1 -0
- package/dist/catalog-fetch.js +85 -0
- package/dist/catalog-fetch.js.map +1 -0
- package/dist/catalog.d.ts +57 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +80 -0
- package/dist/catalog.js.map +1 -0
- package/dist/chains.d.ts +32 -0
- package/dist/chains.d.ts.map +1 -0
- package/dist/chains.js +103 -0
- package/dist/chains.js.map +1 -0
- package/dist/config.d.ts +40 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/deep-links.d.ts +22 -0
- package/dist/deep-links.d.ts.map +1 -0
- package/dist/deep-links.js +201 -0
- package/dist/deep-links.js.map +1 -0
- package/dist/device.d.ts +23 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +59 -0
- package/dist/device.js.map +1 -0
- package/dist/element/attributes.d.ts +48 -0
- package/dist/element/element.d.ts +92 -0
- package/dist/element/index.d.ts +4 -0
- package/dist/element/props.d.ts +67 -0
- package/dist/element/ui/app.d.ts +22 -0
- package/dist/element/ui/assets.d.ts +7 -0
- package/dist/element/ui/qr.d.ts +16 -0
- package/dist/element/ui/use-handheld.d.ts +8 -0
- package/dist/element/ui/wallet-connect-panel.d.ts +26 -0
- package/dist/element/ui/wallet-dialog.d.ts +37 -0
- package/dist/element/ui/wallet-list.d.ts +26 -0
- package/dist/element/ui/wallet-mark.d.ts +15 -0
- package/dist/element.js +2079 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +64 -0
- package/dist/errors.js.map +1 -0
- package/dist/evm.d.ts +49 -0
- package/dist/evm.d.ts.map +1 -0
- package/dist/evm.js +217 -0
- package/dist/evm.js.map +1 -0
- package/dist/icons.d.ts +22 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +98 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/intent.d.ts +60 -0
- package/dist/intent.d.ts.map +1 -0
- package/dist/intent.js +2 -0
- package/dist/intent.js.map +1 -0
- package/dist/kit.d.ts +124 -0
- package/dist/kit.d.ts.map +1 -0
- package/dist/kit.js +362 -0
- package/dist/kit.js.map +1 -0
- package/dist/svm.d.ts +45 -0
- package/dist/svm.d.ts.map +1 -0
- package/dist/svm.js +201 -0
- package/dist/svm.js.map +1 -0
- package/dist/theme.d.ts +101 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +217 -0
- package/dist/theme.js.map +1 -0
- package/dist/types.d.ts +144 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/wallet-standard.d.ts +77 -0
- package/dist/wallet-standard.d.ts.map +1 -0
- package/dist/wallet-standard.js +89 -0
- package/dist/wallet-standard.js.map +1 -0
- package/dist/walletconnect.d.ts +74 -0
- package/dist/walletconnect.d.ts.map +1 -0
- package/dist/walletconnect.js +133 -0
- package/dist/walletconnect.js.map +1 -0
- package/package.json +71 -3
- package/vscode.html-custom-data.json +55 -0
package/dist/svm.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { base58Encode, fromBase64 } from './base58.js';
|
|
2
|
+
import { detect, engineOf, walletsFor } from './catalog.js';
|
|
3
|
+
import { WalletError } from './errors.js';
|
|
4
|
+
import { connectFeature, disconnectFeature, discoverStandardWallets, signFeature, usable, } from './wallet-standard.js';
|
|
5
|
+
/** Reads whatever shape a wallet returns its public key in. */
|
|
6
|
+
function addressOf(value) {
|
|
7
|
+
if (typeof value === 'string') {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
// Wallets that ship a web3.js PublicKey return an object. toBase58 is the
|
|
11
|
+
// documented accessor; toString gives the same thing, and both are tried
|
|
12
|
+
// rather than assuming which one a given wallet exposes.
|
|
13
|
+
const key = value;
|
|
14
|
+
if (typeof key?.toBase58 === 'function') {
|
|
15
|
+
return key.toBase58();
|
|
16
|
+
}
|
|
17
|
+
if (typeof key?.toString === 'function') {
|
|
18
|
+
const text = key.toString();
|
|
19
|
+
return text === '[object Object]' ? undefined : text;
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Prompts the wallet for an account.
|
|
25
|
+
*
|
|
26
|
+
* Two routes because there are two kinds of wallet here. A registered one is
|
|
27
|
+
* asked through its connect feature and hands back accounts; an injected one
|
|
28
|
+
* is asked through its provider. The account object from the first is kept,
|
|
29
|
+
* because signing needs it back.
|
|
30
|
+
*/
|
|
31
|
+
export async function connectSvmWallet(wallet) {
|
|
32
|
+
if (wallet.standard) {
|
|
33
|
+
const { accounts } = await connectFeature(wallet.standard).connect();
|
|
34
|
+
const account = accounts.find((entry) => entry.chains.some((chain) => chain.startsWith('solana:'))) ?? accounts[0];
|
|
35
|
+
if (!account?.address) {
|
|
36
|
+
throw new WalletError('No account was shared by the wallet.');
|
|
37
|
+
}
|
|
38
|
+
return { address: account.address, account };
|
|
39
|
+
}
|
|
40
|
+
const provider = wallet.provider;
|
|
41
|
+
if (!provider) {
|
|
42
|
+
throw new WalletError('This wallet cannot be connected.');
|
|
43
|
+
}
|
|
44
|
+
const response = provider.connect
|
|
45
|
+
? await provider.connect()
|
|
46
|
+
: await provider.request?.({ method: 'connect' });
|
|
47
|
+
const fromResponse = addressOf(response?.publicKey);
|
|
48
|
+
const address = fromResponse ?? addressOf(provider.publicKey);
|
|
49
|
+
if (!address) {
|
|
50
|
+
throw new WalletError('No account was shared by the wallet.');
|
|
51
|
+
}
|
|
52
|
+
return { address };
|
|
53
|
+
}
|
|
54
|
+
/** Ends the session where the wallet offers a way to. */
|
|
55
|
+
export async function disconnectSvmWallet(wallet) {
|
|
56
|
+
if (wallet.standard) {
|
|
57
|
+
await disconnectFeature(wallet.standard)?.disconnect();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
await wallet.provider?.disconnect?.();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Signs the prepared transaction and lets the wallet submit it.
|
|
64
|
+
*
|
|
65
|
+
* Nothing here builds or alters a transaction: the plan arrives built, in
|
|
66
|
+
* both the forms a wallet might want, and this picks the one its route takes.
|
|
67
|
+
* The payer still sees the wallet decode and display it before approving.
|
|
68
|
+
*
|
|
69
|
+
* Letting the wallet send it, rather than signing and sending separately,
|
|
70
|
+
* means one prompt and no RPC endpoint in this bundle.
|
|
71
|
+
*/
|
|
72
|
+
export async function signAndSendSvm(wallet, plan) {
|
|
73
|
+
if (wallet.standard && wallet.account) {
|
|
74
|
+
const [result] = await signFeature(wallet.standard).signAndSendTransaction({
|
|
75
|
+
account: wallet.account,
|
|
76
|
+
chain: wallet.account.chains.find((chain) => chain === plan.chain)
|
|
77
|
+
?? plan.chain,
|
|
78
|
+
transaction: fromBase64(plan.transaction),
|
|
79
|
+
});
|
|
80
|
+
if (!result?.signature) {
|
|
81
|
+
throw new WalletError('The wallet did not return a transaction signature.');
|
|
82
|
+
}
|
|
83
|
+
// Raw bytes from this route, where every other part of the system
|
|
84
|
+
// names a transaction by its base58 string.
|
|
85
|
+
return base58Encode(result.signature);
|
|
86
|
+
}
|
|
87
|
+
const provider = wallet.provider;
|
|
88
|
+
if (typeof provider?.request !== 'function') {
|
|
89
|
+
throw new WalletError('This wallet cannot sign Solana payments. Try another wallet.');
|
|
90
|
+
}
|
|
91
|
+
let response;
|
|
92
|
+
try {
|
|
93
|
+
response = await provider.request({
|
|
94
|
+
method: 'signAndSendTransaction',
|
|
95
|
+
params: { message: plan.message },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
// A wallet that rejected it, or one whose provider does not speak
|
|
100
|
+
// this method. The two are told apart by the code: a rejection is the
|
|
101
|
+
// payer's own choice and must be reported as such rather than as an
|
|
102
|
+
// unsupported wallet.
|
|
103
|
+
const code = error.code;
|
|
104
|
+
if (code === 4001 || code === -32002 || code === -32603) {
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
throw new WalletError('This wallet could not sign the payment. Try another wallet.');
|
|
108
|
+
}
|
|
109
|
+
const signature = response?.signature;
|
|
110
|
+
if (typeof signature !== 'string' || signature === '') {
|
|
111
|
+
throw new WalletError('The wallet did not return a transaction signature.');
|
|
112
|
+
}
|
|
113
|
+
return signature;
|
|
114
|
+
}
|
|
115
|
+
/** Truncates a base58 address for display: 5e9y…xAQf. */
|
|
116
|
+
export function shortSvmAddress(address) {
|
|
117
|
+
if (address.length < 12) {
|
|
118
|
+
return address;
|
|
119
|
+
}
|
|
120
|
+
return `${address.slice(0, 4)}…${address.slice(-4)}`;
|
|
121
|
+
}
|
|
122
|
+
/** Matches a registered wallet to a catalogued id, for its mark. */
|
|
123
|
+
/**
|
|
124
|
+
* The catalogue id for a wallet that registered under a name.
|
|
125
|
+
*
|
|
126
|
+
* Exact first, and only then a containing match. The two differ now that the
|
|
127
|
+
* catalogue names dozens of wallets: "Core" is a substring of plenty of other
|
|
128
|
+
* names, and a loose match handed one wallet another's artwork. The loose
|
|
129
|
+
* match is still worth keeping for the wallet that registers as "Phantom
|
|
130
|
+
* Wallet" where the catalogue says "Phantom".
|
|
131
|
+
*/
|
|
132
|
+
function idFor(name, known) {
|
|
133
|
+
const key = name.toLowerCase();
|
|
134
|
+
const match = known.find((wallet) => wallet.name.toLowerCase() === key)
|
|
135
|
+
?? known.find((wallet) => key.includes(wallet.name.toLowerCase()));
|
|
136
|
+
return match?.id ?? key.replace(/[^a-z0-9]+/g, '-');
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Finds the Solana wallets available in this browser.
|
|
140
|
+
*
|
|
141
|
+
* The Wallet Standard first: it is how wallets announce Solana support, it
|
|
142
|
+
* survives several being installed at once, and the wallet supplies its own
|
|
143
|
+
* name and icon. Sniffing globals is only a fallback for one that has not
|
|
144
|
+
* adopted it, and never overrides a registered wallet.
|
|
145
|
+
*
|
|
146
|
+
* Returns a cleanup function; callers must call it to drop the listener.
|
|
147
|
+
*/
|
|
148
|
+
export function discoverSvmWallets(catalog, onChange) {
|
|
149
|
+
const known = walletsFor(catalog, 'svm');
|
|
150
|
+
let registered = [];
|
|
151
|
+
function emit() {
|
|
152
|
+
const found = new Map();
|
|
153
|
+
for (const wallet of registered) {
|
|
154
|
+
if (!usable(wallet)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const id = idFor(wallet.name, known);
|
|
158
|
+
found.set(id, { id, name: wallet.name, icon: wallet.icon, standard: wallet });
|
|
159
|
+
}
|
|
160
|
+
// Only for a wallet that did not register. Every wallet worth naming
|
|
161
|
+
// does now, so in practice this catches the ones that have not
|
|
162
|
+
// caught up.
|
|
163
|
+
for (const definition of known) {
|
|
164
|
+
if (found.has(definition.id)) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const provider = detect(window, engineOf(definition, 'svm'));
|
|
168
|
+
if (typeof provider?.request === 'function') {
|
|
169
|
+
found.set(definition.id, {
|
|
170
|
+
id: definition.id,
|
|
171
|
+
name: definition.name,
|
|
172
|
+
provider,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Registry order first, so the list is stable rather than dependent
|
|
177
|
+
// on which wallet answered first; anything else follows.
|
|
178
|
+
const ordered = known
|
|
179
|
+
.map((definition) => found.get(definition.id))
|
|
180
|
+
.filter((wallet) => Boolean(wallet));
|
|
181
|
+
for (const [id, wallet] of found) {
|
|
182
|
+
if (!known.some((definition) => definition.id === id)) {
|
|
183
|
+
ordered.push(wallet);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
onChange(ordered);
|
|
187
|
+
}
|
|
188
|
+
const stop = discoverStandardWallets((wallets) => {
|
|
189
|
+
registered = wallets;
|
|
190
|
+
emit();
|
|
191
|
+
});
|
|
192
|
+
// A wallet that injects a global late would otherwise be missed, the same
|
|
193
|
+
// race the EIP-6963 discovery keeps a retry for.
|
|
194
|
+
const retry = window.setTimeout(emit, 400);
|
|
195
|
+
emit();
|
|
196
|
+
return () => {
|
|
197
|
+
stop();
|
|
198
|
+
window.clearTimeout(retry);
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=svm.js.map
|
package/dist/svm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svm.js","sourceRoot":"","sources":["../src/svm.ts"],"names":[],"mappings":"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"}
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a host dresses the wallet picker.
|
|
3
|
+
*
|
|
4
|
+
* Tokens rather than class names or a stylesheet to override. A wallet picker
|
|
5
|
+
* appears inside a merchant's checkout, a dashboard form and a partner's own
|
|
6
|
+
* page, and the only thing those three agree on is that it should not look
|
|
7
|
+
* like somebody else's component. Twelve values is the whole contract.
|
|
8
|
+
*
|
|
9
|
+
* Every value is validated rather than trusted, even though a host is usually
|
|
10
|
+
* passing its own constants. In the embedded checkout the branding comes from
|
|
11
|
+
* an API response, and a colour ends up inside a style: a page that takes
|
|
12
|
+
* payment details must not interpolate whatever a response happens to carry.
|
|
13
|
+
* An unreadable value is dropped and the default stands, which is a picker
|
|
14
|
+
* that looks generic rather than one that does not render.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Light, dark, or whatever the reader's system says.
|
|
18
|
+
*
|
|
19
|
+
* `system` is the default. A picker that ignores the setting is a white
|
|
20
|
+
* rectangle in a dark checkout.
|
|
21
|
+
*/
|
|
22
|
+
export type ThemeMode = 'light' | 'dark' | 'system';
|
|
23
|
+
/** The resolved mode: what `system` turned out to mean. */
|
|
24
|
+
export type ColorScheme = 'light' | 'dark';
|
|
25
|
+
export interface ThemeTokens {
|
|
26
|
+
/** The panel itself. */
|
|
27
|
+
surface?: string;
|
|
28
|
+
/** Behind the panel. */
|
|
29
|
+
ground?: string;
|
|
30
|
+
/** Rows and plates inside it, such as the square a wallet mark sits on. */
|
|
31
|
+
plate?: string;
|
|
32
|
+
/** Primary text. */
|
|
33
|
+
ink?: string;
|
|
34
|
+
/** Secondary text: captions, addresses, the word "Detected". */
|
|
35
|
+
inkMuted?: string;
|
|
36
|
+
/** Hairlines and borders. */
|
|
37
|
+
line?: string;
|
|
38
|
+
/** The host's own colour, worn by the primary action and the focus ring. */
|
|
39
|
+
brand?: string;
|
|
40
|
+
/** Text on `brand`. Computed for contrast when not given. */
|
|
41
|
+
brandInk?: string;
|
|
42
|
+
/** A refusal. */
|
|
43
|
+
danger?: string;
|
|
44
|
+
/** A success. */
|
|
45
|
+
ok?: string;
|
|
46
|
+
/** Corner radius, as a CSS length. */
|
|
47
|
+
radius?: string;
|
|
48
|
+
/** The host's type, so the picker does not arrive in a different face. */
|
|
49
|
+
fontFamily?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface WalletKitTheme extends ThemeTokens {
|
|
52
|
+
mode?: ThemeMode;
|
|
53
|
+
/** Applied on top of the shared tokens when the resolved mode is dark. */
|
|
54
|
+
dark?: ThemeTokens;
|
|
55
|
+
/** Applied on top of the shared tokens when the resolved mode is light. */
|
|
56
|
+
light?: ThemeTokens;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The defaults, which are the hosted checkout's own.
|
|
60
|
+
*
|
|
61
|
+
* Dark is chosen rather than derived. Inverting a light palette gives muddy
|
|
62
|
+
* greys and a brand colour that has stopped contrasting with anything; every
|
|
63
|
+
* step here was picked against the surface it sits on.
|
|
64
|
+
*/
|
|
65
|
+
export declare const LIGHT_TOKENS: Required<Omit<ThemeTokens, 'brandInk' | 'fontFamily'>>;
|
|
66
|
+
export declare const DARK_TOKENS: Required<Omit<ThemeTokens, 'brandInk' | 'fontFamily'>>;
|
|
67
|
+
/** A colour, or nothing where it is not one. */
|
|
68
|
+
export declare function safeColor(value: string | undefined): string | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Black or white, whichever can actually be read on this colour.
|
|
71
|
+
*
|
|
72
|
+
* Relative luminance per WCAG, so a pale brand gets dark text instead of the
|
|
73
|
+
* white that would leave the primary button unreadable. Computed rather than
|
|
74
|
+
* configured: a host choosing this by eye gets it wrong, and the one place it
|
|
75
|
+
* matters is the button somebody has to find.
|
|
76
|
+
*/
|
|
77
|
+
export declare function readableInk(hex: string): string;
|
|
78
|
+
/**
|
|
79
|
+
* Every token for one mode, as the custom properties a stylesheet reads.
|
|
80
|
+
*
|
|
81
|
+
* Shared tokens first, then the mode's own overrides on top, so a host can
|
|
82
|
+
* set one brand colour for both and a different surface for each.
|
|
83
|
+
*/
|
|
84
|
+
export declare function themeVariables(theme: WalletKitTheme | undefined, scheme: ColorScheme): Record<string, string>;
|
|
85
|
+
/** The same thing as a declaration block, for a stylesheet rather than an element. */
|
|
86
|
+
export declare function themeCss(theme: WalletKitTheme | undefined, scheme: ColorScheme, selector?: string): string;
|
|
87
|
+
/** Sets the tokens on one element, which is what a renderer actually does. */
|
|
88
|
+
export declare function applyTheme(target: {
|
|
89
|
+
style: {
|
|
90
|
+
setProperty(name: string, value: string): void;
|
|
91
|
+
};
|
|
92
|
+
}, theme: WalletKitTheme | undefined, scheme: ColorScheme): void;
|
|
93
|
+
/** What `system` means right now, or the mode itself when it is not system. */
|
|
94
|
+
export declare function resolveScheme(mode: ThemeMode | undefined): ColorScheme;
|
|
95
|
+
/**
|
|
96
|
+
* Follows the resolved mode, which only moves when the mode is `system`.
|
|
97
|
+
*
|
|
98
|
+
* Calls back once immediately, so a caller never has to read the value
|
|
99
|
+
* separately before subscribing to it. Returns the cleanup.
|
|
100
|
+
*/
|
|
101
|
+
export declare function subscribeScheme(mode: ThemeMode | undefined, onChange: (scheme: ColorScheme) => void): () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEnD,2DAA2D;AAC3D,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAA;AAE1C,MAAM,WAAW,WAAW;IAC3B,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wBAAwB;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iBAAiB;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iBAAiB;IACjB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW;IAClD,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,WAAW,CAAA;CACnB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,YAAY,CAAC,CAW/E,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,YAAY,CAAC,CAc9E,CAAA;AA+CD,gDAAgD;AAChD,wBAAgB,SAAS,CAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGxE;AAeD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC7B,KAAK,EAAE,cAAc,GAAG,SAAS,EACjC,MAAM,EAAE,WAAW,GACjB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyCxB;AAED,sFAAsF;AACtF,wBAAgB,QAAQ,CACvB,KAAK,EAAE,cAAc,GAAG,SAAS,EACjC,MAAM,EAAE,WAAW,EACnB,QAAQ,SAAU,GAChB,MAAM,CAMR;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CACzB,MAAM,EAAE;IAAE,KAAK,EAAE;QAAE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CAAE,EACrE,KAAK,EAAE,cAAc,GAAG,SAAS,EACjC,MAAM,EAAE,WAAW,GACjB,IAAI,CAON;AAID,+EAA+E;AAC/E,wBAAgB,aAAa,CAAE,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,CAQvE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACrC,MAAM,IAAI,CAcZ"}
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a host dresses the wallet picker.
|
|
3
|
+
*
|
|
4
|
+
* Tokens rather than class names or a stylesheet to override. A wallet picker
|
|
5
|
+
* appears inside a merchant's checkout, a dashboard form and a partner's own
|
|
6
|
+
* page, and the only thing those three agree on is that it should not look
|
|
7
|
+
* like somebody else's component. Twelve values is the whole contract.
|
|
8
|
+
*
|
|
9
|
+
* Every value is validated rather than trusted, even though a host is usually
|
|
10
|
+
* passing its own constants. In the embedded checkout the branding comes from
|
|
11
|
+
* an API response, and a colour ends up inside a style: a page that takes
|
|
12
|
+
* payment details must not interpolate whatever a response happens to carry.
|
|
13
|
+
* An unreadable value is dropped and the default stands, which is a picker
|
|
14
|
+
* that looks generic rather than one that does not render.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The defaults, which are the hosted checkout's own.
|
|
18
|
+
*
|
|
19
|
+
* Dark is chosen rather than derived. Inverting a light palette gives muddy
|
|
20
|
+
* greys and a brand colour that has stopped contrasting with anything; every
|
|
21
|
+
* step here was picked against the surface it sits on.
|
|
22
|
+
*/
|
|
23
|
+
export const LIGHT_TOKENS = {
|
|
24
|
+
surface: '#ffffff',
|
|
25
|
+
ground: '#f6f7f9',
|
|
26
|
+
plate: '#f6f7f9',
|
|
27
|
+
ink: '#101014',
|
|
28
|
+
inkMuted: '#6b6b76',
|
|
29
|
+
line: '#e5e5ea',
|
|
30
|
+
brand: '#533afd',
|
|
31
|
+
danger: '#b42318',
|
|
32
|
+
ok: '#12a150',
|
|
33
|
+
radius: '10px',
|
|
34
|
+
};
|
|
35
|
+
export const DARK_TOKENS = {
|
|
36
|
+
surface: '#16161a',
|
|
37
|
+
ground: '#0e0e11',
|
|
38
|
+
plate: '#1f1f26',
|
|
39
|
+
ink: '#f4f4f6',
|
|
40
|
+
inkMuted: '#9a9aa6',
|
|
41
|
+
line: '#2b2b33',
|
|
42
|
+
// Lifted off the light value, which goes nearly black against a dark
|
|
43
|
+
// panel. A host that sets its own brand keeps it in both modes: it is
|
|
44
|
+
// their colour, and second-guessing it is how a brand ends up wrong.
|
|
45
|
+
brand: '#8b74ff',
|
|
46
|
+
danger: '#f97066',
|
|
47
|
+
ok: '#3ccb7f',
|
|
48
|
+
radius: '10px',
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* The stylesheet's name for each token.
|
|
52
|
+
*
|
|
53
|
+
* Prefixed, because these are set on an element a host also styles, and an
|
|
54
|
+
* unprefixed `--surface` would silently take over or be taken over.
|
|
55
|
+
*/
|
|
56
|
+
const VARIABLE = {
|
|
57
|
+
surface: '--rkw-surface',
|
|
58
|
+
ground: '--rkw-ground',
|
|
59
|
+
plate: '--rkw-plate',
|
|
60
|
+
ink: '--rkw-ink',
|
|
61
|
+
inkMuted: '--rkw-ink-muted',
|
|
62
|
+
line: '--rkw-line',
|
|
63
|
+
brand: '--rkw-brand',
|
|
64
|
+
brandInk: '--rkw-brand-ink',
|
|
65
|
+
danger: '--rkw-danger',
|
|
66
|
+
ok: '--rkw-ok',
|
|
67
|
+
radius: '--rkw-radius',
|
|
68
|
+
fontFamily: '--rkw-font',
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The wash under whatever is being pointed at.
|
|
72
|
+
*
|
|
73
|
+
* Grey until the host picks a colour, and that distinction is deliberate. The
|
|
74
|
+
* default brand is Reclack's own, so a page nobody has branded would
|
|
75
|
+
* otherwise hover in our indigo: somebody else's product, quietly wearing our
|
|
76
|
+
* colour. The moment a host sets a brand it is theirs to hover in.
|
|
77
|
+
*
|
|
78
|
+
* Written as a reference to a token, never with the colour folded in, so
|
|
79
|
+
* nothing a host or an API response supplied is interpolated into a value. A
|
|
80
|
+
* twelfth reads as a hover on both a white panel and a dark one, which a
|
|
81
|
+
* fixed grey does not.
|
|
82
|
+
*/
|
|
83
|
+
function hoverWash(branded) {
|
|
84
|
+
const token = branded ? '--rkw-brand' : '--rkw-ink-muted';
|
|
85
|
+
return `color-mix(in srgb, var(${token}) 12%, transparent)`;
|
|
86
|
+
}
|
|
87
|
+
const HEX = /^#[0-9a-f]{6}$/i;
|
|
88
|
+
const LENGTH = /^\d+(\.\d+)?(px|rem|em|%)$/;
|
|
89
|
+
// Letters, digits, spaces, quotes, commas and hyphens: everything a font
|
|
90
|
+
// stack is made of, and nothing that closes a declaration.
|
|
91
|
+
const FONT = /^[\w\s'",.-]{1,200}$/;
|
|
92
|
+
/** A colour, or nothing where it is not one. */
|
|
93
|
+
export function safeColor(value) {
|
|
94
|
+
const trimmed = value?.trim().toLowerCase();
|
|
95
|
+
return trimmed && HEX.test(trimmed) ? trimmed : undefined;
|
|
96
|
+
}
|
|
97
|
+
function safeRadius(value) {
|
|
98
|
+
if (typeof value === 'number') {
|
|
99
|
+
return Number.isFinite(value) && value >= 0 ? `${value}px` : undefined;
|
|
100
|
+
}
|
|
101
|
+
const trimmed = value?.trim();
|
|
102
|
+
return trimmed && LENGTH.test(trimmed) ? trimmed : undefined;
|
|
103
|
+
}
|
|
104
|
+
function safeFont(value) {
|
|
105
|
+
const trimmed = value?.trim();
|
|
106
|
+
return trimmed && FONT.test(trimmed) ? trimmed : undefined;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Black or white, whichever can actually be read on this colour.
|
|
110
|
+
*
|
|
111
|
+
* Relative luminance per WCAG, so a pale brand gets dark text instead of the
|
|
112
|
+
* white that would leave the primary button unreadable. Computed rather than
|
|
113
|
+
* configured: a host choosing this by eye gets it wrong, and the one place it
|
|
114
|
+
* matters is the button somebody has to find.
|
|
115
|
+
*/
|
|
116
|
+
export function readableInk(hex) {
|
|
117
|
+
const channel = (from) => {
|
|
118
|
+
const raw = Number.parseInt(hex.slice(from, from + 2), 16) / 255;
|
|
119
|
+
return raw <= 0.03928 ? raw / 12.92 : ((raw + 0.055) / 1.055) ** 2.4;
|
|
120
|
+
};
|
|
121
|
+
const luminance = 0.2126 * channel(1) + 0.7152 * channel(3) + 0.0722 * channel(5);
|
|
122
|
+
return luminance > 0.45 ? '#101014' : '#ffffff';
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Every token for one mode, as the custom properties a stylesheet reads.
|
|
126
|
+
*
|
|
127
|
+
* Shared tokens first, then the mode's own overrides on top, so a host can
|
|
128
|
+
* set one brand colour for both and a different surface for each.
|
|
129
|
+
*/
|
|
130
|
+
export function themeVariables(theme, scheme) {
|
|
131
|
+
const base = scheme === 'dark' ? DARK_TOKENS : LIGHT_TOKENS;
|
|
132
|
+
const given = {
|
|
133
|
+
...theme,
|
|
134
|
+
...(scheme === 'dark' ? theme?.dark : theme?.light),
|
|
135
|
+
};
|
|
136
|
+
const variables = {};
|
|
137
|
+
const colors = ['surface', 'ground', 'plate', 'ink', 'inkMuted', 'line', 'brand', 'danger', 'ok'];
|
|
138
|
+
for (const token of colors) {
|
|
139
|
+
variables[VARIABLE[token]] = safeColor(given[token]) ?? base[token];
|
|
140
|
+
}
|
|
141
|
+
variables[VARIABLE.radius] = safeRadius(given.radius) ?? base.radius;
|
|
142
|
+
// Only when the host has type of its own. Absent, the element inherits
|
|
143
|
+
// whatever the page is already set in, which is almost always right.
|
|
144
|
+
const font = safeFont(given.fontFamily);
|
|
145
|
+
if (font) {
|
|
146
|
+
variables[VARIABLE.fontFamily] = font;
|
|
147
|
+
}
|
|
148
|
+
// Taken when given, computed when not: a host that sets a brand colour
|
|
149
|
+
// should not also have to work out what reads on it.
|
|
150
|
+
variables[VARIABLE.brandInk] =
|
|
151
|
+
safeColor(given.brandInk) ?? readableInk(variables[VARIABLE.brand]);
|
|
152
|
+
// Branded means the host said so, not that a brand token has a value:
|
|
153
|
+
// every token always has one, and the default is ours.
|
|
154
|
+
variables['--rkw-hover'] = hoverWash(Boolean(safeColor(given.brand)));
|
|
155
|
+
// Not a colour of ours: what the browser draws its own controls in.
|
|
156
|
+
variables['--rkw-color-scheme'] = scheme;
|
|
157
|
+
// The "installed" badge, which is not the same green as a success
|
|
158
|
+
// message and never was: it is a quieter, deeper one, chosen against a
|
|
159
|
+
// white row. Derived rather than exposed as a token, like the hover
|
|
160
|
+
// wash: a host theming the picker is not choosing this.
|
|
161
|
+
const installed = scheme === 'dark'
|
|
162
|
+
? { mark: '#4ade80', ink: '#86efac' }
|
|
163
|
+
: { mark: '#16a34a', ink: '#15803d' };
|
|
164
|
+
variables['--rkw-installed'] = installed.mark;
|
|
165
|
+
variables['--rkw-installed-ink'] = installed.ink;
|
|
166
|
+
return variables;
|
|
167
|
+
}
|
|
168
|
+
/** The same thing as a declaration block, for a stylesheet rather than an element. */
|
|
169
|
+
export function themeCss(theme, scheme, selector = ':host') {
|
|
170
|
+
const variables = themeVariables(theme, scheme);
|
|
171
|
+
const body = Object.entries(variables)
|
|
172
|
+
.map(([name, value]) => `\t${name}: ${value};`)
|
|
173
|
+
.join('\n');
|
|
174
|
+
return `${selector} {\n${body}\n}`;
|
|
175
|
+
}
|
|
176
|
+
/** Sets the tokens on one element, which is what a renderer actually does. */
|
|
177
|
+
export function applyTheme(target, theme, scheme) {
|
|
178
|
+
for (const [name, value] of Object.entries(themeVariables(theme, scheme))) {
|
|
179
|
+
// setProperty rather than a built style string: the value is written
|
|
180
|
+
// as a value, so nothing in it can close the declaration and start
|
|
181
|
+
// another.
|
|
182
|
+
target.style.setProperty(name, value);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const DARK_QUERY = '(prefers-color-scheme: dark)';
|
|
186
|
+
/** What `system` means right now, or the mode itself when it is not system. */
|
|
187
|
+
export function resolveScheme(mode) {
|
|
188
|
+
if (mode === 'light' || mode === 'dark') {
|
|
189
|
+
return mode;
|
|
190
|
+
}
|
|
191
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
192
|
+
return 'light';
|
|
193
|
+
}
|
|
194
|
+
return window.matchMedia(DARK_QUERY).matches ? 'dark' : 'light';
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Follows the resolved mode, which only moves when the mode is `system`.
|
|
198
|
+
*
|
|
199
|
+
* Calls back once immediately, so a caller never has to read the value
|
|
200
|
+
* separately before subscribing to it. Returns the cleanup.
|
|
201
|
+
*/
|
|
202
|
+
export function subscribeScheme(mode, onChange) {
|
|
203
|
+
if (mode === 'light' || mode === 'dark') {
|
|
204
|
+
onChange(mode);
|
|
205
|
+
return () => undefined;
|
|
206
|
+
}
|
|
207
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
208
|
+
onChange('light');
|
|
209
|
+
return () => undefined;
|
|
210
|
+
}
|
|
211
|
+
const query = window.matchMedia(DARK_QUERY);
|
|
212
|
+
const update = () => onChange(query.matches ? 'dark' : 'light');
|
|
213
|
+
update();
|
|
214
|
+
query.addEventListener('change', update);
|
|
215
|
+
return () => query.removeEventListener('change', update);
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"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"}
|