accounts 0.18.4 → 0.18.5
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/CHANGELOG.md +10 -0
- package/dist/core/Adapter.d.ts +4 -0
- package/dist/core/Adapter.d.ts.map +1 -1
- package/dist/core/Client.d.ts.map +1 -1
- package/dist/core/Client.js +62 -2
- package/dist/core/Client.js.map +1 -1
- package/dist/core/Provider.d.ts.map +1 -1
- package/dist/core/Provider.js +16 -4
- package/dist/core/Provider.js.map +1 -1
- package/dist/core/Schema.d.ts +28 -0
- package/dist/core/Schema.d.ts.map +1 -1
- package/dist/core/adapters/privy.d.ts.map +1 -1
- package/dist/core/adapters/privy.js +7 -2
- package/dist/core/adapters/privy.js.map +1 -1
- package/dist/core/zod/rpc.d.ts +17 -0
- package/dist/core/zod/rpc.d.ts.map +1 -1
- package/dist/core/zod/rpc.js +12 -1
- package/dist/core/zod/rpc.js.map +1 -1
- package/dist/react/Remote.d.ts.map +1 -1
- package/dist/react/Remote.js +5 -1
- package/dist/react/Remote.js.map +1 -1
- package/dist/trusted-hosts.json +1 -0
- package/package.json +4 -4
- package/src/core/Adapter.ts +4 -0
- package/src/core/Client.test.ts +153 -1
- package/src/core/Client.ts +73 -2
- package/src/core/Provider.localnet.test.ts +114 -0
- package/src/core/Provider.ts +25 -8
- package/src/core/Schema.test-d.ts +14 -0
- package/src/core/adapters/privy.test.ts +150 -8
- package/src/core/adapters/privy.ts +13 -5
- package/src/core/zod/request.test.ts +26 -0
- package/src/core/zod/rpc.ts +13 -3
- package/src/react/Remote.test.ts +153 -0
- package/src/react/Remote.ts +7 -1
- package/src/trusted-hosts.json +1 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Address as core_Address, Hex, Secp256k1, Signature } from 'ox'
|
|
2
|
-
import { decodeFunctionData } from 'viem'
|
|
2
|
+
import { BaseError, decodeFunctionData } from 'viem'
|
|
3
3
|
import type { Address } from 'viem/accounts'
|
|
4
4
|
import { Abis } from 'viem/tempo'
|
|
5
5
|
import { describe, expect, test } from 'vp/test'
|
|
@@ -323,6 +323,140 @@ describe('privy', () => {
|
|
|
323
323
|
expect(store.getState().accessKeys).toMatchInlineSnapshot(`[]`)
|
|
324
324
|
})
|
|
325
325
|
|
|
326
|
+
test('behavior: revokeAccessKey forwards a fee payer URL', async () => {
|
|
327
|
+
const { adapter, client, store } = setup()
|
|
328
|
+
store.setState({ accounts: [{ address }], activeAccount: 0 })
|
|
329
|
+
|
|
330
|
+
await adapter.actions.revokeAccessKey!(
|
|
331
|
+
{ accessKeyAddress: other, address, feePayer: 'https://example.com/fee-payer' },
|
|
332
|
+
{
|
|
333
|
+
method: 'wallet_revokeAccessKey',
|
|
334
|
+
params: [{ accessKeyAddress: other, address, feePayer: 'https://example.com/fee-payer' }],
|
|
335
|
+
},
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
expect({
|
|
339
|
+
clientOptions: client.clientOptions,
|
|
340
|
+
transactions: client.transactions,
|
|
341
|
+
}).toMatchInlineSnapshot(`
|
|
342
|
+
{
|
|
343
|
+
"clientOptions": [
|
|
344
|
+
{
|
|
345
|
+
"feePayer": "https://example.com/fee-payer",
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
"transactions": [
|
|
349
|
+
{
|
|
350
|
+
"account": {
|
|
351
|
+
"address": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
352
|
+
"sign": [Function],
|
|
353
|
+
"signTransaction": [Function],
|
|
354
|
+
"source": "privy",
|
|
355
|
+
"type": "local",
|
|
356
|
+
},
|
|
357
|
+
"data": "0x5ae7ab320000000000000000000000002b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
358
|
+
"feePayer": true,
|
|
359
|
+
"to": "0xaAAAaaAA00000000000000000000000000000000",
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
}
|
|
363
|
+
`)
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
test('error: revokeAccessKey preserves local state on external fee payer errors', async () => {
|
|
367
|
+
const transactionError = new BaseError('revoke failed', {
|
|
368
|
+
cause: Object.assign(new Error('execution reverted'), {
|
|
369
|
+
data: { errorName: 'KeyAlreadyRevoked' },
|
|
370
|
+
}),
|
|
371
|
+
})
|
|
372
|
+
const { adapter, store } = setup({ transactionError })
|
|
373
|
+
store.setState({
|
|
374
|
+
accounts: [{ address }],
|
|
375
|
+
activeAccount: 0,
|
|
376
|
+
accessKeys: [
|
|
377
|
+
{
|
|
378
|
+
access: address,
|
|
379
|
+
address: other,
|
|
380
|
+
chainId: 1,
|
|
381
|
+
keyType: 'secp256k1',
|
|
382
|
+
} as never,
|
|
383
|
+
],
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
await expect(
|
|
387
|
+
adapter.actions.revokeAccessKey!(
|
|
388
|
+
{ accessKeyAddress: other, address, feePayer: 'https://example.com/fee-payer' },
|
|
389
|
+
{
|
|
390
|
+
method: 'wallet_revokeAccessKey',
|
|
391
|
+
params: [{ accessKeyAddress: other, address, feePayer: 'https://example.com/fee-payer' }],
|
|
392
|
+
},
|
|
393
|
+
),
|
|
394
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
395
|
+
[ContractFunctionExecutionError: revoke failed
|
|
396
|
+
|
|
397
|
+
Contract Call:
|
|
398
|
+
address: 0xaAAAaaAA00000000000000000000000000000000
|
|
399
|
+
function: revokeKey(address keyId)
|
|
400
|
+
args: (0x2b5ad5c4795c026514f8317c7a215e218dccd6cf)
|
|
401
|
+
sender: 0x7e5f4552091a69125d5dfcb7b8c2659029395bdf
|
|
402
|
+
|
|
403
|
+
Docs: https://viem.sh/docs/contract/writeContract
|
|
404
|
+
Details: execution reverted
|
|
405
|
+
Version: viem@2.56.0]
|
|
406
|
+
`)
|
|
407
|
+
|
|
408
|
+
expect(store.getState().accessKeys).toMatchInlineSnapshot(`
|
|
409
|
+
[
|
|
410
|
+
{
|
|
411
|
+
"access": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
412
|
+
"address": "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
413
|
+
"chainId": 1,
|
|
414
|
+
"keyType": "secp256k1",
|
|
415
|
+
},
|
|
416
|
+
]
|
|
417
|
+
`)
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
test('behavior: revokeAccessKey forwards default fee sponsorship', async () => {
|
|
421
|
+
const { adapter, client, store } = setup()
|
|
422
|
+
store.setState({ accounts: [{ address }], activeAccount: 0 })
|
|
423
|
+
|
|
424
|
+
await adapter.actions.revokeAccessKey!(
|
|
425
|
+
{ accessKeyAddress: other, address, feePayer: true },
|
|
426
|
+
{
|
|
427
|
+
method: 'wallet_revokeAccessKey',
|
|
428
|
+
params: [{ accessKeyAddress: other, address, feePayer: true }],
|
|
429
|
+
},
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
expect({
|
|
433
|
+
clientOptions: client.clientOptions,
|
|
434
|
+
transactions: client.transactions,
|
|
435
|
+
}).toMatchInlineSnapshot(`
|
|
436
|
+
{
|
|
437
|
+
"clientOptions": [
|
|
438
|
+
{
|
|
439
|
+
"feePayer": undefined,
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
"transactions": [
|
|
443
|
+
{
|
|
444
|
+
"account": {
|
|
445
|
+
"address": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
446
|
+
"sign": [Function],
|
|
447
|
+
"signTransaction": [Function],
|
|
448
|
+
"source": "privy",
|
|
449
|
+
"type": "local",
|
|
450
|
+
},
|
|
451
|
+
"data": "0x5ae7ab320000000000000000000000002b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
452
|
+
"feePayer": true,
|
|
453
|
+
"to": "0xaAAAaaAA00000000000000000000000000000000",
|
|
454
|
+
},
|
|
455
|
+
],
|
|
456
|
+
}
|
|
457
|
+
`)
|
|
458
|
+
})
|
|
459
|
+
|
|
326
460
|
test('behavior: signing silently restores wallet accounts via the Privy SDK', async () => {
|
|
327
461
|
const { adapter, client, store } = setup()
|
|
328
462
|
store.setState({ accounts: [{ address }], activeAccount: 0 })
|
|
@@ -642,13 +776,17 @@ function setup(options: setup.Options = {}) {
|
|
|
642
776
|
getAccount: (() => {
|
|
643
777
|
throw new Error('not implemented')
|
|
644
778
|
}) as never,
|
|
645
|
-
getClient: (() =>
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
779
|
+
getClient: ((clientOptions: unknown) => {
|
|
780
|
+
client.clientOptions.push(clientOptions)
|
|
781
|
+
return {
|
|
782
|
+
chain: { id: 1 },
|
|
783
|
+
sendTransaction: async (parameters: unknown) => {
|
|
784
|
+
client.transactions.push(parameters)
|
|
785
|
+
if (options.transactionError) throw options.transactionError
|
|
786
|
+
return Hex.padLeft('0x1', 32)
|
|
787
|
+
},
|
|
788
|
+
}
|
|
789
|
+
}) as never,
|
|
652
790
|
storage,
|
|
653
791
|
store,
|
|
654
792
|
})
|
|
@@ -675,6 +813,8 @@ declare namespace setup {
|
|
|
675
813
|
restoreError?: unknown
|
|
676
814
|
token?: string | null | undefined
|
|
677
815
|
signError?: unknown
|
|
816
|
+
/** Make the mock wallet client transaction fail. */
|
|
817
|
+
transactionError?: unknown
|
|
678
818
|
/** Override the value returned by the embedded provider's `secp256k1_sign`. */
|
|
679
819
|
signResult?: unknown
|
|
680
820
|
/** Force the test wallet to sign with this private key (for wrong-signer tests). */
|
|
@@ -683,6 +823,7 @@ declare namespace setup {
|
|
|
683
823
|
}
|
|
684
824
|
|
|
685
825
|
type MockClient = privy.Client & {
|
|
826
|
+
clientOptions: unknown[]
|
|
686
827
|
createCalls: number
|
|
687
828
|
initCalls: number
|
|
688
829
|
loadCalls: number
|
|
@@ -699,6 +840,7 @@ type MockClient = privy.Client & {
|
|
|
699
840
|
|
|
700
841
|
function createClient(options: setup.Options = {}) {
|
|
701
842
|
const client: MockClient = {
|
|
843
|
+
clientOptions: [] as unknown[],
|
|
702
844
|
createCalls: 0,
|
|
703
845
|
initCalls: 0,
|
|
704
846
|
loadCalls: 0,
|
|
@@ -570,13 +570,21 @@ export function privy<const client extends privy.Client>(
|
|
|
570
570
|
},
|
|
571
571
|
async revokeAccessKey(parameters) {
|
|
572
572
|
const account = await getSigningAccount(parameters.address)
|
|
573
|
+
const { feePayer } = parameters
|
|
573
574
|
try {
|
|
574
|
-
await Actions.accessKey.revoke(
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
575
|
+
await Actions.accessKey.revoke(
|
|
576
|
+
getClient({ feePayer: feePayer === true ? undefined : feePayer }),
|
|
577
|
+
{
|
|
578
|
+
account: account as never,
|
|
579
|
+
accessKey: parameters.accessKeyAddress,
|
|
580
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
581
|
+
...(parameters.keyAuthorization
|
|
582
|
+
? { keyAuthorization: parameters.keyAuthorization }
|
|
583
|
+
: {}),
|
|
584
|
+
},
|
|
585
|
+
)
|
|
578
586
|
} catch (error) {
|
|
579
|
-
if (!AccessKey.isUnavailableError(error)) throw error
|
|
587
|
+
if (typeof feePayer === 'string' || !AccessKey.isUnavailableError(error)) throw error
|
|
580
588
|
}
|
|
581
589
|
store.accessKeys.remove({
|
|
582
590
|
accessKey: parameters.accessKeyAddress,
|
|
@@ -69,6 +69,32 @@ describe('validate', () => {
|
|
|
69
69
|
`)
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
+
test('behavior: preserves access-key revocation sponsorship', () => {
|
|
73
|
+
const result = RpcRequest.validate(Schema.Request, {
|
|
74
|
+
method: 'wallet_revokeAccessKey',
|
|
75
|
+
params: [
|
|
76
|
+
{
|
|
77
|
+
accessKeyAddress: '0x2222222222222222222222222222222222222222',
|
|
78
|
+
address: '0x1111111111111111111111111111111111111111',
|
|
79
|
+
feePayer: 'https://app.example.com/fee-payer',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
expect(result._decoded).toMatchInlineSnapshot(`
|
|
85
|
+
{
|
|
86
|
+
"method": "wallet_revokeAccessKey",
|
|
87
|
+
"params": [
|
|
88
|
+
{
|
|
89
|
+
"accessKeyAddress": "0x2222222222222222222222222222222222222222",
|
|
90
|
+
"address": "0x1111111111111111111111111111111111111111",
|
|
91
|
+
"feePayer": "https://app.example.com/fee-payer",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
}
|
|
95
|
+
`)
|
|
96
|
+
})
|
|
97
|
+
|
|
72
98
|
test('default: validates wallet_switchEthereumChain with hex chainId', () => {
|
|
73
99
|
const result = RpcRequest.validate(Schema.Request, {
|
|
74
100
|
method: 'wallet_switchEthereumChain',
|
package/src/core/zod/rpc.ts
CHANGED
|
@@ -540,11 +540,21 @@ export namespace wallet_authorizeAccessKey_strict {
|
|
|
540
540
|
}
|
|
541
541
|
|
|
542
542
|
export namespace wallet_revokeAccessKey {
|
|
543
|
+
/** Parameters for revoking an access key. */
|
|
544
|
+
export const parameters = z.object({
|
|
545
|
+
/** Root account address. */
|
|
546
|
+
address: u.address(),
|
|
547
|
+
/** Address of the access key to revoke. */
|
|
548
|
+
accessKeyAddress: u.address(),
|
|
549
|
+
/** App-provided fee sponsorship for the revocation transaction. */
|
|
550
|
+
feePayer: z.optional(z.union([z.boolean(), z.string()])),
|
|
551
|
+
/** Signed authorization for an access key that has not been published on-chain. */
|
|
552
|
+
keyAuthorization: z.optional(keyAuthorization),
|
|
553
|
+
})
|
|
554
|
+
|
|
543
555
|
export const schema = Schema.defineItem({
|
|
544
556
|
method: z.literal('wallet_revokeAccessKey'),
|
|
545
|
-
params: z.readonly(
|
|
546
|
-
z.tuple([z.object({ address: u.address(), accessKeyAddress: u.address() })]),
|
|
547
|
-
),
|
|
557
|
+
params: z.readonly(z.tuple([parameters])),
|
|
548
558
|
returns: undefined,
|
|
549
559
|
})
|
|
550
560
|
export type Encoded = Schema.Encoded<typeof schema>
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { vi } from 'vitest'
|
|
2
|
+
import { afterEach, describe, expect, test } from 'vp/test'
|
|
3
|
+
|
|
4
|
+
const hooks = vi.hoisted(() => ({ cleanups: [] as (() => void)[] }))
|
|
5
|
+
|
|
6
|
+
vi.mock('react', () => ({
|
|
7
|
+
useCallback: (fn: unknown) => fn,
|
|
8
|
+
useEffect: (effect: () => void | (() => void)) => {
|
|
9
|
+
const cleanup = effect()
|
|
10
|
+
if (cleanup) hooks.cleanups.push(cleanup)
|
|
11
|
+
},
|
|
12
|
+
useMemo: (fn: () => unknown) => fn(),
|
|
13
|
+
useRef: (current: unknown) => ({ current }),
|
|
14
|
+
useState: (initial: unknown) => [initial, () => {}],
|
|
15
|
+
}))
|
|
16
|
+
|
|
17
|
+
import { useTheme } from './Remote.js'
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
hooks.cleanups.length = 0
|
|
21
|
+
vi.unstubAllGlobals()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('useTheme', () => {
|
|
25
|
+
test('behavior: applies URL theme values to the document', () => {
|
|
26
|
+
const root = createRoot()
|
|
27
|
+
stubBrowser(root, '?accent=%23b9a3ff&radius=medium&scheme=dark')
|
|
28
|
+
|
|
29
|
+
useTheme()
|
|
30
|
+
|
|
31
|
+
expect(readRoot(root)).toMatchInlineSnapshot(`
|
|
32
|
+
{
|
|
33
|
+
"attributes": {
|
|
34
|
+
"data-theme": "dark",
|
|
35
|
+
"data-theme-accent": "custom",
|
|
36
|
+
"data-theme-radius": "medium",
|
|
37
|
+
},
|
|
38
|
+
"colorScheme": "dark",
|
|
39
|
+
"properties": {
|
|
40
|
+
"--theme-accent": "#b9a3ff",
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
`)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('behavior: replaces URL overrides with live theme updates', () => {
|
|
47
|
+
const root = createRoot()
|
|
48
|
+
stubBrowser(root, '?accent=purple&radius=full&scheme=dark')
|
|
49
|
+
let update: ((theme: { scheme?: string }) => void) | undefined
|
|
50
|
+
const remote = {
|
|
51
|
+
messenger: {
|
|
52
|
+
on(_topic: string, listener: typeof update) {
|
|
53
|
+
update = listener
|
|
54
|
+
return () => {}
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
useTheme(remote as never)
|
|
59
|
+
|
|
60
|
+
update?.({ scheme: 'light' })
|
|
61
|
+
|
|
62
|
+
expect(readRoot(root)).toMatchInlineSnapshot(`
|
|
63
|
+
{
|
|
64
|
+
"attributes": {
|
|
65
|
+
"data-theme": "light",
|
|
66
|
+
},
|
|
67
|
+
"colorScheme": "light",
|
|
68
|
+
"properties": {},
|
|
69
|
+
}
|
|
70
|
+
`)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('behavior: restores the document theme when unmounted', () => {
|
|
74
|
+
const root = createRoot(
|
|
75
|
+
{
|
|
76
|
+
'data-theme': 'light',
|
|
77
|
+
'data-theme-accent': 'custom',
|
|
78
|
+
'data-theme-radius': 'small',
|
|
79
|
+
},
|
|
80
|
+
'light',
|
|
81
|
+
{ '--theme-accent': '#ff007a' },
|
|
82
|
+
)
|
|
83
|
+
stubBrowser(root, '?accent=blue&radius=full&scheme=dark')
|
|
84
|
+
useTheme()
|
|
85
|
+
|
|
86
|
+
hooks.cleanups[0]?.()
|
|
87
|
+
|
|
88
|
+
expect(readRoot(root)).toMatchInlineSnapshot(`
|
|
89
|
+
{
|
|
90
|
+
"attributes": {
|
|
91
|
+
"data-theme": "light",
|
|
92
|
+
"data-theme-accent": "custom",
|
|
93
|
+
"data-theme-radius": "small",
|
|
94
|
+
},
|
|
95
|
+
"colorScheme": "light",
|
|
96
|
+
"properties": {
|
|
97
|
+
"--theme-accent": "#ff007a",
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
`)
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Helpers
|
|
105
|
+
|
|
106
|
+
function stubBrowser(root: ReturnType<typeof createRoot>, search: string) {
|
|
107
|
+
vi.stubGlobal('document', { documentElement: root })
|
|
108
|
+
vi.stubGlobal('window', { location: { search } })
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function createRoot(
|
|
112
|
+
initialAttributes: Record<string, string> = {},
|
|
113
|
+
colorScheme = '',
|
|
114
|
+
initialProperties: Record<string, string> = {},
|
|
115
|
+
) {
|
|
116
|
+
const attributes = new Map(Object.entries(initialAttributes))
|
|
117
|
+
const properties = new Map(Object.entries(initialProperties))
|
|
118
|
+
return {
|
|
119
|
+
attributes,
|
|
120
|
+
getAttribute(name: string) {
|
|
121
|
+
return attributes.get(name) ?? null
|
|
122
|
+
},
|
|
123
|
+
removeAttribute(name: string) {
|
|
124
|
+
attributes.delete(name)
|
|
125
|
+
},
|
|
126
|
+
setAttribute(name: string, value: string) {
|
|
127
|
+
attributes.set(name, value)
|
|
128
|
+
},
|
|
129
|
+
style: {
|
|
130
|
+
colorScheme,
|
|
131
|
+
getPropertyValue(name: string) {
|
|
132
|
+
return properties.get(name) ?? ''
|
|
133
|
+
},
|
|
134
|
+
removeProperty(name: string) {
|
|
135
|
+
const value = properties.get(name) ?? ''
|
|
136
|
+
properties.delete(name)
|
|
137
|
+
return value
|
|
138
|
+
},
|
|
139
|
+
setProperty(name: string, value: string) {
|
|
140
|
+
properties.set(name, value)
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
properties,
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function readRoot(root: ReturnType<typeof createRoot>) {
|
|
148
|
+
return {
|
|
149
|
+
attributes: Object.fromEntries(root.attributes),
|
|
150
|
+
colorScheme: root.style.colorScheme,
|
|
151
|
+
properties: Object.fromEntries(root.properties),
|
|
152
|
+
}
|
|
153
|
+
}
|
package/src/react/Remote.ts
CHANGED
|
@@ -126,7 +126,10 @@ function applyTheme(theme: {
|
|
|
126
126
|
root.setAttribute('data-theme-accent', getAccentName(accent))
|
|
127
127
|
if (!isAccentPreset(accent)) root.style.setProperty('--theme-accent', accent)
|
|
128
128
|
}
|
|
129
|
-
if (scheme)
|
|
129
|
+
if (scheme) {
|
|
130
|
+
root.style.colorScheme = scheme
|
|
131
|
+
root.setAttribute('data-theme', scheme)
|
|
132
|
+
}
|
|
130
133
|
if (radius) root.setAttribute('data-theme-radius', radius)
|
|
131
134
|
}
|
|
132
135
|
|
|
@@ -137,11 +140,13 @@ function captureTheme(): ThemeSnapshot {
|
|
|
137
140
|
accentValue: root.style.getPropertyValue('--theme-accent'),
|
|
138
141
|
colorScheme: root.style.colorScheme,
|
|
139
142
|
radius: root.getAttribute('data-theme-radius'),
|
|
143
|
+
scheme: root.getAttribute('data-theme'),
|
|
140
144
|
}
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
function restoreTheme(snapshot: ThemeSnapshot) {
|
|
144
148
|
const root = document.documentElement
|
|
149
|
+
restoreAttribute(root, 'data-theme', snapshot.scheme)
|
|
145
150
|
restoreAttribute(root, 'data-theme-accent', snapshot.accentPreset)
|
|
146
151
|
restoreAttribute(root, 'data-theme-radius', snapshot.radius)
|
|
147
152
|
if (snapshot.accentValue) root.style.setProperty('--theme-accent', snapshot.accentValue)
|
|
@@ -175,6 +180,7 @@ type ThemeSnapshot = {
|
|
|
175
180
|
accentValue: string
|
|
176
181
|
colorScheme: string
|
|
177
182
|
radius: string | null
|
|
183
|
+
scheme: string | null
|
|
178
184
|
}
|
|
179
185
|
|
|
180
186
|
export declare namespace useEnsureVisibility {
|