feat: add ShieldAI theme system with auto-shifting CSS and useTheme hook
This commit is contained in:
329
pnpm-lock.yaml
generated
329
pnpm-lock.yaml
generated
@@ -22,7 +22,7 @@ importers:
|
|||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.1.5
|
specifier: ^4.1.5
|
||||||
version: 4.1.7(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
version: 4.1.7(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(jsdom@29.1.1)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
||||||
|
|
||||||
web:
|
web:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -63,12 +63,30 @@ importers:
|
|||||||
specifier: ^7.0.0
|
specifier: ^7.0.0
|
||||||
version: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)
|
version: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
jsdom:
|
||||||
|
specifier: ^29.1.1
|
||||||
|
version: 29.1.1
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.1.5
|
specifier: ^4.1.5
|
||||||
version: 4.1.7(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
version: 4.1.7(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(jsdom@29.1.1)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
'@asamuzakjp/css-color@5.1.11':
|
||||||
|
resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
|
|
||||||
|
'@asamuzakjp/dom-selector@7.1.1':
|
||||||
|
resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
|
|
||||||
|
'@asamuzakjp/generational-cache@1.0.1':
|
||||||
|
resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
|
|
||||||
|
'@asamuzakjp/nwsapi@2.3.9':
|
||||||
|
resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
|
||||||
|
|
||||||
'@babel/code-frame@7.27.1':
|
'@babel/code-frame@7.27.1':
|
||||||
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
|
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -210,10 +228,50 @@ packages:
|
|||||||
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
|
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@bramus/specificity@2.4.2':
|
||||||
|
resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@cloudflare/kv-asset-handler@0.4.2':
|
'@cloudflare/kv-asset-handler@0.4.2':
|
||||||
resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==}
|
resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@csstools/color-helpers@6.0.2':
|
||||||
|
resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==}
|
||||||
|
engines: {node: '>=20.19.0'}
|
||||||
|
|
||||||
|
'@csstools/css-calc@3.2.1':
|
||||||
|
resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==}
|
||||||
|
engines: {node: '>=20.19.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@csstools/css-parser-algorithms': ^4.0.0
|
||||||
|
'@csstools/css-tokenizer': ^4.0.0
|
||||||
|
|
||||||
|
'@csstools/css-color-parser@4.1.1':
|
||||||
|
resolution: {integrity: sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==}
|
||||||
|
engines: {node: '>=20.19.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@csstools/css-parser-algorithms': ^4.0.0
|
||||||
|
'@csstools/css-tokenizer': ^4.0.0
|
||||||
|
|
||||||
|
'@csstools/css-parser-algorithms@4.0.0':
|
||||||
|
resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
|
||||||
|
engines: {node: '>=20.19.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@csstools/css-tokenizer': ^4.0.0
|
||||||
|
|
||||||
|
'@csstools/css-syntax-patches-for-csstree@1.1.4':
|
||||||
|
resolution: {integrity: sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==}
|
||||||
|
peerDependencies:
|
||||||
|
css-tree: ^3.2.1
|
||||||
|
peerDependenciesMeta:
|
||||||
|
css-tree:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@csstools/css-tokenizer@4.0.0':
|
||||||
|
resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
|
||||||
|
engines: {node: '>=20.19.0'}
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.25.12':
|
'@esbuild/aix-ppc64@0.25.12':
|
||||||
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
|
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -682,6 +740,15 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@exodus/bytes@1.15.1':
|
||||||
|
resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@noble/hashes': ^1.8.0 || ^2.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@noble/hashes':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@ioredis/commands@1.5.1':
|
'@ioredis/commands@1.5.1':
|
||||||
resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==}
|
resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==}
|
||||||
|
|
||||||
@@ -1483,6 +1550,9 @@ packages:
|
|||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
bidi-js@1.0.3:
|
||||||
|
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
|
||||||
|
|
||||||
bindings@1.5.0:
|
bindings@1.5.0:
|
||||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||||
|
|
||||||
@@ -1630,9 +1700,17 @@ packages:
|
|||||||
crossws@0.3.5:
|
crossws@0.3.5:
|
||||||
resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
|
resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
|
||||||
|
|
||||||
|
css-tree@3.2.1:
|
||||||
|
resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
|
||||||
|
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
|
||||||
|
|
||||||
csstype@3.2.3:
|
csstype@3.2.3:
|
||||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||||
|
|
||||||
|
data-urls@7.0.0:
|
||||||
|
resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
|
|
||||||
db0@0.3.4:
|
db0@0.3.4:
|
||||||
resolution: {integrity: sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==}
|
resolution: {integrity: sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1665,6 +1743,9 @@ packages:
|
|||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
decimal.js@10.6.0:
|
||||||
|
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||||
|
|
||||||
deepmerge@4.3.1:
|
deepmerge@4.3.1:
|
||||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1754,6 +1835,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
|
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
|
||||||
engines: {node: '>=0.12'}
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
|
entities@8.0.0:
|
||||||
|
resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
|
||||||
|
engines: {node: '>=20.19.0'}
|
||||||
|
|
||||||
error-stack-parser-es@1.0.5:
|
error-stack-parser-es@1.0.5:
|
||||||
resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
|
resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
|
||||||
|
|
||||||
@@ -1938,6 +2023,10 @@ packages:
|
|||||||
hookable@5.5.3:
|
hookable@5.5.3:
|
||||||
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
|
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
|
||||||
|
|
||||||
|
html-encoding-sniffer@6.0.0:
|
||||||
|
resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
|
|
||||||
html-entities@2.3.3:
|
html-entities@2.3.3:
|
||||||
resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==}
|
resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==}
|
||||||
|
|
||||||
@@ -2023,6 +2112,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==}
|
resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
is-potential-custom-element-name@1.0.1:
|
||||||
|
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
|
||||||
|
|
||||||
is-reference@1.2.1:
|
is-reference@1.2.1:
|
||||||
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
|
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
|
||||||
|
|
||||||
@@ -2072,6 +2164,15 @@ packages:
|
|||||||
js-tokens@9.0.1:
|
js-tokens@9.0.1:
|
||||||
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
|
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
|
||||||
|
|
||||||
|
jsdom@29.1.1:
|
||||||
|
resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
canvas: ^3.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
canvas:
|
||||||
|
optional: true
|
||||||
|
|
||||||
jsesc@3.1.0:
|
jsesc@3.1.0:
|
||||||
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -2207,6 +2308,9 @@ packages:
|
|||||||
mdast-util-to-hast@13.2.1:
|
mdast-util-to-hast@13.2.1:
|
||||||
resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
|
resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
|
||||||
|
|
||||||
|
mdn-data@2.27.1:
|
||||||
|
resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
|
||||||
|
|
||||||
merge-anything@5.1.7:
|
merge-anything@5.1.7:
|
||||||
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
|
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
|
||||||
engines: {node: '>=12.13'}
|
engines: {node: '>=12.13'}
|
||||||
@@ -2353,6 +2457,9 @@ packages:
|
|||||||
parse5@7.3.0:
|
parse5@7.3.0:
|
||||||
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
||||||
|
|
||||||
|
parse5@8.0.1:
|
||||||
|
resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==}
|
||||||
|
|
||||||
parseurl@1.3.3:
|
parseurl@1.3.3:
|
||||||
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -2423,6 +2530,10 @@ packages:
|
|||||||
property-information@7.1.0:
|
property-information@7.1.0:
|
||||||
resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
|
resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
|
||||||
|
|
||||||
|
punycode@2.3.1:
|
||||||
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
quansync@0.2.11:
|
quansync@0.2.11:
|
||||||
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
|
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
|
||||||
|
|
||||||
@@ -2470,6 +2581,10 @@ packages:
|
|||||||
regex@5.1.1:
|
regex@5.1.1:
|
||||||
resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==}
|
resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==}
|
||||||
|
|
||||||
|
require-from-string@2.0.2:
|
||||||
|
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
resolve-from@5.0.0:
|
resolve-from@5.0.0:
|
||||||
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2517,6 +2632,10 @@ packages:
|
|||||||
safe-buffer@5.2.1:
|
safe-buffer@5.2.1:
|
||||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||||
|
|
||||||
|
saxes@6.0.0:
|
||||||
|
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
|
||||||
|
engines: {node: '>=v12.22.7'}
|
||||||
|
|
||||||
scule@1.3.0:
|
scule@1.3.0:
|
||||||
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
|
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
|
||||||
|
|
||||||
@@ -2683,6 +2802,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
symbol-tree@3.2.4:
|
||||||
|
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||||
|
|
||||||
tagged-tag@1.0.0:
|
tagged-tag@1.0.0:
|
||||||
resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
|
resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
@@ -2740,6 +2862,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
|
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
tldts-core@7.4.0:
|
||||||
|
resolution: {integrity: sha512-/mb9kRld+x1sIMXxWNOAp5m6C+D4GrAORWlJkOJ5dElvxdN1eutz/o7qHLp9gFvDF4Y3/L2xeScoxz6AbEo8rQ==}
|
||||||
|
|
||||||
|
tldts@7.4.0:
|
||||||
|
resolution: {integrity: sha512-yHBe+zVfzNZ3QfTPW/Z6KK1G2t340gFjMHqI/4KKSt/abzYydzuCnpqdaF5gCCABby+9Yfbj59oR5F2Fd5CBzg==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
@@ -2748,9 +2877,17 @@ packages:
|
|||||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
|
|
||||||
|
tough-cookie@6.0.1:
|
||||||
|
resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
tr46@0.0.3:
|
tr46@0.0.3:
|
||||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||||
|
|
||||||
|
tr46@6.0.0:
|
||||||
|
resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
trim-lines@3.0.1:
|
trim-lines@3.0.1:
|
||||||
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
|
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
|
||||||
|
|
||||||
@@ -2782,6 +2919,10 @@ packages:
|
|||||||
undici-types@7.24.6:
|
undici-types@7.24.6:
|
||||||
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
|
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
|
||||||
|
|
||||||
|
undici@7.26.0:
|
||||||
|
resolution: {integrity: sha512-3O9Tf67pGhgOv9jM35AbhkXAKi13f3oy3aE4CSgr+TckGeY+/iu97ZXN+J7DpHPzLbVApFd1IFhcnBjREYXYcg==}
|
||||||
|
engines: {node: '>=20.18.1'}
|
||||||
|
|
||||||
unenv@2.0.0-rc.24:
|
unenv@2.0.0-rc.24:
|
||||||
resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==}
|
resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==}
|
||||||
|
|
||||||
@@ -3021,12 +3162,28 @@ packages:
|
|||||||
jsdom:
|
jsdom:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
w3c-xmlserializer@5.0.0:
|
||||||
|
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
webidl-conversions@3.0.1:
|
webidl-conversions@3.0.1:
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
|
||||||
|
webidl-conversions@8.0.1:
|
||||||
|
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
webpack-virtual-modules@0.6.2:
|
webpack-virtual-modules@0.6.2:
|
||||||
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
||||||
|
|
||||||
|
whatwg-mimetype@5.0.0:
|
||||||
|
resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
whatwg-url@16.0.1:
|
||||||
|
resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
whatwg-url@5.0.0:
|
||||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
|
|
||||||
@@ -3056,6 +3213,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==}
|
resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
xml-name-validator@5.0.0:
|
||||||
|
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
xmlchars@2.2.0:
|
||||||
|
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
|
||||||
|
|
||||||
y18n@5.0.8:
|
y18n@5.0.8:
|
||||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -3090,6 +3254,26 @@ packages:
|
|||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
|
'@asamuzakjp/css-color@5.1.11':
|
||||||
|
dependencies:
|
||||||
|
'@asamuzakjp/generational-cache': 1.0.1
|
||||||
|
'@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
|
||||||
|
'@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
|
||||||
|
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
|
||||||
|
'@csstools/css-tokenizer': 4.0.0
|
||||||
|
|
||||||
|
'@asamuzakjp/dom-selector@7.1.1':
|
||||||
|
dependencies:
|
||||||
|
'@asamuzakjp/generational-cache': 1.0.1
|
||||||
|
'@asamuzakjp/nwsapi': 2.3.9
|
||||||
|
bidi-js: 1.0.3
|
||||||
|
css-tree: 3.2.1
|
||||||
|
is-potential-custom-element-name: 1.0.1
|
||||||
|
|
||||||
|
'@asamuzakjp/generational-cache@1.0.1': {}
|
||||||
|
|
||||||
|
'@asamuzakjp/nwsapi@2.3.9': {}
|
||||||
|
|
||||||
'@babel/code-frame@7.27.1':
|
'@babel/code-frame@7.27.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/helper-validator-identifier': 7.29.7
|
'@babel/helper-validator-identifier': 7.29.7
|
||||||
@@ -3288,8 +3472,36 @@ snapshots:
|
|||||||
|
|
||||||
'@bcoe/v8-coverage@1.0.2': {}
|
'@bcoe/v8-coverage@1.0.2': {}
|
||||||
|
|
||||||
|
'@bramus/specificity@2.4.2':
|
||||||
|
dependencies:
|
||||||
|
css-tree: 3.2.1
|
||||||
|
|
||||||
'@cloudflare/kv-asset-handler@0.4.2': {}
|
'@cloudflare/kv-asset-handler@0.4.2': {}
|
||||||
|
|
||||||
|
'@csstools/color-helpers@6.0.2': {}
|
||||||
|
|
||||||
|
'@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
|
||||||
|
'@csstools/css-tokenizer': 4.0.0
|
||||||
|
|
||||||
|
'@csstools/css-color-parser@4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@csstools/color-helpers': 6.0.2
|
||||||
|
'@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
|
||||||
|
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
|
||||||
|
'@csstools/css-tokenizer': 4.0.0
|
||||||
|
|
||||||
|
'@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@csstools/css-tokenizer': 4.0.0
|
||||||
|
|
||||||
|
'@csstools/css-syntax-patches-for-csstree@1.1.4(css-tree@3.2.1)':
|
||||||
|
optionalDependencies:
|
||||||
|
css-tree: 3.2.1
|
||||||
|
|
||||||
|
'@csstools/css-tokenizer@4.0.0': {}
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.25.12':
|
'@esbuild/aix-ppc64@0.25.12':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -3524,6 +3736,8 @@ snapshots:
|
|||||||
'@esbuild/win32-x64@0.28.0':
|
'@esbuild/win32-x64@0.28.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@exodus/bytes@1.15.1': {}
|
||||||
|
|
||||||
'@ioredis/commands@1.5.1': {}
|
'@ioredis/commands@1.5.1': {}
|
||||||
|
|
||||||
'@isaacs/cliui@8.0.2':
|
'@isaacs/cliui@8.0.2':
|
||||||
@@ -4161,7 +4375,7 @@ snapshots:
|
|||||||
obug: 2.1.1
|
obug: 2.1.1
|
||||||
std-env: 4.1.0
|
std-env: 4.1.0
|
||||||
tinyrainbow: 3.1.0
|
tinyrainbow: 3.1.0
|
||||||
vitest: 4.1.7(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
vitest: 4.1.7(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(jsdom@29.1.1)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
||||||
|
|
||||||
'@vitest/expect@4.1.7':
|
'@vitest/expect@4.1.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4338,6 +4552,10 @@ snapshots:
|
|||||||
|
|
||||||
baseline-browser-mapping@2.10.32: {}
|
baseline-browser-mapping@2.10.32: {}
|
||||||
|
|
||||||
|
bidi-js@1.0.3:
|
||||||
|
dependencies:
|
||||||
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
bindings@1.5.0:
|
bindings@1.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
file-uri-to-path: 1.0.0
|
file-uri-to-path: 1.0.0
|
||||||
@@ -4479,14 +4697,28 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
uncrypto: 0.1.3
|
uncrypto: 0.1.3
|
||||||
|
|
||||||
|
css-tree@3.2.1:
|
||||||
|
dependencies:
|
||||||
|
mdn-data: 2.27.1
|
||||||
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
csstype@3.2.3: {}
|
csstype@3.2.3: {}
|
||||||
|
|
||||||
|
data-urls@7.0.0:
|
||||||
|
dependencies:
|
||||||
|
whatwg-mimetype: 5.0.0
|
||||||
|
whatwg-url: 16.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@noble/hashes'
|
||||||
|
|
||||||
db0@0.3.4: {}
|
db0@0.3.4: {}
|
||||||
|
|
||||||
debug@4.4.3:
|
debug@4.4.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
|
decimal.js@10.6.0: {}
|
||||||
|
|
||||||
deepmerge@4.3.1: {}
|
deepmerge@4.3.1: {}
|
||||||
|
|
||||||
default-browser-id@5.0.1: {}
|
default-browser-id@5.0.1: {}
|
||||||
@@ -4547,6 +4779,8 @@ snapshots:
|
|||||||
|
|
||||||
entities@6.0.1: {}
|
entities@6.0.1: {}
|
||||||
|
|
||||||
|
entities@8.0.0: {}
|
||||||
|
|
||||||
error-stack-parser-es@1.0.5: {}
|
error-stack-parser-es@1.0.5: {}
|
||||||
|
|
||||||
error-stack-parser@2.1.4:
|
error-stack-parser@2.1.4:
|
||||||
@@ -4799,6 +5033,12 @@ snapshots:
|
|||||||
|
|
||||||
hookable@5.5.3: {}
|
hookable@5.5.3: {}
|
||||||
|
|
||||||
|
html-encoding-sniffer@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@exodus/bytes': 1.15.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@noble/hashes'
|
||||||
|
|
||||||
html-entities@2.3.3: {}
|
html-entities@2.3.3: {}
|
||||||
|
|
||||||
html-escaper@2.0.2: {}
|
html-escaper@2.0.2: {}
|
||||||
@@ -4874,6 +5114,8 @@ snapshots:
|
|||||||
|
|
||||||
is-path-inside@4.0.0: {}
|
is-path-inside@4.0.0: {}
|
||||||
|
|
||||||
|
is-potential-custom-element-name@1.0.1: {}
|
||||||
|
|
||||||
is-reference@1.2.1:
|
is-reference@1.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.9
|
'@types/estree': 1.0.9
|
||||||
@@ -4917,6 +5159,32 @@ snapshots:
|
|||||||
|
|
||||||
js-tokens@9.0.1: {}
|
js-tokens@9.0.1: {}
|
||||||
|
|
||||||
|
jsdom@29.1.1:
|
||||||
|
dependencies:
|
||||||
|
'@asamuzakjp/css-color': 5.1.11
|
||||||
|
'@asamuzakjp/dom-selector': 7.1.1
|
||||||
|
'@bramus/specificity': 2.4.2
|
||||||
|
'@csstools/css-syntax-patches-for-csstree': 1.1.4(css-tree@3.2.1)
|
||||||
|
'@exodus/bytes': 1.15.1
|
||||||
|
css-tree: 3.2.1
|
||||||
|
data-urls: 7.0.0
|
||||||
|
decimal.js: 10.6.0
|
||||||
|
html-encoding-sniffer: 6.0.0
|
||||||
|
is-potential-custom-element-name: 1.0.1
|
||||||
|
lru-cache: 11.5.0
|
||||||
|
parse5: 8.0.1
|
||||||
|
saxes: 6.0.0
|
||||||
|
symbol-tree: 3.2.4
|
||||||
|
tough-cookie: 6.0.1
|
||||||
|
undici: 7.26.0
|
||||||
|
w3c-xmlserializer: 5.0.0
|
||||||
|
webidl-conversions: 8.0.1
|
||||||
|
whatwg-mimetype: 5.0.0
|
||||||
|
whatwg-url: 16.0.1
|
||||||
|
xml-name-validator: 5.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@noble/hashes'
|
||||||
|
|
||||||
jsesc@3.1.0: {}
|
jsesc@3.1.0: {}
|
||||||
|
|
||||||
json5@2.2.3: {}
|
json5@2.2.3: {}
|
||||||
@@ -5047,6 +5315,8 @@ snapshots:
|
|||||||
unist-util-visit: 5.1.0
|
unist-util-visit: 5.1.0
|
||||||
vfile: 6.0.3
|
vfile: 6.0.3
|
||||||
|
|
||||||
|
mdn-data@2.27.1: {}
|
||||||
|
|
||||||
merge-anything@5.1.7:
|
merge-anything@5.1.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-what: 4.1.16
|
is-what: 4.1.16
|
||||||
@@ -5273,6 +5543,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
entities: 6.0.1
|
entities: 6.0.1
|
||||||
|
|
||||||
|
parse5@8.0.1:
|
||||||
|
dependencies:
|
||||||
|
entities: 8.0.0
|
||||||
|
|
||||||
parseurl@1.3.3: {}
|
parseurl@1.3.3: {}
|
||||||
|
|
||||||
path-key@3.1.1: {}
|
path-key@3.1.1: {}
|
||||||
@@ -5331,6 +5605,8 @@ snapshots:
|
|||||||
|
|
||||||
property-information@7.1.0: {}
|
property-information@7.1.0: {}
|
||||||
|
|
||||||
|
punycode@2.3.1: {}
|
||||||
|
|
||||||
quansync@0.2.11: {}
|
quansync@0.2.11: {}
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
@@ -5385,6 +5661,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
regex-utilities: 2.3.0
|
regex-utilities: 2.3.0
|
||||||
|
|
||||||
|
require-from-string@2.0.2: {}
|
||||||
|
|
||||||
resolve-from@5.0.0: {}
|
resolve-from@5.0.0: {}
|
||||||
|
|
||||||
resolve@1.22.12:
|
resolve@1.22.12:
|
||||||
@@ -5448,6 +5726,10 @@ snapshots:
|
|||||||
|
|
||||||
safe-buffer@5.2.1: {}
|
safe-buffer@5.2.1: {}
|
||||||
|
|
||||||
|
saxes@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
xmlchars: 2.2.0
|
||||||
|
|
||||||
scule@1.3.0: {}
|
scule@1.3.0: {}
|
||||||
|
|
||||||
semver@6.3.1: {}
|
semver@6.3.1: {}
|
||||||
@@ -5622,6 +5904,8 @@ snapshots:
|
|||||||
|
|
||||||
supports-preserve-symlinks-flag@1.0.0: {}
|
supports-preserve-symlinks-flag@1.0.0: {}
|
||||||
|
|
||||||
|
symbol-tree@3.2.4: {}
|
||||||
|
|
||||||
tagged-tag@1.0.0: {}
|
tagged-tag@1.0.0: {}
|
||||||
|
|
||||||
tailwindcss@4.3.0: {}
|
tailwindcss@4.3.0: {}
|
||||||
@@ -5687,14 +5971,28 @@ snapshots:
|
|||||||
|
|
||||||
tinyrainbow@3.1.0: {}
|
tinyrainbow@3.1.0: {}
|
||||||
|
|
||||||
|
tldts-core@7.4.0: {}
|
||||||
|
|
||||||
|
tldts@7.4.0:
|
||||||
|
dependencies:
|
||||||
|
tldts-core: 7.4.0
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
|
|
||||||
toidentifier@1.0.1: {}
|
toidentifier@1.0.1: {}
|
||||||
|
|
||||||
|
tough-cookie@6.0.1:
|
||||||
|
dependencies:
|
||||||
|
tldts: 7.4.0
|
||||||
|
|
||||||
tr46@0.0.3: {}
|
tr46@0.0.3: {}
|
||||||
|
|
||||||
|
tr46@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
punycode: 2.3.1
|
||||||
|
|
||||||
trim-lines@3.0.1: {}
|
trim-lines@3.0.1: {}
|
||||||
|
|
||||||
turbo@2.9.14:
|
turbo@2.9.14:
|
||||||
@@ -5727,6 +6025,8 @@ snapshots:
|
|||||||
|
|
||||||
undici-types@7.24.6: {}
|
undici-types@7.24.6: {}
|
||||||
|
|
||||||
|
undici@7.26.0: {}
|
||||||
|
|
||||||
unenv@2.0.0-rc.24:
|
unenv@2.0.0-rc.24:
|
||||||
dependencies:
|
dependencies:
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
@@ -5882,7 +6182,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)
|
vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)
|
||||||
|
|
||||||
vitest@4.1.7(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)):
|
vitest@4.1.7(@types/node@25.9.1)(@vitest/coverage-v8@4.1.7)(jsdom@29.1.1)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 4.1.7
|
'@vitest/expect': 4.1.7
|
||||||
'@vitest/mocker': 4.1.7(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
'@vitest/mocker': 4.1.7(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))
|
||||||
@@ -5907,13 +6207,30 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 25.9.1
|
'@types/node': 25.9.1
|
||||||
'@vitest/coverage-v8': 4.1.7(vitest@4.1.7)
|
'@vitest/coverage-v8': 4.1.7(vitest@4.1.7)
|
||||||
|
jsdom: 29.1.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- msw
|
- msw
|
||||||
|
|
||||||
|
w3c-xmlserializer@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
xml-name-validator: 5.0.0
|
||||||
|
|
||||||
webidl-conversions@3.0.1: {}
|
webidl-conversions@3.0.1: {}
|
||||||
|
|
||||||
|
webidl-conversions@8.0.1: {}
|
||||||
|
|
||||||
webpack-virtual-modules@0.6.2: {}
|
webpack-virtual-modules@0.6.2: {}
|
||||||
|
|
||||||
|
whatwg-mimetype@5.0.0: {}
|
||||||
|
|
||||||
|
whatwg-url@16.0.1:
|
||||||
|
dependencies:
|
||||||
|
'@exodus/bytes': 1.15.1
|
||||||
|
tr46: 6.0.0
|
||||||
|
webidl-conversions: 8.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@noble/hashes'
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
whatwg-url@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
tr46: 0.0.3
|
tr46: 0.0.3
|
||||||
@@ -5951,6 +6268,10 @@ snapshots:
|
|||||||
is-wsl: 3.1.1
|
is-wsl: 3.1.1
|
||||||
powershell-utils: 0.1.0
|
powershell-utils: 0.1.0
|
||||||
|
|
||||||
|
xml-name-validator@5.0.0: {}
|
||||||
|
|
||||||
|
xmlchars@2.2.0: {}
|
||||||
|
|
||||||
y18n@5.0.8: {}
|
y18n@5.0.8: {}
|
||||||
|
|
||||||
yallist@3.1.1: {}
|
yallist@3.1.1: {}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"node": ">=22"
|
"node": ">=22"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"jsdom": "^29.1.1",
|
||||||
"vitest": "^4.1.5"
|
"vitest": "^4.1.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
395
web/src/app.css
395
web/src/app.css
@@ -1 +1,396 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@property --color-bg {
|
||||||
|
syntax: "<color>";
|
||||||
|
inherits: true;
|
||||||
|
initial-value: #fafbfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property --color-bg-secondary {
|
||||||
|
syntax: "<color>";
|
||||||
|
inherits: true;
|
||||||
|
initial-value: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property --color-text-primary {
|
||||||
|
syntax: "<color>";
|
||||||
|
inherits: true;
|
||||||
|
initial-value: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property --color-text-secondary {
|
||||||
|
syntax: "<color>";
|
||||||
|
inherits: true;
|
||||||
|
initial-value: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property --color-border {
|
||||||
|
syntax: "<color>";
|
||||||
|
inherits: true;
|
||||||
|
initial-value: #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-bg: #fafbfc;
|
||||||
|
--color-bg-secondary: #f3f4f6;
|
||||||
|
--color-bg-tertiary: #e5e7eb;
|
||||||
|
|
||||||
|
--color-text-primary: #111827;
|
||||||
|
--color-text-secondary: #6b7280;
|
||||||
|
--color-text-tertiary: #9ca3af;
|
||||||
|
|
||||||
|
--color-brand-primary: #4f46e5;
|
||||||
|
--color-brand-primary-light: #818cf8;
|
||||||
|
--color-brand-primary-dark: #4338ca;
|
||||||
|
|
||||||
|
--color-brand-accent: #06b6d4;
|
||||||
|
--color-brand-accent-light: #67e8f9;
|
||||||
|
--color-brand-accent-dark: #0891b2;
|
||||||
|
|
||||||
|
--color-border: #e5e7eb;
|
||||||
|
--color-border-dark: #d1d5db;
|
||||||
|
|
||||||
|
--color-success: #06b6d4;
|
||||||
|
--color-warning: #f59e0b;
|
||||||
|
--color-error: #ef4444;
|
||||||
|
--color-info: #4f46e5;
|
||||||
|
|
||||||
|
--color-success-bg: #ecfeff;
|
||||||
|
--color-warning-bg: #fffbeb;
|
||||||
|
--color-error-bg: #fef2f2;
|
||||||
|
--color-info-bg: #eef2ff;
|
||||||
|
|
||||||
|
--color-amber: #f59e0b;
|
||||||
|
--color-danger: #ef4444;
|
||||||
|
|
||||||
|
--color-glass: rgba(255, 255, 255, 0.8);
|
||||||
|
--color-glass-dark: rgba(17, 24, 39, 0.8);
|
||||||
|
--gradient-card-start: #ffffff;
|
||||||
|
--gradient-card-end: #f3f4f6;
|
||||||
|
|
||||||
|
--color-dot-grid: #e5e7eb;
|
||||||
|
|
||||||
|
--color-focus-ring: #4f46e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-bg: #fafbfc;
|
||||||
|
--color-bg-secondary: #f3f4f6;
|
||||||
|
--color-bg-tertiary: #e5e7eb;
|
||||||
|
|
||||||
|
--color-text-primary: #111827;
|
||||||
|
--color-text-secondary: #6b7280;
|
||||||
|
--color-text-tertiary: #9ca3af;
|
||||||
|
|
||||||
|
--color-brand-primary: #4f46e5;
|
||||||
|
--color-brand-primary-light: #818cf8;
|
||||||
|
--color-brand-primary-dark: #4338ca;
|
||||||
|
|
||||||
|
--color-brand-accent: #06b6d4;
|
||||||
|
--color-brand-accent-light: #67e8f9;
|
||||||
|
--color-brand-accent-dark: #0891b2;
|
||||||
|
|
||||||
|
--color-border: #e5e7eb;
|
||||||
|
--color-border-dark: #d1d5db;
|
||||||
|
|
||||||
|
--color-success: #06b6d4;
|
||||||
|
--color-warning: #f59e0b;
|
||||||
|
--color-error: #ef4444;
|
||||||
|
--color-info: #4f46e5;
|
||||||
|
|
||||||
|
--color-success-bg: #ecfeff;
|
||||||
|
--color-warning-bg: #fffbeb;
|
||||||
|
--color-error-bg: #fef2f2;
|
||||||
|
--color-info-bg: #eef2ff;
|
||||||
|
|
||||||
|
--color-amber: #f59e0b;
|
||||||
|
--color-danger: #ef4444;
|
||||||
|
|
||||||
|
--color-glass: rgba(255, 255, 255, 0.8);
|
||||||
|
--color-glass-dark: rgba(17, 24, 39, 0.8);
|
||||||
|
--gradient-card-start: #ffffff;
|
||||||
|
--gradient-card-end: #f3f4f6;
|
||||||
|
--color-dot-grid: #e5e7eb;
|
||||||
|
--color-focus-ring: #4f46e5;
|
||||||
|
|
||||||
|
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||||
|
sans-serif;
|
||||||
|
|
||||||
|
--spacing-0: 0px;
|
||||||
|
--spacing-1: 4px;
|
||||||
|
--spacing-2: 8px;
|
||||||
|
--spacing-3: 12px;
|
||||||
|
--spacing-4: 16px;
|
||||||
|
--spacing-5: 20px;
|
||||||
|
--spacing-6: 24px;
|
||||||
|
--spacing-7: 32px;
|
||||||
|
--spacing-8: 40px;
|
||||||
|
--spacing-9: 48px;
|
||||||
|
--spacing-10: 56px;
|
||||||
|
--spacing-11: 64px;
|
||||||
|
--spacing-12: 72px;
|
||||||
|
|
||||||
|
--radius-none: 0px;
|
||||||
|
--radius-sm: 4px;
|
||||||
|
--radius-md: 8px;
|
||||||
|
--radius-lg: 12px;
|
||||||
|
--radius-xl: 16px;
|
||||||
|
--radius-full: 9999px;
|
||||||
|
|
||||||
|
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||||
|
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--color-bg: #111827;
|
||||||
|
--color-bg-secondary: #1f2937;
|
||||||
|
--color-bg-tertiary: #374151;
|
||||||
|
|
||||||
|
--color-text-primary: #f9fafb;
|
||||||
|
--color-text-secondary: #d1d5db;
|
||||||
|
--color-text-tertiary: #9ca3af;
|
||||||
|
|
||||||
|
--color-border: #374151;
|
||||||
|
--color-border-dark: #4b5563;
|
||||||
|
|
||||||
|
--color-success-bg: #0c4a6e;
|
||||||
|
--color-warning-bg: #78350f;
|
||||||
|
--color-error-bg: #7f1d1d;
|
||||||
|
--color-info-bg: #1e1b4b;
|
||||||
|
|
||||||
|
--color-glass: rgba(17, 24, 39, 0.8);
|
||||||
|
--color-glass-dark: rgba(17, 24, 39, 0.9);
|
||||||
|
--gradient-card-start: #1f2937;
|
||||||
|
--gradient-card-end: #0b1120;
|
||||||
|
|
||||||
|
--color-dot-grid: #374151;
|
||||||
|
--color-focus-ring: #818cf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-bg: #111827;
|
||||||
|
--color-bg-secondary: #1f2937;
|
||||||
|
--color-bg-tertiary: #374151;
|
||||||
|
|
||||||
|
--color-text-primary: #f9fafb;
|
||||||
|
--color-text-secondary: #d1d5db;
|
||||||
|
--color-text-tertiary: #9ca3af;
|
||||||
|
|
||||||
|
--color-border: #374151;
|
||||||
|
--color-border-dark: #4b5563;
|
||||||
|
|
||||||
|
--color-success-bg: #0c4a6e;
|
||||||
|
--color-warning-bg: #78350f;
|
||||||
|
--color-error-bg: #7f1d1d;
|
||||||
|
--color-info-bg: #1e1b4b;
|
||||||
|
|
||||||
|
--color-glass: rgba(17, 24, 39, 0.8);
|
||||||
|
--color-glass-dark: rgba(17, 24, 39, 0.9);
|
||||||
|
--gradient-card-start: #1f2937;
|
||||||
|
--gradient-card-end: #0b1120;
|
||||||
|
--color-dot-grid: #374151;
|
||||||
|
--color-focus-ring: #818cf8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.light {
|
||||||
|
--color-bg: #fafbfc;
|
||||||
|
--color-bg-secondary: #f3f4f6;
|
||||||
|
--color-bg-tertiary: #e5e7eb;
|
||||||
|
|
||||||
|
--color-text-primary: #111827;
|
||||||
|
--color-text-secondary: #6b7280;
|
||||||
|
--color-text-tertiary: #9ca3af;
|
||||||
|
|
||||||
|
--color-border: #e5e7eb;
|
||||||
|
--color-border-dark: #d1d5db;
|
||||||
|
|
||||||
|
--color-success-bg: #ecfeff;
|
||||||
|
--color-warning-bg: #fffbeb;
|
||||||
|
--color-error-bg: #fef2f2;
|
||||||
|
--color-info-bg: #eef2ff;
|
||||||
|
|
||||||
|
--color-glass: rgba(255, 255, 255, 0.8);
|
||||||
|
--color-glass-dark: rgba(17, 24, 39, 0.8);
|
||||||
|
--gradient-card-start: #ffffff;
|
||||||
|
--gradient-card-end: #f3f4f6;
|
||||||
|
|
||||||
|
--color-dot-grid: #e5e7eb;
|
||||||
|
--color-focus-ring: #4f46e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark {
|
||||||
|
--color-bg: #111827;
|
||||||
|
--color-bg-secondary: #1f2937;
|
||||||
|
--color-bg-tertiary: #374151;
|
||||||
|
|
||||||
|
--color-text-primary: #f9fafb;
|
||||||
|
--color-text-secondary: #d1d5db;
|
||||||
|
--color-text-tertiary: #9ca3af;
|
||||||
|
|
||||||
|
--color-border: #374151;
|
||||||
|
--color-border-dark: #4b5563;
|
||||||
|
|
||||||
|
--color-success-bg: #0c4a6e;
|
||||||
|
--color-warning-bg: #78350f;
|
||||||
|
--color-error-bg: #7f1d1d;
|
||||||
|
--color-info-bg: #1e1b4b;
|
||||||
|
|
||||||
|
--color-glass: rgba(17, 24, 39, 0.8);
|
||||||
|
--color-glass-dark: rgba(17, 24, 39, 0.9);
|
||||||
|
--gradient-card-start: #1f2937;
|
||||||
|
--gradient-card-end: #0b1120;
|
||||||
|
|
||||||
|
--color-dot-grid: #374151;
|
||||||
|
--color-focus-ring: #818cf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
background-image: linear-gradient(90deg, transparent 1.5px),
|
||||||
|
radial-gradient(
|
||||||
|
circle,
|
||||||
|
color-mix(in srgb, var(--color-dot-grid) 80%, transparent) 1px,
|
||||||
|
transparent 3px
|
||||||
|
);
|
||||||
|
background-size:
|
||||||
|
54px 100%,
|
||||||
|
36px 36px;
|
||||||
|
background-position:
|
||||||
|
0 0,
|
||||||
|
18px 0;
|
||||||
|
background-attachment: fixed;
|
||||||
|
transition:
|
||||||
|
background-color 500ms ease-in-out,
|
||||||
|
color 500ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
html {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
line-height: 1.5;
|
||||||
|
transition:
|
||||||
|
background-color 500ms ease-in-out,
|
||||||
|
color 500ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*:focus-visible {
|
||||||
|
outline: 2px solid var(--color-focus-ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.text-balance {
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-primary {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--color-brand-primary) 0%,
|
||||||
|
var(--color-brand-primary-dark) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-accent {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--color-brand-accent) 0%,
|
||||||
|
var(--color-brand-accent-dark) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-subtle {
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
var(--color-bg) 0%,
|
||||||
|
var(--color-bg-secondary) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-card {
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
var(--gradient-card-start) 0%,
|
||||||
|
var(--gradient-card-end) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass {
|
||||||
|
background: var(--color-glass);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-dark {
|
||||||
|
background: var(--color-glass-dark);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gradient-primary {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--color-brand-primary) 0%,
|
||||||
|
var(--color-brand-primary-light) 100%
|
||||||
|
);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gradient-accent {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--color-brand-accent) 0%,
|
||||||
|
var(--color-brand-accent-light) 100%
|
||||||
|
);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-glow-primary {
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 4px rgba(79, 70, 229, 0.1),
|
||||||
|
0 4px 12px rgba(79, 70, 229, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-glow-accent {
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 4px rgba(6, 182, 212, 0.1),
|
||||||
|
0 4px 12px rgba(6, 182, 212, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-noise {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.02'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-dot-grid {
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
background-image: linear-gradient(90deg, transparent 1.5px),
|
||||||
|
radial-gradient(
|
||||||
|
circle,
|
||||||
|
color-mix(in srgb, var(--color-dot-grid) 80%, transparent) 1px,
|
||||||
|
transparent 3px
|
||||||
|
);
|
||||||
|
background-size:
|
||||||
|
54px 100%,
|
||||||
|
36px 36px;
|
||||||
|
background-position:
|
||||||
|
0 0,
|
||||||
|
18px 0;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
288
web/src/lib/theme.test.ts
Normal file
288
web/src/lib/theme.test.ts
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||||
|
import { createRoot } from "solid-js";
|
||||||
|
import {
|
||||||
|
useTheme,
|
||||||
|
getSystemTheme,
|
||||||
|
getStoredTheme,
|
||||||
|
getResolvedTheme,
|
||||||
|
applyThemeClass,
|
||||||
|
updateMetaThemeColor,
|
||||||
|
persistTheme,
|
||||||
|
} from "./theme";
|
||||||
|
|
||||||
|
function createLocalStorageMock() {
|
||||||
|
const store = new Map<string, string>();
|
||||||
|
return {
|
||||||
|
getItem: (key: string) => store.get(key) ?? null,
|
||||||
|
setItem: (key: string, value: string) => store.set(key, value),
|
||||||
|
removeItem: (key: string) => store.delete(key),
|
||||||
|
clear: () => store.clear(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupDOM() {
|
||||||
|
document.documentElement.classList.remove("light", "dark");
|
||||||
|
document
|
||||||
|
.querySelectorAll('meta[name="theme-color"]')
|
||||||
|
.forEach((el) => el.remove());
|
||||||
|
const ls = createLocalStorageMock();
|
||||||
|
vi.stubGlobal("localStorage", ls);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMatchMediaMock(matches: boolean) {
|
||||||
|
return vi.fn().mockImplementation((query: string) => ({
|
||||||
|
matches,
|
||||||
|
media: query,
|
||||||
|
onchange: null,
|
||||||
|
addEventListener: vi.fn(),
|
||||||
|
removeEventListener: vi.fn(),
|
||||||
|
addListener: vi.fn(),
|
||||||
|
removeListener: vi.fn(),
|
||||||
|
dispatchEvent: vi.fn(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function runWithRoot<T>(fn: () => T): T {
|
||||||
|
return createRoot((dispose) => {
|
||||||
|
const result = fn();
|
||||||
|
return result;
|
||||||
|
}) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("getSystemTheme", () => {
|
||||||
|
it("returns 'light' when prefers-color-scheme is light", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(false));
|
||||||
|
expect(getSystemTheme()).toBe("light");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 'dark' when prefers-color-scheme is dark", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(true));
|
||||||
|
expect(getSystemTheme()).toBe("dark");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getStoredTheme", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupDOM();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 'system' when nothing is stored", () => {
|
||||||
|
expect(getStoredTheme()).toBe("system");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 'light' when stored", () => {
|
||||||
|
localStorage.setItem("shieldai-theme", "light");
|
||||||
|
expect(getStoredTheme()).toBe("light");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 'dark' when stored", () => {
|
||||||
|
localStorage.setItem("shieldai-theme", "dark");
|
||||||
|
expect(getStoredTheme()).toBe("dark");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 'system' for invalid value", () => {
|
||||||
|
localStorage.setItem("shieldai-theme", "invalid");
|
||||||
|
expect(getStoredTheme()).toBe("system");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getResolvedTheme", () => {
|
||||||
|
it("resolves explicit theme directly", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(true));
|
||||||
|
expect(getResolvedTheme("light")).toBe("light");
|
||||||
|
expect(getResolvedTheme("dark")).toBe("dark");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves 'system' to OS preference", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(false));
|
||||||
|
expect(getResolvedTheme("system")).toBe("light");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves 'system' to dark with dark OS", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(true));
|
||||||
|
expect(getResolvedTheme("system")).toBe("dark");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("applyThemeClass", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupDOM();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds 'light' class and removes 'dark'", () => {
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
|
applyThemeClass("light");
|
||||||
|
expect(document.documentElement.classList.contains("light")).toBe(true);
|
||||||
|
expect(document.documentElement.classList.contains("dark")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds 'dark' class and removes 'light'", () => {
|
||||||
|
document.documentElement.classList.add("light");
|
||||||
|
applyThemeClass("dark");
|
||||||
|
expect(document.documentElement.classList.contains("dark")).toBe(true);
|
||||||
|
expect(document.documentElement.classList.contains("light")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("updateMetaThemeColor", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupDOM();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a theme-color meta tag for light mode", () => {
|
||||||
|
updateMetaThemeColor("light");
|
||||||
|
const meta = document.querySelector('meta[name="theme-color"]');
|
||||||
|
expect(meta).not.toBeNull();
|
||||||
|
expect(meta!.getAttribute("content")).toBe("#fafbfc");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a theme-color meta tag for dark mode", () => {
|
||||||
|
updateMetaThemeColor("dark");
|
||||||
|
const meta = document.querySelector('meta[name="theme-color"]');
|
||||||
|
expect(meta).not.toBeNull();
|
||||||
|
expect(meta!.getAttribute("content")).toBe("#111827");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates existing meta tag instead of creating new one", () => {
|
||||||
|
updateMetaThemeColor("light");
|
||||||
|
updateMetaThemeColor("dark");
|
||||||
|
const metas = document.querySelectorAll('meta[name="theme-color"]');
|
||||||
|
expect(metas.length).toBe(1);
|
||||||
|
expect(metas[0].getAttribute("content")).toBe("#111827");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("persistTheme", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupDOM();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("writes theme to localStorage", () => {
|
||||||
|
persistTheme("dark");
|
||||||
|
expect(localStorage.getItem("shieldai-theme")).toBe("dark");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("writes 'system' to localStorage", () => {
|
||||||
|
persistTheme("system");
|
||||||
|
expect(localStorage.getItem("shieldai-theme")).toBe("system");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("useTheme", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupDOM();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("initial theme", () => {
|
||||||
|
it("returns 'system' when no theme is stored", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(false));
|
||||||
|
runWithRoot(() => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
expect(theme()).toBe("system");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 'light' from localStorage", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(false));
|
||||||
|
localStorage.setItem("shieldai-theme", "light");
|
||||||
|
runWithRoot(() => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
expect(theme()).toBe("light");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 'dark' from localStorage", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(false));
|
||||||
|
localStorage.setItem("shieldai-theme", "dark");
|
||||||
|
runWithRoot(() => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
expect(theme()).toBe("dark");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("resolved theme", () => {
|
||||||
|
it("resolves 'system' to 'light' with light OS preference", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(false));
|
||||||
|
runWithRoot(() => {
|
||||||
|
const { resolved } = useTheme();
|
||||||
|
expect(resolved()).toBe("light");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves 'system' to 'dark' with dark OS preference", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(true));
|
||||||
|
runWithRoot(() => {
|
||||||
|
const { resolved } = useTheme();
|
||||||
|
expect(resolved()).toBe("dark");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("setTheme", () => {
|
||||||
|
it("sets theme signal and persists to localStorage", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(false));
|
||||||
|
runWithRoot(() => {
|
||||||
|
const { setTheme, theme } = useTheme();
|
||||||
|
setTheme("dark");
|
||||||
|
expect(theme()).toBe("dark");
|
||||||
|
expect(localStorage.getItem("shieldai-theme")).toBe("dark");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("persists 'system' to localStorage", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(false));
|
||||||
|
runWithRoot(() => {
|
||||||
|
const { setTheme } = useTheme();
|
||||||
|
setTheme("system");
|
||||||
|
expect(localStorage.getItem("shieldai-theme")).toBe("system");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("toggle", () => {
|
||||||
|
it("toggles from light to dark", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(false));
|
||||||
|
localStorage.setItem("shieldai-theme", "light");
|
||||||
|
runWithRoot(() => {
|
||||||
|
const { toggle, resolved, theme } = useTheme();
|
||||||
|
toggle();
|
||||||
|
expect(resolved()).toBe("dark");
|
||||||
|
expect(theme()).toBe("dark");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toggles from dark to light", () => {
|
||||||
|
vi.stubGlobal("matchMedia", createMatchMediaMock(true));
|
||||||
|
localStorage.setItem("shieldai-theme", "dark");
|
||||||
|
runWithRoot(() => {
|
||||||
|
const { toggle, resolved, theme } = useTheme();
|
||||||
|
toggle();
|
||||||
|
expect(resolved()).toBe("light");
|
||||||
|
expect(theme()).toBe("light");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("system theme listener", () => {
|
||||||
|
it("registers matchMedia change listener", () => {
|
||||||
|
const addEventListener = vi.fn();
|
||||||
|
const removeEventListener = vi.fn();
|
||||||
|
vi.stubGlobal(
|
||||||
|
"matchMedia",
|
||||||
|
vi.fn().mockReturnValue({
|
||||||
|
matches: false,
|
||||||
|
addEventListener,
|
||||||
|
removeEventListener,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
runWithRoot(() => {
|
||||||
|
useTheme();
|
||||||
|
expect(addEventListener).toHaveBeenCalledWith(
|
||||||
|
"change",
|
||||||
|
expect.any(Function),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
94
web/src/lib/theme.ts
Normal file
94
web/src/lib/theme.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import { createSignal, createEffect, createRoot, onCleanup } from "solid-js";
|
||||||
|
|
||||||
|
type Theme = "light" | "dark" | "system";
|
||||||
|
type ResolvedTheme = "light" | "dark";
|
||||||
|
|
||||||
|
const STORAGE_KEY = "shieldai-theme";
|
||||||
|
|
||||||
|
export function getSystemTheme(): ResolvedTheme {
|
||||||
|
if (typeof window === "undefined") return "light";
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
? "dark"
|
||||||
|
: "light";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStoredTheme(): Theme {
|
||||||
|
try {
|
||||||
|
const stored = globalThis.localStorage?.getItem(STORAGE_KEY);
|
||||||
|
if (stored === "light" || stored === "dark") return stored;
|
||||||
|
} catch {
|
||||||
|
// storage unavailable
|
||||||
|
}
|
||||||
|
return "system";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getResolvedTheme(theme: Theme): ResolvedTheme {
|
||||||
|
return theme === "system" ? getSystemTheme() : theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyThemeClass(resolved: ResolvedTheme) {
|
||||||
|
if (typeof document === "undefined") return;
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.remove("light", "dark");
|
||||||
|
root.classList.add(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateMetaThemeColor(resolved: ResolvedTheme) {
|
||||||
|
if (typeof document === "undefined") return;
|
||||||
|
const color = resolved === "dark" ? "#111827" : "#fafbfc";
|
||||||
|
let meta = document.querySelector<HTMLMetaElement>(
|
||||||
|
'meta[name="theme-color"]',
|
||||||
|
);
|
||||||
|
if (!meta) {
|
||||||
|
meta = document.createElement("meta");
|
||||||
|
meta.name = "theme-color";
|
||||||
|
document.head.appendChild(meta);
|
||||||
|
}
|
||||||
|
meta.content = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function persistTheme(theme: Theme) {
|
||||||
|
try {
|
||||||
|
globalThis.localStorage?.setItem(STORAGE_KEY, theme);
|
||||||
|
} catch {
|
||||||
|
// storage unavailable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTheme() {
|
||||||
|
const initial = getStoredTheme();
|
||||||
|
const [theme, setTheme] = createSignal<Theme>(initial);
|
||||||
|
|
||||||
|
const resolved = () => getResolvedTheme(theme());
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const current = resolved();
|
||||||
|
applyThemeClass(current);
|
||||||
|
updateMetaThemeColor(current);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
const listener = () => {
|
||||||
|
if (theme() === "system") {
|
||||||
|
const systemResolved = getSystemTheme();
|
||||||
|
applyThemeClass(systemResolved);
|
||||||
|
updateMetaThemeColor(systemResolved);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mediaQuery.addEventListener("change", listener);
|
||||||
|
onCleanup(() => mediaQuery.removeEventListener("change", listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
const setAndPersist = (newTheme: Theme) => {
|
||||||
|
setTheme(newTheme);
|
||||||
|
persistTheme(newTheme);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
const next = resolved() === "dark" ? "light" : "dark";
|
||||||
|
setAndPersist(next);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { theme, resolved, setTheme: setAndPersist, toggle };
|
||||||
|
}
|
||||||
13
web/vitest.config.ts
Normal file
13
web/vitest.config.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
import { resolve } from "path";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
environment: "jsdom",
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"~": resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user