Compare commits
541
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c80af2630 | ||
|
|
6cdbb6b4eb | ||
|
|
dbe356f397 | ||
|
|
bb1d602c29 | ||
|
|
073f9d99b5 | ||
|
|
bfb8c531c2 | ||
|
|
052f887a9a | ||
|
|
759e68616e | ||
|
|
93e43d8e5e | ||
|
|
53c77e29df | ||
|
|
260739a227 | ||
|
|
c3ab76c8ad | ||
|
|
389d97ece9 | ||
|
|
e36b3433fc | ||
|
|
ded9bd26bb | ||
|
|
c890853992 | ||
|
|
2a4e8bc01c | ||
|
|
c19d031144 | ||
|
|
0cc9a22a42 | ||
|
|
b4075cd856 | ||
|
|
53227bfc2a | ||
|
|
d3baaf7408 | ||
|
|
0384e6b0e1 | ||
|
|
c3d33562c7 | ||
|
|
f3513bacff | ||
|
|
3aff88c23d | ||
|
|
58f7da6e9f | ||
|
|
5a199b04cb | ||
|
|
eb968a6651 | ||
|
|
a813fcb41c | ||
|
|
a58d1be822 | ||
|
|
07dc8d8ce4 | ||
|
|
d377246491 | ||
|
|
7030f49a74 | ||
|
|
c4e4f2a058 | ||
|
|
2729705594 | ||
|
|
ea13b6e8aa | ||
|
|
85ab9798c6 | ||
|
|
33290c54cd | ||
|
|
5d613a038d | ||
|
|
db78a59f03 | ||
|
|
7c3eeeb0fa | ||
|
|
e8357a87b0 | ||
|
|
06c543e938 | ||
|
|
759ce8fb8e | ||
|
|
38847e13bb | ||
|
|
e0c6459faa | ||
|
|
80b278ddab | ||
|
|
ef7ef6538e | ||
|
|
d23c21023a | ||
|
|
dfa2a9f225 | ||
|
|
6f78a71fa7 | ||
|
|
f8f1f46a4f | ||
|
|
ab705dacfa | ||
|
|
d1b93616f7 | ||
|
|
69215d456c | ||
|
|
54e52896a4 | ||
|
|
b18fb16e9c | ||
|
|
1250486ddf | ||
|
|
d645e8bbe1 | ||
|
|
cad415872e | ||
|
|
e8746ddb1d | ||
|
|
80020ade2e | ||
|
|
08ef97b162 | ||
|
|
1aedb265dd | ||
|
|
5c13b209aa | ||
|
|
43a9c50389 | ||
|
|
55224d64a2 | ||
|
|
c325aa1142 | ||
|
|
6e020ef9ef | ||
|
|
aca1eb6b5b | ||
|
|
3d095e7fe7 | ||
|
|
632f20558a | ||
|
|
f96c4badd8 | ||
|
|
cbe1c81470 | ||
|
|
c25155586c | ||
|
|
08b94a6890 | ||
|
|
8cddc9ea55 | ||
|
|
578239e0d0 | ||
|
|
626fa1462b | ||
|
|
968239bb76 | ||
|
|
8c24879246 | ||
|
|
9127055ae7 | ||
|
|
f5a6a4af7f | ||
|
|
6e00348bd7 | ||
|
|
95f7403daf | ||
|
|
14d1e20287 | ||
|
|
b8e2895dfc | ||
|
|
6e028ec2dc | ||
|
|
8e0ddd1ac9 | ||
|
|
da78b758d4 | ||
|
|
360765c591 | ||
|
|
db0078bf17 | ||
|
|
98578d3a7b | ||
|
|
bc3616d9c6 | ||
|
|
71306cbd1f | ||
|
|
0866034946 | ||
|
|
2ccaa10e79 | ||
|
|
e92d5b592c | ||
|
|
00ec29dae6 | ||
|
|
438916de5f | ||
|
|
8d4a67324e | ||
|
|
0d683eaa8e | ||
|
|
8fd1b92e6e | ||
|
|
22e3240296 | ||
|
|
e1d0b2ba6e | ||
|
|
ccc27e23df | ||
|
|
ad4bdd9f0f | ||
|
|
5479928a9d | ||
|
|
40836e9683 | ||
|
|
9a48f8e9e3 | ||
|
|
91b8ba2186 | ||
|
|
d075c097ac | ||
|
|
88fd6a294b | ||
|
|
ea8ef37d50 | ||
|
|
d510bd52a4 | ||
|
|
e0a854f035 | ||
|
|
bd914a8c06 | ||
|
|
a49102db01 | ||
|
|
21012fab4b | ||
|
|
9a71a73f50 | ||
|
|
2190e8c656 | ||
|
|
1fd496a5e2 | ||
|
|
74d584af34 | ||
|
|
46f415ecb0 | ||
|
|
d0399045da | ||
|
|
4be0ba19ca | ||
|
|
d5a5e6e062 | ||
|
|
e8dad85233 | ||
|
|
f197b8a0cd | ||
|
|
efaf854e09 | ||
|
|
704276753b | ||
|
|
2c5437791b | ||
|
|
94ab87ffad | ||
|
|
416f419a81 | ||
|
|
3ba03a97dc | ||
|
|
b1a22e08f5 | ||
|
|
c551a4b6e3 | ||
|
|
524ea95a00 | ||
|
|
0e9664d300 | ||
|
|
fffa718f5e | ||
|
|
cce4f64e0b | ||
|
|
ce6e9a822d | ||
|
|
f66e6d7033 | ||
|
|
de2de099b4 | ||
|
|
0233dd1b39 | ||
|
|
40b275d7e6 | ||
|
|
e4a34beb8b | ||
|
|
ac54535486 | ||
|
|
1a43e5fe87 | ||
|
|
46be47d0be | ||
|
|
4af9defb89 | ||
|
|
12b621068a | ||
|
|
07e7ebdb8e | ||
|
|
5092b5f07b | ||
|
|
d8ef9f808d | ||
|
|
d7192d6af9 | ||
|
|
68e6c540bb | ||
|
|
b572c68100 | ||
|
|
25cb03dbe5 | ||
|
|
d47510785a | ||
|
|
657f3d5089 | ||
|
|
49939c4d8d | ||
|
|
529eb6e147 | ||
|
|
a7cae8f674 | ||
|
|
12ae80856e | ||
|
|
7e619a9302 | ||
|
|
2abafbcd2f | ||
|
|
8b08d340ac | ||
|
|
81983d4a2e | ||
|
|
7443b99295 | ||
|
|
306fc05c00 | ||
|
|
9d8d0e97ec | ||
|
|
f4086ac459 | ||
|
|
b9b5d42bd8 | ||
|
|
83ed1adcbd | ||
|
|
9b57db30d1 | ||
|
|
df8e6e6014 | ||
|
|
472a6cc83e | ||
|
|
47d43aaf2d | ||
|
|
da3dea0429 | ||
|
|
9862303eed | ||
|
|
b14622352e | ||
|
|
ea643f1e3f | ||
|
|
f5fd54598f | ||
|
|
0f7b17b1b4 | ||
|
|
4d3e983edb | ||
|
|
d3fc29bdec | ||
|
|
fe58c649cb | ||
|
|
af2a09940c | ||
|
|
7e016fdda6 | ||
|
|
beb97d21ff | ||
|
|
b0345284f9 | ||
|
|
d71153eae6 | ||
|
|
e60ded01df | ||
|
|
4b2a14c154 | ||
|
|
b4717d8092 | ||
|
|
dc8f8cc567 | ||
|
|
99110d12c4 | ||
|
|
74b1349cf6 | ||
|
|
3b3505cfe8 | ||
|
|
55bd6e487e | ||
|
|
1ee916a3c3 | ||
|
|
a5d47f076e | ||
|
|
acd1eb574d | ||
|
|
a71dcc189e | ||
|
|
3789a31423 | ||
|
|
bb6e350d68 | ||
|
|
f9a441d4f4 | ||
|
|
1c05ebaea2 | ||
|
|
520c47e81d | ||
|
|
e5b08da0f1 | ||
|
|
fe2cc0cff1 | ||
|
|
fbc8f6eba9 | ||
|
|
8cba7d7f53 | ||
|
|
6450ba1b79 | ||
|
|
dc1c25cff5 | ||
|
|
3f3550a16e | ||
|
|
161e3db795 | ||
|
|
5a8a0f6a56 | ||
|
|
37f30993fa | ||
|
|
ebc194ca9a | ||
|
|
4edb4fa4fa | ||
|
|
63176bb049 | ||
|
|
216a2d87cf | ||
|
|
dd1f981d23 | ||
|
|
bfc9b24b48 | ||
|
|
2691e1e666 | ||
|
|
3f16e0d89f | ||
|
|
994c55f709 | ||
|
|
2f32f2ceb5 | ||
|
|
076dfb3752 | ||
|
|
60aa0cb96e | ||
|
|
e5973e2860 | ||
|
|
dbd1987f0a | ||
|
|
f270ea65c5 | ||
|
|
1698448016 | ||
|
|
564d3edfac | ||
|
|
679270d9e0 | ||
|
|
9f66a45970 | ||
|
|
779610d668 | ||
|
|
1fb611ef0a | ||
|
|
972f5ecc7d | ||
|
|
8d720f9463 | ||
|
|
92931437c4 | ||
|
|
08ca1237cc | ||
|
|
6473e15793 | ||
|
|
16cac69a72 | ||
|
|
b2da41cfad | ||
|
|
fcf2da9571 | ||
|
|
253b7ea784 | ||
|
|
3a9fd1bb36 | ||
|
|
f84ac697dc | ||
|
|
76a79284d2 | ||
|
|
99a1e73fa1 | ||
|
|
ba4c86448b | ||
|
|
b36837ae93 | ||
|
|
e03932e586 | ||
|
|
6b019a125a | ||
|
|
6a2fed7042 | ||
|
|
74baae597a | ||
|
|
d78d31430d | ||
|
|
096e14d787 | ||
|
|
bbb3120b59 | ||
|
|
9e4438f5bf | ||
|
|
87438fb38e | ||
|
|
6b6d6e9e07 | ||
|
|
207a59aad4 | ||
|
|
b3ae1931fc | ||
|
|
4d08123ca0 | ||
|
|
7d3c7a9f65 | ||
|
|
50dfa9caf3 | ||
|
|
1f86aa8bb9 | ||
|
|
d83756eaaf | ||
|
|
c0b43d3cb4 | ||
|
|
3206ed47e0 | ||
|
|
346c5e0da6 | ||
|
|
5b431c36f8 | ||
|
|
44d24d42b8 | ||
|
|
3a9e6b558c | ||
|
|
9d92ae7530 | ||
|
|
e6e7eaf6e0 | ||
|
|
8ce5c2b900 | ||
|
|
78be8fecdc | ||
|
|
b5e9f96660 | ||
|
|
ad17e8d1f0 | ||
|
|
b75d4d1c5e | ||
|
|
cc67bc005d | ||
|
|
0ce849c3d5 | ||
|
|
6e13e2f74e | ||
|
|
9fd61aef6e | ||
|
|
bb3926bf45 | ||
|
|
b2b123a392 | ||
|
|
09ff3b9bb9 | ||
|
|
2256362ba2 | ||
|
|
077ca4454f | ||
|
|
05cbb11709 | ||
|
|
fcc561ebb7 | ||
|
|
ee6ca104e5 | ||
|
|
4347a77d89 | ||
|
|
76b10d85ee | ||
|
|
45a770cdb1 | ||
|
|
a57c8669b6 | ||
|
|
f9fcdead55 | ||
|
|
ff669d4414 | ||
|
|
9b2d595cfc | ||
|
|
3839d70a94 | ||
|
|
6fe265e7d8 | ||
|
|
2aed4d263b | ||
|
|
e2ac588c84 | ||
|
|
8917dfdf5e | ||
|
|
86900d71f5 | ||
|
|
adcc661798 | ||
|
|
f4a28b2659 | ||
|
|
a160a35d0c | ||
|
|
90eaf9b3fc | ||
|
|
16d516dbdb | ||
|
|
0026bc5815 | ||
|
|
bcdaf7e779 | ||
|
|
874e22a045 | ||
|
|
905226c01e | ||
|
|
73adf7e86f | ||
|
|
4c37e17ac2 | ||
|
|
cd6e07355b | ||
|
|
29703aee9a | ||
|
|
3997d3f2d7 | ||
|
|
1fccb3bda4 | ||
|
|
16b2bfa8ef | ||
|
|
4eb6b57503 | ||
|
|
7599396162 | ||
|
|
d99d1315ee | ||
|
|
d831432f93 | ||
|
|
0ddf8e6c6e | ||
|
|
a520c4ff98 | ||
|
|
a184714f67 | ||
|
|
9b76337236 | ||
|
|
4dc72669e5 | ||
|
|
dfa59dd21d | ||
|
|
f642a6c5b9 | ||
|
|
e37104cb10 | ||
|
|
dc654c93d2 | ||
|
|
c67b0a9ba4 | ||
|
|
5b699a0d9b | ||
|
|
bc557e828d | ||
|
|
fcaa041ef9 | ||
|
|
3c9d80d75f | ||
|
|
a761f66a16 | ||
|
|
15e80fca69 | ||
|
|
43680534df | ||
|
|
aa522aad62 | ||
|
|
82319bbd83 | ||
|
|
45fa4eda15 | ||
|
|
f242541ef3 | ||
|
|
562f067131 | ||
|
|
1ff46c75fa | ||
|
|
73d5cacc06 | ||
|
|
b8828f2609 | ||
|
|
2f7b2cf603 | ||
|
|
eaf18d9915 | ||
|
|
7aa7dd3690 | ||
|
|
bee4b6801e | ||
|
|
3565d8e44d | ||
|
|
0187b6bb72 | ||
|
|
0eb898abcf | ||
|
|
5a309c2dbf | ||
|
|
452f11ff77 | ||
|
|
774c24e76e | ||
|
|
ec4a44087b | ||
|
|
501347cda5 | ||
|
|
3f3816c0f2 | ||
|
|
0a3c72d678 | ||
|
|
66b7a4991e | ||
|
|
1550ae47c0 | ||
|
|
33ba064c40 | ||
|
|
96ae5925c3 | ||
|
|
3a750b0809 | ||
|
|
1258f7aeea | ||
|
|
797a56873d | ||
|
|
05867f9318 | ||
|
|
5947fe72e4 | ||
|
|
f3d4dd5099 | ||
|
|
b68a4a8838 | ||
|
|
b7a1d8f2f5 | ||
|
|
80e1173ef7 | ||
|
|
8ae10f1c94 | ||
|
|
f24251f89e | ||
|
|
3600bd27f4 | ||
|
|
92089bb295 | ||
|
|
a70932f742 | ||
|
|
217cf24c3c | ||
|
|
c87939ad12 | ||
|
|
a092f567b7 | ||
|
|
883a6577d5 | ||
|
|
20b52cad2a | ||
|
|
528291532b | ||
|
|
2b77a84c4f | ||
|
|
7d0b52dc29 | ||
|
|
c86c2acf4c | ||
|
|
29bf731d47 | ||
|
|
3c5a256f0f | ||
|
|
067338bc25 | ||
|
|
736cd10847 | ||
|
|
a03daa4252 | ||
|
|
b01eec38d1 | ||
|
|
35cb06e0e4 | ||
|
|
f3b7d2f786 | ||
|
|
1facf7d8e4 | ||
|
|
ddd9c71cca | ||
|
|
520a814fc2 | ||
|
|
2072c8681a | ||
|
|
21990621e2 | ||
|
|
68a0947292 | ||
|
|
f1f44644e2 | ||
|
|
afb8a0d28a | ||
|
|
f05f175842 | ||
|
|
c0b214232d | ||
|
|
f4f8f2d151 | ||
|
|
b4ad5c138e | ||
|
|
789e111a0f | ||
|
|
eaa76dad0c | ||
|
|
66f9bdab32 | ||
|
|
efaa9166fb | ||
|
|
eb2044989e | ||
|
|
5d37e58d34 | ||
|
|
20088a87b0 | ||
|
|
05019dae76 | ||
|
|
bf37a88f7f | ||
|
|
d839f70834 | ||
|
|
498a4ab408 | ||
|
|
835e48cd28 | ||
|
|
1a5a63843e | ||
|
|
d954e1e3b6 | ||
|
|
8f22a6b69d | ||
|
|
fd37d5b54e | ||
|
|
e146083b73 | ||
|
|
d7a1c268d9 | ||
|
|
5c4345da4f | ||
|
|
0be37cc2c6 | ||
|
|
64f0205f97 | ||
|
|
b3a1360ad9 | ||
|
|
08d4d6d4af | ||
|
|
9c69c1de9f | ||
|
|
735f3d17bc | ||
|
|
db7243c364 | ||
|
|
f0912ee838 | ||
|
|
983f8ffeca | ||
|
|
c474380684 | ||
|
|
7ca767de55 | ||
|
|
1954c1255e | ||
|
|
b4f33485a7 | ||
|
|
f2504d8eb2 | ||
|
|
e47f383137 | ||
|
|
cd56845dce | ||
|
|
1c24dd02a6 | ||
|
|
b7b09fdfc2 | ||
|
|
62702fbd11 | ||
|
|
22c68a6992 | ||
|
|
ca1b597b01 | ||
|
|
d527ceeb2b | ||
|
|
2e9c22d911 | ||
|
|
762c58b756 | ||
|
|
61d0b3e4dc | ||
|
|
71a7ad1a4e | ||
|
|
20399bbdfe | ||
|
|
547a975707 | ||
|
|
c009cab15b | ||
|
|
e6bc3b253b | ||
|
|
f1a13f25a4 | ||
|
|
4695e685cf | ||
|
|
c6092e4ad9 | ||
|
|
e6045ca925 | ||
|
|
ebbb4dd88a | ||
|
|
087473be6e | ||
|
|
e0eb460fc8 | ||
|
|
7c6b3f981e | ||
|
|
a5b6c57a76 | ||
|
|
65724b693b | ||
|
|
8b9a85b7e7 | ||
|
|
7cbec9a1a7 | ||
|
|
c4ba5961c8 | ||
|
|
82b432349e | ||
|
|
68ed664a3f | ||
|
|
3a30773874 | ||
|
|
0c0057a7de | ||
|
|
fa79736b87 | ||
|
|
2e0c2c9db7 | ||
|
|
3e9366487a | ||
|
|
025ed04da0 | ||
|
|
b81eca4ebc | ||
|
|
20c18689c0 | ||
|
|
a803cf8aee | ||
|
|
c526e2d908 | ||
|
|
bdbbcd8a0c | ||
|
|
43c2da24d0 | ||
|
|
3205db9c16 | ||
|
|
efbab087df | ||
|
|
9280db3297 | ||
|
|
0cc3c3bc78 | ||
|
|
ee8b38ab26 | ||
|
|
44fa3d5392 | ||
|
|
a457828a67 | ||
|
|
4752c83155 | ||
|
|
f94ee5ce90 | ||
|
|
76386f5cfc | ||
|
|
a9275def43 | ||
|
|
50ed4c6b5d | ||
|
|
f882cca98a | ||
|
|
2d2a044961 | ||
|
|
b41fbda68f | ||
|
|
794c5981a5 | ||
|
|
d5738f542c | ||
|
|
02b7eb59f8 | ||
|
|
a8f23fb548 | ||
|
|
1a642a79a6 | ||
|
|
b6b0097755 | ||
|
|
afb1cad26d | ||
|
|
58186004db | ||
|
|
a593ed4c9b | ||
|
|
449270aacc | ||
|
|
e20535655b | ||
|
|
1662e149b3 | ||
|
|
dfe3e79304 | ||
|
|
e92a2ec9db | ||
|
|
7cba1ff793 | ||
|
|
8c3cc0d447 | ||
|
|
e30562d5f4 | ||
|
|
7c06ef2477 | ||
|
|
58eccf7f53 | ||
|
|
6da60bd5d9 | ||
|
|
8a43c24934 | ||
|
|
b03172d723 | ||
|
|
e342795bd0 | ||
|
|
a44d4acb3f | ||
|
|
8b8a358de1 | ||
|
|
075fa2c0e8 | ||
|
|
8b287caaad | ||
|
|
b3e6b7a985 | ||
|
|
5c74bff8e1 | ||
|
|
172bbdaced | ||
|
|
563b4c33f2 | ||
|
|
982b71e861 |
@@ -9,6 +9,7 @@ on:
|
|||||||
- "nix/**"
|
- "nix/**"
|
||||||
- "packages/app/**"
|
- "packages/app/**"
|
||||||
- "packages/desktop/**"
|
- "packages/desktop/**"
|
||||||
|
- ".github/workflows/nix-desktop.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- "flake.nix"
|
- "flake.nix"
|
||||||
@@ -16,6 +17,7 @@ on:
|
|||||||
- "nix/**"
|
- "nix/**"
|
||||||
- "packages/app/**"
|
- "packages/app/**"
|
||||||
- "packages/desktop/**"
|
- "packages/desktop/**"
|
||||||
|
- ".github/workflows/nix-desktop.yml"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -25,6 +27,8 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- blacksmith-4vcpu-ubuntu-2404
|
- blacksmith-4vcpu-ubuntu-2404
|
||||||
|
- blacksmith-4vcpu-ubuntu-2404-arm
|
||||||
|
- macos-15-intel
|
||||||
- macos-latest
|
- macos-latest
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
@@ -33,7 +37,7 @@ jobs:
|
|||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Nix
|
- name: Setup Nix
|
||||||
uses: DeterminateSystems/nix-installer-action@v21
|
uses: nixbuild/nix-quick-install-action@v34
|
||||||
|
|
||||||
- name: Build desktop via flake
|
- name: Build desktop via flake
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ jobs:
|
|||||||
|
|
||||||
publish-tauri:
|
publish-tauri:
|
||||||
needs: publish
|
needs: publish
|
||||||
continue-on-error: true
|
continue-on-error: false
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -10,22 +10,24 @@ on:
|
|||||||
- "bun.lock"
|
- "bun.lock"
|
||||||
- "package.json"
|
- "package.json"
|
||||||
- "packages/*/package.json"
|
- "packages/*/package.json"
|
||||||
|
- ".github/workflows/update-nix-hashes.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- "bun.lock"
|
- "bun.lock"
|
||||||
- "package.json"
|
- "package.json"
|
||||||
- "packages/*/package.json"
|
- "packages/*/package.json"
|
||||||
|
- ".github/workflows/update-nix-hashes.yml"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update:
|
update-flake:
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
env:
|
env:
|
||||||
SYSTEM: x86_64-linux
|
TITLE: flake.lock
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@@ -33,39 +35,270 @@ jobs:
|
|||||||
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||||
|
|
||||||
- name: Setup Nix
|
- name: Setup Nix
|
||||||
uses: DeterminateSystems/nix-installer-action@v20
|
uses: nixbuild/nix-quick-install-action@v34
|
||||||
|
|
||||||
- name: Configure git
|
- name: Configure git
|
||||||
run: |
|
run: |
|
||||||
git config --global user.email "action@github.com"
|
git config --global user.email "action@github.com"
|
||||||
git config --global user.name "Github Action"
|
git config --global user.name "Github Action"
|
||||||
|
|
||||||
- name: Update flake.lock
|
- name: Update ${{ env.TITLE }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
echo "📦 Updating flake.lock..."
|
echo "Updating $TITLE..."
|
||||||
nix flake update
|
nix flake update
|
||||||
echo "✅ flake.lock updated successfully"
|
echo "$TITLE updated successfully"
|
||||||
|
|
||||||
- name: Update node_modules hash
|
- name: Commit ${{ env.TITLE }} changes
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
echo "🔄 Updating node_modules hash..."
|
|
||||||
nix/scripts/update-hashes.sh
|
|
||||||
echo "✅ node_modules hash updated successfully"
|
|
||||||
|
|
||||||
- name: Commit hash changes
|
|
||||||
env:
|
env:
|
||||||
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
|
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
echo "🔍 Checking for changes in tracked Nix files..."
|
echo "Checking for changes in tracked files..."
|
||||||
|
|
||||||
summarize() {
|
summarize() {
|
||||||
local status="$1"
|
local status="$1"
|
||||||
{
|
{
|
||||||
echo "### Nix Hash Update"
|
echo "### Nix $TITLE"
|
||||||
|
echo ""
|
||||||
|
echo "- ref: ${GITHUB_REF_NAME}"
|
||||||
|
echo "- status: ${status}"
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
if [ -n "${GITHUB_SERVER_URL:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ] && [ -n "${GITHUB_RUN_ID:-}" ]; then
|
||||||
|
echo "- run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
fi
|
||||||
|
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
}
|
||||||
|
FILES=(flake.lock flake.nix)
|
||||||
|
STATUS="$(git status --short -- "${FILES[@]}" || true)"
|
||||||
|
if [ -z "$STATUS" ]; then
|
||||||
|
echo "No changes detected."
|
||||||
|
summarize "no changes"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Changes detected:"
|
||||||
|
echo "$STATUS"
|
||||||
|
echo "Staging files..."
|
||||||
|
git add "${FILES[@]}"
|
||||||
|
echo "Committing changes..."
|
||||||
|
git commit -m "Update $TITLE"
|
||||||
|
echo "Changes committed"
|
||||||
|
|
||||||
|
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
||||||
|
echo "Pulling latest from branch: $BRANCH"
|
||||||
|
git pull --rebase --autostash origin "$BRANCH"
|
||||||
|
echo "Pushing changes to branch: $BRANCH"
|
||||||
|
git push origin HEAD:"$BRANCH"
|
||||||
|
echo "Changes pushed successfully"
|
||||||
|
|
||||||
|
summarize "committed $(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
|
compute-node-modules-hash:
|
||||||
|
needs: update-flake
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- system: x86_64-linux
|
||||||
|
host: blacksmith-4vcpu-ubuntu-2404
|
||||||
|
- system: aarch64-linux
|
||||||
|
host: blacksmith-4vcpu-ubuntu-2404-arm
|
||||||
|
- system: x86_64-darwin
|
||||||
|
host: macos-15-intel
|
||||||
|
- system: aarch64-darwin
|
||||||
|
host: macos-latest
|
||||||
|
runs-on: ${{ matrix.host }}
|
||||||
|
env:
|
||||||
|
SYSTEM: ${{ matrix.system }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.head_ref || github.ref_name }}
|
||||||
|
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||||
|
|
||||||
|
- name: Setup Nix
|
||||||
|
uses: nixbuild/nix-quick-install-action@v34
|
||||||
|
|
||||||
|
- name: Compute node_modules hash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DUMMY="sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||||
|
HASH_FILE="nix/hashes.json"
|
||||||
|
OUTPUT_FILE="hash-${SYSTEM}.txt"
|
||||||
|
|
||||||
|
export NIX_KEEP_OUTPUTS=1
|
||||||
|
export NIX_KEEP_DERIVATIONS=1
|
||||||
|
|
||||||
|
BUILD_LOG=$(mktemp)
|
||||||
|
TMP_JSON=$(mktemp)
|
||||||
|
trap 'rm -f "$BUILD_LOG" "$TMP_JSON"' EXIT
|
||||||
|
|
||||||
|
if [ ! -f "$HASH_FILE" ]; then
|
||||||
|
mkdir -p "$(dirname "$HASH_FILE")"
|
||||||
|
echo '{"nodeModules":{}}' > "$HASH_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set dummy hash to force nix to rebuild and reveal correct hash
|
||||||
|
jq --arg system "$SYSTEM" --arg value "$DUMMY" \
|
||||||
|
'.nodeModules = (.nodeModules // {}) | .nodeModules[$system] = $value' "$HASH_FILE" > "$TMP_JSON"
|
||||||
|
mv "$TMP_JSON" "$HASH_FILE"
|
||||||
|
|
||||||
|
MODULES_ATTR=".#packages.${SYSTEM}.default.node_modules"
|
||||||
|
DRV_PATH="$(nix eval --raw "${MODULES_ATTR}.drvPath")"
|
||||||
|
|
||||||
|
echo "Building node_modules for ${SYSTEM} to discover correct hash..."
|
||||||
|
echo "Attempting to realize derivation: ${DRV_PATH}"
|
||||||
|
REALISE_OUT=$(nix-store --realise "$DRV_PATH" --keep-failed 2>&1 | tee "$BUILD_LOG" || true)
|
||||||
|
|
||||||
|
BUILD_PATH=$(echo "$REALISE_OUT" | grep "^/nix/store/" | head -n1 || true)
|
||||||
|
CORRECT_HASH=""
|
||||||
|
|
||||||
|
if [ -n "$BUILD_PATH" ] && [ -d "$BUILD_PATH" ]; then
|
||||||
|
echo "Realized node_modules output: $BUILD_PATH"
|
||||||
|
CORRECT_HASH=$(nix hash path --sri "$BUILD_PATH" 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try to extract hash from build log
|
||||||
|
if [ -z "$CORRECT_HASH" ]; then
|
||||||
|
CORRECT_HASH="$(grep -E 'got:\s+sha256-[A-Za-z0-9+/=]+' "$BUILD_LOG" | awk '{print $2}' | head -n1 || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CORRECT_HASH" ]; then
|
||||||
|
CORRECT_HASH="$(grep -A2 'hash mismatch' "$BUILD_LOG" | grep 'got:' | awk '{print $2}' | sed 's/sha256:/sha256-/' || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try to hash from kept failed build directory
|
||||||
|
if [ -z "$CORRECT_HASH" ]; then
|
||||||
|
KEPT_DIR=$(grep -oE "build directory.*'[^']+'" "$BUILD_LOG" | grep -oE "'/[^']+'" | tr -d "'" | head -n1 || true)
|
||||||
|
if [ -z "$KEPT_DIR" ]; then
|
||||||
|
KEPT_DIR=$(grep -oE '/nix/var/nix/builds/[^ ]+' "$BUILD_LOG" | head -n1 || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$KEPT_DIR" ] && [ -d "$KEPT_DIR" ]; then
|
||||||
|
HASH_PATH="$KEPT_DIR"
|
||||||
|
[ -d "$KEPT_DIR/build" ] && HASH_PATH="$KEPT_DIR/build"
|
||||||
|
|
||||||
|
if [ -d "$HASH_PATH/node_modules" ]; then
|
||||||
|
CORRECT_HASH=$(nix hash path --sri "$HASH_PATH" 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CORRECT_HASH" ]; then
|
||||||
|
echo "Failed to determine correct node_modules hash for ${SYSTEM}."
|
||||||
|
cat "$BUILD_LOG"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$CORRECT_HASH" > "$OUTPUT_FILE"
|
||||||
|
echo "Hash for ${SYSTEM}: $CORRECT_HASH"
|
||||||
|
|
||||||
|
- name: Upload hash artifact
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: hash-${{ matrix.system }}
|
||||||
|
path: hash-${{ matrix.system }}.txt
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
commit-node-modules-hashes:
|
||||||
|
needs: compute-node-modules-hash
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
|
env:
|
||||||
|
TITLE: node_modules hashes
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.head_ref || github.ref_name }}
|
||||||
|
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||||
|
|
||||||
|
- name: Configure git
|
||||||
|
run: |
|
||||||
|
git config --global user.email "action@github.com"
|
||||||
|
git config --global user.name "Github Action"
|
||||||
|
|
||||||
|
- name: Pull latest changes
|
||||||
|
env:
|
||||||
|
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||||
|
run: |
|
||||||
|
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
||||||
|
git pull --rebase --autostash origin "$BRANCH"
|
||||||
|
|
||||||
|
- name: Download all hash artifacts
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
pattern: hash-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Merge hashes into hashes.json
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HASH_FILE="nix/hashes.json"
|
||||||
|
|
||||||
|
if [ ! -f "$HASH_FILE" ]; then
|
||||||
|
mkdir -p "$(dirname "$HASH_FILE")"
|
||||||
|
echo '{"nodeModules":{}}' > "$HASH_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Merging hashes into ${HASH_FILE}..."
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
files=(hash-*.txt)
|
||||||
|
if [ ${#files[@]} -eq 0 ]; then
|
||||||
|
echo "No hash files found, nothing to update"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
EXPECTED_SYSTEMS="x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin"
|
||||||
|
for sys in $EXPECTED_SYSTEMS; do
|
||||||
|
if [ ! -f "hash-${sys}.txt" ]; then
|
||||||
|
echo "WARNING: Missing hash file for $sys"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for f in "${files[@]}"; do
|
||||||
|
system="${f#hash-}"
|
||||||
|
system="${system%.txt}"
|
||||||
|
hash=$(cat "$f")
|
||||||
|
if [ -z "$hash" ]; then
|
||||||
|
echo "WARNING: Empty hash for $system, skipping"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
echo " $system: $hash"
|
||||||
|
jq --arg sys "$system" --arg h "$hash" \
|
||||||
|
'.nodeModules = (.nodeModules // {}) | .nodeModules[$sys] = $h' "$HASH_FILE" > "${HASH_FILE}.tmp"
|
||||||
|
mv "${HASH_FILE}.tmp" "$HASH_FILE"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "All hashes merged:"
|
||||||
|
cat "$HASH_FILE"
|
||||||
|
|
||||||
|
- name: Commit ${{ env.TITLE }} changes
|
||||||
|
env:
|
||||||
|
TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HASH_FILE="nix/hashes.json"
|
||||||
|
echo "Checking for changes..."
|
||||||
|
|
||||||
|
summarize() {
|
||||||
|
local status="$1"
|
||||||
|
{
|
||||||
|
echo "### Nix $TITLE"
|
||||||
echo ""
|
echo ""
|
||||||
echo "- ref: ${GITHUB_REF_NAME}"
|
echo "- ref: ${GITHUB_REF_NAME}"
|
||||||
echo "- status: ${status}"
|
echo "- status: ${status}"
|
||||||
@@ -76,27 +309,22 @@ jobs:
|
|||||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||||
}
|
}
|
||||||
|
|
||||||
FILES=(flake.lock flake.nix nix/node-modules.nix nix/hashes.json)
|
FILES=("$HASH_FILE")
|
||||||
STATUS="$(git status --short -- "${FILES[@]}" || true)"
|
STATUS="$(git status --short -- "${FILES[@]}" || true)"
|
||||||
if [ -z "$STATUS" ]; then
|
if [ -z "$STATUS" ]; then
|
||||||
echo "✅ No changes detected. Hashes are already up to date."
|
echo "No changes detected."
|
||||||
summarize "no changes"
|
summarize "no changes"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "📝 Changes detected:"
|
echo "Changes detected:"
|
||||||
echo "$STATUS"
|
echo "$STATUS"
|
||||||
echo "🔗 Staging files..."
|
|
||||||
git add "${FILES[@]}"
|
git add "${FILES[@]}"
|
||||||
echo "💾 Committing changes..."
|
git commit -m "Update $TITLE"
|
||||||
git commit -m "Update Nix flake.lock and hashes"
|
|
||||||
echo "✅ Changes committed"
|
|
||||||
|
|
||||||
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
|
||||||
echo "🌳 Pulling latest from branch: $BRANCH"
|
git pull --rebase --autostash origin "$BRANCH"
|
||||||
git pull --rebase origin "$BRANCH"
|
|
||||||
echo "🚀 Pushing changes to branch: $BRANCH"
|
|
||||||
git push origin HEAD:"$BRANCH"
|
git push origin HEAD:"$BRANCH"
|
||||||
echo "✅ Changes pushed successfully"
|
echo "Changes pushed successfully"
|
||||||
|
|
||||||
summarize "committed $(git rev-parse --short HEAD)"
|
summarize "committed $(git rev-parse --short HEAD)"
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ opencode.json
|
|||||||
a.out
|
a.out
|
||||||
target
|
target
|
||||||
.scripts
|
.scripts
|
||||||
|
.direnv/
|
||||||
|
|
||||||
# Local dev files
|
# Local dev files
|
||||||
opencode-dev
|
opencode-dev
|
||||||
logs/
|
logs/
|
||||||
|
*.bun-build
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ Desktop app issues:
|
|||||||
|
|
||||||
#### zen
|
#### zen
|
||||||
|
|
||||||
**Only** add if the issue mentions "zen" or "opencode zen". Zen is our gateway for coding models. **Do not** add for other gateways or inference providers.
|
**Only** add if the issue mentions "zen" or "opencode zen" or "opencode black".
|
||||||
|
|
||||||
If the issue doesn't have "zen" in it then don't add zen label
|
If the issue doesn't have "zen" or "opencode black" in it then don't add zen label
|
||||||
|
|
||||||
#### docs
|
#### docs
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
description: "Bump AI sdk dependencies minor / patch versions only"
|
||||||
|
---
|
||||||
|
|
||||||
|
Please read @package.json and @packages/opencode/package.json.
|
||||||
|
|
||||||
|
Your job is to look into AI SDK dependencies, figure out if they have versions that can be upgraded (minor or patch versions ONLY no major ignore major changes).
|
||||||
|
|
||||||
|
I want a report of every dependency and the version that can be upgraded to.
|
||||||
|
What would be even better is if you can give me links to the changelog for each dependency, or at least some reference info so I can see what bugs were fixed or new features were added.
|
||||||
|
|
||||||
|
Consider using subagents for each dep to save your context window.
|
||||||
|
|
||||||
|
Here is a short list of some deps (please be comprehensive tho):
|
||||||
|
|
||||||
|
- "ai"
|
||||||
|
- "@ai-sdk/openai"
|
||||||
|
- "@ai-sdk/anthropic"
|
||||||
|
- "@openrouter/ai-sdk-provider"
|
||||||
|
- etc, etc
|
||||||
|
|
||||||
|
DO NOT upgrade the dependencies yet, just make a list of all dependencies and their versions that can be upgraded to minor or patch versions only.
|
||||||
|
|
||||||
|
Write up your findings to ai-sdk-updates.md
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
- To test opencode in the `packages/opencode` directory you can run `bun dev`
|
- To test opencode in `packages/opencode`, run `bun dev`.
|
||||||
- To regenerate the javascript SDK, run ./packages/sdk/js/script/build.ts
|
- To regenerate the JavaScript SDK, run `./packages/sdk/js/script/build.ts`.
|
||||||
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
|
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
|
||||||
- the default branch in this repo is `dev`
|
- The default branch in this repo is `dev`.
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
# Security
|
||||||
|
|
||||||
|
## Threat Model
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
OpenCode is an AI-powered coding assistant that runs locally on your machine. It provides an agent system with access to powerful tools including shell execution, file operations, and web access.
|
||||||
|
|
||||||
|
### No Sandbox
|
||||||
|
|
||||||
|
OpenCode does **not** sandbox the agent. The permission system exists as a UX feature to help users stay aware of what actions the agent is taking - it prompts for confirmation before executing commands, writing files, etc. However, it is not designed to provide security isolation.
|
||||||
|
|
||||||
|
If you need true isolation, run OpenCode inside a Docker container or VM.
|
||||||
|
|
||||||
|
### Server Mode
|
||||||
|
|
||||||
|
Server mode is opt-in only. When enabled, set `OPENCODE_SERVER_PASSWORD` to require HTTP Basic Auth. Without this, the server runs unauthenticated (with a warning). It is the end user's responsibility to secure the server - any functionality it provides is not a vulnerability.
|
||||||
|
|
||||||
|
### Out of Scope
|
||||||
|
|
||||||
|
| Category | Rationale |
|
||||||
|
| ------------------------------- | ----------------------------------------------------------------------- |
|
||||||
|
| **Server access when opted-in** | If you enable server mode, API access is expected behavior |
|
||||||
|
| **Sandbox escapes** | The permission system is not a sandbox (see above) |
|
||||||
|
| **LLM provider data handling** | Data sent to your configured LLM provider is governed by their policies |
|
||||||
|
| **MCP server behavior** | External MCP servers you configure are outside our trust boundary |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Reporting Security Issues
|
||||||
|
|
||||||
|
We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
|
||||||
|
|
||||||
|
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/anomalyco/opencode/security/advisories/new) tab.
|
||||||
|
|
||||||
|
The team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
|
||||||
|
|
||||||
|
## Escalation
|
||||||
|
|
||||||
|
If you do not receive an acknowledgement of your report within 6 business days, you may send an email to security@anoma.ly
|
||||||
@@ -1,198 +1,205 @@
|
|||||||
# Download Stats
|
# Download Stats
|
||||||
|
|
||||||
| Date | GitHub Downloads | npm Downloads | Total |
|
| Date | GitHub Downloads | npm Downloads | Total |
|
||||||
| ---------- | -------------------- | ------------------- | -------------------- |
|
| ---------- | -------------------- | -------------------- | -------------------- |
|
||||||
| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) |
|
| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) |
|
||||||
| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) |
|
| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) |
|
||||||
| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) |
|
| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) |
|
||||||
| 2025-07-02 | 24,814 (+2,706) | 46,168 (+2,423) | 70,982 (+5,129) |
|
| 2025-07-02 | 24,814 (+2,706) | 46,168 (+2,423) | 70,982 (+5,129) |
|
||||||
| 2025-07-03 | 27,834 (+3,020) | 49,955 (+3,787) | 77,789 (+6,807) |
|
| 2025-07-03 | 27,834 (+3,020) | 49,955 (+3,787) | 77,789 (+6,807) |
|
||||||
| 2025-07-04 | 30,608 (+2,774) | 54,758 (+4,803) | 85,366 (+7,577) |
|
| 2025-07-04 | 30,608 (+2,774) | 54,758 (+4,803) | 85,366 (+7,577) |
|
||||||
| 2025-07-05 | 32,524 (+1,916) | 58,371 (+3,613) | 90,895 (+5,529) |
|
| 2025-07-05 | 32,524 (+1,916) | 58,371 (+3,613) | 90,895 (+5,529) |
|
||||||
| 2025-07-06 | 33,766 (+1,242) | 59,694 (+1,323) | 93,460 (+2,565) |
|
| 2025-07-06 | 33,766 (+1,242) | 59,694 (+1,323) | 93,460 (+2,565) |
|
||||||
| 2025-07-08 | 38,052 (+4,286) | 64,468 (+4,774) | 102,520 (+9,060) |
|
| 2025-07-08 | 38,052 (+4,286) | 64,468 (+4,774) | 102,520 (+9,060) |
|
||||||
| 2025-07-09 | 40,924 (+2,872) | 67,935 (+3,467) | 108,859 (+6,339) |
|
| 2025-07-09 | 40,924 (+2,872) | 67,935 (+3,467) | 108,859 (+6,339) |
|
||||||
| 2025-07-10 | 43,796 (+2,872) | 71,402 (+3,467) | 115,198 (+6,339) |
|
| 2025-07-10 | 43,796 (+2,872) | 71,402 (+3,467) | 115,198 (+6,339) |
|
||||||
| 2025-07-11 | 46,982 (+3,186) | 77,462 (+6,060) | 124,444 (+9,246) |
|
| 2025-07-11 | 46,982 (+3,186) | 77,462 (+6,060) | 124,444 (+9,246) |
|
||||||
| 2025-07-12 | 49,302 (+2,320) | 82,177 (+4,715) | 131,479 (+7,035) |
|
| 2025-07-12 | 49,302 (+2,320) | 82,177 (+4,715) | 131,479 (+7,035) |
|
||||||
| 2025-07-13 | 50,803 (+1,501) | 86,394 (+4,217) | 137,197 (+5,718) |
|
| 2025-07-13 | 50,803 (+1,501) | 86,394 (+4,217) | 137,197 (+5,718) |
|
||||||
| 2025-07-14 | 53,283 (+2,480) | 87,860 (+1,466) | 141,143 (+3,946) |
|
| 2025-07-14 | 53,283 (+2,480) | 87,860 (+1,466) | 141,143 (+3,946) |
|
||||||
| 2025-07-15 | 57,590 (+4,307) | 91,036 (+3,176) | 148,626 (+7,483) |
|
| 2025-07-15 | 57,590 (+4,307) | 91,036 (+3,176) | 148,626 (+7,483) |
|
||||||
| 2025-07-16 | 62,313 (+4,723) | 95,258 (+4,222) | 157,571 (+8,945) |
|
| 2025-07-16 | 62,313 (+4,723) | 95,258 (+4,222) | 157,571 (+8,945) |
|
||||||
| 2025-07-17 | 66,684 (+4,371) | 100,048 (+4,790) | 166,732 (+9,161) |
|
| 2025-07-17 | 66,684 (+4,371) | 100,048 (+4,790) | 166,732 (+9,161) |
|
||||||
| 2025-07-18 | 70,379 (+3,695) | 102,587 (+2,539) | 172,966 (+6,234) |
|
| 2025-07-18 | 70,379 (+3,695) | 102,587 (+2,539) | 172,966 (+6,234) |
|
||||||
| 2025-07-19 | 73,497 (+3,117) | 105,904 (+3,317) | 179,401 (+6,434) |
|
| 2025-07-19 | 73,497 (+3,117) | 105,904 (+3,317) | 179,401 (+6,434) |
|
||||||
| 2025-07-20 | 76,453 (+2,956) | 109,044 (+3,140) | 185,497 (+6,096) |
|
| 2025-07-20 | 76,453 (+2,956) | 109,044 (+3,140) | 185,497 (+6,096) |
|
||||||
| 2025-07-21 | 80,197 (+3,744) | 113,537 (+4,493) | 193,734 (+8,237) |
|
| 2025-07-21 | 80,197 (+3,744) | 113,537 (+4,493) | 193,734 (+8,237) |
|
||||||
| 2025-07-22 | 84,251 (+4,054) | 118,073 (+4,536) | 202,324 (+8,590) |
|
| 2025-07-22 | 84,251 (+4,054) | 118,073 (+4,536) | 202,324 (+8,590) |
|
||||||
| 2025-07-23 | 88,589 (+4,338) | 121,436 (+3,363) | 210,025 (+7,701) |
|
| 2025-07-23 | 88,589 (+4,338) | 121,436 (+3,363) | 210,025 (+7,701) |
|
||||||
| 2025-07-24 | 92,469 (+3,880) | 124,091 (+2,655) | 216,560 (+6,535) |
|
| 2025-07-24 | 92,469 (+3,880) | 124,091 (+2,655) | 216,560 (+6,535) |
|
||||||
| 2025-07-25 | 96,417 (+3,948) | 126,985 (+2,894) | 223,402 (+6,842) |
|
| 2025-07-25 | 96,417 (+3,948) | 126,985 (+2,894) | 223,402 (+6,842) |
|
||||||
| 2025-07-26 | 100,646 (+4,229) | 131,411 (+4,426) | 232,057 (+8,655) |
|
| 2025-07-26 | 100,646 (+4,229) | 131,411 (+4,426) | 232,057 (+8,655) |
|
||||||
| 2025-07-27 | 102,644 (+1,998) | 134,736 (+3,325) | 237,380 (+5,323) |
|
| 2025-07-27 | 102,644 (+1,998) | 134,736 (+3,325) | 237,380 (+5,323) |
|
||||||
| 2025-07-28 | 105,446 (+2,802) | 136,016 (+1,280) | 241,462 (+4,082) |
|
| 2025-07-28 | 105,446 (+2,802) | 136,016 (+1,280) | 241,462 (+4,082) |
|
||||||
| 2025-07-29 | 108,998 (+3,552) | 137,542 (+1,526) | 246,540 (+5,078) |
|
| 2025-07-29 | 108,998 (+3,552) | 137,542 (+1,526) | 246,540 (+5,078) |
|
||||||
| 2025-07-30 | 113,544 (+4,546) | 140,317 (+2,775) | 253,861 (+7,321) |
|
| 2025-07-30 | 113,544 (+4,546) | 140,317 (+2,775) | 253,861 (+7,321) |
|
||||||
| 2025-07-31 | 118,339 (+4,795) | 143,344 (+3,027) | 261,683 (+7,822) |
|
| 2025-07-31 | 118,339 (+4,795) | 143,344 (+3,027) | 261,683 (+7,822) |
|
||||||
| 2025-08-01 | 123,539 (+5,200) | 146,680 (+3,336) | 270,219 (+8,536) |
|
| 2025-08-01 | 123,539 (+5,200) | 146,680 (+3,336) | 270,219 (+8,536) |
|
||||||
| 2025-08-02 | 127,864 (+4,325) | 149,236 (+2,556) | 277,100 (+6,881) |
|
| 2025-08-02 | 127,864 (+4,325) | 149,236 (+2,556) | 277,100 (+6,881) |
|
||||||
| 2025-08-03 | 131,397 (+3,533) | 150,451 (+1,215) | 281,848 (+4,748) |
|
| 2025-08-03 | 131,397 (+3,533) | 150,451 (+1,215) | 281,848 (+4,748) |
|
||||||
| 2025-08-04 | 136,266 (+4,869) | 153,260 (+2,809) | 289,526 (+7,678) |
|
| 2025-08-04 | 136,266 (+4,869) | 153,260 (+2,809) | 289,526 (+7,678) |
|
||||||
| 2025-08-05 | 141,596 (+5,330) | 155,752 (+2,492) | 297,348 (+7,822) |
|
| 2025-08-05 | 141,596 (+5,330) | 155,752 (+2,492) | 297,348 (+7,822) |
|
||||||
| 2025-08-06 | 147,067 (+5,471) | 158,309 (+2,557) | 305,376 (+8,028) |
|
| 2025-08-06 | 147,067 (+5,471) | 158,309 (+2,557) | 305,376 (+8,028) |
|
||||||
| 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) |
|
| 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) |
|
||||||
| 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) |
|
| 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) |
|
||||||
| 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) |
|
| 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) |
|
||||||
| 2025-08-10 | 165,695 (+2,925) | 167,109 (+1,388) | 332,804 (+4,313) |
|
| 2025-08-10 | 165,695 (+2,925) | 167,109 (+1,388) | 332,804 (+4,313) |
|
||||||
| 2025-08-11 | 169,297 (+3,602) | 167,953 (+844) | 337,250 (+4,446) |
|
| 2025-08-11 | 169,297 (+3,602) | 167,953 (+844) | 337,250 (+4,446) |
|
||||||
| 2025-08-12 | 176,307 (+7,010) | 171,876 (+3,923) | 348,183 (+10,933) |
|
| 2025-08-12 | 176,307 (+7,010) | 171,876 (+3,923) | 348,183 (+10,933) |
|
||||||
| 2025-08-13 | 182,997 (+6,690) | 177,182 (+5,306) | 360,179 (+11,996) |
|
| 2025-08-13 | 182,997 (+6,690) | 177,182 (+5,306) | 360,179 (+11,996) |
|
||||||
| 2025-08-14 | 189,063 (+6,066) | 179,741 (+2,559) | 368,804 (+8,625) |
|
| 2025-08-14 | 189,063 (+6,066) | 179,741 (+2,559) | 368,804 (+8,625) |
|
||||||
| 2025-08-15 | 193,608 (+4,545) | 181,792 (+2,051) | 375,400 (+6,596) |
|
| 2025-08-15 | 193,608 (+4,545) | 181,792 (+2,051) | 375,400 (+6,596) |
|
||||||
| 2025-08-16 | 198,118 (+4,510) | 184,558 (+2,766) | 382,676 (+7,276) |
|
| 2025-08-16 | 198,118 (+4,510) | 184,558 (+2,766) | 382,676 (+7,276) |
|
||||||
| 2025-08-17 | 201,299 (+3,181) | 186,269 (+1,711) | 387,568 (+4,892) |
|
| 2025-08-17 | 201,299 (+3,181) | 186,269 (+1,711) | 387,568 (+4,892) |
|
||||||
| 2025-08-18 | 204,559 (+3,260) | 187,399 (+1,130) | 391,958 (+4,390) |
|
| 2025-08-18 | 204,559 (+3,260) | 187,399 (+1,130) | 391,958 (+4,390) |
|
||||||
| 2025-08-19 | 209,814 (+5,255) | 189,668 (+2,269) | 399,482 (+7,524) |
|
| 2025-08-19 | 209,814 (+5,255) | 189,668 (+2,269) | 399,482 (+7,524) |
|
||||||
| 2025-08-20 | 214,497 (+4,683) | 191,481 (+1,813) | 405,978 (+6,496) |
|
| 2025-08-20 | 214,497 (+4,683) | 191,481 (+1,813) | 405,978 (+6,496) |
|
||||||
| 2025-08-21 | 220,465 (+5,968) | 194,784 (+3,303) | 415,249 (+9,271) |
|
| 2025-08-21 | 220,465 (+5,968) | 194,784 (+3,303) | 415,249 (+9,271) |
|
||||||
| 2025-08-22 | 225,899 (+5,434) | 197,204 (+2,420) | 423,103 (+7,854) |
|
| 2025-08-22 | 225,899 (+5,434) | 197,204 (+2,420) | 423,103 (+7,854) |
|
||||||
| 2025-08-23 | 229,005 (+3,106) | 199,238 (+2,034) | 428,243 (+5,140) |
|
| 2025-08-23 | 229,005 (+3,106) | 199,238 (+2,034) | 428,243 (+5,140) |
|
||||||
| 2025-08-24 | 232,098 (+3,093) | 201,157 (+1,919) | 433,255 (+5,012) |
|
| 2025-08-24 | 232,098 (+3,093) | 201,157 (+1,919) | 433,255 (+5,012) |
|
||||||
| 2025-08-25 | 236,607 (+4,509) | 202,650 (+1,493) | 439,257 (+6,002) |
|
| 2025-08-25 | 236,607 (+4,509) | 202,650 (+1,493) | 439,257 (+6,002) |
|
||||||
| 2025-08-26 | 242,783 (+6,176) | 205,242 (+2,592) | 448,025 (+8,768) |
|
| 2025-08-26 | 242,783 (+6,176) | 205,242 (+2,592) | 448,025 (+8,768) |
|
||||||
| 2025-08-27 | 248,409 (+5,626) | 205,242 (+0) | 453,651 (+5,626) |
|
| 2025-08-27 | 248,409 (+5,626) | 205,242 (+0) | 453,651 (+5,626) |
|
||||||
| 2025-08-28 | 252,796 (+4,387) | 205,242 (+0) | 458,038 (+4,387) |
|
| 2025-08-28 | 252,796 (+4,387) | 205,242 (+0) | 458,038 (+4,387) |
|
||||||
| 2025-08-29 | 256,045 (+3,249) | 211,075 (+5,833) | 467,120 (+9,082) |
|
| 2025-08-29 | 256,045 (+3,249) | 211,075 (+5,833) | 467,120 (+9,082) |
|
||||||
| 2025-08-30 | 258,863 (+2,818) | 212,397 (+1,322) | 471,260 (+4,140) |
|
| 2025-08-30 | 258,863 (+2,818) | 212,397 (+1,322) | 471,260 (+4,140) |
|
||||||
| 2025-08-31 | 262,004 (+3,141) | 213,944 (+1,547) | 475,948 (+4,688) |
|
| 2025-08-31 | 262,004 (+3,141) | 213,944 (+1,547) | 475,948 (+4,688) |
|
||||||
| 2025-09-01 | 265,359 (+3,355) | 215,115 (+1,171) | 480,474 (+4,526) |
|
| 2025-09-01 | 265,359 (+3,355) | 215,115 (+1,171) | 480,474 (+4,526) |
|
||||||
| 2025-09-02 | 270,483 (+5,124) | 217,075 (+1,960) | 487,558 (+7,084) |
|
| 2025-09-02 | 270,483 (+5,124) | 217,075 (+1,960) | 487,558 (+7,084) |
|
||||||
| 2025-09-03 | 274,793 (+4,310) | 219,755 (+2,680) | 494,548 (+6,990) |
|
| 2025-09-03 | 274,793 (+4,310) | 219,755 (+2,680) | 494,548 (+6,990) |
|
||||||
| 2025-09-04 | 280,430 (+5,637) | 222,103 (+2,348) | 502,533 (+7,985) |
|
| 2025-09-04 | 280,430 (+5,637) | 222,103 (+2,348) | 502,533 (+7,985) |
|
||||||
| 2025-09-05 | 283,769 (+3,339) | 223,793 (+1,690) | 507,562 (+5,029) |
|
| 2025-09-05 | 283,769 (+3,339) | 223,793 (+1,690) | 507,562 (+5,029) |
|
||||||
| 2025-09-06 | 286,245 (+2,476) | 225,036 (+1,243) | 511,281 (+3,719) |
|
| 2025-09-06 | 286,245 (+2,476) | 225,036 (+1,243) | 511,281 (+3,719) |
|
||||||
| 2025-09-07 | 288,623 (+2,378) | 225,866 (+830) | 514,489 (+3,208) |
|
| 2025-09-07 | 288,623 (+2,378) | 225,866 (+830) | 514,489 (+3,208) |
|
||||||
| 2025-09-08 | 293,341 (+4,718) | 227,073 (+1,207) | 520,414 (+5,925) |
|
| 2025-09-08 | 293,341 (+4,718) | 227,073 (+1,207) | 520,414 (+5,925) |
|
||||||
| 2025-09-09 | 300,036 (+6,695) | 229,788 (+2,715) | 529,824 (+9,410) |
|
| 2025-09-09 | 300,036 (+6,695) | 229,788 (+2,715) | 529,824 (+9,410) |
|
||||||
| 2025-09-10 | 307,287 (+7,251) | 233,435 (+3,647) | 540,722 (+10,898) |
|
| 2025-09-10 | 307,287 (+7,251) | 233,435 (+3,647) | 540,722 (+10,898) |
|
||||||
| 2025-09-11 | 314,083 (+6,796) | 237,356 (+3,921) | 551,439 (+10,717) |
|
| 2025-09-11 | 314,083 (+6,796) | 237,356 (+3,921) | 551,439 (+10,717) |
|
||||||
| 2025-09-12 | 321,046 (+6,963) | 240,728 (+3,372) | 561,774 (+10,335) |
|
| 2025-09-12 | 321,046 (+6,963) | 240,728 (+3,372) | 561,774 (+10,335) |
|
||||||
| 2025-09-13 | 324,894 (+3,848) | 245,539 (+4,811) | 570,433 (+8,659) |
|
| 2025-09-13 | 324,894 (+3,848) | 245,539 (+4,811) | 570,433 (+8,659) |
|
||||||
| 2025-09-14 | 328,876 (+3,982) | 248,245 (+2,706) | 577,121 (+6,688) |
|
| 2025-09-14 | 328,876 (+3,982) | 248,245 (+2,706) | 577,121 (+6,688) |
|
||||||
| 2025-09-15 | 334,201 (+5,325) | 250,983 (+2,738) | 585,184 (+8,063) |
|
| 2025-09-15 | 334,201 (+5,325) | 250,983 (+2,738) | 585,184 (+8,063) |
|
||||||
| 2025-09-16 | 342,609 (+8,408) | 255,264 (+4,281) | 597,873 (+12,689) |
|
| 2025-09-16 | 342,609 (+8,408) | 255,264 (+4,281) | 597,873 (+12,689) |
|
||||||
| 2025-09-17 | 351,117 (+8,508) | 260,970 (+5,706) | 612,087 (+14,214) |
|
| 2025-09-17 | 351,117 (+8,508) | 260,970 (+5,706) | 612,087 (+14,214) |
|
||||||
| 2025-09-18 | 358,717 (+7,600) | 266,922 (+5,952) | 625,639 (+13,552) |
|
| 2025-09-18 | 358,717 (+7,600) | 266,922 (+5,952) | 625,639 (+13,552) |
|
||||||
| 2025-09-19 | 365,401 (+6,684) | 271,859 (+4,937) | 637,260 (+11,621) |
|
| 2025-09-19 | 365,401 (+6,684) | 271,859 (+4,937) | 637,260 (+11,621) |
|
||||||
| 2025-09-20 | 372,092 (+6,691) | 276,917 (+5,058) | 649,009 (+11,749) |
|
| 2025-09-20 | 372,092 (+6,691) | 276,917 (+5,058) | 649,009 (+11,749) |
|
||||||
| 2025-09-21 | 377,079 (+4,987) | 280,261 (+3,344) | 657,340 (+8,331) |
|
| 2025-09-21 | 377,079 (+4,987) | 280,261 (+3,344) | 657,340 (+8,331) |
|
||||||
| 2025-09-22 | 382,492 (+5,413) | 284,009 (+3,748) | 666,501 (+9,161) |
|
| 2025-09-22 | 382,492 (+5,413) | 284,009 (+3,748) | 666,501 (+9,161) |
|
||||||
| 2025-09-23 | 387,008 (+4,516) | 289,129 (+5,120) | 676,137 (+9,636) |
|
| 2025-09-23 | 387,008 (+4,516) | 289,129 (+5,120) | 676,137 (+9,636) |
|
||||||
| 2025-09-24 | 393,325 (+6,317) | 294,927 (+5,798) | 688,252 (+12,115) |
|
| 2025-09-24 | 393,325 (+6,317) | 294,927 (+5,798) | 688,252 (+12,115) |
|
||||||
| 2025-09-25 | 398,879 (+5,554) | 301,663 (+6,736) | 700,542 (+12,290) |
|
| 2025-09-25 | 398,879 (+5,554) | 301,663 (+6,736) | 700,542 (+12,290) |
|
||||||
| 2025-09-26 | 404,334 (+5,455) | 306,713 (+5,050) | 711,047 (+10,505) |
|
| 2025-09-26 | 404,334 (+5,455) | 306,713 (+5,050) | 711,047 (+10,505) |
|
||||||
| 2025-09-27 | 411,618 (+7,284) | 317,763 (+11,050) | 729,381 (+18,334) |
|
| 2025-09-27 | 411,618 (+7,284) | 317,763 (+11,050) | 729,381 (+18,334) |
|
||||||
| 2025-09-28 | 414,910 (+3,292) | 322,522 (+4,759) | 737,432 (+8,051) |
|
| 2025-09-28 | 414,910 (+3,292) | 322,522 (+4,759) | 737,432 (+8,051) |
|
||||||
| 2025-09-29 | 419,919 (+5,009) | 328,033 (+5,511) | 747,952 (+10,520) |
|
| 2025-09-29 | 419,919 (+5,009) | 328,033 (+5,511) | 747,952 (+10,520) |
|
||||||
| 2025-09-30 | 427,991 (+8,072) | 336,472 (+8,439) | 764,463 (+16,511) |
|
| 2025-09-30 | 427,991 (+8,072) | 336,472 (+8,439) | 764,463 (+16,511) |
|
||||||
| 2025-10-01 | 433,591 (+5,600) | 341,742 (+5,270) | 775,333 (+10,870) |
|
| 2025-10-01 | 433,591 (+5,600) | 341,742 (+5,270) | 775,333 (+10,870) |
|
||||||
| 2025-10-02 | 440,852 (+7,261) | 348,099 (+6,357) | 788,951 (+13,618) |
|
| 2025-10-02 | 440,852 (+7,261) | 348,099 (+6,357) | 788,951 (+13,618) |
|
||||||
| 2025-10-03 | 446,829 (+5,977) | 359,937 (+11,838) | 806,766 (+17,815) |
|
| 2025-10-03 | 446,829 (+5,977) | 359,937 (+11,838) | 806,766 (+17,815) |
|
||||||
| 2025-10-04 | 452,561 (+5,732) | 370,386 (+10,449) | 822,947 (+16,181) |
|
| 2025-10-04 | 452,561 (+5,732) | 370,386 (+10,449) | 822,947 (+16,181) |
|
||||||
| 2025-10-05 | 455,559 (+2,998) | 374,745 (+4,359) | 830,304 (+7,357) |
|
| 2025-10-05 | 455,559 (+2,998) | 374,745 (+4,359) | 830,304 (+7,357) |
|
||||||
| 2025-10-06 | 460,927 (+5,368) | 379,489 (+4,744) | 840,416 (+10,112) |
|
| 2025-10-06 | 460,927 (+5,368) | 379,489 (+4,744) | 840,416 (+10,112) |
|
||||||
| 2025-10-07 | 467,336 (+6,409) | 385,438 (+5,949) | 852,774 (+12,358) |
|
| 2025-10-07 | 467,336 (+6,409) | 385,438 (+5,949) | 852,774 (+12,358) |
|
||||||
| 2025-10-08 | 474,643 (+7,307) | 394,139 (+8,701) | 868,782 (+16,008) |
|
| 2025-10-08 | 474,643 (+7,307) | 394,139 (+8,701) | 868,782 (+16,008) |
|
||||||
| 2025-10-09 | 479,203 (+4,560) | 400,526 (+6,387) | 879,729 (+10,947) |
|
| 2025-10-09 | 479,203 (+4,560) | 400,526 (+6,387) | 879,729 (+10,947) |
|
||||||
| 2025-10-10 | 484,374 (+5,171) | 406,015 (+5,489) | 890,389 (+10,660) |
|
| 2025-10-10 | 484,374 (+5,171) | 406,015 (+5,489) | 890,389 (+10,660) |
|
||||||
| 2025-10-11 | 488,427 (+4,053) | 414,699 (+8,684) | 903,126 (+12,737) |
|
| 2025-10-11 | 488,427 (+4,053) | 414,699 (+8,684) | 903,126 (+12,737) |
|
||||||
| 2025-10-12 | 492,125 (+3,698) | 418,745 (+4,046) | 910,870 (+7,744) |
|
| 2025-10-12 | 492,125 (+3,698) | 418,745 (+4,046) | 910,870 (+7,744) |
|
||||||
| 2025-10-14 | 505,130 (+13,005) | 429,286 (+10,541) | 934,416 (+23,546) |
|
| 2025-10-14 | 505,130 (+13,005) | 429,286 (+10,541) | 934,416 (+23,546) |
|
||||||
| 2025-10-15 | 512,717 (+7,587) | 439,290 (+10,004) | 952,007 (+17,591) |
|
| 2025-10-15 | 512,717 (+7,587) | 439,290 (+10,004) | 952,007 (+17,591) |
|
||||||
| 2025-10-16 | 517,719 (+5,002) | 447,137 (+7,847) | 964,856 (+12,849) |
|
| 2025-10-16 | 517,719 (+5,002) | 447,137 (+7,847) | 964,856 (+12,849) |
|
||||||
| 2025-10-17 | 526,239 (+8,520) | 457,467 (+10,330) | 983,706 (+18,850) |
|
| 2025-10-17 | 526,239 (+8,520) | 457,467 (+10,330) | 983,706 (+18,850) |
|
||||||
| 2025-10-18 | 531,564 (+5,325) | 465,272 (+7,805) | 996,836 (+13,130) |
|
| 2025-10-18 | 531,564 (+5,325) | 465,272 (+7,805) | 996,836 (+13,130) |
|
||||||
| 2025-10-19 | 536,209 (+4,645) | 469,078 (+3,806) | 1,005,287 (+8,451) |
|
| 2025-10-19 | 536,209 (+4,645) | 469,078 (+3,806) | 1,005,287 (+8,451) |
|
||||||
| 2025-10-20 | 541,264 (+5,055) | 472,952 (+3,874) | 1,014,216 (+8,929) |
|
| 2025-10-20 | 541,264 (+5,055) | 472,952 (+3,874) | 1,014,216 (+8,929) |
|
||||||
| 2025-10-21 | 548,721 (+7,457) | 479,703 (+6,751) | 1,028,424 (+14,208) |
|
| 2025-10-21 | 548,721 (+7,457) | 479,703 (+6,751) | 1,028,424 (+14,208) |
|
||||||
| 2025-10-22 | 557,949 (+9,228) | 491,395 (+11,692) | 1,049,344 (+20,920) |
|
| 2025-10-22 | 557,949 (+9,228) | 491,395 (+11,692) | 1,049,344 (+20,920) |
|
||||||
| 2025-10-23 | 564,716 (+6,767) | 498,736 (+7,341) | 1,063,452 (+14,108) |
|
| 2025-10-23 | 564,716 (+6,767) | 498,736 (+7,341) | 1,063,452 (+14,108) |
|
||||||
| 2025-10-24 | 572,692 (+7,976) | 506,905 (+8,169) | 1,079,597 (+16,145) |
|
| 2025-10-24 | 572,692 (+7,976) | 506,905 (+8,169) | 1,079,597 (+16,145) |
|
||||||
| 2025-10-25 | 578,927 (+6,235) | 516,129 (+9,224) | 1,095,056 (+15,459) |
|
| 2025-10-25 | 578,927 (+6,235) | 516,129 (+9,224) | 1,095,056 (+15,459) |
|
||||||
| 2025-10-26 | 584,409 (+5,482) | 521,179 (+5,050) | 1,105,588 (+10,532) |
|
| 2025-10-26 | 584,409 (+5,482) | 521,179 (+5,050) | 1,105,588 (+10,532) |
|
||||||
| 2025-10-27 | 589,999 (+5,590) | 526,001 (+4,822) | 1,116,000 (+10,412) |
|
| 2025-10-27 | 589,999 (+5,590) | 526,001 (+4,822) | 1,116,000 (+10,412) |
|
||||||
| 2025-10-28 | 595,776 (+5,777) | 532,438 (+6,437) | 1,128,214 (+12,214) |
|
| 2025-10-28 | 595,776 (+5,777) | 532,438 (+6,437) | 1,128,214 (+12,214) |
|
||||||
| 2025-10-29 | 606,259 (+10,483) | 542,064 (+9,626) | 1,148,323 (+20,109) |
|
| 2025-10-29 | 606,259 (+10,483) | 542,064 (+9,626) | 1,148,323 (+20,109) |
|
||||||
| 2025-10-30 | 613,746 (+7,487) | 542,064 (+0) | 1,155,810 (+7,487) |
|
| 2025-10-30 | 613,746 (+7,487) | 542,064 (+0) | 1,155,810 (+7,487) |
|
||||||
| 2025-10-30 | 617,846 (+4,100) | 555,026 (+12,962) | 1,172,872 (+17,062) |
|
| 2025-10-30 | 617,846 (+4,100) | 555,026 (+12,962) | 1,172,872 (+17,062) |
|
||||||
| 2025-10-31 | 626,612 (+8,766) | 564,579 (+9,553) | 1,191,191 (+18,319) |
|
| 2025-10-31 | 626,612 (+8,766) | 564,579 (+9,553) | 1,191,191 (+18,319) |
|
||||||
| 2025-11-01 | 636,100 (+9,488) | 581,806 (+17,227) | 1,217,906 (+26,715) |
|
| 2025-11-01 | 636,100 (+9,488) | 581,806 (+17,227) | 1,217,906 (+26,715) |
|
||||||
| 2025-11-02 | 644,067 (+7,967) | 590,004 (+8,198) | 1,234,071 (+16,165) |
|
| 2025-11-02 | 644,067 (+7,967) | 590,004 (+8,198) | 1,234,071 (+16,165) |
|
||||||
| 2025-11-03 | 653,130 (+9,063) | 597,139 (+7,135) | 1,250,269 (+16,198) |
|
| 2025-11-03 | 653,130 (+9,063) | 597,139 (+7,135) | 1,250,269 (+16,198) |
|
||||||
| 2025-11-04 | 663,912 (+10,782) | 608,056 (+10,917) | 1,271,968 (+21,699) |
|
| 2025-11-04 | 663,912 (+10,782) | 608,056 (+10,917) | 1,271,968 (+21,699) |
|
||||||
| 2025-11-05 | 675,074 (+11,162) | 619,690 (+11,634) | 1,294,764 (+22,796) |
|
| 2025-11-05 | 675,074 (+11,162) | 619,690 (+11,634) | 1,294,764 (+22,796) |
|
||||||
| 2025-11-06 | 686,252 (+11,178) | 630,885 (+11,195) | 1,317,137 (+22,373) |
|
| 2025-11-06 | 686,252 (+11,178) | 630,885 (+11,195) | 1,317,137 (+22,373) |
|
||||||
| 2025-11-07 | 696,646 (+10,394) | 642,146 (+11,261) | 1,338,792 (+21,655) |
|
| 2025-11-07 | 696,646 (+10,394) | 642,146 (+11,261) | 1,338,792 (+21,655) |
|
||||||
| 2025-11-08 | 706,035 (+9,389) | 653,489 (+11,343) | 1,359,524 (+20,732) |
|
| 2025-11-08 | 706,035 (+9,389) | 653,489 (+11,343) | 1,359,524 (+20,732) |
|
||||||
| 2025-11-09 | 713,462 (+7,427) | 660,459 (+6,970) | 1,373,921 (+14,397) |
|
| 2025-11-09 | 713,462 (+7,427) | 660,459 (+6,970) | 1,373,921 (+14,397) |
|
||||||
| 2025-11-10 | 722,288 (+8,826) | 668,225 (+7,766) | 1,390,513 (+16,592) |
|
| 2025-11-10 | 722,288 (+8,826) | 668,225 (+7,766) | 1,390,513 (+16,592) |
|
||||||
| 2025-11-11 | 729,769 (+7,481) | 677,501 (+9,276) | 1,407,270 (+16,757) |
|
| 2025-11-11 | 729,769 (+7,481) | 677,501 (+9,276) | 1,407,270 (+16,757) |
|
||||||
| 2025-11-12 | 740,180 (+10,411) | 686,454 (+8,953) | 1,426,634 (+19,364) |
|
| 2025-11-12 | 740,180 (+10,411) | 686,454 (+8,953) | 1,426,634 (+19,364) |
|
||||||
| 2025-11-13 | 749,905 (+9,725) | 696,157 (+9,703) | 1,446,062 (+19,428) |
|
| 2025-11-13 | 749,905 (+9,725) | 696,157 (+9,703) | 1,446,062 (+19,428) |
|
||||||
| 2025-11-14 | 759,928 (+10,023) | 705,237 (+9,080) | 1,465,165 (+19,103) |
|
| 2025-11-14 | 759,928 (+10,023) | 705,237 (+9,080) | 1,465,165 (+19,103) |
|
||||||
| 2025-11-15 | 765,955 (+6,027) | 712,870 (+7,633) | 1,478,825 (+13,660) |
|
| 2025-11-15 | 765,955 (+6,027) | 712,870 (+7,633) | 1,478,825 (+13,660) |
|
||||||
| 2025-11-16 | 771,069 (+5,114) | 716,596 (+3,726) | 1,487,665 (+8,840) |
|
| 2025-11-16 | 771,069 (+5,114) | 716,596 (+3,726) | 1,487,665 (+8,840) |
|
||||||
| 2025-11-17 | 780,161 (+9,092) | 723,339 (+6,743) | 1,503,500 (+15,835) |
|
| 2025-11-17 | 780,161 (+9,092) | 723,339 (+6,743) | 1,503,500 (+15,835) |
|
||||||
| 2025-11-18 | 791,563 (+11,402) | 732,544 (+9,205) | 1,524,107 (+20,607) |
|
| 2025-11-18 | 791,563 (+11,402) | 732,544 (+9,205) | 1,524,107 (+20,607) |
|
||||||
| 2025-11-19 | 804,409 (+12,846) | 747,624 (+15,080) | 1,552,033 (+27,926) |
|
| 2025-11-19 | 804,409 (+12,846) | 747,624 (+15,080) | 1,552,033 (+27,926) |
|
||||||
| 2025-11-20 | 814,620 (+10,211) | 757,907 (+10,283) | 1,572,527 (+20,494) |
|
| 2025-11-20 | 814,620 (+10,211) | 757,907 (+10,283) | 1,572,527 (+20,494) |
|
||||||
| 2025-11-21 | 826,309 (+11,689) | 769,307 (+11,400) | 1,595,616 (+23,089) |
|
| 2025-11-21 | 826,309 (+11,689) | 769,307 (+11,400) | 1,595,616 (+23,089) |
|
||||||
| 2025-11-22 | 837,269 (+10,960) | 780,996 (+11,689) | 1,618,265 (+22,649) |
|
| 2025-11-22 | 837,269 (+10,960) | 780,996 (+11,689) | 1,618,265 (+22,649) |
|
||||||
| 2025-11-23 | 846,609 (+9,340) | 795,069 (+14,073) | 1,641,678 (+23,413) |
|
| 2025-11-23 | 846,609 (+9,340) | 795,069 (+14,073) | 1,641,678 (+23,413) |
|
||||||
| 2025-11-24 | 856,733 (+10,124) | 804,033 (+8,964) | 1,660,766 (+19,088) |
|
| 2025-11-24 | 856,733 (+10,124) | 804,033 (+8,964) | 1,660,766 (+19,088) |
|
||||||
| 2025-11-25 | 869,423 (+12,690) | 817,339 (+13,306) | 1,686,762 (+25,996) |
|
| 2025-11-25 | 869,423 (+12,690) | 817,339 (+13,306) | 1,686,762 (+25,996) |
|
||||||
| 2025-11-26 | 881,414 (+11,991) | 832,518 (+15,179) | 1,713,932 (+27,170) |
|
| 2025-11-26 | 881,414 (+11,991) | 832,518 (+15,179) | 1,713,932 (+27,170) |
|
||||||
| 2025-11-27 | 893,960 (+12,546) | 846,180 (+13,662) | 1,740,140 (+26,208) |
|
| 2025-11-27 | 893,960 (+12,546) | 846,180 (+13,662) | 1,740,140 (+26,208) |
|
||||||
| 2025-11-28 | 901,741 (+7,781) | 856,482 (+10,302) | 1,758,223 (+18,083) |
|
| 2025-11-28 | 901,741 (+7,781) | 856,482 (+10,302) | 1,758,223 (+18,083) |
|
||||||
| 2025-11-29 | 908,689 (+6,948) | 863,361 (+6,879) | 1,772,050 (+13,827) |
|
| 2025-11-29 | 908,689 (+6,948) | 863,361 (+6,879) | 1,772,050 (+13,827) |
|
||||||
| 2025-11-30 | 916,116 (+7,427) | 870,194 (+6,833) | 1,786,310 (+14,260) |
|
| 2025-11-30 | 916,116 (+7,427) | 870,194 (+6,833) | 1,786,310 (+14,260) |
|
||||||
| 2025-12-01 | 925,898 (+9,782) | 876,500 (+6,306) | 1,802,398 (+16,088) |
|
| 2025-12-01 | 925,898 (+9,782) | 876,500 (+6,306) | 1,802,398 (+16,088) |
|
||||||
| 2025-12-02 | 939,250 (+13,352) | 890,919 (+14,419) | 1,830,169 (+27,771) |
|
| 2025-12-02 | 939,250 (+13,352) | 890,919 (+14,419) | 1,830,169 (+27,771) |
|
||||||
| 2025-12-03 | 952,249 (+12,999) | 903,713 (+12,794) | 1,855,962 (+25,793) |
|
| 2025-12-03 | 952,249 (+12,999) | 903,713 (+12,794) | 1,855,962 (+25,793) |
|
||||||
| 2025-12-04 | 965,611 (+13,362) | 916,471 (+12,758) | 1,882,082 (+26,120) |
|
| 2025-12-04 | 965,611 (+13,362) | 916,471 (+12,758) | 1,882,082 (+26,120) |
|
||||||
| 2025-12-05 | 977,996 (+12,385) | 930,616 (+14,145) | 1,908,612 (+26,530) |
|
| 2025-12-05 | 977,996 (+12,385) | 930,616 (+14,145) | 1,908,612 (+26,530) |
|
||||||
| 2025-12-06 | 987,884 (+9,888) | 943,773 (+13,157) | 1,931,657 (+23,045) |
|
| 2025-12-06 | 987,884 (+9,888) | 943,773 (+13,157) | 1,931,657 (+23,045) |
|
||||||
| 2025-12-07 | 994,046 (+6,162) | 951,425 (+7,652) | 1,945,471 (+13,814) |
|
| 2025-12-07 | 994,046 (+6,162) | 951,425 (+7,652) | 1,945,471 (+13,814) |
|
||||||
| 2025-12-08 | 1,000,898 (+6,852) | 957,149 (+5,724) | 1,958,047 (+12,576) |
|
| 2025-12-08 | 1,000,898 (+6,852) | 957,149 (+5,724) | 1,958,047 (+12,576) |
|
||||||
| 2025-12-09 | 1,011,488 (+10,590) | 973,922 (+16,773) | 1,985,410 (+27,363) |
|
| 2025-12-09 | 1,011,488 (+10,590) | 973,922 (+16,773) | 1,985,410 (+27,363) |
|
||||||
| 2025-12-10 | 1,025,891 (+14,403) | 991,708 (+17,786) | 2,017,599 (+32,189) |
|
| 2025-12-10 | 1,025,891 (+14,403) | 991,708 (+17,786) | 2,017,599 (+32,189) |
|
||||||
| 2025-12-11 | 1,045,110 (+19,219) | 1,010,559 (+18,851) | 2,055,669 (+38,070) |
|
| 2025-12-11 | 1,045,110 (+19,219) | 1,010,559 (+18,851) | 2,055,669 (+38,070) |
|
||||||
| 2025-12-12 | 1,061,340 (+16,230) | 1,030,838 (+20,279) | 2,092,178 (+36,509) |
|
| 2025-12-12 | 1,061,340 (+16,230) | 1,030,838 (+20,279) | 2,092,178 (+36,509) |
|
||||||
| 2025-12-13 | 1,073,561 (+12,221) | 1,044,608 (+13,770) | 2,118,169 (+25,991) |
|
| 2025-12-13 | 1,073,561 (+12,221) | 1,044,608 (+13,770) | 2,118,169 (+25,991) |
|
||||||
| 2025-12-14 | 1,082,042 (+8,481) | 1,052,425 (+7,817) | 2,134,467 (+16,298) |
|
| 2025-12-14 | 1,082,042 (+8,481) | 1,052,425 (+7,817) | 2,134,467 (+16,298) |
|
||||||
| 2025-12-15 | 1,093,632 (+11,590) | 1,059,078 (+6,653) | 2,152,710 (+18,243) |
|
| 2025-12-15 | 1,093,632 (+11,590) | 1,059,078 (+6,653) | 2,152,710 (+18,243) |
|
||||||
| 2025-12-16 | 1,120,477 (+26,845) | 1,078,022 (+18,944) | 2,198,499 (+45,789) |
|
| 2025-12-16 | 1,120,477 (+26,845) | 1,078,022 (+18,944) | 2,198,499 (+45,789) |
|
||||||
| 2025-12-17 | 1,151,067 (+30,590) | 1,097,661 (+19,639) | 2,248,728 (+50,229) |
|
| 2025-12-17 | 1,151,067 (+30,590) | 1,097,661 (+19,639) | 2,248,728 (+50,229) |
|
||||||
| 2025-12-18 | 1,178,658 (+27,591) | 1,113,418 (+15,757) | 2,292,076 (+43,348) |
|
| 2025-12-18 | 1,178,658 (+27,591) | 1,113,418 (+15,757) | 2,292,076 (+43,348) |
|
||||||
| 2025-12-19 | 1,203,485 (+24,827) | 1,129,698 (+16,280) | 2,333,183 (+41,107) |
|
| 2025-12-19 | 1,203,485 (+24,827) | 1,129,698 (+16,280) | 2,333,183 (+41,107) |
|
||||||
| 2025-12-20 | 1,223,000 (+19,515) | 1,146,258 (+16,560) | 2,369,258 (+36,075) |
|
| 2025-12-20 | 1,223,000 (+19,515) | 1,146,258 (+16,560) | 2,369,258 (+36,075) |
|
||||||
| 2025-12-21 | 1,242,675 (+19,675) | 1,158,909 (+12,651) | 2,401,584 (+32,326) |
|
| 2025-12-21 | 1,242,675 (+19,675) | 1,158,909 (+12,651) | 2,401,584 (+32,326) |
|
||||||
| 2025-12-22 | 1,262,522 (+19,847) | 1,169,121 (+10,212) | 2,431,643 (+30,059) |
|
| 2025-12-22 | 1,262,522 (+19,847) | 1,169,121 (+10,212) | 2,431,643 (+30,059) |
|
||||||
| 2025-12-23 | 1,286,548 (+24,026) | 1,186,439 (+17,318) | 2,472,987 (+41,344) |
|
| 2025-12-23 | 1,286,548 (+24,026) | 1,186,439 (+17,318) | 2,472,987 (+41,344) |
|
||||||
| 2025-12-24 | 1,309,323 (+22,775) | 1,203,767 (+17,328) | 2,513,090 (+40,103) |
|
| 2025-12-24 | 1,309,323 (+22,775) | 1,203,767 (+17,328) | 2,513,090 (+40,103) |
|
||||||
| 2025-12-25 | 1,333,032 (+23,709) | 1,217,283 (+13,516) | 2,550,315 (+37,225) |
|
| 2025-12-25 | 1,333,032 (+23,709) | 1,217,283 (+13,516) | 2,550,315 (+37,225) |
|
||||||
| 2025-12-26 | 1,352,411 (+19,379) | 1,227,615 (+10,332) | 2,580,026 (+29,711) |
|
| 2025-12-26 | 1,352,411 (+19,379) | 1,227,615 (+10,332) | 2,580,026 (+29,711) |
|
||||||
| 2025-12-27 | 1,371,771 (+19,360) | 1,238,236 (+10,621) | 2,610,007 (+29,981) |
|
| 2025-12-27 | 1,371,771 (+19,360) | 1,238,236 (+10,621) | 2,610,007 (+29,981) |
|
||||||
| 2025-12-28 | 1,390,388 (+18,617) | 1,245,690 (+7,454) | 2,636,078 (+26,071) |
|
| 2025-12-28 | 1,390,388 (+18,617) | 1,245,690 (+7,454) | 2,636,078 (+26,071) |
|
||||||
| 2025-12-29 | 1,415,560 (+25,172) | 1,257,101 (+11,411) | 2,672,661 (+36,583) |
|
| 2025-12-29 | 1,415,560 (+25,172) | 1,257,101 (+11,411) | 2,672,661 (+36,583) |
|
||||||
| 2025-12-30 | 1,445,450 (+29,890) | 1,272,689 (+15,588) | 2,718,139 (+45,478) |
|
| 2025-12-30 | 1,445,450 (+29,890) | 1,272,689 (+15,588) | 2,718,139 (+45,478) |
|
||||||
| 2025-12-31 | 1,479,598 (+34,148) | 1,293,235 (+20,546) | 2,772,833 (+54,694) |
|
| 2025-12-31 | 1,479,598 (+34,148) | 1,293,235 (+20,546) | 2,772,833 (+54,694) |
|
||||||
| 2026-01-01 | 1,508,883 (+29,285) | 1,309,874 (+16,639) | 2,818,757 (+45,924) |
|
| 2026-01-01 | 1,508,883 (+29,285) | 1,309,874 (+16,639) | 2,818,757 (+45,924) |
|
||||||
| 2026-01-02 | 1,563,474 (+54,591) | 1,320,959 (+11,085) | 2,884,433 (+65,676) |
|
| 2026-01-02 | 1,563,474 (+54,591) | 1,320,959 (+11,085) | 2,884,433 (+65,676) |
|
||||||
| 2026-01-03 | 1,618,065 (+54,591) | 1,331,914 (+10,955) | 2,949,979 (+65,546) |
|
| 2026-01-03 | 1,618,065 (+54,591) | 1,331,914 (+10,955) | 2,949,979 (+65,546) |
|
||||||
| 2026-01-04 | 1,672,656 (+39,702) | 1,339,883 (+7,969) | 3,012,539 (+62,560) |
|
| 2026-01-04 | 1,672,656 (+39,702) | 1,339,883 (+7,969) | 3,012,539 (+62,560) |
|
||||||
| 2026-01-05 | 1,738,171 (+65,515) | 1,353,043 (+13,160) | 3,091,214 (+78,675) |
|
| 2026-01-05 | 1,738,171 (+65,515) | 1,353,043 (+13,160) | 3,091,214 (+78,675) |
|
||||||
| 2026-01-06 | 1,960,988 (+222,817) | 1,377,377 (+24,334) | 3,338,365 (+247,151) |
|
| 2026-01-06 | 1,960,988 (+222,817) | 1,377,377 (+24,334) | 3,338,365 (+247,151) |
|
||||||
| 2026-01-07 | 2,123,239 (+162,251) | 1,398,648 (+21,271) | 3,521,887 (+183,522) |
|
| 2026-01-07 | 2,123,239 (+162,251) | 1,398,648 (+21,271) | 3,521,887 (+183,522) |
|
||||||
| 2026-01-08 | 2,272,630 (+149,391) | 1,432,480 (+33,832) | 3,705,110 (+183,223) |
|
| 2026-01-08 | 2,272,630 (+149,391) | 1,432,480 (+33,832) | 3,705,110 (+183,223) |
|
||||||
| 2026-01-09 | 2,443,565 (+170,935) | 1,469,451 (+36,971) | 3,913,016 (+207,906) |
|
| 2026-01-09 | 2,443,565 (+170,935) | 1,469,451 (+36,971) | 3,913,016 (+207,906) |
|
||||||
|
| 2026-01-10 | 2,632,023 (+188,458) | 1,503,670 (+34,219) | 4,135,693 (+222,677) |
|
||||||
|
| 2026-01-11 | 2,836,394 (+204,371) | 1,530,479 (+26,809) | 4,366,873 (+231,180) |
|
||||||
|
| 2026-01-12 | 3,053,594 (+217,200) | 1,553,671 (+23,192) | 4,607,265 (+240,392) |
|
||||||
|
| 2026-01-13 | 3,297,078 (+243,484) | 1,595,062 (+41,391) | 4,892,140 (+284,875) |
|
||||||
|
| 2026-01-14 | 3,568,928 (+271,850) | 1,645,362 (+50,300) | 5,214,290 (+322,150) |
|
||||||
|
| 2026-01-16 | 4,121,550 (+552,622) | 1,754,418 (+109,056) | 5,875,968 (+661,678) |
|
||||||
|
| 2026-01-17 | 4,389,558 (+268,008) | 1,805,315 (+50,897) | 6,194,873 (+318,905) |
|
||||||
|
|||||||
+69
-9
@@ -1,11 +1,71 @@
|
|||||||
## Style Guide
|
## Style Guide
|
||||||
|
|
||||||
- Try to keep things in one function unless composable or reusable
|
- Keep things in one function unless composable or reusable
|
||||||
- AVOID unnecessary destructuring of variables. instead of doing `const { a, b }
|
- Avoid unnecessary destructuring. Instead of `const { a, b } = obj`, use `obj.a` and `obj.b` to preserve context
|
||||||
= obj` just reference it as obj.a and obj.b. this preserves context
|
- Avoid `try`/`catch` where possible
|
||||||
- AVOID `try`/`catch` where possible
|
- Avoid using the `any` type
|
||||||
- AVOID `else` statements
|
- Prefer single word variable names where possible
|
||||||
- AVOID using `any` type
|
- Use Bun APIs when possible, like `Bun.file()`
|
||||||
- AVOID `let` statements
|
|
||||||
- PREFER single word variable names where possible
|
# Avoid let statements
|
||||||
- Use as many bun apis as possible like Bun.file()
|
|
||||||
|
We don't like `let` statements, especially combined with if/else statements.
|
||||||
|
Prefer `const`.
|
||||||
|
|
||||||
|
Good:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const foo = condition ? 1 : 2
|
||||||
|
```
|
||||||
|
|
||||||
|
Bad:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
let foo
|
||||||
|
|
||||||
|
if (condition) foo = 1
|
||||||
|
else foo = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
# Avoid else statements
|
||||||
|
|
||||||
|
Prefer early returns or using an `iife` to avoid else statements.
|
||||||
|
|
||||||
|
Good:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function foo() {
|
||||||
|
if (condition) return 1
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Bad:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function foo() {
|
||||||
|
if (condition) return 1
|
||||||
|
else return 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Prefer single word naming
|
||||||
|
|
||||||
|
Try your best to find a single word name for your variables, functions, etc.
|
||||||
|
Only use multiple words if you cannot.
|
||||||
|
|
||||||
|
Good:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const foo = 1
|
||||||
|
const bar = 2
|
||||||
|
const baz = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
Bad:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const fooBar = 1
|
||||||
|
const barBaz = 2
|
||||||
|
const bazFoo = 3
|
||||||
|
```
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
},
|
},
|
||||||
"packages/app": {
|
"packages/app": {
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/app": {
|
"packages/console/app": {
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cloudflare/vite-plugin": "1.15.2",
|
"@cloudflare/vite-plugin": "1.15.2",
|
||||||
"@ibm/plex": "6.4.1",
|
"@ibm/plex": "6.4.1",
|
||||||
@@ -81,24 +81,30 @@
|
|||||||
"@opencode-ai/console-mail": "workspace:*",
|
"@opencode-ai/console-mail": "workspace:*",
|
||||||
"@opencode-ai/console-resource": "workspace:*",
|
"@opencode-ai/console-resource": "workspace:*",
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
|
"@smithy/eventstream-codec": "4.2.7",
|
||||||
|
"@smithy/util-utf8": "4.2.0",
|
||||||
"@solidjs/meta": "catalog:",
|
"@solidjs/meta": "catalog:",
|
||||||
"@solidjs/router": "catalog:",
|
"@solidjs/router": "catalog:",
|
||||||
"@solidjs/start": "catalog:",
|
"@solidjs/start": "catalog:",
|
||||||
|
"@stripe/stripe-js": "8.6.1",
|
||||||
"chart.js": "4.5.1",
|
"chart.js": "4.5.1",
|
||||||
"nitro": "3.0.1-alpha.1",
|
"nitro": "3.0.1-alpha.1",
|
||||||
"solid-js": "catalog:",
|
"solid-js": "catalog:",
|
||||||
|
"solid-list": "0.3.0",
|
||||||
|
"solid-stripe": "0.8.1",
|
||||||
"vite": "catalog:",
|
"vite": "catalog:",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript/native-preview": "catalog:",
|
"@typescript/native-preview": "catalog:",
|
||||||
|
"@webgpu/types": "0.1.54",
|
||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
"wrangler": "4.50.0",
|
"wrangler": "4.50.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages/console/core": {
|
"packages/console/core": {
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-sts": "3.782.0",
|
"@aws-sdk/client-sts": "3.782.0",
|
||||||
"@jsx-email/render": "1.1.1",
|
"@jsx-email/render": "1.1.1",
|
||||||
@@ -125,7 +131,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/function": {
|
"packages/console/function": {
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "2.0.0",
|
"@ai-sdk/anthropic": "2.0.0",
|
||||||
"@ai-sdk/openai": "2.0.2",
|
"@ai-sdk/openai": "2.0.2",
|
||||||
@@ -149,7 +155,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/mail": {
|
"packages/console/mail": {
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsx-email/all": "2.2.3",
|
"@jsx-email/all": "2.2.3",
|
||||||
"@jsx-email/cli": "1.4.3",
|
"@jsx-email/cli": "1.4.3",
|
||||||
@@ -173,7 +179,7 @@
|
|||||||
},
|
},
|
||||||
"packages/desktop": {
|
"packages/desktop": {
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/app": "workspace:*",
|
"@opencode-ai/app": "workspace:*",
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
@@ -202,7 +208,7 @@
|
|||||||
},
|
},
|
||||||
"packages/enterprise": {
|
"packages/enterprise": {
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
"@opencode-ai/util": "workspace:*",
|
"@opencode-ai/util": "workspace:*",
|
||||||
@@ -231,7 +237,7 @@
|
|||||||
},
|
},
|
||||||
"packages/function": {
|
"packages/function": {
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-app": "8.0.1",
|
"@octokit/auth-app": "8.0.1",
|
||||||
"@octokit/rest": "catalog:",
|
"@octokit/rest": "catalog:",
|
||||||
@@ -247,7 +253,7 @@
|
|||||||
},
|
},
|
||||||
"packages/opencode": {
|
"packages/opencode": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
@@ -255,26 +261,27 @@
|
|||||||
"@actions/core": "1.11.1",
|
"@actions/core": "1.11.1",
|
||||||
"@actions/github": "6.0.1",
|
"@actions/github": "6.0.1",
|
||||||
"@agentclientprotocol/sdk": "0.5.1",
|
"@agentclientprotocol/sdk": "0.5.1",
|
||||||
"@ai-sdk/amazon-bedrock": "3.0.57",
|
"@ai-sdk/amazon-bedrock": "3.0.73",
|
||||||
"@ai-sdk/anthropic": "2.0.56",
|
"@ai-sdk/anthropic": "2.0.57",
|
||||||
"@ai-sdk/azure": "2.0.82",
|
"@ai-sdk/azure": "2.0.91",
|
||||||
"@ai-sdk/cerebras": "1.0.33",
|
"@ai-sdk/cerebras": "1.0.34",
|
||||||
"@ai-sdk/cohere": "2.0.21",
|
"@ai-sdk/cohere": "2.0.22",
|
||||||
"@ai-sdk/deepinfra": "1.0.30",
|
"@ai-sdk/deepinfra": "1.0.31",
|
||||||
"@ai-sdk/gateway": "2.0.23",
|
"@ai-sdk/gateway": "2.0.25",
|
||||||
"@ai-sdk/google": "2.0.49",
|
"@ai-sdk/google": "2.0.52",
|
||||||
"@ai-sdk/google-vertex": "3.0.81",
|
"@ai-sdk/google-vertex": "3.0.97",
|
||||||
"@ai-sdk/groq": "2.0.33",
|
"@ai-sdk/groq": "2.0.34",
|
||||||
"@ai-sdk/mistral": "2.0.26",
|
"@ai-sdk/mistral": "2.0.27",
|
||||||
"@ai-sdk/openai": "2.0.71",
|
"@ai-sdk/openai": "2.0.89",
|
||||||
"@ai-sdk/openai-compatible": "1.0.29",
|
"@ai-sdk/openai-compatible": "1.0.30",
|
||||||
"@ai-sdk/perplexity": "2.0.22",
|
"@ai-sdk/perplexity": "2.0.23",
|
||||||
"@ai-sdk/provider": "2.0.0",
|
"@ai-sdk/provider": "2.0.1",
|
||||||
"@ai-sdk/provider-utils": "3.0.19",
|
"@ai-sdk/provider-utils": "3.0.20",
|
||||||
"@ai-sdk/togetherai": "1.0.30",
|
"@ai-sdk/togetherai": "1.0.31",
|
||||||
"@ai-sdk/vercel": "1.0.31",
|
"@ai-sdk/vercel": "1.0.31",
|
||||||
"@ai-sdk/xai": "2.0.42",
|
"@ai-sdk/xai": "2.0.51",
|
||||||
"@clack/prompts": "1.0.0-alpha.1",
|
"@clack/prompts": "1.0.0-alpha.1",
|
||||||
|
"@gitlab/gitlab-ai-provider": "3.1.1",
|
||||||
"@hono/standard-validator": "0.1.5",
|
"@hono/standard-validator": "0.1.5",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
"@modelcontextprotocol/sdk": "1.25.2",
|
"@modelcontextprotocol/sdk": "1.25.2",
|
||||||
@@ -286,8 +293,8 @@
|
|||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@opencode-ai/util": "workspace:*",
|
"@opencode-ai/util": "workspace:*",
|
||||||
"@openrouter/ai-sdk-provider": "1.5.2",
|
"@openrouter/ai-sdk-provider": "1.5.2",
|
||||||
"@opentui/core": "0.1.72",
|
"@opentui/core": "0.1.74",
|
||||||
"@opentui/solid": "0.1.72",
|
"@opentui/solid": "0.1.74",
|
||||||
"@parcel/watcher": "2.5.1",
|
"@parcel/watcher": "2.5.1",
|
||||||
"@pierre/diffs": "catalog:",
|
"@pierre/diffs": "catalog:",
|
||||||
"@solid-primitives/event-bus": "1.1.2",
|
"@solid-primitives/event-bus": "1.1.2",
|
||||||
@@ -350,7 +357,7 @@
|
|||||||
},
|
},
|
||||||
"packages/plugin": {
|
"packages/plugin": {
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
@@ -370,9 +377,9 @@
|
|||||||
},
|
},
|
||||||
"packages/sdk/js": {
|
"packages/sdk/js": {
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hey-api/openapi-ts": "0.88.1",
|
"@hey-api/openapi-ts": "0.90.4",
|
||||||
"@tsconfig/node22": "catalog:",
|
"@tsconfig/node22": "catalog:",
|
||||||
"@types/node": "catalog:",
|
"@types/node": "catalog:",
|
||||||
"@typescript/native-preview": "catalog:",
|
"@typescript/native-preview": "catalog:",
|
||||||
@@ -381,7 +388,7 @@
|
|||||||
},
|
},
|
||||||
"packages/slack": {
|
"packages/slack": {
|
||||||
"name": "@opencode-ai/slack",
|
"name": "@opencode-ai/slack",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@slack/bolt": "^3.17.1",
|
"@slack/bolt": "^3.17.1",
|
||||||
@@ -394,7 +401,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@opencode-ai/ui",
|
"name": "@opencode-ai/ui",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -406,6 +413,7 @@
|
|||||||
"@solid-primitives/resize-observer": "2.1.3",
|
"@solid-primitives/resize-observer": "2.1.3",
|
||||||
"@solidjs/meta": "catalog:",
|
"@solidjs/meta": "catalog:",
|
||||||
"@typescript/native-preview": "catalog:",
|
"@typescript/native-preview": "catalog:",
|
||||||
|
"dompurify": "3.3.1",
|
||||||
"fuzzysort": "catalog:",
|
"fuzzysort": "catalog:",
|
||||||
"katex": "0.16.27",
|
"katex": "0.16.27",
|
||||||
"luxon": "catalog:",
|
"luxon": "catalog:",
|
||||||
@@ -416,6 +424,7 @@
|
|||||||
"shiki": "catalog:",
|
"shiki": "catalog:",
|
||||||
"solid-js": "catalog:",
|
"solid-js": "catalog:",
|
||||||
"solid-list": "catalog:",
|
"solid-list": "catalog:",
|
||||||
|
"strip-ansi": "7.1.2",
|
||||||
"virtua": "catalog:",
|
"virtua": "catalog:",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -433,7 +442,7 @@
|
|||||||
},
|
},
|
||||||
"packages/util": {
|
"packages/util": {
|
||||||
"name": "@opencode-ai/util",
|
"name": "@opencode-ai/util",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
},
|
},
|
||||||
@@ -444,7 +453,7 @@
|
|||||||
},
|
},
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"name": "@opencode-ai/web",
|
"name": "@opencode-ai/web",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/cloudflare": "12.6.3",
|
"@astrojs/cloudflare": "12.6.3",
|
||||||
"@astrojs/markdown-remark": "6.3.1",
|
"@astrojs/markdown-remark": "6.3.1",
|
||||||
@@ -500,12 +509,13 @@
|
|||||||
"@tailwindcss/vite": "4.1.11",
|
"@tailwindcss/vite": "4.1.11",
|
||||||
"@tsconfig/bun": "1.0.9",
|
"@tsconfig/bun": "1.0.9",
|
||||||
"@tsconfig/node22": "22.0.2",
|
"@tsconfig/node22": "22.0.2",
|
||||||
"@types/bun": "1.3.4",
|
"@types/bun": "1.3.5",
|
||||||
"@types/luxon": "3.7.1",
|
"@types/luxon": "3.7.1",
|
||||||
"@types/node": "22.13.9",
|
"@types/node": "22.13.9",
|
||||||
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
||||||
"ai": "5.0.97",
|
"ai": "5.0.119",
|
||||||
"diff": "8.0.2",
|
"diff": "8.0.2",
|
||||||
|
"dompurify": "3.3.1",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
"hono": "4.10.7",
|
"hono": "4.10.7",
|
||||||
"hono-openapi": "1.1.2",
|
"hono-openapi": "1.1.2",
|
||||||
@@ -541,48 +551,52 @@
|
|||||||
|
|
||||||
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.5.1", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-9bq2TgjhLBSUSC5jE04MEe+Hqw8YePzKghhYZ9QcjOyonY3q2oJfX6GoSO83hURpEnsqEPIrex6VZN3+61fBJg=="],
|
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.5.1", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-9bq2TgjhLBSUSC5jE04MEe+Hqw8YePzKghhYZ9QcjOyonY3q2oJfX6GoSO83hURpEnsqEPIrex6VZN3+61fBJg=="],
|
||||||
|
|
||||||
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@3.0.57", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.45", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-mOUSLe+RgZzx0rtL1p9QXmSd/08z1EkBR+vQ1ydpd1t5P0Nx2kB8afiukEgM8nuDvmO9eYQlp7VTy1n5ffPs2g=="],
|
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@3.0.73", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.57", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-EAAGJ/dfbAZaqIhK3w52hq6cftSLZwXdC6uHKh8Cls1T0N4MxS6ykDf54UyFO3bZWkQxR+Mdw1B3qireGOxtJQ=="],
|
||||||
|
|
||||||
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-uyyaO4KhxoIKZztREqLPh+6/K3ZJx/rp72JKoUEL9/kC+vfQTThUfPnY/bUryUpcnawx8IY/tSoYNOi/8PCv7w=="],
|
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-uyyaO4KhxoIKZztREqLPh+6/K3ZJx/rp72JKoUEL9/kC+vfQTThUfPnY/bUryUpcnawx8IY/tSoYNOi/8PCv7w=="],
|
||||||
|
|
||||||
"@ai-sdk/azure": ["@ai-sdk/azure@2.0.82", "", { "dependencies": { "@ai-sdk/openai": "2.0.80", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Bpab51ETBB4adZC1xGMYsryL/CB8j1sA+t5aDqhRv3t3WRLTxhaBDcFKtQTIuxiEQTFosz9Q2xQqdfBvQm5jHw=="],
|
"@ai-sdk/azure": ["@ai-sdk/azure@2.0.91", "", { "dependencies": { "@ai-sdk/openai": "2.0.89", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9tznVSs6LGQNKKxb8pKd7CkBV9yk+a/ENpFicHCj2CmBUKefxzwJ9JbUqrlK3VF6dGZw3LXq0dWxt7/Yekaj1w=="],
|
||||||
|
|
||||||
"@ai-sdk/cerebras": ["@ai-sdk/cerebras@1.0.33", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2gSSS/7kunIwMdC4td5oWsUAzoLw84ccGpz6wQbxVnrb1iWnrEnKa5tRBduaP6IXpzLWsu8wME3+dQhZy+gT7w=="],
|
"@ai-sdk/cerebras": ["@ai-sdk/cerebras@1.0.34", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XOK0dJsAGoPYi/lfR4KFBi8xhvJ46oCpAxUD6FmJAuJ4eh0qlj5zDt+myvzM8gvN7S6K7zHD+mdWlOPKGQT8Vg=="],
|
||||||
|
|
||||||
"@ai-sdk/cohere": ["@ai-sdk/cohere@2.0.21", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZjaZFvJlc5XOPi3QwTLEFZbHIgTJc6YGvxz+8zIMGVZi/hdynR8/f/C1A9x6mhzmBtAqi/dZ2h11oouAQH5z4g=="],
|
"@ai-sdk/cohere": ["@ai-sdk/cohere@2.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yJ9kP5cEDJwo8qpITq5TQFD8YNfNtW+HbyvWwrKMbFzmiMvIZuk95HIaFXE7PCTuZsqMA05yYu+qX/vQ3rNKjA=="],
|
||||||
|
|
||||||
"@ai-sdk/deepinfra": ["@ai-sdk/deepinfra@1.0.30", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XK8oRZFApzo6xnS5C+FhWUUkB2itA5Nfon3pU9dJVM0goViq8GwdleZTBRqhu4DE4KJURo5DGWpJr2hfV54cEg=="],
|
"@ai-sdk/deepinfra": ["@ai-sdk/deepinfra@1.0.31", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-87qFcYNvDF/89hB//MQjYTb3tlsAfmgeZrZ34RESeBTZpSgs0EzYOMqPMwFTHUNp4wteoifikDJbaS/9Da8cfw=="],
|
||||||
|
|
||||||
"@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-qmX7afPRszUqG5hryHF3UN8ITPIRSGmDW6VYCmByzjoUkgm3MekzSx2hMV1wr0P+llDeuXb378SjqUfpvWJulg=="],
|
"@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.25", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Rq+FX55ne7lMiqai7NcvvDZj4HLsr+hg77WayqmySqc6zhw3tIOLxd4Ty6OpwNj0C0bVMi3iCl2zvJIEirh9XA=="],
|
||||||
|
|
||||||
"@ai-sdk/google": ["@ai-sdk/google@2.0.49", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-efwKk4mOV0SpumUaQskeYABk37FJPmEYwoDJQEjyLRmGSjtHRe9P5Cwof5ffLvaFav2IaJpBGEz98pyTs7oNWA=="],
|
"@ai-sdk/google": ["@ai-sdk/google@2.0.52", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2XUnGi3f7TV4ujoAhA+Fg3idUoG/+Y2xjCRg70a1/m0DH1KSQqYaCboJ1C19y6ZHGdf5KNT20eJdswP6TvrY2g=="],
|
||||||
|
|
||||||
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.81", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.50", "@ai-sdk/google": "2.0.44", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", "google-auth-library": "^9.15.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yrl5Ug0Mqwo9ya45oxczgy2RWgpEA/XQQCSFYP+3NZMQ4yA3Iim1vkOjVCsGaZZ8rjVk395abi1ZMZV0/6rqVA=="],
|
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.97", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.57", "@ai-sdk/google": "2.0.52", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-s4tI7Z15i6FlbtCvS4SBRal8wRfkOXJzKxlS6cU4mJW/QfUfoVy4b22836NVNJwDvkG/HkDSfzwm/X8mn46MhA=="],
|
||||||
|
|
||||||
"@ai-sdk/groq": ["@ai-sdk/groq@2.0.33", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-FWGl7xNr88NBveao3y9EcVWYUt9ABPrwLFY7pIutSNgaTf32vgvyhREobaMrLU4Scr5G/2tlNqOPZ5wkYMaZig=="],
|
"@ai-sdk/groq": ["@ai-sdk/groq@2.0.34", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-wfCYkVgmVjxNA32T57KbLabVnv9aFUflJ4urJ7eWgTwbnmGQHElCTu+rJ3ydxkXSqxOkXPwMOttDm7XNrvPjmg=="],
|
||||||
|
|
||||||
"@ai-sdk/mistral": ["@ai-sdk/mistral@2.0.26", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-jxDB++4WI1wEx5ONNBI+VbkmYJOYIuS8UQY13/83UGRaiW7oB/WHiH4ETe6KzbKpQPB3XruwTJQjUMsMfKyTXA=="],
|
"@ai-sdk/mistral": ["@ai-sdk/mistral@2.0.27", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-gaptHgaXjMw3+eA0Q4FABcsj5nQNP6EpFaGUR+Pj5WJy7Kn6mApl975/x57224MfeJIShNpt8wFKK3tvh5ewKg=="],
|
||||||
|
|
||||||
"@ai-sdk/openai": ["@ai-sdk/openai@2.0.2", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-D4zYz2uR90aooKQvX1XnS00Z7PkbrcY+snUvPfm5bCabTG7bzLrVtD56nJ5bSaZG8lmuOMfXpyiEEArYLyWPpw=="],
|
"@ai-sdk/openai": ["@ai-sdk/openai@2.0.2", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-D4zYz2uR90aooKQvX1XnS00Z7PkbrcY+snUvPfm5bCabTG7bzLrVtD56nJ5bSaZG8lmuOMfXpyiEEArYLyWPpw=="],
|
||||||
|
|
||||||
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-luHVcU+yKzwv3ekKgbP3v+elUVxb2Rt+8c6w9qi7g2NYG2/pEL21oIrnaEnc6UtTZLLZX9EFBcpq2N1FQKDIMw=="],
|
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-luHVcU+yKzwv3ekKgbP3v+elUVxb2Rt+8c6w9qi7g2NYG2/pEL21oIrnaEnc6UtTZLLZX9EFBcpq2N1FQKDIMw=="],
|
||||||
|
|
||||||
"@ai-sdk/perplexity": ["@ai-sdk/perplexity@2.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-zwzcnk08R2J3mZcQPn4Ifl4wYGrvANR7jsBB0hCTUSbb+Rx3ybpikSWiGuXQXxdiRc1I5MWXgj70m+bZaLPvHw=="],
|
"@ai-sdk/perplexity": ["@ai-sdk/perplexity@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-aiaRvnc6mhQZKhTTSXPCjPH8Iqr5D/PfCN1hgVP/3RGTBbJtsd9HemIBSABeSdAKbsMH/PwJxgnqH75HEamcBA=="],
|
||||||
|
|
||||||
"@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
|
"@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="],
|
||||||
|
|
||||||
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="],
|
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],
|
||||||
|
|
||||||
"@ai-sdk/togetherai": ["@ai-sdk/togetherai@1.0.30", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9bxQbIXnWSN4bNismrza3NvIo+ui/Y3pj3UN6e9vCszCWFCN45RgISi4oDe10RqmzaJ/X8cfO/Tem+K8MT3wGQ=="],
|
"@ai-sdk/togetherai": ["@ai-sdk/togetherai@1.0.31", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RlYubjStoZQxna4Ng91Vvo8YskvL7lW9zj68IwZfCnaDBSAp1u6Nhc5BR4ZtKnY6PA3XEtu4bATIQl7yiiQ+Lw=="],
|
||||||
|
|
||||||
"@ai-sdk/vercel": ["@ai-sdk/vercel@1.0.31", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ggvwAMt/KsbqcdR6ILQrjwrRONLV/8aG6rOLbjcOGvV0Ai+WdZRRKQj5nOeQ06PvwVQtKdkp7S4IinpXIhCiHg=="],
|
"@ai-sdk/vercel": ["@ai-sdk/vercel@1.0.31", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ggvwAMt/KsbqcdR6ILQrjwrRONLV/8aG6rOLbjcOGvV0Ai+WdZRRKQj5nOeQ06PvwVQtKdkp7S4IinpXIhCiHg=="],
|
||||||
|
|
||||||
"@ai-sdk/xai": ["@ai-sdk/xai@2.0.42", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-wlwO4yRoZ/d+ca29vN8SDzxus7POdnL7GBTyRdSrt6icUF0hooLesauC8qRUC4aLxtqvMEc1YHtJOU7ZnLWbTQ=="],
|
"@ai-sdk/xai": ["@ai-sdk/xai@2.0.51", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-AI3le03qiegkZvn9hpnpDwez49lOvQLj4QUBT8H41SMbrdTYOxn3ktTwrsSu90cNDdzKGMvoH0u2GHju1EdnCg=="],
|
||||||
|
|
||||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||||
|
|
||||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||||
|
|
||||||
|
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.71.2", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ=="],
|
||||||
|
|
||||||
|
"@anycable/core": ["@anycable/core@0.9.2", "", { "dependencies": { "nanoevents": "^7.0.1" } }, "sha512-x5ZXDcW/N4cxWl93CnbHs/u7qq4793jS2kNPWm+duPrXlrva+ml2ZGT7X9tuOBKzyIHf60zWCdIK7TUgMPAwXA=="],
|
||||||
|
|
||||||
"@astrojs/cloudflare": ["@astrojs/cloudflare@12.6.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-xhJptF5tU2k5eo70nIMyL1Udma0CqmUEnGSlGyFflLqSY82CRQI6nWZ/xZt0ZvmXuErUjIx0YYQNfZsz5CNjLQ=="],
|
"@astrojs/cloudflare": ["@astrojs/cloudflare@12.6.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-xhJptF5tU2k5eo70nIMyL1Udma0CqmUEnGSlGyFflLqSY82CRQI6nWZ/xZt0ZvmXuErUjIx0YYQNfZsz5CNjLQ=="],
|
||||||
|
|
||||||
"@astrojs/compiler": ["@astrojs/compiler@2.13.0", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="],
|
"@astrojs/compiler": ["@astrojs/compiler@2.13.0", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="],
|
||||||
@@ -903,13 +917,17 @@
|
|||||||
|
|
||||||
"@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="],
|
"@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="],
|
||||||
|
|
||||||
|
"@gitlab/gitlab-ai-provider": ["@gitlab/gitlab-ai-provider@3.1.1", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=2.0.0", "@ai-sdk/provider-utils": ">=3.0.0" } }, "sha512-7AtFrCflq2NzC99bj7YaqbQDCZyaScM1+L4ujllV5syiRTFE239Uhnd/yEkPXa7sUAnNRfN3CWusCkQ2zK/q9g=="],
|
||||||
|
|
||||||
|
"@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
|
||||||
|
|
||||||
"@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="],
|
"@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="],
|
||||||
|
|
||||||
"@hey-api/codegen-core": ["@hey-api/codegen-core@0.3.3", "", { "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-vArVDtrvdzFewu1hnjUm4jX1NBITlSCeO81EdWq676MxQbyxsGcDPAgohaSA+Wvr4HjPSvsg2/1s2zYxUtXebg=="],
|
"@hey-api/codegen-core": ["@hey-api/codegen-core@0.5.2", "", { "dependencies": { "ansi-colors": "4.1.3", "color-support": "1.1.3" }, "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-88cqrrB2cLXN8nMOHidQTcVOnZsJ5kebEbBefjMCifaUCwTA30ouSSWvTZqrOX4O104zjJyu7M8Gcv/NNYQuaA=="],
|
||||||
|
|
||||||
"@hey-api/json-schema-ref-parser": ["@hey-api/json-schema-ref-parser@1.2.2", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.1", "lodash": "^4.17.21" } }, "sha512-oS+5yAdwnK20lSeFO1d53Ku+yaGCsY8PcrmSq2GtSs3bsBfRnHAbpPKSVzQcaxAOrzj5NB+f34WhZglVrNayBA=="],
|
"@hey-api/json-schema-ref-parser": ["@hey-api/json-schema-ref-parser@1.2.2", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.1", "lodash": "^4.17.21" } }, "sha512-oS+5yAdwnK20lSeFO1d53Ku+yaGCsY8PcrmSq2GtSs3bsBfRnHAbpPKSVzQcaxAOrzj5NB+f34WhZglVrNayBA=="],
|
||||||
|
|
||||||
"@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.88.1", "", { "dependencies": { "@hey-api/codegen-core": "^0.3.3", "@hey-api/json-schema-ref-parser": "1.2.2", "ansi-colors": "4.1.3", "c12": "3.3.2", "color-support": "1.1.3", "commander": "14.0.2", "open": "11.0.0", "semver": "7.7.2" }, "peerDependencies": { "typescript": ">=5.5.3" }, "bin": { "openapi-ts": "bin/run.js" } }, "sha512-x/nDTupOnV9VuSeNIiJpgIpc915GHduhyseJeMTnI0JMsXaObmpa0rgPr3ASVEYMLgpvqozIEG1RTOOnal6zLQ=="],
|
"@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.90.4", "", { "dependencies": { "@hey-api/codegen-core": "^0.5.2", "@hey-api/json-schema-ref-parser": "1.2.2", "ansi-colors": "4.1.3", "c12": "3.3.3", "color-support": "1.1.3", "commander": "14.0.2", "open": "11.0.0", "semver": "7.7.3" }, "peerDependencies": { "typescript": ">=5.5.3" }, "bin": { "openapi-ts": "bin/run.js" } }, "sha512-9l++kjcb0ui4JqPlueZ6OZ9zKn6eK/8//Z2jHcIXb5MRwDRgubOOSpTU5llEv3uvWfT10VzcMp99dySWq0AASw=="],
|
||||||
|
|
||||||
"@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="],
|
"@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="],
|
||||||
|
|
||||||
@@ -1201,21 +1219,21 @@
|
|||||||
|
|
||||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||||
|
|
||||||
"@opentui/core": ["@opentui/core@0.1.72", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.72", "@opentui/core-darwin-x64": "0.1.72", "@opentui/core-linux-arm64": "0.1.72", "@opentui/core-linux-x64": "0.1.72", "@opentui/core-win32-arm64": "0.1.72", "@opentui/core-win32-x64": "0.1.72", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-l4WQzubBJ80Q0n77Lxuodjwwm8qj/sOa7IXxEAzzDDXY/7bsIhdSpVhRTt+KevBRlok5J+w/KMKYr8UzkA4/hA=="],
|
"@opentui/core": ["@opentui/core@0.1.74", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.74", "@opentui/core-darwin-x64": "0.1.74", "@opentui/core-linux-arm64": "0.1.74", "@opentui/core-linux-x64": "0.1.74", "@opentui/core-win32-arm64": "0.1.74", "@opentui/core-win32-x64": "0.1.74", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-g4W16ymv12JdgZ+9B4t7mpIICvzWy2+eHERfmDf80ALduOQCUedKQdULcBFhVCYUXIkDRtIy6CID5thMAah3FA=="],
|
||||||
|
|
||||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.72", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RoU48kOrhLZYDBiXaDu1LXS2bwRdlJlFle8eUQiqJjLRbMIY34J/srBuL0JnAS3qKW4J34NepUQa0l0/S43Q3w=="],
|
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.74", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rfmlDLtm/u17CnuhJgCxPeYMvOST+A2MOdVOk46IurtHO849bdYqK6iudKNlFRs1FOrymgSKF9GlWBHAOKeRjg=="],
|
||||||
|
|
||||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.72", "", { "os": "darwin", "cpu": "x64" }, "sha512-hHUQw8i2LWPToRW1rjAiRqmNf34iJPS9ve9CJDygvFs5JOqUxN5yrfLfKfE+1bQjfFDHnpqW1HUk96iLhkPj8Q=="],
|
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.74", "", { "os": "darwin", "cpu": "x64" }, "sha512-WAD8orsDV0ZdW/5GwjOOB4FY96772xbkz+rcV7WRzEFUVaqoBaC04IuqYzS9d5s+cjkbT5Cpj47hrVYkkVQKng=="],
|
||||||
|
|
||||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.72", "", { "os": "linux", "cpu": "arm64" }, "sha512-63yml0OQ8tVa0JuDF9lBAWiChX6Q+iDO7lKv7c2n0352n/WyPr3iAgq4uSoH49HXuKeAXY/VwHGjvPzjXD/SDA=="],
|
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.74", "", { "os": "linux", "cpu": "arm64" }, "sha512-lgmHzrzLy4e+rgBS+lhtsMLLgIMLbtLNMm6EzVPyYVDlLDGjM7+ulXMem7AtpaRrWrUUl4REiG9BoQUsCFDwYA=="],
|
||||||
|
|
||||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.72", "", { "os": "linux", "cpu": "x64" }, "sha512-51veiQXNLvzDsFzsEvt71uK7WhiRe2DnvlJSGBSe6aRRHHxjCFYHzYi7t6bitJqtDTUj+EaMPbH81oZ6xy7tyg=="],
|
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.74", "", { "os": "linux", "cpu": "x64" }, "sha512-8Mn2WbdBQ29xCThuPZezjDhd1N3+fXwKkGvCBOdTI0le6h2A/vCNbfUVjwfr/EGZSRXxCG+Yapol34BAULGpOA=="],
|
||||||
|
|
||||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.72", "", { "os": "win32", "cpu": "arm64" }, "sha512-1Ep6OcaYTy1RlLOln+LNN7DL1iNyLwLjG2M8aO0pVJKFvxeD5P7rdRzY065E4uhkHeJIHuduUqxvUjD0dyuwbw=="],
|
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.74", "", { "os": "win32", "cpu": "arm64" }, "sha512-dvYUXz03avnI6ZluyLp00HPmR0UT/IE/6QS97XBsgJlUTtpnbKkBtB5jD1NHwWkElaRj1Qv2QP36ngFoJqbl9g=="],
|
||||||
|
|
||||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.72", "", { "os": "win32", "cpu": "x64" }, "sha512-5QUv91UkOINlkEaPky3kaxmJvshcJMBAX7LZtIroduaKBGpWRA1aogNhPZzp+30WkvgOU7aOtUktAZuFXb9WdQ=="],
|
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.74", "", { "os": "win32", "cpu": "x64" }, "sha512-3wfWXaAKOIlDQz6ZZIESf2M+YGZ7uFHijjTEM8w/STRlLw8Y6+QyGYi1myHSM4d6RSO+/s2EMDxvjDf899W9vQ=="],
|
||||||
|
|
||||||
"@opentui/solid": ["@opentui/solid@0.1.72", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.72", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-hytoLPboL/MTY/BQUnf/HlBuNXTVONney0X+PIQI82wT7kMx7+HHI2wnowpM3dyvA7l6NfORSud2cs9kIUBFBw=="],
|
"@opentui/solid": ["@opentui/solid@0.1.74", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.74", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-Vz82cI8T9YeJjGsVg4ULp6ral4N+xyt1j9A6Tbu3aaQgEKiB74LW03EXREehfjPr1irOFxtKfWPbx5NKH0Upag=="],
|
||||||
|
|
||||||
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
||||||
|
|
||||||
@@ -1507,7 +1525,7 @@
|
|||||||
|
|
||||||
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ=="],
|
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ=="],
|
||||||
|
|
||||||
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.5", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA=="],
|
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.7", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ=="],
|
||||||
|
|
||||||
"@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.5", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw=="],
|
"@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.5", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw=="],
|
||||||
|
|
||||||
@@ -1597,6 +1615,8 @@
|
|||||||
|
|
||||||
"@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
|
"@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
|
||||||
|
|
||||||
|
"@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
|
||||||
|
|
||||||
"@solid-primitives/active-element": ["@solid-primitives/active-element@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-9t5K4aR2naVDj950XU8OjnLgOg94a8k5wr6JNOPK+N5ESLsJDq42c1ZP8UKpewi1R+wplMMxiM6OPKRzbxJY7A=="],
|
"@solid-primitives/active-element": ["@solid-primitives/active-element@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-9t5K4aR2naVDj950XU8OjnLgOg94a8k5wr6JNOPK+N5ESLsJDq42c1ZP8UKpewi1R+wplMMxiM6OPKRzbxJY7A=="],
|
||||||
|
|
||||||
"@solid-primitives/audio": ["@solid-primitives/audio@1.4.2", "", { "dependencies": { "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-UMD3ORQfI5Ky8yuKPxidDiEazsjv/dsoiKK5yZxLnsgaeNR1Aym3/77h/qT1jBYeXUgj4DX6t7NMpFUSVr14OQ=="],
|
"@solid-primitives/audio": ["@solid-primitives/audio@1.4.2", "", { "dependencies": { "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-UMD3ORQfI5Ky8yuKPxidDiEazsjv/dsoiKK5yZxLnsgaeNR1Aym3/77h/qT1jBYeXUgj4DX6t7NMpFUSVr14OQ=="],
|
||||||
@@ -1649,6 +1669,8 @@
|
|||||||
|
|
||||||
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
||||||
|
|
||||||
|
"@stripe/stripe-js": ["@stripe/stripe-js@8.6.1", "", {}, "sha512-UJ05U2062XDgydbUcETH1AoRQLNhigQ2KmDn1BG8sC3xfzu6JKg95Qt6YozdzFpxl1Npii/02m2LEWFt1RYjVA=="],
|
||||||
|
|
||||||
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
|
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
|
"@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
|
||||||
@@ -1755,7 +1777,7 @@
|
|||||||
|
|
||||||
"@types/braces": ["@types/braces@3.0.5", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="],
|
"@types/braces": ["@types/braces@3.0.5", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="],
|
"@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
|
||||||
|
|
||||||
"@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
|
"@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
|
||||||
|
|
||||||
@@ -1827,6 +1849,8 @@
|
|||||||
|
|
||||||
"@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="],
|
"@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="],
|
||||||
|
|
||||||
|
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
||||||
|
|
||||||
"@types/tsscmp": ["@types/tsscmp@1.0.2", "", {}, "sha512-cy7BRSU8GYYgxjcx0Py+8lo5MthuDhlyu076KUcYzVNXL23luYgRHkMG2fIFEc6neckeh/ntP82mw+U4QjZq+g=="],
|
"@types/tsscmp": ["@types/tsscmp@1.0.2", "", {}, "sha512-cy7BRSU8GYYgxjcx0Py+8lo5MthuDhlyu076KUcYzVNXL23luYgRHkMG2fIFEc6neckeh/ntP82mw+U4QjZq+g=="],
|
||||||
|
|
||||||
"@types/tunnel": ["@types/tunnel@0.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA=="],
|
"@types/tunnel": ["@types/tunnel@0.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA=="],
|
||||||
@@ -1883,7 +1907,7 @@
|
|||||||
|
|
||||||
"@vitest/utils": ["@vitest/utils@4.0.16", "", { "dependencies": { "@vitest/pretty-format": "4.0.16", "tinyrainbow": "^3.0.3" } }, "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA=="],
|
"@vitest/utils": ["@vitest/utils@4.0.16", "", { "dependencies": { "@vitest/pretty-format": "4.0.16", "tinyrainbow": "^3.0.3" } }, "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA=="],
|
||||||
|
|
||||||
"@webgpu/types": ["@webgpu/types@0.1.66", "", {}, "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA=="],
|
"@webgpu/types": ["@webgpu/types@0.1.54", "", {}, "sha512-81oaalC8LFrXjhsczomEQ0u3jG+TqE6V9QHLA8GNZq/Rnot0KDugu3LhSYSlie8tSdooAN1Hov05asrUUp9qgg=="],
|
||||||
|
|
||||||
"@zip.js/zip.js": ["@zip.js/zip.js@2.7.62", "", {}, "sha512-OaLvZ8j4gCkLn048ypkZu29KX30r8/OfFF2w4Jo5WXFr+J04J+lzJ5TKZBVgFXhlvSkqNFQdfnY1Q8TMTCyBVA=="],
|
"@zip.js/zip.js": ["@zip.js/zip.js@2.7.62", "", {}, "sha512-OaLvZ8j4gCkLn048ypkZu29KX30r8/OfFF2w4Jo5WXFr+J04J+lzJ5TKZBVgFXhlvSkqNFQdfnY1Q8TMTCyBVA=="],
|
||||||
|
|
||||||
@@ -1903,7 +1927,7 @@
|
|||||||
|
|
||||||
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
|
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
|
||||||
|
|
||||||
"ai": ["ai@5.0.97", "", { "dependencies": { "@ai-sdk/gateway": "2.0.12", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8zBx0b/owis4eJI2tAlV8a1Rv0BANmLxontcAelkLNwEHhgfgXeKpDkhNB6OgV+BJSwboIUDkgd9312DdJnCOQ=="],
|
"ai": ["ai@5.0.119", "", { "dependencies": { "@ai-sdk/gateway": "2.0.25", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HUOwhc17fl2SZTJGZyA/99aNu706qKfXaUBCy9vgZiXBwrxg2eTzn2BCz7kmYDsfx6Fg2ACBy2icm41bsDXCTw=="],
|
||||||
|
|
||||||
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||||
|
|
||||||
@@ -2055,7 +2079,7 @@
|
|||||||
|
|
||||||
"bun-pty": ["bun-pty@0.4.4", "", {}, "sha512-WK4G6uWsZgu1v4hKIlw6G1q2AOf8Rbga2Yr7RnxArVjjyb+mtVa/CFc9GOJf+OYSJSH8k7LonAtQOVeNAddRyg=="],
|
"bun-pty": ["bun-pty@0.4.4", "", {}, "sha512-WK4G6uWsZgu1v4hKIlw6G1q2AOf8Rbga2Yr7RnxArVjjyb+mtVa/CFc9GOJf+OYSJSH8k7LonAtQOVeNAddRyg=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="],
|
"bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
|
||||||
|
|
||||||
"bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="],
|
"bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="],
|
||||||
|
|
||||||
@@ -2071,7 +2095,7 @@
|
|||||||
|
|
||||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||||
|
|
||||||
"c12": ["c12@3.3.2", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.8", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-QkikB2X5voO1okL3QsES0N690Sn/K9WokXqUsDQsWy5SnYb+psYQFGA10iy1bZHj3fjISKsI67Q90gruvWWM3A=="],
|
"c12": ["c12@3.3.3", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.8", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q=="],
|
||||||
|
|
||||||
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
||||||
|
|
||||||
@@ -2205,6 +2229,8 @@
|
|||||||
|
|
||||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||||
|
|
||||||
|
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||||
|
|
||||||
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
||||||
|
|
||||||
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
||||||
@@ -2277,6 +2303,8 @@
|
|||||||
|
|
||||||
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||||
|
|
||||||
|
"dompurify": ["dompurify@3.3.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q=="],
|
||||||
|
|
||||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||||
|
|
||||||
"dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="],
|
"dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="],
|
||||||
@@ -2309,6 +2337,10 @@
|
|||||||
|
|
||||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||||
|
|
||||||
|
"engine.io-client": ["engine.io-client@6.6.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw=="],
|
||||||
|
|
||||||
|
"engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
|
||||||
|
|
||||||
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
||||||
|
|
||||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||||
@@ -2419,6 +2451,8 @@
|
|||||||
|
|
||||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
|
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||||
|
|
||||||
"file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="],
|
"file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="],
|
||||||
|
|
||||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||||
@@ -2449,6 +2483,8 @@
|
|||||||
|
|
||||||
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
|
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
|
||||||
|
|
||||||
|
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||||
|
|
||||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||||
|
|
||||||
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
||||||
@@ -2471,9 +2507,9 @@
|
|||||||
|
|
||||||
"fuzzysort": ["fuzzysort@3.1.0", "", {}, "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ=="],
|
"fuzzysort": ["fuzzysort@3.1.0", "", {}, "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ=="],
|
||||||
|
|
||||||
"gaxios": ["gaxios@6.7.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
|
"gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
|
||||||
|
|
||||||
"gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="],
|
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
||||||
|
|
||||||
"gel": ["gel@2.2.0", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ=="],
|
"gel": ["gel@2.2.0", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ=="],
|
||||||
|
|
||||||
@@ -2519,17 +2555,21 @@
|
|||||||
|
|
||||||
"globby": ["globby@11.0.4", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.1.1", "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" } }, "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg=="],
|
"globby": ["globby@11.0.4", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.1.1", "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" } }, "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg=="],
|
||||||
|
|
||||||
"google-auth-library": ["google-auth-library@9.15.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^6.1.1", "gcp-metadata": "^6.1.0", "gtoken": "^7.0.0", "jws": "^4.0.0" } }, "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng=="],
|
"google-auth-library": ["google-auth-library@10.5.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="],
|
||||||
|
|
||||||
"google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
|
"google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
|
||||||
|
|
||||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||||
|
|
||||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||||
|
|
||||||
|
"graphql": ["graphql@16.12.0", "", {}, "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ=="],
|
||||||
|
|
||||||
|
"graphql-request": ["graphql-request@6.1.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0", "cross-fetch": "^3.1.5" }, "peerDependencies": { "graphql": "14 - 16" } }, "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw=="],
|
||||||
|
|
||||||
"gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
|
"gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
|
||||||
|
|
||||||
"gtoken": ["gtoken@7.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw=="],
|
"gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
|
||||||
|
|
||||||
"h3": ["h3@2.0.1-rc.4", "", { "dependencies": { "rou3": "^0.7.8", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-vZq8pEUp6THsXKXrUXX44eOqfChic2wVQ1GlSzQCBr7DeFBkfIZAo2WyNND4GSv54TAa0E4LYIK73WSPdgKUgw=="],
|
"h3": ["h3@2.0.1-rc.4", "", { "dependencies": { "rou3": "^0.7.8", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-vZq8pEUp6THsXKXrUXX44eOqfChic2wVQ1GlSzQCBr7DeFBkfIZAo2WyNND4GSv54TAa0E4LYIK73WSPdgKUgw=="],
|
||||||
|
|
||||||
@@ -2755,6 +2795,8 @@
|
|||||||
|
|
||||||
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||||
|
|
||||||
|
"isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="],
|
||||||
|
|
||||||
"iterate-iterator": ["iterate-iterator@1.0.2", "", {}, "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw=="],
|
"iterate-iterator": ["iterate-iterator@1.0.2", "", {}, "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw=="],
|
||||||
|
|
||||||
"iterate-value": ["iterate-value@1.0.2", "", { "dependencies": { "es-get-iterator": "^1.0.2", "iterate-iterator": "^1.0.1" } }, "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ=="],
|
"iterate-value": ["iterate-value@1.0.2", "", { "dependencies": { "es-get-iterator": "^1.0.2", "iterate-iterator": "^1.0.1" } }, "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ=="],
|
||||||
@@ -2787,6 +2829,8 @@
|
|||||||
|
|
||||||
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
|
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
|
||||||
|
|
||||||
|
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
|
||||||
|
|
||||||
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||||
|
|
||||||
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
||||||
@@ -3063,6 +3107,8 @@
|
|||||||
|
|
||||||
"named-placeholders": ["named-placeholders@1.1.3", "", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="],
|
"named-placeholders": ["named-placeholders@1.1.3", "", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="],
|
||||||
|
|
||||||
|
"nanoevents": ["nanoevents@7.0.1", "", {}, "sha512-o6lpKiCxLeijK4hgsqfR6CNToPyRU3keKyyI6uwuHRvpRTbZ0wXw51WRgyldVugZqoJfkGFrjrIenYH3bfEO3Q=="],
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||||
@@ -3417,6 +3463,8 @@
|
|||||||
|
|
||||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||||
|
|
||||||
|
"rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="],
|
"rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="],
|
||||||
|
|
||||||
"rou3": ["rou3@0.7.10", "", {}, "sha512-aoFj6f7MJZ5muJ+Of79nrhs9N3oLGqi2VEMe94Zbkjb6Wupha46EuoYgpWSOZlXww3bbd8ojgXTAA2mzimX5Ww=="],
|
"rou3": ["rou3@0.7.10", "", {}, "sha512-aoFj6f7MJZ5muJ+Of79nrhs9N3oLGqi2VEMe94Zbkjb6Wupha46EuoYgpWSOZlXww3bbd8ojgXTAA2mzimX5Ww=="],
|
||||||
@@ -3447,7 +3495,7 @@
|
|||||||
|
|
||||||
"selderee": ["selderee@0.11.0", "", { "dependencies": { "parseley": "^0.12.0" } }, "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA=="],
|
"selderee": ["selderee@0.11.0", "", { "dependencies": { "parseley": "^0.12.0" } }, "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA=="],
|
||||||
|
|
||||||
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
|
|
||||||
"send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
|
"send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
|
||||||
|
|
||||||
@@ -3503,6 +3551,10 @@
|
|||||||
|
|
||||||
"smol-toml": ["smol-toml@1.5.2", "", {}, "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ=="],
|
"smol-toml": ["smol-toml@1.5.2", "", {}, "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ=="],
|
||||||
|
|
||||||
|
"socket.io-client": ["socket.io-client@4.8.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="],
|
||||||
|
|
||||||
|
"socket.io-parser": ["socket.io-parser@4.2.5", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ=="],
|
||||||
|
|
||||||
"solid-js": ["solid-js@1.9.10", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew=="],
|
"solid-js": ["solid-js@1.9.10", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew=="],
|
||||||
|
|
||||||
"solid-list": ["solid-list@0.3.0", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-t4hx/F/l8Vmq+ib9HtZYl7Z9F1eKxq3eKJTXlvcm7P7yI4Z8O7QSOOEVHb/K6DD7M0RxzVRobK/BS5aSfLRwKg=="],
|
"solid-list": ["solid-list@0.3.0", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-t4hx/F/l8Vmq+ib9HtZYl7Z9F1eKxq3eKJTXlvcm7P7yI4Z8O7QSOOEVHb/K6DD7M0RxzVRobK/BS5aSfLRwKg=="],
|
||||||
@@ -3513,6 +3565,8 @@
|
|||||||
|
|
||||||
"solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="],
|
"solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="],
|
||||||
|
|
||||||
|
"solid-stripe": ["solid-stripe@0.8.1", "", { "peerDependencies": { "@stripe/stripe-js": ">=1.44.1 <8.0.0", "solid-js": "^1.6.0" } }, "sha512-l2SkWoe51rsvk9u1ILBRWyCHODZebChSGMR6zHYJTivTRC0XWrRnNNKs5x1PYXsaIU71KYI6ov5CZB5cOtGLWw=="],
|
||||||
|
|
||||||
"solid-use": ["solid-use@0.9.1", "", { "peerDependencies": { "solid-js": "^1.7" } }, "sha512-UwvXDVPlrrbj/9ewG9ys5uL2IO4jSiwys2KPzK4zsnAcmEl7iDafZWW1Mo4BSEWOmQCGK6IvpmGHo1aou8iOFw=="],
|
"solid-use": ["solid-use@0.9.1", "", { "peerDependencies": { "solid-js": "^1.7" } }, "sha512-UwvXDVPlrrbj/9ewG9ys5uL2IO4jSiwys2KPzK4zsnAcmEl7iDafZWW1Mo4BSEWOmQCGK6IvpmGHo1aou8iOFw=="],
|
||||||
|
|
||||||
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||||
@@ -3667,6 +3721,8 @@
|
|||||||
|
|
||||||
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
||||||
|
|
||||||
|
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
|
||||||
|
|
||||||
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
|
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
|
||||||
|
|
||||||
"tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
"tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
||||||
@@ -3859,6 +3915,8 @@
|
|||||||
|
|
||||||
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
|
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
|
||||||
|
|
||||||
|
"xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="],
|
||||||
|
|
||||||
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
|
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
|
||||||
|
|
||||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||||
@@ -3909,35 +3967,35 @@
|
|||||||
|
|
||||||
"@agentclientprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"@agentclientprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
"@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.45", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Ipv62vavDCmrV/oE/lXehL9FzwQuZOnnlhPEftWizx464Wb6lvnBTJx8uhmEYruFSzOWTI95Z33ncZ4tA8E6RQ=="],
|
"@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.57", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DREpYqW2pylgaj69gZ+K8u92bo9DaMgFdictYnY+IwYeY3bawQ4zI7l/o1VkDsBDljAx8iYz5lPURwVZNu+Xpg=="],
|
||||||
|
|
||||||
"@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
"@ai-sdk/amazon-bedrock/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.5", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA=="],
|
||||||
|
|
||||||
|
"@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
|
||||||
|
|
||||||
"@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
"@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
||||||
|
|
||||||
"@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@2.0.80", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tNHuraF11db+8xJEDBoU9E3vMcpnHFKRhnLQ3DQX2LnEzfPB9DksZ8rE+yVuDN1WRW9cm2OWAhgHFgVKs7ICuw=="],
|
"@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@2.0.89", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4+qWkBCbL9HPKbgrUO/F2uXZ8GqrYxHa8SWEYIzxEJ9zvWw3ISr3t1/27O1i8MGSym+PzEyHBT48EV4LAwWaEw=="],
|
||||||
|
|
||||||
"@ai-sdk/cerebras/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="],
|
"@ai-sdk/cerebras/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@ai-sdk/deepinfra/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="],
|
"@ai-sdk/deepinfra/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="],
|
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.57", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DREpYqW2pylgaj69gZ+K8u92bo9DaMgFdictYnY+IwYeY3bawQ4zI7l/o1VkDsBDljAx8iYz5lPURwVZNu+Xpg=="],
|
||||||
|
|
||||||
"@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="],
|
"@ai-sdk/openai/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
|
||||||
|
|
||||||
"@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
"@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
||||||
|
|
||||||
|
"@ai-sdk/openai-compatible/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
|
||||||
|
|
||||||
"@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
"@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="],
|
||||||
|
|
||||||
"@ai-sdk/togetherai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="],
|
"@ai-sdk/togetherai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@ai-sdk/vercel/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
"@ai-sdk/vercel/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@ai-sdk/vercel/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="],
|
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"@ai-sdk/vercel/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],
|
|
||||||
|
|
||||||
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="],
|
|
||||||
|
|
||||||
"@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
"@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||||
|
|
||||||
@@ -4011,6 +4069,8 @@
|
|||||||
|
|
||||||
"@expressive-code/plugin-shiki/shiki": ["shiki@3.15.0", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="],
|
"@expressive-code/plugin-shiki/shiki": ["shiki@3.15.0", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="],
|
||||||
|
|
||||||
|
"@gitlab/gitlab-ai-provider/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
"@hey-api/json-schema-ref-parser/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
"@hey-api/json-schema-ref-parser/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||||
|
|
||||||
"@hey-api/openapi-ts/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
|
"@hey-api/openapi-ts/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
|
||||||
@@ -4181,6 +4241,10 @@
|
|||||||
|
|
||||||
"@slack/web-api/p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="],
|
"@slack/web-api/p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="],
|
||||||
|
|
||||||
|
"@smithy/eventstream-codec/@smithy/types": ["@smithy/types@4.11.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA=="],
|
||||||
|
|
||||||
|
"@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.5", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA=="],
|
||||||
|
|
||||||
"@solidjs/start/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
"@solidjs/start/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
||||||
|
|
||||||
"@solidjs/start/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
"@solidjs/start/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
||||||
@@ -4205,10 +4269,6 @@
|
|||||||
|
|
||||||
"accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
"accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
"ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="],
|
|
||||||
|
|
||||||
"ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
|
||||||
|
|
||||||
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
@@ -4221,6 +4281,8 @@
|
|||||||
|
|
||||||
"astro/diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="],
|
"astro/diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="],
|
||||||
|
|
||||||
|
"astro/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
|
|
||||||
"astro/shiki": ["shiki@3.15.0", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="],
|
"astro/shiki": ["shiki@3.15.0", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="],
|
||||||
|
|
||||||
"astro/unstorage": ["unstorage@1.17.3", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", "h3": "^1.15.4", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.1" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3 || ^7.0.0", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q=="],
|
"astro/unstorage": ["unstorage@1.17.3", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", "h3": "^1.15.4", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.1" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3 || ^7.0.0", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q=="],
|
||||||
@@ -4241,6 +4303,10 @@
|
|||||||
|
|
||||||
"body-parser/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
"body-parser/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
||||||
|
|
||||||
|
"bun-webgpu/@webgpu/types": ["@webgpu/types@0.1.66", "", {}, "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA=="],
|
||||||
|
|
||||||
|
"c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||||
|
|
||||||
"clean-css/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"clean-css/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
"compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
"compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||||
@@ -4257,6 +4323,10 @@
|
|||||||
|
|
||||||
"editorconfig/minimatch": ["minimatch@9.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w=="],
|
"editorconfig/minimatch": ["minimatch@9.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w=="],
|
||||||
|
|
||||||
|
"editorconfig/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
|
|
||||||
|
"engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||||
|
|
||||||
"es-get-iterator/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
"es-get-iterator/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
|
|
||||||
"esbuild-plugin-copy/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"esbuild-plugin-copy/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
@@ -4273,13 +4343,15 @@
|
|||||||
|
|
||||||
"express/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
"express/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
||||||
|
|
||||||
|
"fetch-blob/web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
||||||
|
|
||||||
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
"gaxios/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
"gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||||
|
|
||||||
"gaxios/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
"gel/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
|
|
||||||
"glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
"glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
||||||
|
|
||||||
@@ -4295,6 +4367,8 @@
|
|||||||
|
|
||||||
"jsonwebtoken/jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="],
|
"jsonwebtoken/jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="],
|
||||||
|
|
||||||
|
"jsonwebtoken/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
|
|
||||||
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
|
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
|
||||||
|
|
||||||
"lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
"lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||||
@@ -4321,11 +4395,11 @@
|
|||||||
|
|
||||||
"nypm/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
"nypm/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||||
|
|
||||||
"opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.56", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XHJKu0Yvfu9SPzRfsAFESa+9T7f2YJY6TxykKMfRsAwpeWAiX/Gbx5J5uM15AzYC3Rw8tVP3oH+j7jEivENirQ=="],
|
"opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.57", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DREpYqW2pylgaj69gZ+K8u92bo9DaMgFdictYnY+IwYeY3bawQ4zI7l/o1VkDsBDljAx8iYz5lPURwVZNu+Xpg=="],
|
||||||
|
|
||||||
"opencode/@ai-sdk/openai": ["@ai-sdk/openai@2.0.71", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tg+gj+R0z/On9P4V7hy7/7o04cQPjKGayMCL3gzWD/aNGjAKkhEnaocuNDidSnghizt8g2zJn16cAuAolnW+qQ=="],
|
"opencode/@ai-sdk/openai": ["@ai-sdk/openai@2.0.89", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4+qWkBCbL9HPKbgrUO/F2uXZ8GqrYxHa8SWEYIzxEJ9zvWw3ISr3t1/27O1i8MGSym+PzEyHBT48EV4LAwWaEw=="],
|
||||||
|
|
||||||
"opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="],
|
"opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
|
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
|
||||||
|
|
||||||
@@ -4363,6 +4437,8 @@
|
|||||||
|
|
||||||
"readdir-glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
"readdir-glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
||||||
|
|
||||||
|
"rimraf/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
|
||||||
|
|
||||||
"router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
"router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
||||||
|
|
||||||
"safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
"safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
@@ -4377,6 +4453,8 @@
|
|||||||
|
|
||||||
"sharp/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
"sharp/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
|
|
||||||
|
"sharp/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
|
|
||||||
"shiki/@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="],
|
"shiki/@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="],
|
||||||
|
|
||||||
"shiki/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
|
"shiki/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
|
||||||
@@ -4855,6 +4933,8 @@
|
|||||||
|
|
||||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
|
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||||
|
|
||||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
"drizzle-kit/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
||||||
@@ -4925,8 +5005,6 @@
|
|||||||
|
|
||||||
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||||
|
|
||||||
"opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
|
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk/express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
|
"opencontrol/@modelcontextprotocol/sdk/express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
|
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
|
||||||
@@ -4943,6 +5021,12 @@
|
|||||||
|
|
||||||
"readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
|
"rimraf/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||||
|
|
||||||
|
"rimraf/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
|
"rimraf/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||||
|
|
||||||
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
@@ -5125,6 +5209,8 @@
|
|||||||
|
|
||||||
"pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
|
"pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
|
||||||
|
|
||||||
|
"rimraf/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
|
||||||
"tw-to-css/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"tw-to-css/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"tw-to-css/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
"tw-to-css/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||||
|
|||||||
Generated
+3
-3
@@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767364772,
|
"lastModified": 1768456270,
|
||||||
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
|
"narHash": "sha256-NgaL2CCiUR6nsqUIY4yxkzz07iQUlUCany44CFv+OxY=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
|
"rev": "f4606b01b39e09065df37905a2133905246db9ed",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
{
|
{
|
||||||
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
@@ -27,11 +28,28 @@
|
|||||||
"aarch64-darwin" = "bun-darwin-arm64";
|
"aarch64-darwin" = "bun-darwin-arm64";
|
||||||
"x86_64-darwin" = "bun-darwin-x64";
|
"x86_64-darwin" = "bun-darwin-x64";
|
||||||
};
|
};
|
||||||
defaultNodeModules = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
|
||||||
|
# Parse "bun-{os}-{cpu}" to {os, cpu}
|
||||||
|
parseBunTarget =
|
||||||
|
target:
|
||||||
|
let
|
||||||
|
parts = lib.splitString "-" target;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
os = builtins.elemAt parts 1;
|
||||||
|
cpu = builtins.elemAt parts 2;
|
||||||
|
};
|
||||||
|
|
||||||
hashesFile = "${./nix}/hashes.json";
|
hashesFile = "${./nix}/hashes.json";
|
||||||
hashesData =
|
hashesData =
|
||||||
if builtins.pathExists hashesFile then builtins.fromJSON (builtins.readFile hashesFile) else { };
|
if builtins.pathExists hashesFile then builtins.fromJSON (builtins.readFile hashesFile) else { };
|
||||||
nodeModulesHash = hashesData.nodeModules or defaultNodeModules;
|
# Lookup hash: supports per-system ({system: hash}) or legacy single hash
|
||||||
|
nodeModulesHashFor =
|
||||||
|
system:
|
||||||
|
if builtins.isAttrs hashesData.nodeModules then
|
||||||
|
hashesData.nodeModules.${system}
|
||||||
|
else
|
||||||
|
hashesData.nodeModules;
|
||||||
modelsDev = forEachSystem (
|
modelsDev = forEachSystem (
|
||||||
system:
|
system:
|
||||||
let
|
let
|
||||||
@@ -63,8 +81,11 @@
|
|||||||
system:
|
system:
|
||||||
let
|
let
|
||||||
pkgs = pkgsFor system;
|
pkgs = pkgsFor system;
|
||||||
|
bunPlatform = parseBunTarget bunTarget.${system};
|
||||||
mkNodeModules = pkgs.callPackage ./nix/node-modules.nix {
|
mkNodeModules = pkgs.callPackage ./nix/node-modules.nix {
|
||||||
hash = nodeModulesHash;
|
hash = nodeModulesHashFor system;
|
||||||
|
bunCpu = bunPlatform.cpu;
|
||||||
|
bunOs = bunPlatform.os;
|
||||||
};
|
};
|
||||||
mkOpencode = pkgs.callPackage ./nix/opencode.nix { };
|
mkOpencode = pkgs.callPackage ./nix/opencode.nix { };
|
||||||
mkDesktop = pkgs.callPackage ./nix/desktop.nix { };
|
mkDesktop = pkgs.callPackage ./nix/desktop.nix { };
|
||||||
@@ -87,33 +108,10 @@
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
default = opencodePkg;
|
default = self.packages.${system}.opencode;
|
||||||
|
opencode = opencodePkg;
|
||||||
desktop = desktopPkg;
|
desktop = desktopPkg;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
apps = forEachSystem (
|
|
||||||
system:
|
|
||||||
let
|
|
||||||
pkgs = pkgsFor system;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
opencode-dev = {
|
|
||||||
type = "app";
|
|
||||||
meta = {
|
|
||||||
description = "Nix devshell shell for OpenCode";
|
|
||||||
runtimeInputs = [ pkgs.bun ];
|
|
||||||
};
|
|
||||||
program = "${
|
|
||||||
pkgs.writeShellApplication {
|
|
||||||
name = "opencode-dev";
|
|
||||||
text = ''
|
|
||||||
exec bun run dev "$@"
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
}/bin/opencode-dev";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -81,17 +81,20 @@ This will walk you through installing the GitHub app, creating the workflow, and
|
|||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Run opencode
|
- name: Run opencode
|
||||||
uses: anomalyco/opencode/github@latest
|
uses: anomalyco/opencode/github@latest
|
||||||
env:
|
env:
|
||||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
model: anthropic/claude-sonnet-4-20250514
|
model: anthropic/claude-sonnet-4-20250514
|
||||||
|
use_github_token: true
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Store the API keys in secrets. In your organization or project **settings**, expand **Secrets and variables** on the left and select **Actions**. Add the required API keys.
|
3. Store the API keys in secrets. In your organization or project **settings**, expand **Secrets and variables** on the left and select **Actions**. Add the required API keys.
|
||||||
|
|||||||
@@ -119,9 +119,11 @@ const ZEN_MODELS = [
|
|||||||
new sst.Secret("ZEN_MODELS5"),
|
new sst.Secret("ZEN_MODELS5"),
|
||||||
new sst.Secret("ZEN_MODELS6"),
|
new sst.Secret("ZEN_MODELS6"),
|
||||||
new sst.Secret("ZEN_MODELS7"),
|
new sst.Secret("ZEN_MODELS7"),
|
||||||
|
new sst.Secret("ZEN_MODELS8"),
|
||||||
]
|
]
|
||||||
const ZEN_BLACK = new sst.Secret("ZEN_BLACK")
|
const ZEN_BLACK = new sst.Secret("ZEN_BLACK")
|
||||||
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
|
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
|
||||||
|
const STRIPE_PUBLISHABLE_KEY = new sst.Secret("STRIPE_PUBLISHABLE_KEY")
|
||||||
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
||||||
properties: { value: auth.url.apply((url) => url!) },
|
properties: { value: auth.url.apply((url) => url!) },
|
||||||
})
|
})
|
||||||
@@ -163,6 +165,7 @@ new sst.cloudflare.x.SolidStart("Console", {
|
|||||||
AWS_SES_ACCESS_KEY_ID,
|
AWS_SES_ACCESS_KEY_ID,
|
||||||
AWS_SES_SECRET_ACCESS_KEY,
|
AWS_SES_SECRET_ACCESS_KEY,
|
||||||
ZEN_BLACK,
|
ZEN_BLACK,
|
||||||
|
new sst.Secret("ZEN_SESSION_SECRET"),
|
||||||
...ZEN_MODELS,
|
...ZEN_MODELS,
|
||||||
...($dev
|
...($dev
|
||||||
? [
|
? [
|
||||||
@@ -176,6 +179,7 @@ new sst.cloudflare.x.SolidStart("Console", {
|
|||||||
//VITE_DOCS_URL: web.url.apply((url) => url!),
|
//VITE_DOCS_URL: web.url.apply((url) => url!),
|
||||||
//VITE_API_URL: gateway.url.apply((url) => url!),
|
//VITE_API_URL: gateway.url.apply((url) => url!),
|
||||||
VITE_AUTH_URL: auth.url.apply((url) => url!),
|
VITE_AUTH_URL: auth.url.apply((url) => url!),
|
||||||
|
VITE_STRIPE_PUBLISHABLE_KEY: STRIPE_PUBLISHABLE_KEY.value,
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
server: {
|
server: {
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ case $current_shell in
|
|||||||
config_files="$HOME/.config/fish/config.fish"
|
config_files="$HOME/.config/fish/config.fish"
|
||||||
;;
|
;;
|
||||||
zsh)
|
zsh)
|
||||||
config_files="$HOME/.zshrc $HOME/.zshenv $XDG_CONFIG_HOME/zsh/.zshrc $XDG_CONFIG_HOME/zsh/.zshenv"
|
config_files="${ZDOTDIR:-$HOME}/.zshrc ${ZDOTDIR:-$HOME}/.zshenv $XDG_CONFIG_HOME/zsh/.zshrc $XDG_CONFIG_HOME/zsh/.zshenv"
|
||||||
;;
|
;;
|
||||||
bash)
|
bash)
|
||||||
config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
|
config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
cargo,
|
cargo,
|
||||||
rustc,
|
rustc,
|
||||||
makeBinaryWrapper,
|
makeBinaryWrapper,
|
||||||
|
copyDesktopItems,
|
||||||
|
makeDesktopItem,
|
||||||
nodejs,
|
nodejs,
|
||||||
jq,
|
jq,
|
||||||
}:
|
}:
|
||||||
@@ -57,12 +59,28 @@ rustPlatform.buildRustPackage rec {
|
|||||||
pkg-config
|
pkg-config
|
||||||
bun
|
bun
|
||||||
makeBinaryWrapper
|
makeBinaryWrapper
|
||||||
|
copyDesktopItems
|
||||||
cargo
|
cargo
|
||||||
rustc
|
rustc
|
||||||
nodejs
|
nodejs
|
||||||
jq
|
jq
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# based on packages/desktop/src-tauri/release/appstream.metainfo.xml
|
||||||
|
desktopItems = lib.optionals stdenv.isLinux [
|
||||||
|
(makeDesktopItem {
|
||||||
|
name = "ai.opencode.opencode";
|
||||||
|
desktopName = "OpenCode";
|
||||||
|
comment = "Open source AI coding agent";
|
||||||
|
exec = "opencode-desktop";
|
||||||
|
icon = "opencode";
|
||||||
|
terminal = false;
|
||||||
|
type = "Application";
|
||||||
|
categories = [ "Development" "IDE" ];
|
||||||
|
startupWMClass = "opencode";
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
openssl
|
openssl
|
||||||
]
|
]
|
||||||
@@ -121,6 +139,10 @@ rustPlatform.buildRustPackage rec {
|
|||||||
# It looks for them in the location specified in tauri.conf.json.
|
# It looks for them in the location specified in tauri.conf.json.
|
||||||
|
|
||||||
postInstall = lib.optionalString stdenv.isLinux ''
|
postInstall = lib.optionalString stdenv.isLinux ''
|
||||||
|
# Install icon
|
||||||
|
mkdir -p $out/share/icons/hicolor/128x128/apps
|
||||||
|
cp ../../../packages/desktop/src-tauri/icons/prod/128x128.png $out/share/icons/hicolor/128x128/apps/opencode.png
|
||||||
|
|
||||||
# Wrap the binary to ensure it finds the libraries
|
# Wrap the binary to ensure it finds the libraries
|
||||||
wrapProgram $out/bin/opencode-desktop \
|
wrapProgram $out/bin/opencode-desktop \
|
||||||
--prefix LD_LIBRARY_PATH : ${
|
--prefix LD_LIBRARY_PATH : ${
|
||||||
|
|||||||
+6
-1
@@ -1,3 +1,8 @@
|
|||||||
{
|
{
|
||||||
"nodeModules": "sha256-+QM5BDFxzrm1HY5ealjCm7jIO1t/rpW1q4GGLViPMmA="
|
"nodeModules": {
|
||||||
|
"x86_64-linux": "sha256-4zchRpxzvHnPMcwumgL9yaX0deIXS5IGPp131eYsSvg=",
|
||||||
|
"aarch64-linux": "sha256-3/BSRsl5pI0Iz3qAFZxIkOehFLZ2Ox9UsbdDHYzqlVg=",
|
||||||
|
"aarch64-darwin": "sha256-86d/G1q6xiHSSlm+/irXoKLb/yLQbV348uuSrBV70+Q=",
|
||||||
|
"x86_64-darwin": "sha256-WYaP44PWRGtoG1DIuUJUH4DvuaCuFhlJZ9fPzGsiIfE="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
bun,
|
bun,
|
||||||
cacert,
|
cacert,
|
||||||
curl,
|
curl,
|
||||||
|
bunCpu,
|
||||||
|
bunOs,
|
||||||
}:
|
}:
|
||||||
args:
|
args:
|
||||||
stdenvNoCC.mkDerivation {
|
stdenvNoCC.mkDerivation {
|
||||||
@@ -29,8 +31,8 @@ stdenvNoCC.mkDerivation {
|
|||||||
export HOME=$(mktemp -d)
|
export HOME=$(mktemp -d)
|
||||||
export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
|
export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
|
||||||
bun install \
|
bun install \
|
||||||
--cpu="*" \
|
--cpu="${bunCpu}" \
|
||||||
--os="*" \
|
--os="${bunOs}" \
|
||||||
--frozen-lockfile \
|
--frozen-lockfile \
|
||||||
--ignore-scripts \
|
--ignore-scripts \
|
||||||
--no-progress \
|
--no-progress \
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
DUMMY="sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
|
||||||
SYSTEM=${SYSTEM:-x86_64-linux}
|
|
||||||
DEFAULT_HASH_FILE=${MODULES_HASH_FILE:-nix/hashes.json}
|
|
||||||
HASH_FILE=${HASH_FILE:-$DEFAULT_HASH_FILE}
|
|
||||||
|
|
||||||
if [ ! -f "$HASH_FILE" ]; then
|
|
||||||
cat >"$HASH_FILE" <<EOF
|
|
||||||
{
|
|
||||||
"nodeModules": "$DUMMY"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
||||||
if ! git ls-files --error-unmatch "$HASH_FILE" >/dev/null 2>&1; then
|
|
||||||
git add -N "$HASH_FILE" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
export DUMMY
|
|
||||||
export NIX_KEEP_OUTPUTS=1
|
|
||||||
export NIX_KEEP_DERIVATIONS=1
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
rm -f "${JSON_OUTPUT:-}" "${BUILD_LOG:-}" "${TMP_EXPR:-}"
|
|
||||||
}
|
|
||||||
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
write_node_modules_hash() {
|
|
||||||
local value="$1"
|
|
||||||
local temp
|
|
||||||
temp=$(mktemp)
|
|
||||||
jq --arg value "$value" '.nodeModules = $value' "$HASH_FILE" >"$temp"
|
|
||||||
mv "$temp" "$HASH_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
TARGET="packages.${SYSTEM}.default"
|
|
||||||
MODULES_ATTR=".#packages.${SYSTEM}.default.node_modules"
|
|
||||||
CORRECT_HASH=""
|
|
||||||
|
|
||||||
DRV_PATH="$(nix eval --raw "${MODULES_ATTR}.drvPath")"
|
|
||||||
|
|
||||||
echo "Setting dummy node_modules outputHash for ${SYSTEM}..."
|
|
||||||
write_node_modules_hash "$DUMMY"
|
|
||||||
|
|
||||||
BUILD_LOG=$(mktemp)
|
|
||||||
JSON_OUTPUT=$(mktemp)
|
|
||||||
|
|
||||||
echo "Building node_modules for ${SYSTEM} to discover correct outputHash..."
|
|
||||||
echo "Attempting to realize derivation: ${DRV_PATH}"
|
|
||||||
REALISE_OUT=$(nix-store --realise "$DRV_PATH" --keep-failed 2>&1 | tee "$BUILD_LOG" || true)
|
|
||||||
|
|
||||||
BUILD_PATH=$(echo "$REALISE_OUT" | grep "^/nix/store/" | head -n1 || true)
|
|
||||||
if [ -n "$BUILD_PATH" ] && [ -d "$BUILD_PATH" ]; then
|
|
||||||
echo "Realized node_modules output: $BUILD_PATH"
|
|
||||||
CORRECT_HASH=$(nix hash path --sri "$BUILD_PATH" 2>/dev/null || true)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$CORRECT_HASH" ]; then
|
|
||||||
CORRECT_HASH="$(grep -E 'got:\s+sha256-[A-Za-z0-9+/=]+' "$BUILD_LOG" | awk '{print $2}' | head -n1 || true)"
|
|
||||||
|
|
||||||
if [ -z "$CORRECT_HASH" ]; then
|
|
||||||
CORRECT_HASH="$(grep -A2 'hash mismatch' "$BUILD_LOG" | grep 'got:' | awk '{print $2}' | sed 's/sha256:/sha256-/' || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$CORRECT_HASH" ]; then
|
|
||||||
echo "Searching for kept failed build directory..."
|
|
||||||
KEPT_DIR=$(grep -oE "build directory.*'[^']+'" "$BUILD_LOG" | grep -oE "'/[^']+'" | tr -d "'" | head -n1)
|
|
||||||
|
|
||||||
if [ -z "$KEPT_DIR" ]; then
|
|
||||||
KEPT_DIR=$(grep -oE '/nix/var/nix/builds/[^ ]+' "$BUILD_LOG" | head -n1)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$KEPT_DIR" ] && [ -d "$KEPT_DIR" ]; then
|
|
||||||
echo "Found kept build directory: $KEPT_DIR"
|
|
||||||
if [ -d "$KEPT_DIR/build" ]; then
|
|
||||||
HASH_PATH="$KEPT_DIR/build"
|
|
||||||
else
|
|
||||||
HASH_PATH="$KEPT_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Attempting to hash: $HASH_PATH"
|
|
||||||
ls -la "$HASH_PATH" || true
|
|
||||||
|
|
||||||
if [ -d "$HASH_PATH/node_modules" ]; then
|
|
||||||
CORRECT_HASH=$(nix hash path --sri "$HASH_PATH" 2>/dev/null || true)
|
|
||||||
echo "Computed hash from kept build: $CORRECT_HASH"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$CORRECT_HASH" ]; then
|
|
||||||
echo "Failed to determine correct node_modules hash for ${SYSTEM}."
|
|
||||||
echo "Build log:"
|
|
||||||
cat "$BUILD_LOG"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
write_node_modules_hash "$CORRECT_HASH"
|
|
||||||
|
|
||||||
jq -e --arg hash "$CORRECT_HASH" '.nodeModules == $hash' "$HASH_FILE" >/dev/null
|
|
||||||
|
|
||||||
echo "node_modules hash updated for ${SYSTEM}: $CORRECT_HASH"
|
|
||||||
|
|
||||||
rm -f "$BUILD_LOG"
|
|
||||||
unset BUILD_LOG
|
|
||||||
+3
-2
@@ -21,7 +21,7 @@
|
|||||||
"packages/slack"
|
"packages/slack"
|
||||||
],
|
],
|
||||||
"catalog": {
|
"catalog": {
|
||||||
"@types/bun": "1.3.4",
|
"@types/bun": "1.3.5",
|
||||||
"@octokit/rest": "22.0.0",
|
"@octokit/rest": "22.0.0",
|
||||||
"@hono/zod-validator": "0.4.2",
|
"@hono/zod-validator": "0.4.2",
|
||||||
"ulid": "3.0.1",
|
"ulid": "3.0.1",
|
||||||
@@ -36,7 +36,8 @@
|
|||||||
"@solid-primitives/storage": "4.3.3",
|
"@solid-primitives/storage": "4.3.3",
|
||||||
"@tailwindcss/vite": "4.1.11",
|
"@tailwindcss/vite": "4.1.11",
|
||||||
"diff": "8.0.2",
|
"diff": "8.0.2",
|
||||||
"ai": "5.0.97",
|
"dompurify": "3.3.1",
|
||||||
|
"ai": "5.0.119",
|
||||||
"hono": "4.10.7",
|
"hono": "4.10.7",
|
||||||
"hono-openapi": "1.1.2",
|
"hono-openapi": "1.1.2",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
|
|||||||
@@ -13,12 +13,11 @@
|
|||||||
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
|
<meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
|
||||||
<meta property="og:image" content="/social-share.png" />
|
<meta property="og:image" content="/social-share.png" />
|
||||||
<meta property="twitter:image" content="/social-share.png" />
|
<meta property="twitter:image" content="/social-share.png" />
|
||||||
<!-- Theme preload script - applies cached theme to avoid FOUC -->
|
|
||||||
<script id="oc-theme-preload-script" src="/oc-theme-preload.js"></script>
|
<script id="oc-theme-preload-script" src="/oc-theme-preload.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="antialiased overscroll-none text-12-regular overflow-hidden">
|
<body class="antialiased overscroll-none text-12-regular overflow-hidden">
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root" class="flex flex-col h-dvh"></div>
|
<div id="root" class="flex flex-col h-dvh p-px"></div>
|
||||||
<script src="/src/entry.tsx" type="module"></script>
|
<script src="/src/entry.tsx" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
+13
-13
@@ -29,23 +29,14 @@ import { Suspense } from "solid-js"
|
|||||||
|
|
||||||
const Home = lazy(() => import("@/pages/home"))
|
const Home = lazy(() => import("@/pages/home"))
|
||||||
const Session = lazy(() => import("@/pages/session"))
|
const Session = lazy(() => import("@/pages/session"))
|
||||||
const Loading = () => <div class="size-full flex items-center justify-center text-text-weak">Loading...</div>
|
const Loading = () => <div class="size-full" />
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
__OPENCODE__?: { updaterEnabled?: boolean; port?: number; serverReady?: boolean }
|
__OPENCODE__?: { updaterEnabled?: boolean; serverPassword?: string }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultServerUrl = iife(() => {
|
|
||||||
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
|
|
||||||
if (window.__OPENCODE__) return `http://127.0.0.1:${window.__OPENCODE__.port}`
|
|
||||||
if (import.meta.env.DEV)
|
|
||||||
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`
|
|
||||||
|
|
||||||
return window.location.origin
|
|
||||||
})
|
|
||||||
|
|
||||||
export function AppBaseProviders(props: ParentProps) {
|
export function AppBaseProviders(props: ParentProps) {
|
||||||
return (
|
return (
|
||||||
<MetaProvider>
|
<MetaProvider>
|
||||||
@@ -74,9 +65,18 @@ function ServerKey(props: ParentProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AppInterface() {
|
export function AppInterface(props: { defaultUrl?: string }) {
|
||||||
|
const defaultServerUrl = () => {
|
||||||
|
if (props.defaultUrl) return props.defaultUrl
|
||||||
|
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
|
||||||
|
if (import.meta.env.DEV)
|
||||||
|
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`
|
||||||
|
|
||||||
|
return window.location.origin
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ServerProvider defaultUrl={defaultServerUrl}>
|
<ServerProvider defaultUrl={defaultServerUrl()}>
|
||||||
<ServerKey>
|
<ServerKey>
|
||||||
<GlobalSDKProvider>
|
<GlobalSDKProvider>
|
||||||
<GlobalSyncProvider>
|
<GlobalSyncProvider>
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { Component, createMemo } from "solid-js"
|
||||||
|
import { useNavigate, useParams } from "@solidjs/router"
|
||||||
|
import { useSync } from "@/context/sync"
|
||||||
|
import { useSDK } from "@/context/sdk"
|
||||||
|
import { usePrompt } from "@/context/prompt"
|
||||||
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
|
import { List } from "@opencode-ai/ui/list"
|
||||||
|
import { extractPromptFromParts } from "@/utils/prompt"
|
||||||
|
import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client"
|
||||||
|
import { base64Encode } from "@opencode-ai/util/encode"
|
||||||
|
|
||||||
|
interface ForkableMessage {
|
||||||
|
id: string
|
||||||
|
text: string
|
||||||
|
time: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(date: Date): string {
|
||||||
|
return date.toLocaleTimeString(undefined, { timeStyle: "short" })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DialogFork: Component = () => {
|
||||||
|
const params = useParams()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const sync = useSync()
|
||||||
|
const sdk = useSDK()
|
||||||
|
const prompt = usePrompt()
|
||||||
|
const dialog = useDialog()
|
||||||
|
|
||||||
|
const messages = createMemo((): ForkableMessage[] => {
|
||||||
|
const sessionID = params.id
|
||||||
|
if (!sessionID) return []
|
||||||
|
|
||||||
|
const msgs = sync.data.message[sessionID] ?? []
|
||||||
|
const result: ForkableMessage[] = []
|
||||||
|
|
||||||
|
for (const message of msgs) {
|
||||||
|
if (message.role !== "user") continue
|
||||||
|
|
||||||
|
const parts = sync.data.part[message.id] ?? []
|
||||||
|
const textPart = parts.find((x): x is SDKTextPart => x.type === "text" && !x.synthetic && !x.ignored)
|
||||||
|
if (!textPart) continue
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
id: message.id,
|
||||||
|
text: textPart.text.replace(/\n/g, " ").slice(0, 200),
|
||||||
|
time: formatTime(new Date(message.time.created)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.reverse()
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSelect = (item: ForkableMessage | undefined) => {
|
||||||
|
if (!item) return
|
||||||
|
|
||||||
|
const sessionID = params.id
|
||||||
|
if (!sessionID) return
|
||||||
|
|
||||||
|
const parts = sync.data.part[item.id] ?? []
|
||||||
|
const restored = extractPromptFromParts(parts, { directory: sdk.directory })
|
||||||
|
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
sdk.client.session.fork({ sessionID, messageID: item.id }).then((forked) => {
|
||||||
|
if (!forked.data) return
|
||||||
|
navigate(`/${base64Encode(sdk.directory)}/session/${forked.data.id}`)
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
prompt.set(restored)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog title="Fork from message">
|
||||||
|
<List
|
||||||
|
class="flex-1 min-h-0 [&_[data-slot=list-scroll]]:flex-1 [&_[data-slot=list-scroll]]:min-h-0"
|
||||||
|
search={{ placeholder: "Search", autofocus: true }}
|
||||||
|
emptyMessage="No messages to fork from"
|
||||||
|
key={(x) => x.id}
|
||||||
|
items={messages}
|
||||||
|
filterKeys={["text"]}
|
||||||
|
onSelect={handleSelect}
|
||||||
|
>
|
||||||
|
{(item) => (
|
||||||
|
<div class="w-full flex items-center gap-2">
|
||||||
|
<span class="truncate flex-1 min-w-0 text-left" style={{ "font-weight": "400" }}>
|
||||||
|
{item.text}
|
||||||
|
</span>
|
||||||
|
<span class="text-text-weak shrink-0" style={{ "font-weight": "400" }}>
|
||||||
|
{item.time}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</List>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,14 +1,30 @@
|
|||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||||
|
import { Keybind } from "@opencode-ai/ui/keybind"
|
||||||
import { List } from "@opencode-ai/ui/list"
|
import { List } from "@opencode-ai/ui/list"
|
||||||
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
||||||
import { useParams } from "@solidjs/router"
|
import { useParams } from "@solidjs/router"
|
||||||
import { createMemo } from "solid-js"
|
import { createMemo, createSignal, onCleanup, Show } from "solid-js"
|
||||||
|
import { formatKeybind, useCommand, type CommandOption } from "@/context/command"
|
||||||
import { useLayout } from "@/context/layout"
|
import { useLayout } from "@/context/layout"
|
||||||
import { useFile } from "@/context/file"
|
import { useFile } from "@/context/file"
|
||||||
|
|
||||||
|
type EntryType = "command" | "file"
|
||||||
|
|
||||||
|
type Entry = {
|
||||||
|
id: string
|
||||||
|
type: EntryType
|
||||||
|
title: string
|
||||||
|
description?: string
|
||||||
|
keybind?: string
|
||||||
|
category: "Commands" | "Files"
|
||||||
|
option?: CommandOption
|
||||||
|
path?: string
|
||||||
|
}
|
||||||
|
|
||||||
export function DialogSelectFile() {
|
export function DialogSelectFile() {
|
||||||
|
const command = useCommand()
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const file = useFile()
|
const file = useFile()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
@@ -16,35 +32,148 @@ export function DialogSelectFile() {
|
|||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey()))
|
const tabs = createMemo(() => layout.tabs(sessionKey()))
|
||||||
const view = createMemo(() => layout.view(sessionKey()))
|
const view = createMemo(() => layout.view(sessionKey()))
|
||||||
|
const state = { cleanup: undefined as (() => void) | void, committed: false }
|
||||||
|
const [grouped, setGrouped] = createSignal(false)
|
||||||
|
const common = ["session.new", "session.previous", "session.next", "terminal.toggle", "review.toggle"]
|
||||||
|
const limit = 5
|
||||||
|
|
||||||
|
const allowed = createMemo(() =>
|
||||||
|
command.options.filter(
|
||||||
|
(option) => !option.disabled && !option.id.startsWith("suggested.") && option.id !== "file.open",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const commandItem = (option: CommandOption): Entry => ({
|
||||||
|
id: "command:" + option.id,
|
||||||
|
type: "command",
|
||||||
|
title: option.title,
|
||||||
|
description: option.description,
|
||||||
|
keybind: option.keybind,
|
||||||
|
category: "Commands",
|
||||||
|
option,
|
||||||
|
})
|
||||||
|
|
||||||
|
const fileItem = (path: string): Entry => ({
|
||||||
|
id: "file:" + path,
|
||||||
|
type: "file",
|
||||||
|
title: path,
|
||||||
|
category: "Files",
|
||||||
|
path,
|
||||||
|
})
|
||||||
|
|
||||||
|
const list = createMemo(() => allowed().map(commandItem))
|
||||||
|
|
||||||
|
const picks = createMemo(() => {
|
||||||
|
const all = allowed()
|
||||||
|
const order = new Map(common.map((id, index) => [id, index]))
|
||||||
|
const picked = all.filter((option) => order.has(option.id))
|
||||||
|
const base = picked.length ? picked : all.slice(0, limit)
|
||||||
|
const sorted = picked.length ? [...base].sort((a, b) => (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0)) : base
|
||||||
|
return sorted.map(commandItem)
|
||||||
|
})
|
||||||
|
|
||||||
|
const recent = createMemo(() => {
|
||||||
|
const all = tabs().all()
|
||||||
|
const active = tabs().active()
|
||||||
|
const order = active ? [active, ...all.filter((item) => item !== active)] : all
|
||||||
|
const seen = new Set<string>()
|
||||||
|
const items: Entry[] = []
|
||||||
|
|
||||||
|
for (const item of order) {
|
||||||
|
const path = file.pathFromTab(item)
|
||||||
|
if (!path) continue
|
||||||
|
if (seen.has(path)) continue
|
||||||
|
seen.add(path)
|
||||||
|
items.push(fileItem(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.slice(0, limit)
|
||||||
|
})
|
||||||
|
|
||||||
|
const items = async (filter: string) => {
|
||||||
|
const query = filter.trim()
|
||||||
|
setGrouped(query.length > 0)
|
||||||
|
if (!query) return [...picks(), ...recent()]
|
||||||
|
const files = await file.searchFiles(query)
|
||||||
|
const entries = files.map(fileItem)
|
||||||
|
return [...list(), ...entries]
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMove = (item: Entry | undefined) => {
|
||||||
|
state.cleanup?.()
|
||||||
|
if (!item) return
|
||||||
|
if (item.type !== "command") return
|
||||||
|
state.cleanup = item.option?.onHighlight?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = (path: string) => {
|
||||||
|
const value = file.tab(path)
|
||||||
|
tabs().open(value)
|
||||||
|
file.load(path)
|
||||||
|
view().reviewPanel.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSelect = (item: Entry | undefined) => {
|
||||||
|
if (!item) return
|
||||||
|
state.committed = true
|
||||||
|
state.cleanup = undefined
|
||||||
|
dialog.close()
|
||||||
|
|
||||||
|
if (item.type === "command") {
|
||||||
|
item.option?.onSelect?.("palette")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item.path) return
|
||||||
|
open(item.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
if (state.committed) return
|
||||||
|
state.cleanup?.()
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog title="Select file">
|
<Dialog class="pt-3 pb-0 !max-h-[480px]">
|
||||||
<List
|
<List
|
||||||
search={{ placeholder: "Search files", autofocus: true }}
|
search={{ placeholder: "Search files and commands", autofocus: true, hideIcon: true, class: "pl-3 pr-2 !mb-0" }}
|
||||||
emptyMessage="No files found"
|
emptyMessage="No results found"
|
||||||
items={file.searchFiles}
|
items={items}
|
||||||
key={(x) => x}
|
key={(item) => item.id}
|
||||||
onSelect={(path) => {
|
filterKeys={["title", "description", "category"]}
|
||||||
if (path) {
|
groupBy={(item) => item.category}
|
||||||
const value = file.tab(path)
|
onMove={handleMove}
|
||||||
tabs().open(value)
|
onSelect={handleSelect}
|
||||||
file.load(path)
|
|
||||||
view().reviewPanel.open()
|
|
||||||
}
|
|
||||||
dialog.close()
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(item) => (
|
||||||
<div class="w-full flex items-center justify-between rounded-md">
|
<Show
|
||||||
<div class="flex items-center gap-x-3 grow min-w-0">
|
when={item.type === "command"}
|
||||||
<FileIcon node={{ path: i, type: "file" }} class="shrink-0 size-4" />
|
fallback={
|
||||||
<div class="flex items-center text-14-regular">
|
<div class="w-full flex items-center justify-between rounded-md pl-1">
|
||||||
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
|
<div class="flex items-center gap-x-3 grow min-w-0">
|
||||||
{getDirectory(i)}
|
<FileIcon node={{ path: item.path ?? "", type: "file" }} class="shrink-0 size-4" />
|
||||||
</span>
|
<div class="flex items-center text-14-regular">
|
||||||
<span class="text-text-strong whitespace-nowrap">{getFilename(i)}</span>
|
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
|
||||||
|
{getDirectory(item.path ?? "")}
|
||||||
|
</span>
|
||||||
|
<span class="text-text-strong whitespace-nowrap">{getFilename(item.path ?? "")}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div class="w-full flex items-center justify-between gap-4 pl-1">
|
||||||
|
<div class="flex items-center gap-2 min-w-0">
|
||||||
|
<span class="text-14-regular text-text-strong whitespace-nowrap">{item.title}</span>
|
||||||
|
<Show when={item.description}>
|
||||||
|
<span class="text-14-regular text-text-weak truncate">{item.description}</span>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
<Show when={item.keybind}>
|
||||||
|
<Keybind class="rounded-[4px]">{formatKeybind(item.keybind ?? "")}</Keybind>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Show>
|
||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { createEffect, createMemo, onCleanup } from "solid-js"
|
import { createResource, createEffect, createMemo, onCleanup, Show } from "solid-js"
|
||||||
import { createStore, reconcile } from "solid-js/store"
|
import { createStore, reconcile } from "solid-js/store"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
import { List } from "@opencode-ai/ui/list"
|
import { List } from "@opencode-ai/ui/list"
|
||||||
import { TextField } from "@opencode-ai/ui/text-field"
|
import { TextField } from "@opencode-ai/ui/text-field"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { normalizeServerUrl, serverDisplayName, useServer } from "@/context/server"
|
import { normalizeServerUrl, serverDisplayName, useServer } from "@/context/server"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
|
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
|
||||||
@@ -35,6 +36,8 @@ export function DialogSelectServer() {
|
|||||||
error: "",
|
error: "",
|
||||||
status: {} as Record<string, ServerStatus | undefined>,
|
status: {} as Record<string, ServerStatus | undefined>,
|
||||||
})
|
})
|
||||||
|
const [defaultUrl, defaultUrlActions] = createResource(() => platform.getDefaultServerUrl?.())
|
||||||
|
const isDesktop = platform.platform === "desktop"
|
||||||
|
|
||||||
const items = createMemo(() => {
|
const items = createMemo(() => {
|
||||||
const current = server.url
|
const current = server.url
|
||||||
@@ -114,6 +117,10 @@ export function DialogSelectServer() {
|
|||||||
select(value, true)
|
select(value, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleRemove(url: string) {
|
||||||
|
server.remove(url)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog title="Servers" description="Switch which OpenCode server this app connects to.">
|
<Dialog title="Servers" description="Switch which OpenCode server this app connects to.">
|
||||||
<div class="flex flex-col gap-4 pb-4">
|
<div class="flex flex-col gap-4 pb-4">
|
||||||
@@ -128,20 +135,33 @@ export function DialogSelectServer() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(i) => (
|
||||||
<div
|
<div class="flex items-center gap-2 min-w-0 flex-1 group/item">
|
||||||
class="flex items-center gap-2 min-w-0 flex-1"
|
|
||||||
classList={{ "opacity-50": store.status[i]?.healthy === false }}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
classList={{
|
class="flex items-center gap-2 min-w-0 flex-1"
|
||||||
"size-1.5 rounded-full shrink-0": true,
|
classList={{ "opacity-50": store.status[i]?.healthy === false }}
|
||||||
"bg-icon-success-base": store.status[i]?.healthy === true,
|
>
|
||||||
"bg-icon-critical-base": store.status[i]?.healthy === false,
|
<div
|
||||||
"bg-border-weak-base": store.status[i] === undefined,
|
classList={{
|
||||||
}}
|
"size-1.5 rounded-full shrink-0": true,
|
||||||
/>
|
"bg-icon-success-base": store.status[i]?.healthy === true,
|
||||||
<span class="truncate">{serverDisplayName(i)}</span>
|
"bg-icon-critical-base": store.status[i]?.healthy === false,
|
||||||
<span class="text-text-weak">{store.status[i]?.version}</span>
|
"bg-border-weak-base": store.status[i] === undefined,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span class="truncate">{serverDisplayName(i)}</span>
|
||||||
|
<span class="text-text-weak">{store.status[i]?.version}</span>
|
||||||
|
</div>
|
||||||
|
<Show when={current() !== i && server.list.includes(i)}>
|
||||||
|
<IconButton
|
||||||
|
icon="circle-x"
|
||||||
|
variant="ghost"
|
||||||
|
class="bg-transparent transition-opacity shrink-0 hover:scale-110"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleRemove(i)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
@@ -173,6 +193,53 @@ export function DialogSelectServer() {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Show when={isDesktop}>
|
||||||
|
<div class="mt-6 px-3 flex flex-col gap-1.5">
|
||||||
|
<div class="px-3">
|
||||||
|
<h3 class="text-14-regular text-text-weak">Default server</h3>
|
||||||
|
<p class="text-12-regular text-text-weak mt-1">
|
||||||
|
Connect to this server on app launch instead of starting a local server. Requires restart.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 px-3 py-2">
|
||||||
|
<Show
|
||||||
|
when={defaultUrl()}
|
||||||
|
fallback={
|
||||||
|
<Show
|
||||||
|
when={server.url}
|
||||||
|
fallback={<span class="text-14-regular text-text-weak">No server selected</span>}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="small"
|
||||||
|
onClick={async () => {
|
||||||
|
await platform.setDefaultServerUrl?.(server.url)
|
||||||
|
defaultUrlActions.refetch(server.url)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Set current server as default
|
||||||
|
</Button>
|
||||||
|
</Show>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-2 flex-1 min-w-0">
|
||||||
|
<span class="truncate text-14-regular">{serverDisplayName(defaultUrl()!)}</span>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="small"
|
||||||
|
onClick={async () => {
|
||||||
|
await platform.setDefaultServerUrl?.(null)
|
||||||
|
defaultUrlActions.refetch()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</Button>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,11 +33,14 @@ import { useSync } from "@/context/sync"
|
|||||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
|
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
||||||
|
import type { IconName } from "@opencode-ai/ui/icons/provider"
|
||||||
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { Select } from "@opencode-ai/ui/select"
|
import { Select } from "@opencode-ai/ui/select"
|
||||||
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
|
import { ImagePreview } from "@opencode-ai/ui/image-preview"
|
||||||
import { ModelSelectorPopover } from "@/components/dialog-select-model"
|
import { ModelSelectorPopover } from "@/components/dialog-select-model"
|
||||||
import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
|
import { DialogSelectModelUnpaid } from "@/components/dialog-select-model-unpaid"
|
||||||
import { useProviders } from "@/hooks/use-providers"
|
import { useProviders } from "@/hooks/use-providers"
|
||||||
@@ -361,6 +364,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
if (!isFocused()) setStore("popover", null)
|
if (!isFocused()) setStore("popover", null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Safety: reset composing state on focus change to prevent stuck state
|
||||||
|
// This handles edge cases where compositionend event may not fire
|
||||||
|
createEffect(() => {
|
||||||
|
if (!isFocused()) setComposing(false)
|
||||||
|
})
|
||||||
|
|
||||||
type AtOption = { type: "agent"; name: string; display: string } | { type: "file"; path: string; display: string }
|
type AtOption = { type: "agent"; name: string; display: string } | { type: "file"; path: string; display: string }
|
||||||
|
|
||||||
const agentList = createMemo(() =>
|
const agentList = createMemo(() =>
|
||||||
@@ -386,6 +395,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
const {
|
const {
|
||||||
flat: atFlat,
|
flat: atFlat,
|
||||||
active: atActive,
|
active: atActive,
|
||||||
|
setActive: setAtActive,
|
||||||
onInput: atOnInput,
|
onInput: atOnInput,
|
||||||
onKeyDown: atOnKeyDown,
|
onKeyDown: atOnKeyDown,
|
||||||
} = useFilteredList<AtOption>({
|
} = useFilteredList<AtOption>({
|
||||||
@@ -452,6 +462,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
const {
|
const {
|
||||||
flat: slashFlat,
|
flat: slashFlat,
|
||||||
active: slashActive,
|
active: slashActive,
|
||||||
|
setActive: setSlashActive,
|
||||||
onInput: slashOnInput,
|
onInput: slashOnInput,
|
||||||
onKeyDown: slashOnKeyDown,
|
onKeyDown: slashOnKeyDown,
|
||||||
refetch: slashRefetch,
|
refetch: slashRefetch,
|
||||||
@@ -876,6 +887,14 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Shift+Enter BEFORE IME check - Shift+Enter is never used for IME input
|
||||||
|
// and should always insert a newline regardless of composition state
|
||||||
|
if (event.key === "Enter" && event.shiftKey) {
|
||||||
|
addPart({ type: "text", content: "\n", start: 0, end: 0 })
|
||||||
|
event.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (event.key === "Enter" && isImeComposing(event)) {
|
if (event.key === "Enter" && isImeComposing(event)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -939,11 +958,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === "Enter" && event.shiftKey) {
|
// Note: Shift+Enter is handled earlier, before IME check
|
||||||
addPart({ type: "text", content: "\n", start: 0, end: 0 })
|
|
||||||
event.preventDefault()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (event.key === "Enter" && !event.shiftKey) {
|
if (event.key === "Enter" && !event.shiftKey) {
|
||||||
handleSubmit(event)
|
handleSubmit(event)
|
||||||
}
|
}
|
||||||
@@ -1299,6 +1314,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
class="absolute inset-x-0 -top-3 -translate-y-full origin-bottom-left max-h-80 min-h-10
|
class="absolute inset-x-0 -top-3 -translate-y-full origin-bottom-left max-h-80 min-h-10
|
||||||
overflow-auto no-scrollbar flex flex-col p-2 rounded-md
|
overflow-auto no-scrollbar flex flex-col p-2 rounded-md
|
||||||
border border-border-base bg-surface-raised-stronger-non-alpha shadow-md"
|
border border-border-base bg-surface-raised-stronger-non-alpha shadow-md"
|
||||||
|
onMouseDown={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={store.popover === "at"}>
|
<Match when={store.popover === "at"}>
|
||||||
@@ -1314,6 +1330,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
"bg-surface-raised-base-hover": atActive() === atKey(item),
|
"bg-surface-raised-base-hover": atActive() === atKey(item),
|
||||||
}}
|
}}
|
||||||
onClick={() => handleAtSelect(item)}
|
onClick={() => handleAtSelect(item)}
|
||||||
|
onMouseEnter={() => setAtActive(atKey(item))}
|
||||||
>
|
>
|
||||||
<Show
|
<Show
|
||||||
when={item.type === "agent"}
|
when={item.type === "agent"}
|
||||||
@@ -1360,6 +1377,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
"bg-surface-raised-base-hover": slashActive() === cmd.id,
|
"bg-surface-raised-base-hover": slashActive() === cmd.id,
|
||||||
}}
|
}}
|
||||||
onClick={() => handleSlashSelect(cmd)}
|
onClick={() => handleSlashSelect(cmd)}
|
||||||
|
onMouseEnter={() => setSlashActive(cmd.id)}
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 min-w-0">
|
<div class="flex items-center gap-2 min-w-0">
|
||||||
<span class="text-14-regular text-text-strong whitespace-nowrap">/{cmd.trigger}</span>
|
<span class="text-14-regular text-text-strong whitespace-nowrap">/{cmd.trigger}</span>
|
||||||
@@ -1479,7 +1497,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
<img
|
<img
|
||||||
src={attachment.dataUrl}
|
src={attachment.dataUrl}
|
||||||
alt={attachment.filename}
|
alt={attachment.filename}
|
||||||
class="size-16 rounded-md object-cover border border-border-base"
|
class="size-16 rounded-md object-cover border border-border-base hover:border-border-strong-base transition-colors"
|
||||||
|
onClick={() =>
|
||||||
|
dialog.show(() => <ImagePreview src={attachment.dataUrl} alt={attachment.filename} />)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
<button
|
<button
|
||||||
@@ -1551,6 +1572,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
fallback={
|
fallback={
|
||||||
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
||||||
<Button as="div" variant="ghost" onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}>
|
<Button as="div" variant="ghost" onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}>
|
||||||
|
<Show when={local.model.current()?.provider?.id}>
|
||||||
|
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
|
||||||
|
</Show>
|
||||||
{local.model.current()?.name ?? "Select model"}
|
{local.model.current()?.name ?? "Select model"}
|
||||||
<Icon name="chevron-down" size="small" />
|
<Icon name="chevron-down" size="small" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1560,6 +1584,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
<ModelSelectorPopover>
|
<ModelSelectorPopover>
|
||||||
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
||||||
<Button as="div" variant="ghost">
|
<Button as="div" variant="ghost">
|
||||||
|
<Show when={local.model.current()?.provider?.id}>
|
||||||
|
<ProviderIcon id={local.model.current()!.provider.id as IconName} class="size-4 shrink-0" />
|
||||||
|
</Show>
|
||||||
{local.model.current()?.name ?? "Select model"}
|
{local.model.current()?.name ?? "Select model"}
|
||||||
<Icon name="chevron-down" size="small" />
|
<Icon name="chevron-down" size="small" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1574,10 +1601,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="text-text-base _hidden group-hover/prompt-input:inline-block"
|
class="text-text-base _hidden group-hover/prompt-input:inline-block capitalize text-12-regular"
|
||||||
onClick={() => local.model.variant.cycle()}
|
onClick={() => local.model.variant.cycle()}
|
||||||
>
|
>
|
||||||
<span class="capitalize text-12-regular">{local.model.variant.current() ?? "Default"}</span>
|
{local.model.variant.current() ?? "Default"}
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -1,267 +1,203 @@
|
|||||||
import { createMemo, createResource, Show } from "solid-js"
|
import { createMemo, createResource, Show } from "solid-js"
|
||||||
import { A, useNavigate, useParams } from "@solidjs/router"
|
import { Portal } from "solid-js/web"
|
||||||
|
import { useParams } from "@solidjs/router"
|
||||||
import { useLayout } from "@/context/layout"
|
import { useLayout } from "@/context/layout"
|
||||||
import { useCommand } from "@/context/command"
|
import { useCommand } from "@/context/command"
|
||||||
import { useServer } from "@/context/server"
|
// import { useServer } from "@/context/server"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
// import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
import { useGlobalSDK } from "@/context/global-sdk"
|
import { useGlobalSDK } from "@/context/global-sdk"
|
||||||
import { getFilename } from "@opencode-ai/util/path"
|
import { getFilename } from "@opencode-ai/util/path"
|
||||||
import { base64Decode, base64Encode } from "@opencode-ai/util/encode"
|
import { base64Decode } from "@opencode-ai/util/encode"
|
||||||
import { iife } from "@opencode-ai/util/iife"
|
import { iife } from "@opencode-ai/util/iife"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||||
import { Select } from "@opencode-ai/ui/select"
|
|
||||||
import { Popover } from "@opencode-ai/ui/popover"
|
import { Popover } from "@opencode-ai/ui/popover"
|
||||||
import { TextField } from "@opencode-ai/ui/text-field"
|
import { TextField } from "@opencode-ai/ui/text-field"
|
||||||
import { DialogSelectServer } from "@/components/dialog-select-server"
|
import { Keybind } from "@opencode-ai/ui/keybind"
|
||||||
import { SessionLspIndicator } from "@/components/session-lsp-indicator"
|
|
||||||
import { SessionMcpIndicator } from "@/components/session-mcp-indicator"
|
|
||||||
import type { Session } from "@opencode-ai/sdk/v2/client"
|
|
||||||
import { same } from "@/utils/same"
|
|
||||||
|
|
||||||
export function SessionHeader() {
|
export function SessionHeader() {
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const navigate = useNavigate()
|
|
||||||
const command = useCommand()
|
const command = useCommand()
|
||||||
const server = useServer()
|
// const server = useServer()
|
||||||
const dialog = useDialog()
|
// const dialog = useDialog()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
|
|
||||||
const projectDirectory = createMemo(() => base64Decode(params.dir ?? ""))
|
const projectDirectory = createMemo(() => base64Decode(params.dir ?? ""))
|
||||||
|
const project = createMemo(() => {
|
||||||
const sessions = createMemo(() => (sync.data.session ?? []).filter((s) => !s.parentID))
|
const directory = projectDirectory()
|
||||||
const currentSession = createMemo(() => sync.data.session.find((s) => s.id === params.id))
|
if (!directory) return
|
||||||
const parentSession = createMemo(() => {
|
return layout.projects.list().find((p) => p.worktree === directory || p.sandboxes?.includes(directory))
|
||||||
const current = currentSession()
|
|
||||||
if (!current?.parentID) return undefined
|
|
||||||
return sync.data.session.find((s) => s.id === current.parentID)
|
|
||||||
})
|
})
|
||||||
|
const name = createMemo(() => {
|
||||||
|
const current = project()
|
||||||
|
if (current) return current.name || getFilename(current.worktree)
|
||||||
|
return getFilename(projectDirectory())
|
||||||
|
})
|
||||||
|
const hotkey = createMemo(() => command.keybind("file.open"))
|
||||||
|
|
||||||
|
const currentSession = createMemo(() => sync.data.session.find((s) => s.id === params.id))
|
||||||
const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
|
const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
|
||||||
const worktrees = createMemo(() => layout.projects.list().map((p) => p.worktree), [], { equals: same })
|
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
||||||
const view = createMemo(() => layout.view(sessionKey()))
|
const view = createMemo(() => layout.view(sessionKey()))
|
||||||
|
|
||||||
function navigateToProject(directory: string) {
|
const centerMount = createMemo(() => document.getElementById("opencode-titlebar-center"))
|
||||||
navigate(`/${base64Encode(directory)}`)
|
const rightMount = createMemo(() => document.getElementById("opencode-titlebar-right"))
|
||||||
}
|
|
||||||
|
|
||||||
function navigateToSession(session: Session | undefined) {
|
|
||||||
if (!session) return
|
|
||||||
// Only navigate if we're actually changing to a different session
|
|
||||||
if (session.id === params.id) return
|
|
||||||
navigate(`/${params.dir}/session/${session.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header class="h-12 shrink-0 bg-background-base border-b border-border-weak-base flex">
|
<>
|
||||||
<button
|
<Show when={centerMount()}>
|
||||||
type="button"
|
{(mount) => (
|
||||||
class="xl:hidden w-12 shrink-0 flex items-center justify-center border-r border-border-weak-base hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active transition-colors"
|
<Portal mount={mount()}>
|
||||||
onClick={layout.mobileSidebar.toggle}
|
<button
|
||||||
>
|
type="button"
|
||||||
<Icon name="menu" size="small" />
|
class="hidden md:flex w-[320px] p-1 pl-1.5 items-center gap-2 justify-between rounded-md border border-border-weak-base bg-surface-raised-base transition-colors cursor-default hover:bg-surface-raised-base-hover focus:bg-surface-raised-base-hover active:bg-surface-raised-base-active"
|
||||||
</button>
|
onClick={() => command.trigger("file.open")}
|
||||||
<div class="px-4 flex items-center justify-between gap-4 w-full">
|
|
||||||
<div class="flex items-center gap-3 min-w-0">
|
|
||||||
<div class="flex items-center gap-2 min-w-0">
|
|
||||||
<div class="hidden xl:flex items-center gap-2">
|
|
||||||
<Select
|
|
||||||
options={worktrees()}
|
|
||||||
current={sync.project?.worktree ?? projectDirectory()}
|
|
||||||
label={(x) => getFilename(x)}
|
|
||||||
onSelect={(x) => (x ? navigateToProject(x) : undefined)}
|
|
||||||
class="text-14-regular text-text-base"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
{/* @ts-ignore */}
|
|
||||||
{(i) => (
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<Icon name="folder" size="small" />
|
|
||||||
<div class="text-text-strong">{getFilename(i)}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
<div class="text-text-weaker">/</div>
|
|
||||||
</div>
|
|
||||||
<Show
|
|
||||||
when={parentSession()}
|
|
||||||
fallback={
|
|
||||||
<>
|
|
||||||
<Select
|
|
||||||
options={sessions()}
|
|
||||||
current={currentSession()}
|
|
||||||
placeholder="New session"
|
|
||||||
label={(x) => x.title}
|
|
||||||
value={(x) => x.id}
|
|
||||||
onSelect={navigateToSession}
|
|
||||||
class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md"
|
|
||||||
variant="ghost"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 min-w-0">
|
<div class="flex items-center gap-2">
|
||||||
<Select
|
<Icon name="magnifying-glass" size="normal" class="icon-base" />
|
||||||
options={sessions()}
|
<span class="flex-1 min-w-0 text-14-regular text-text-weak truncate h-3.5 flex items-center overflow-visible">
|
||||||
current={parentSession()}
|
Search {name()}
|
||||||
placeholder="Back to parent session"
|
</span>
|
||||||
label={(x) => x.title}
|
|
||||||
value={(x) => x.id}
|
|
||||||
onSelect={(session) => {
|
|
||||||
// Only navigate if selecting a different session than current parent
|
|
||||||
const currentParent = parentSession()
|
|
||||||
if (session && currentParent && session.id !== currentParent.id) {
|
|
||||||
navigateToSession(session)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md"
|
|
||||||
variant="ghost"
|
|
||||||
/>
|
|
||||||
<div class="text-text-weaker">/</div>
|
|
||||||
<div class="flex items-center gap-1.5 min-w-0">
|
|
||||||
<Tooltip value="Back to parent session">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="flex items-center justify-center gap-1 p-1 rounded hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active transition-colors flex-shrink-0"
|
|
||||||
onClick={() => navigateToSession(parentSession())}
|
|
||||||
>
|
|
||||||
<Icon name="arrow-left" size="small" class="text-icon-base" />
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
|
||||||
</div>
|
<Show when={hotkey()}>{(keybind) => <Keybind>{keybind()}</Keybind>}</Show>
|
||||||
<Show when={currentSession() && !parentSession()}>
|
</button>
|
||||||
<TooltipKeybind class="hidden xl:block" title="New session" keybind={command.keybind("session.new")}>
|
</Portal>
|
||||||
<IconButton as={A} href={`/${params.dir}/session`} icon="edit-small-2" variant="ghost" />
|
)}
|
||||||
</TooltipKeybind>
|
</Show>
|
||||||
</Show>
|
<Show when={rightMount()}>
|
||||||
</div>
|
{(mount) => (
|
||||||
<div class="flex items-center gap-3">
|
<Portal mount={mount()}>
|
||||||
<div class="hidden md:flex items-center gap-1">
|
<div class="flex items-center gap-3">
|
||||||
<Button
|
{/* <div class="hidden md:flex items-center gap-1"> */}
|
||||||
size="small"
|
{/* <Button */}
|
||||||
variant="ghost"
|
{/* size="small" */}
|
||||||
onClick={() => {
|
{/* variant="ghost" */}
|
||||||
dialog.show(() => <DialogSelectServer />)
|
{/* onClick={() => { */}
|
||||||
}}
|
{/* dialog.show(() => <DialogSelectServer />) */}
|
||||||
>
|
{/* }} */}
|
||||||
<div
|
{/* > */}
|
||||||
classList={{
|
{/* <div */}
|
||||||
"size-1.5 rounded-full": true,
|
{/* classList={{ */}
|
||||||
"bg-icon-success-base": server.healthy() === true,
|
{/* "size-1.5 rounded-full": true, */}
|
||||||
"bg-icon-critical-base": server.healthy() === false,
|
{/* "bg-icon-success-base": server.healthy() === true, */}
|
||||||
"bg-border-weak-base": server.healthy() === undefined,
|
{/* "bg-icon-critical-base": server.healthy() === false, */}
|
||||||
}}
|
{/* "bg-border-weak-base": server.healthy() === undefined, */}
|
||||||
/>
|
{/* }} */}
|
||||||
<Icon name="server" size="small" class="text-icon-weak" />
|
{/* /> */}
|
||||||
<span class="text-12-regular text-text-weak truncate max-w-[200px]">{server.name}</span>
|
{/* <Icon name="server" size="small" class="text-icon-weak" /> */}
|
||||||
</Button>
|
{/* <span class="text-12-regular text-text-weak truncate max-w-[200px]">{server.name}</span> */}
|
||||||
<SessionLspIndicator />
|
{/* </Button> */}
|
||||||
<SessionMcpIndicator />
|
{/* <SessionLspIndicator /> */}
|
||||||
</div>
|
{/* <SessionMcpIndicator /> */}
|
||||||
<div class="flex items-center gap-1">
|
{/* </div> */}
|
||||||
<Show when={currentSession()?.summary?.files}>
|
<div class="flex items-center gap-1">
|
||||||
<TooltipKeybind
|
<Show when={currentSession()?.summary?.files}>
|
||||||
class="hidden md:block shrink-0"
|
<TooltipKeybind
|
||||||
title="Toggle review"
|
class="hidden md:block shrink-0"
|
||||||
keybind={command.keybind("review.toggle")}
|
title="Toggle review"
|
||||||
>
|
keybind={command.keybind("review.toggle")}
|
||||||
<Button
|
>
|
||||||
variant="ghost"
|
<Button
|
||||||
class="group/review-toggle size-6 p-0"
|
variant="ghost"
|
||||||
onClick={() => view().reviewPanel.toggle()}
|
class="group/review-toggle size-6 p-0"
|
||||||
|
onClick={() => view().reviewPanel.toggle()}
|
||||||
|
>
|
||||||
|
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
|
||||||
|
<Icon
|
||||||
|
name={view().reviewPanel.opened() ? "layout-right" : "layout-left"}
|
||||||
|
size="small"
|
||||||
|
class="group-hover/review-toggle:hidden"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
name={view().reviewPanel.opened() ? "layout-right-partial" : "layout-left-partial"}
|
||||||
|
size="small"
|
||||||
|
class="hidden group-hover/review-toggle:inline-block"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
name={view().reviewPanel.opened() ? "layout-right-full" : "layout-left-full"}
|
||||||
|
size="small"
|
||||||
|
class="hidden group-active/review-toggle:inline-block"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</TooltipKeybind>
|
||||||
|
</Show>
|
||||||
|
<TooltipKeybind
|
||||||
|
class="hidden md:block shrink-0"
|
||||||
|
title="Toggle terminal"
|
||||||
|
keybind={command.keybind("terminal.toggle")}
|
||||||
>
|
>
|
||||||
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
|
<Button
|
||||||
<Icon
|
variant="ghost"
|
||||||
name={view().reviewPanel.opened() ? "layout-right" : "layout-left"}
|
class="group/terminal-toggle size-6 p-0"
|
||||||
size="small"
|
onClick={() => view().terminal.toggle()}
|
||||||
class="group-hover/review-toggle:hidden"
|
>
|
||||||
/>
|
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
|
||||||
<Icon
|
<Icon
|
||||||
name={view().reviewPanel.opened() ? "layout-right-partial" : "layout-left-partial"}
|
size="small"
|
||||||
size="small"
|
name={view().terminal.opened() ? "layout-bottom-full" : "layout-bottom"}
|
||||||
class="hidden group-hover/review-toggle:inline-block"
|
class="group-hover/terminal-toggle:hidden"
|
||||||
/>
|
/>
|
||||||
<Icon
|
<Icon
|
||||||
name={view().reviewPanel.opened() ? "layout-right-full" : "layout-left-full"}
|
size="small"
|
||||||
size="small"
|
name="layout-bottom-partial"
|
||||||
class="hidden group-active/review-toggle:inline-block"
|
class="hidden group-hover/terminal-toggle:inline-block"
|
||||||
/>
|
/>
|
||||||
</div>
|
<Icon
|
||||||
</Button>
|
size="small"
|
||||||
</TooltipKeybind>
|
name={view().terminal.opened() ? "layout-bottom" : "layout-bottom-full"}
|
||||||
</Show>
|
class="hidden group-active/terminal-toggle:inline-block"
|
||||||
<TooltipKeybind
|
/>
|
||||||
class="hidden md:block shrink-0"
|
</div>
|
||||||
title="Toggle terminal"
|
</Button>
|
||||||
keybind={command.keybind("terminal.toggle")}
|
</TooltipKeybind>
|
||||||
>
|
</div>
|
||||||
<Button variant="ghost" class="group/terminal-toggle size-6 p-0" onClick={() => view().terminal.toggle()}>
|
<Show when={shareEnabled() && currentSession()}>
|
||||||
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
|
<Popover
|
||||||
<Icon
|
title="Share session"
|
||||||
size="small"
|
trigger={
|
||||||
name={view().terminal.opened() ? "layout-bottom-full" : "layout-bottom"}
|
<Tooltip class="shrink-0" value="Share session">
|
||||||
class="group-hover/terminal-toggle:hidden"
|
<IconButton icon="share" variant="ghost" class="" />
|
||||||
/>
|
</Tooltip>
|
||||||
<Icon
|
}
|
||||||
size="small"
|
>
|
||||||
name="layout-bottom-partial"
|
{iife(() => {
|
||||||
class="hidden group-hover/terminal-toggle:inline-block"
|
const [url] = createResource(
|
||||||
/>
|
() => currentSession(),
|
||||||
<Icon
|
async (session) => {
|
||||||
size="small"
|
if (!session) return
|
||||||
name={view().terminal.opened() ? "layout-bottom" : "layout-bottom-full"}
|
let shareURL = session.share?.url
|
||||||
class="hidden group-active/terminal-toggle:inline-block"
|
if (!shareURL) {
|
||||||
/>
|
shareURL = await globalSDK.client.session
|
||||||
</div>
|
.share({ sessionID: session.id, directory: projectDirectory() })
|
||||||
</Button>
|
.then((r) => r.data?.share?.url)
|
||||||
</TooltipKeybind>
|
.catch((e) => {
|
||||||
</div>
|
console.error("Failed to share session", e)
|
||||||
<Show when={shareEnabled() && currentSession()}>
|
return undefined
|
||||||
<Popover
|
})
|
||||||
title="Share session"
|
}
|
||||||
trigger={
|
return shareURL
|
||||||
<Tooltip class="shrink-0" value="Share session">
|
},
|
||||||
<IconButton icon="share" variant="ghost" class="" />
|
{ initialValue: "" },
|
||||||
</Tooltip>
|
)
|
||||||
}
|
return (
|
||||||
>
|
<Show when={url.latest}>
|
||||||
{iife(() => {
|
{(shareUrl) => <TextField value={shareUrl()} readOnly copyable class="w-72" />}
|
||||||
const [url] = createResource(
|
</Show>
|
||||||
() => currentSession(),
|
)
|
||||||
async (session) => {
|
})}
|
||||||
if (!session) return
|
</Popover>
|
||||||
let shareURL = session.share?.url
|
</Show>
|
||||||
if (!shareURL) {
|
</div>
|
||||||
shareURL = await globalSDK.client.session
|
</Portal>
|
||||||
.share({ sessionID: session.id, directory: projectDirectory() })
|
)}
|
||||||
.then((r) => r.data?.share?.url)
|
</Show>
|
||||||
.catch((e) => {
|
</>
|
||||||
console.error("Failed to share session", e)
|
|
||||||
return undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return shareURL
|
|
||||||
},
|
|
||||||
{ initialValue: "" },
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
<Show when={url.latest}>
|
|
||||||
{(shareUrl) => <TextField value={shareUrl()} readOnly copyable class="w-72" />}
|
|
||||||
</Show>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Popover>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,9 +100,12 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
const mod = await import("ghostty-web")
|
const mod = await import("ghostty-web")
|
||||||
ghostty = await mod.Ghostty.load()
|
ghostty = await mod.Ghostty.load()
|
||||||
|
|
||||||
const socket = new WebSocket(
|
const url = new URL(sdk.url + `/pty/${local.pty.id}/connect?directory=${encodeURIComponent(sdk.directory)}`)
|
||||||
sdk.url + `/pty/${local.pty.id}/connect?directory=${encodeURIComponent(sdk.directory)}`,
|
if (window.__OPENCODE__?.serverPassword) {
|
||||||
)
|
url.username = "opencode"
|
||||||
|
url.password = window.__OPENCODE__?.serverPassword
|
||||||
|
}
|
||||||
|
const socket = new WebSocket(url)
|
||||||
ws = socket
|
ws = socket
|
||||||
|
|
||||||
const t = new mod.Terminal({
|
const t = new mod.Terminal({
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import { createEffect, createMemo, Show } from "solid-js"
|
||||||
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
|
import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||||
|
import { useTheme } from "@opencode-ai/ui/theme"
|
||||||
|
|
||||||
|
import { useLayout } from "@/context/layout"
|
||||||
|
import { usePlatform } from "@/context/platform"
|
||||||
|
import { useCommand } from "@/context/command"
|
||||||
|
|
||||||
|
export function Titlebar() {
|
||||||
|
const layout = useLayout()
|
||||||
|
const platform = usePlatform()
|
||||||
|
const command = useCommand()
|
||||||
|
const theme = useTheme()
|
||||||
|
|
||||||
|
const mac = createMemo(() => platform.platform === "desktop" && platform.os === "macos")
|
||||||
|
const reserve = createMemo(
|
||||||
|
() => platform.platform === "desktop" && (platform.os === "windows" || platform.os === "linux"),
|
||||||
|
)
|
||||||
|
const web = createMemo(() => platform.platform === "web")
|
||||||
|
|
||||||
|
const getWin = () => {
|
||||||
|
if (platform.platform !== "desktop") return
|
||||||
|
|
||||||
|
const tauri = (
|
||||||
|
window as unknown as {
|
||||||
|
__TAURI__?: { window?: { getCurrentWindow?: () => { startDragging?: () => Promise<void> } } }
|
||||||
|
}
|
||||||
|
).__TAURI__
|
||||||
|
if (!tauri?.window?.getCurrentWindow) return
|
||||||
|
|
||||||
|
return tauri.window.getCurrentWindow()
|
||||||
|
}
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (platform.platform !== "desktop") return
|
||||||
|
|
||||||
|
const scheme = theme.colorScheme()
|
||||||
|
const value = scheme === "system" ? null : scheme
|
||||||
|
|
||||||
|
const tauri = (window as unknown as { __TAURI__?: { webviewWindow?: { getCurrentWebviewWindow?: () => unknown } } })
|
||||||
|
.__TAURI__
|
||||||
|
const get = tauri?.webviewWindow?.getCurrentWebviewWindow
|
||||||
|
if (!get) return
|
||||||
|
|
||||||
|
const win = get() as { setTheme?: (theme?: "light" | "dark" | null) => Promise<void> }
|
||||||
|
if (!win.setTheme) return
|
||||||
|
|
||||||
|
void win.setTheme(value).catch(() => undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
const interactive = (target: EventTarget | null) => {
|
||||||
|
if (!(target instanceof Element)) return false
|
||||||
|
|
||||||
|
const selector =
|
||||||
|
"button, a, input, textarea, select, option, [role='button'], [role='menuitem'], [contenteditable='true'], [contenteditable='']"
|
||||||
|
|
||||||
|
return !!target.closest(selector)
|
||||||
|
}
|
||||||
|
|
||||||
|
const drag = (e: MouseEvent) => {
|
||||||
|
if (platform.platform !== "desktop") return
|
||||||
|
if (e.buttons !== 1) return
|
||||||
|
if (interactive(e.target)) return
|
||||||
|
|
||||||
|
const win = getWin()
|
||||||
|
if (!win?.startDragging) return
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
void win.startDragging().catch(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header class="h-10 shrink-0 bg-background-base flex items-center relative">
|
||||||
|
<div
|
||||||
|
classList={{
|
||||||
|
"flex items-center w-full min-w-0 pr-2": true,
|
||||||
|
"pl-2": !mac(),
|
||||||
|
}}
|
||||||
|
onMouseDown={drag}
|
||||||
|
>
|
||||||
|
<Show when={mac()}>
|
||||||
|
<div class="w-[72px] h-full shrink-0" data-tauri-drag-region />
|
||||||
|
<div class="xl:hidden w-10 shrink-0 flex items-center justify-center">
|
||||||
|
<IconButton icon="menu" variant="ghost" class="size-8 rounded-md" onClick={layout.mobileSidebar.toggle} />
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<Show when={!mac()}>
|
||||||
|
<div class="xl:hidden w-[48px] shrink-0 flex items-center justify-center">
|
||||||
|
<IconButton icon="menu" variant="ghost" class="size-8 rounded-md" onClick={layout.mobileSidebar.toggle} />
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<TooltipKeybind
|
||||||
|
class={web() ? "hidden xl:flex shrink-0 ml-14" : "hidden xl:flex shrink-0"}
|
||||||
|
placement="bottom"
|
||||||
|
title="Toggle sidebar"
|
||||||
|
keybind={command.keybind("sidebar.toggle")}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
icon={layout.sidebar.opened() ? "layout-left" : "layout-right"}
|
||||||
|
variant="ghost"
|
||||||
|
class="size-8 rounded-md"
|
||||||
|
onClick={layout.sidebar.toggle}
|
||||||
|
/>
|
||||||
|
</TooltipKeybind>
|
||||||
|
<div id="opencode-titlebar-left" class="flex items-center gap-3 min-w-0 px-2" />
|
||||||
|
<div class="flex-1 h-full" data-tauri-drag-region />
|
||||||
|
<div id="opencode-titlebar-right" class="flex items-center gap-3 shrink-0" />
|
||||||
|
<Show when={reserve()}>
|
||||||
|
<div class="w-[120px] h-full shrink-0" data-tauri-drag-region />
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
<div class="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||||
|
<div id="opencode-titlebar-center" class="pointer-events-auto" />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import { createMemo, createSignal, onCleanup, onMount, Show, type Accessor } from "solid-js"
|
import { createMemo, createSignal, onCleanup, onMount, type Accessor } from "solid-js"
|
||||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
|
||||||
import { List } from "@opencode-ai/ui/list"
|
|
||||||
|
|
||||||
const IS_MAC = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform)
|
const IS_MAC = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform)
|
||||||
|
|
||||||
@@ -107,74 +105,27 @@ export function formatKeybind(config: string): string {
|
|||||||
if (kb.meta) parts.push(IS_MAC ? "⌘" : "Meta")
|
if (kb.meta) parts.push(IS_MAC ? "⌘" : "Meta")
|
||||||
|
|
||||||
if (kb.key) {
|
if (kb.key) {
|
||||||
const displayKey = kb.key.length === 1 ? kb.key.toUpperCase() : kb.key.charAt(0).toUpperCase() + kb.key.slice(1)
|
const arrows: Record<string, string> = {
|
||||||
|
arrowup: "↑",
|
||||||
|
arrowdown: "↓",
|
||||||
|
arrowleft: "←",
|
||||||
|
arrowright: "→",
|
||||||
|
}
|
||||||
|
const displayKey =
|
||||||
|
arrows[kb.key.toLowerCase()] ??
|
||||||
|
(kb.key.length === 1 ? kb.key.toUpperCase() : kb.key.charAt(0).toUpperCase() + kb.key.slice(1))
|
||||||
parts.push(displayKey)
|
parts.push(displayKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
return IS_MAC ? parts.join("") : parts.join("+")
|
return IS_MAC ? parts.join("") : parts.join("+")
|
||||||
}
|
}
|
||||||
|
|
||||||
function DialogCommand(props: { options: CommandOption[] }) {
|
|
||||||
const dialog = useDialog()
|
|
||||||
let cleanup: (() => void) | void
|
|
||||||
let committed = false
|
|
||||||
|
|
||||||
const handleMove = (option: CommandOption | undefined) => {
|
|
||||||
cleanup?.()
|
|
||||||
cleanup = option?.onHighlight?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSelect = (option: CommandOption | undefined) => {
|
|
||||||
if (option) {
|
|
||||||
committed = true
|
|
||||||
cleanup = undefined
|
|
||||||
dialog.close()
|
|
||||||
option.onSelect?.("palette")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onCleanup(() => {
|
|
||||||
if (!committed) {
|
|
||||||
cleanup?.()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog title="Commands">
|
|
||||||
<List
|
|
||||||
search={{ placeholder: "Search commands", autofocus: true }}
|
|
||||||
emptyMessage="No commands found"
|
|
||||||
items={() => props.options.filter((x) => !x.id.startsWith("suggested.") || !x.disabled)}
|
|
||||||
key={(x) => x?.id}
|
|
||||||
filterKeys={["title", "description", "category"]}
|
|
||||||
groupBy={(x) => x.category ?? ""}
|
|
||||||
onMove={handleMove}
|
|
||||||
onSelect={handleSelect}
|
|
||||||
>
|
|
||||||
{(option) => (
|
|
||||||
<div class="w-full flex items-center justify-between gap-4">
|
|
||||||
<div class="flex items-center gap-2 min-w-0">
|
|
||||||
<span class="text-14-regular text-text-strong whitespace-nowrap">{option.title}</span>
|
|
||||||
<Show when={option.description}>
|
|
||||||
<span class="text-14-regular text-text-weak truncate">{option.description}</span>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<Show when={option.keybind}>
|
|
||||||
<span class="text-12-regular text-text-subtle shrink-0">{formatKeybind(option.keybind!)}</span>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</List>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const { use: useCommand, provider: CommandProvider } = createSimpleContext({
|
export const { use: useCommand, provider: CommandProvider } = createSimpleContext({
|
||||||
name: "Command",
|
name: "Command",
|
||||||
init: () => {
|
init: () => {
|
||||||
|
const dialog = useDialog()
|
||||||
const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
|
const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
|
||||||
const [suspendCount, setSuspendCount] = createSignal(0)
|
const [suspendCount, setSuspendCount] = createSignal(0)
|
||||||
const dialog = useDialog()
|
|
||||||
|
|
||||||
const options = createMemo(() => {
|
const options = createMemo(() => {
|
||||||
const seen = new Set<string>()
|
const seen = new Set<string>()
|
||||||
@@ -202,14 +153,21 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
|
|||||||
|
|
||||||
const suspended = () => suspendCount() > 0
|
const suspended = () => suspendCount() > 0
|
||||||
|
|
||||||
const showPalette = () => {
|
const run = (id: string, source?: "palette" | "keybind" | "slash") => {
|
||||||
if (!dialog.active) {
|
for (const option of options()) {
|
||||||
dialog.show(() => <DialogCommand options={options().filter((x) => !x.disabled)} />)
|
if (option.id === id || option.id === "suggested." + id) {
|
||||||
|
option.onSelect?.(source)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showPalette = () => {
|
||||||
|
run("file.open", "palette")
|
||||||
|
}
|
||||||
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
if (suspended()) return
|
if (suspended() || dialog.active) return
|
||||||
|
|
||||||
const paletteKeybinds = parseKeybind("mod+shift+p")
|
const paletteKeybinds = parseKeybind("mod+shift+p")
|
||||||
if (matchKeybind(paletteKeybinds, event)) {
|
if (matchKeybind(paletteKeybinds, event)) {
|
||||||
@@ -248,12 +206,7 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
trigger(id: string, source?: "palette" | "keybind" | "slash") {
|
trigger(id: string, source?: "palette" | "keybind" | "slash") {
|
||||||
for (const option of options()) {
|
run(id, source)
|
||||||
if (option.id === id || option.id === "suggested." + id) {
|
|
||||||
option.onSelect?.(source)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
keybind(id: string) {
|
keybind(id: string) {
|
||||||
const option = options().find((x) => x.id === id || x.id === "suggested." + id)
|
const option = options().find((x) => x.id === id || x.id === "suggested." + id)
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
|
|||||||
name: "GlobalSDK",
|
name: "GlobalSDK",
|
||||||
init: () => {
|
init: () => {
|
||||||
const server = useServer()
|
const server = useServer()
|
||||||
|
const platform = usePlatform()
|
||||||
const abort = new AbortController()
|
const abort = new AbortController()
|
||||||
|
|
||||||
const eventSdk = createOpencodeClient({
|
const eventSdk = createOpencodeClient({
|
||||||
baseUrl: server.url,
|
baseUrl: server.url,
|
||||||
signal: abort.signal,
|
signal: abort.signal,
|
||||||
|
fetch: platform.fetch,
|
||||||
})
|
})
|
||||||
const emitter = createGlobalEmitter<{
|
const emitter = createGlobalEmitter<{
|
||||||
[key: string]: Event
|
[key: string]: Event
|
||||||
@@ -93,7 +95,6 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
|
|||||||
stop()
|
stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
const platform = usePlatform()
|
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: server.url,
|
baseUrl: server.url,
|
||||||
fetch: platform.fetch,
|
fetch: platform.fetch,
|
||||||
|
|||||||
@@ -16,16 +16,32 @@ import {
|
|||||||
type LspStatus,
|
type LspStatus,
|
||||||
type VcsInfo,
|
type VcsInfo,
|
||||||
type PermissionRequest,
|
type PermissionRequest,
|
||||||
|
type QuestionRequest,
|
||||||
createOpencodeClient,
|
createOpencodeClient,
|
||||||
} from "@opencode-ai/sdk/v2/client"
|
} from "@opencode-ai/sdk/v2/client"
|
||||||
import { createStore, produce, reconcile } from "solid-js/store"
|
import { createStore, produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store"
|
||||||
import { Binary } from "@opencode-ai/util/binary"
|
import { Binary } from "@opencode-ai/util/binary"
|
||||||
import { retry } from "@opencode-ai/util/retry"
|
import { retry } from "@opencode-ai/util/retry"
|
||||||
import { useGlobalSDK } from "./global-sdk"
|
import { useGlobalSDK } from "./global-sdk"
|
||||||
import { ErrorPage, type InitError } from "../pages/error"
|
import { ErrorPage, type InitError } from "../pages/error"
|
||||||
import { batch, createContext, useContext, onCleanup, onMount, type ParentProps, Switch, Match } from "solid-js"
|
import {
|
||||||
|
batch,
|
||||||
|
createContext,
|
||||||
|
createEffect,
|
||||||
|
getOwner,
|
||||||
|
runWithOwner,
|
||||||
|
useContext,
|
||||||
|
onCleanup,
|
||||||
|
onMount,
|
||||||
|
type Accessor,
|
||||||
|
type ParentProps,
|
||||||
|
Switch,
|
||||||
|
Match,
|
||||||
|
} from "solid-js"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
import { getFilename } from "@opencode-ai/util/path"
|
import { getFilename } from "@opencode-ai/util/path"
|
||||||
|
import { usePlatform } from "./platform"
|
||||||
|
import { Persist, persisted } from "@/utils/persist"
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
status: "loading" | "partial" | "complete"
|
status: "loading" | "partial" | "complete"
|
||||||
@@ -36,6 +52,7 @@ type State = {
|
|||||||
config: Config
|
config: Config
|
||||||
path: Path
|
path: Path
|
||||||
session: Session[]
|
session: Session[]
|
||||||
|
sessionTotal: number
|
||||||
session_status: {
|
session_status: {
|
||||||
[sessionID: string]: SessionStatus
|
[sessionID: string]: SessionStatus
|
||||||
}
|
}
|
||||||
@@ -48,6 +65,9 @@ type State = {
|
|||||||
permission: {
|
permission: {
|
||||||
[sessionID: string]: PermissionRequest[]
|
[sessionID: string]: PermissionRequest[]
|
||||||
}
|
}
|
||||||
|
question: {
|
||||||
|
[sessionID: string]: QuestionRequest[]
|
||||||
|
}
|
||||||
mcp: {
|
mcp: {
|
||||||
[name: string]: McpStatus
|
[name: string]: McpStatus
|
||||||
}
|
}
|
||||||
@@ -62,8 +82,18 @@ type State = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VcsCache = {
|
||||||
|
store: Store<{ value: VcsInfo | undefined }>
|
||||||
|
setStore: SetStoreFunction<{ value: VcsInfo | undefined }>
|
||||||
|
ready: Accessor<boolean>
|
||||||
|
}
|
||||||
|
|
||||||
function createGlobalSync() {
|
function createGlobalSync() {
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
|
const platform = usePlatform()
|
||||||
|
const owner = getOwner()
|
||||||
|
if (!owner) throw new Error("GlobalSync must be created within owner")
|
||||||
|
const vcsCache = new Map<string, VcsCache>()
|
||||||
const [globalStore, setGlobalStore] = createStore<{
|
const [globalStore, setGlobalStore] = createStore<{
|
||||||
ready: boolean
|
ready: boolean
|
||||||
error?: InitError
|
error?: InitError
|
||||||
@@ -79,52 +109,81 @@ function createGlobalSync() {
|
|||||||
provider_auth: {},
|
provider_auth: {},
|
||||||
})
|
})
|
||||||
|
|
||||||
const children: Record<string, ReturnType<typeof createStore<State>>> = {}
|
const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
|
||||||
|
|
||||||
function child(directory: string) {
|
function child(directory: string) {
|
||||||
if (!directory) console.error("No directory provided")
|
if (!directory) console.error("No directory provided")
|
||||||
if (!children[directory]) {
|
if (!children[directory]) {
|
||||||
children[directory] = createStore<State>({
|
const cache = runWithOwner(owner, () =>
|
||||||
project: "",
|
persisted(
|
||||||
provider: { all: [], connected: [], default: {} },
|
Persist.workspace(directory, "vcs", ["vcs.v1"]),
|
||||||
config: {},
|
createStore({ value: undefined as VcsInfo | undefined }),
|
||||||
path: { state: "", config: "", worktree: "", directory: "", home: "" },
|
),
|
||||||
status: "loading" as const,
|
)
|
||||||
agent: [],
|
if (!cache) throw new Error("Failed to create persisted cache")
|
||||||
command: [],
|
vcsCache.set(directory, { store: cache[0], setStore: cache[1], ready: cache[3] })
|
||||||
session: [],
|
|
||||||
session_status: {},
|
const init = () => {
|
||||||
session_diff: {},
|
children[directory] = createStore<State>({
|
||||||
todo: {},
|
project: "",
|
||||||
permission: {},
|
provider: { all: [], connected: [], default: {} },
|
||||||
mcp: {},
|
config: {},
|
||||||
lsp: [],
|
path: { state: "", config: "", worktree: "", directory: "", home: "" },
|
||||||
vcs: undefined,
|
status: "loading" as const,
|
||||||
limit: 5,
|
agent: [],
|
||||||
message: {},
|
command: [],
|
||||||
part: {},
|
session: [],
|
||||||
})
|
sessionTotal: 0,
|
||||||
bootstrapInstance(directory)
|
session_status: {},
|
||||||
|
session_diff: {},
|
||||||
|
todo: {},
|
||||||
|
permission: {},
|
||||||
|
question: {},
|
||||||
|
mcp: {},
|
||||||
|
lsp: [],
|
||||||
|
vcs: cache[0].value,
|
||||||
|
limit: 5,
|
||||||
|
message: {},
|
||||||
|
part: {},
|
||||||
|
})
|
||||||
|
bootstrapInstance(directory)
|
||||||
|
}
|
||||||
|
|
||||||
|
runWithOwner(owner, init)
|
||||||
}
|
}
|
||||||
return children[directory]
|
const childStore = children[directory]
|
||||||
|
if (!childStore) throw new Error("Failed to create store")
|
||||||
|
return childStore
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSessions(directory: string) {
|
async function loadSessions(directory: string) {
|
||||||
const [store, setStore] = child(directory)
|
const [store, setStore] = child(directory)
|
||||||
globalSDK.client.session
|
const limit = store.limit
|
||||||
.list({ directory })
|
|
||||||
|
return globalSDK.client.session
|
||||||
|
.list({ directory, roots: true })
|
||||||
.then((x) => {
|
.then((x) => {
|
||||||
const fourHoursAgo = Date.now() - 4 * 60 * 60 * 1000
|
|
||||||
const nonArchived = (x.data ?? [])
|
const nonArchived = (x.data ?? [])
|
||||||
.filter((s) => !!s?.id)
|
.filter((s) => !!s?.id)
|
||||||
.filter((s) => !s.time?.archived)
|
.filter((s) => !s.time?.archived)
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => a.id.localeCompare(b.id))
|
.sort((a, b) => a.id.localeCompare(b.id))
|
||||||
|
|
||||||
|
const sandboxWorkspace = globalStore.project.some((p) => (p.sandboxes ?? []).includes(directory))
|
||||||
|
if (sandboxWorkspace) {
|
||||||
|
setStore("session", reconcile(nonArchived, { key: "id" }))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fourHoursAgo = Date.now() - 4 * 60 * 60 * 1000
|
||||||
// Include up to the limit, plus any updated in the last 4 hours
|
// Include up to the limit, plus any updated in the last 4 hours
|
||||||
const sessions = nonArchived.filter((s, i) => {
|
const sessions = nonArchived.filter((s, i) => {
|
||||||
if (i < store.limit) return true
|
if (i < limit) return true
|
||||||
const updated = new Date(s.time?.updated ?? s.time?.created).getTime()
|
const updated = new Date(s.time?.updated ?? s.time?.created).getTime()
|
||||||
return updated > fourHoursAgo
|
return updated > fourHoursAgo
|
||||||
})
|
})
|
||||||
|
// Store total session count (used for "load more" pagination)
|
||||||
|
setStore("sessionTotal", nonArchived.length)
|
||||||
setStore("session", reconcile(sessions, { key: "id" }))
|
setStore("session", reconcile(sessions, { key: "id" }))
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@@ -137,12 +196,22 @@ function createGlobalSync() {
|
|||||||
async function bootstrapInstance(directory: string) {
|
async function bootstrapInstance(directory: string) {
|
||||||
if (!directory) return
|
if (!directory) return
|
||||||
const [store, setStore] = child(directory)
|
const [store, setStore] = child(directory)
|
||||||
|
const cache = vcsCache.get(directory)
|
||||||
|
if (!cache) return
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: globalSDK.url,
|
baseUrl: globalSDK.url,
|
||||||
|
fetch: platform.fetch,
|
||||||
directory,
|
directory,
|
||||||
throwOnError: true,
|
throwOnError: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (!cache.ready()) return
|
||||||
|
const cached = cache.store.value
|
||||||
|
if (!cached?.branch) return
|
||||||
|
setStore("vcs", (value) => value ?? cached)
|
||||||
|
})
|
||||||
|
|
||||||
const blockingRequests = {
|
const blockingRequests = {
|
||||||
project: () => sdk.project.current().then((x) => setStore("project", x.data!.id)),
|
project: () => sdk.project.current().then((x) => setStore("project", x.data!.id)),
|
||||||
provider: () =>
|
provider: () =>
|
||||||
@@ -172,7 +241,11 @@ function createGlobalSync() {
|
|||||||
loadSessions(directory),
|
loadSessions(directory),
|
||||||
sdk.mcp.status().then((x) => setStore("mcp", x.data!)),
|
sdk.mcp.status().then((x) => setStore("mcp", x.data!)),
|
||||||
sdk.lsp.status().then((x) => setStore("lsp", x.data!)),
|
sdk.lsp.status().then((x) => setStore("lsp", x.data!)),
|
||||||
sdk.vcs.get().then((x) => setStore("vcs", x.data)),
|
sdk.vcs.get().then((x) => {
|
||||||
|
const next = x.data ?? store.vcs
|
||||||
|
setStore("vcs", next)
|
||||||
|
if (next?.branch) cache.setStore("value", next)
|
||||||
|
}),
|
||||||
sdk.permission.list().then((x) => {
|
sdk.permission.list().then((x) => {
|
||||||
const grouped: Record<string, PermissionRequest[]> = {}
|
const grouped: Record<string, PermissionRequest[]> = {}
|
||||||
for (const perm of x.data ?? []) {
|
for (const perm of x.data ?? []) {
|
||||||
@@ -205,6 +278,38 @@ function createGlobalSync() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
sdk.question.list().then((x) => {
|
||||||
|
const grouped: Record<string, QuestionRequest[]> = {}
|
||||||
|
for (const question of x.data ?? []) {
|
||||||
|
if (!question?.id || !question.sessionID) continue
|
||||||
|
const existing = grouped[question.sessionID]
|
||||||
|
if (existing) {
|
||||||
|
existing.push(question)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
grouped[question.sessionID] = [question]
|
||||||
|
}
|
||||||
|
|
||||||
|
batch(() => {
|
||||||
|
for (const sessionID of Object.keys(store.question)) {
|
||||||
|
if (grouped[sessionID]) continue
|
||||||
|
setStore("question", sessionID, [])
|
||||||
|
}
|
||||||
|
for (const [sessionID, questions] of Object.entries(grouped)) {
|
||||||
|
setStore(
|
||||||
|
"question",
|
||||||
|
sessionID,
|
||||||
|
reconcile(
|
||||||
|
questions
|
||||||
|
.filter((q) => !!q?.id)
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => a.id.localeCompare(b.id)),
|
||||||
|
{ key: "id" },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
setStore("status", "complete")
|
setStore("status", "complete")
|
||||||
})
|
})
|
||||||
@@ -246,6 +351,23 @@ function createGlobalSync() {
|
|||||||
bootstrapInstance(directory)
|
bootstrapInstance(directory)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case "session.created": {
|
||||||
|
const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
|
||||||
|
if (result.found) {
|
||||||
|
setStore("session", result.index, reconcile(event.properties.info))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
setStore(
|
||||||
|
"session",
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(result.index, 0, event.properties.info)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if (!event.properties.info.parentID) {
|
||||||
|
setStore("sessionTotal", store.sessionTotal + 1)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
case "session.updated": {
|
case "session.updated": {
|
||||||
const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
|
const result = Binary.search(store.session, event.properties.info.id, (s) => s.id)
|
||||||
if (event.properties.info.time.archived) {
|
if (event.properties.info.time.archived) {
|
||||||
@@ -257,6 +379,8 @@ function createGlobalSync() {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (event.properties.info.parentID) break
|
||||||
|
setStore("sessionTotal", (value) => Math.max(0, value - 1))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (result.found) {
|
if (result.found) {
|
||||||
@@ -353,7 +477,10 @@ function createGlobalSync() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "vcs.branch.updated": {
|
case "vcs.branch.updated": {
|
||||||
setStore("vcs", { branch: event.properties.branch })
|
const next = { branch: event.properties.branch }
|
||||||
|
setStore("vcs", next)
|
||||||
|
const cache = vcsCache.get(directory)
|
||||||
|
if (cache) cache.setStore("value", next)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "permission.asked": {
|
case "permission.asked": {
|
||||||
@@ -393,9 +520,48 @@ function createGlobalSync() {
|
|||||||
)
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case "question.asked": {
|
||||||
|
const sessionID = event.properties.sessionID
|
||||||
|
const questions = store.question[sessionID]
|
||||||
|
if (!questions) {
|
||||||
|
setStore("question", sessionID, [event.properties])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = Binary.search(questions, event.properties.id, (q) => q.id)
|
||||||
|
if (result.found) {
|
||||||
|
setStore("question", sessionID, result.index, reconcile(event.properties))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
setStore(
|
||||||
|
"question",
|
||||||
|
sessionID,
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(result.index, 0, event.properties)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "question.replied":
|
||||||
|
case "question.rejected": {
|
||||||
|
const questions = store.question[event.properties.sessionID]
|
||||||
|
if (!questions) break
|
||||||
|
const result = Binary.search(questions, event.properties.requestID, (q) => q.id)
|
||||||
|
if (!result.found) break
|
||||||
|
setStore(
|
||||||
|
"question",
|
||||||
|
event.properties.sessionID,
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(result.index, 1)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
case "lsp.updated": {
|
case "lsp.updated": {
|
||||||
const sdk = createOpencodeClient({
|
const sdk = createOpencodeClient({
|
||||||
baseUrl: globalSDK.url,
|
baseUrl: globalSDK.url,
|
||||||
|
fetch: platform.fetch,
|
||||||
directory,
|
directory,
|
||||||
throwOnError: true,
|
throwOnError: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -47,12 +47,34 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
const globalSdk = useGlobalSDK()
|
const globalSdk = useGlobalSDK()
|
||||||
const globalSync = useGlobalSync()
|
const globalSync = useGlobalSync()
|
||||||
const server = useServer()
|
const server = useServer()
|
||||||
|
|
||||||
|
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||||
|
typeof value === "object" && value !== null && !Array.isArray(value)
|
||||||
|
|
||||||
|
const migrate = (value: unknown) => {
|
||||||
|
if (!isRecord(value)) return value
|
||||||
|
const sidebar = value.sidebar
|
||||||
|
if (!isRecord(sidebar)) return value
|
||||||
|
if (typeof sidebar.workspaces !== "boolean") return value
|
||||||
|
return {
|
||||||
|
...value,
|
||||||
|
sidebar: {
|
||||||
|
...sidebar,
|
||||||
|
workspaces: {},
|
||||||
|
workspacesDefault: sidebar.workspaces,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = Persist.global("layout", ["layout.v6"])
|
||||||
const [store, setStore, _, ready] = persisted(
|
const [store, setStore, _, ready] = persisted(
|
||||||
Persist.global("layout", ["layout.v6"]),
|
{ ...target, migrate },
|
||||||
createStore({
|
createStore({
|
||||||
sidebar: {
|
sidebar: {
|
||||||
opened: false,
|
opened: false,
|
||||||
width: 280,
|
width: 344,
|
||||||
|
workspaces: {} as Record<string, boolean>,
|
||||||
|
workspacesDefault: false,
|
||||||
},
|
},
|
||||||
terminal: {
|
terminal: {
|
||||||
height: 280,
|
height: 280,
|
||||||
@@ -304,6 +326,16 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
resize(width: number) {
|
resize(width: number) {
|
||||||
setStore("sidebar", "width", width)
|
setStore("sidebar", "width", width)
|
||||||
},
|
},
|
||||||
|
workspaces(directory: string) {
|
||||||
|
return createMemo(() => store.sidebar.workspaces[directory] ?? store.sidebar.workspacesDefault ?? false)
|
||||||
|
},
|
||||||
|
setWorkspaces(directory: string, value: boolean) {
|
||||||
|
setStore("sidebar", "workspaces", directory, value)
|
||||||
|
},
|
||||||
|
toggleWorkspaces(directory: string) {
|
||||||
|
const current = store.sidebar.workspaces[directory] ?? store.sidebar.workspacesDefault ?? false
|
||||||
|
setStore("sidebar", "workspaces", directory, !current)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
terminal: {
|
terminal: {
|
||||||
height: createMemo(() => store.terminal.height),
|
height: createMemo(() => store.terminal.height),
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ export type Platform = {
|
|||||||
/** Platform discriminator */
|
/** Platform discriminator */
|
||||||
platform: "web" | "desktop"
|
platform: "web" | "desktop"
|
||||||
|
|
||||||
|
/** Desktop OS (Tauri only) */
|
||||||
|
os?: "macos" | "windows" | "linux"
|
||||||
|
|
||||||
/** App version */
|
/** App version */
|
||||||
version?: string
|
version?: string
|
||||||
|
|
||||||
@@ -37,6 +40,12 @@ export type Platform = {
|
|||||||
|
|
||||||
/** Fetch override */
|
/** Fetch override */
|
||||||
fetch?: typeof fetch
|
fetch?: typeof fetch
|
||||||
|
|
||||||
|
/** Get the configured default server URL (desktop only) */
|
||||||
|
getDefaultServerUrl?(): Promise<string | null>
|
||||||
|
|
||||||
|
/** Set the default server URL to use on app startup (desktop only) */
|
||||||
|
setDefaultServerUrl?(url: string | null): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const { use: usePlatform, provider: PlatformProvider } = createSimpleContext({
|
export const { use: usePlatform, provider: PlatformProvider } = createSimpleContext({
|
||||||
|
|||||||
@@ -16,10 +16,7 @@ export function normalizeServerUrl(input: string) {
|
|||||||
|
|
||||||
export function serverDisplayName(url: string) {
|
export function serverDisplayName(url: string) {
|
||||||
if (!url) return ""
|
if (!url) return ""
|
||||||
return url
|
return url.replace(/^https?:\/\//, "").replace(/\/+$/, "")
|
||||||
.replace(/^https?:\/\//, "")
|
|
||||||
.replace(/\/+$/, "")
|
|
||||||
.split("/")[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function projectsKey(url: string) {
|
function projectsKey(url: string) {
|
||||||
@@ -39,6 +36,7 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
|
|||||||
createStore({
|
createStore({
|
||||||
list: [] as string[],
|
list: [] as string[],
|
||||||
projects: {} as Record<string, StoredProject[]>,
|
projects: {} as Record<string, StoredProject[]>,
|
||||||
|
lastProject: {} as Record<string, string>,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -200,6 +198,16 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
|
|||||||
result.splice(toIndex, 0, item)
|
result.splice(toIndex, 0, item)
|
||||||
setStore("projects", key, result)
|
setStore("projects", key, result)
|
||||||
},
|
},
|
||||||
|
last() {
|
||||||
|
const key = origin()
|
||||||
|
if (!key) return
|
||||||
|
return store.lastProject[key]
|
||||||
|
},
|
||||||
|
touch(directory: string) {
|
||||||
|
const key = origin()
|
||||||
|
if (!key) return
|
||||||
|
setStore("lastProject", key, directory)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const [store, setStore] = globalSync.child(sdk.directory)
|
const [store, setStore] = globalSync.child(sdk.directory)
|
||||||
const absolute = (path: string) => (store.path.directory + "/" + path).replace("//", "/")
|
const absolute = (path: string) => (store.path.directory + "/" + path).replace("//", "/")
|
||||||
const chunk = 200
|
const chunk = 400
|
||||||
const inflight = new Map<string, Promise<void>>()
|
const inflight = new Map<string, Promise<void>>()
|
||||||
const inflightDiff = new Map<string, Promise<void>>()
|
const inflightDiff = new Map<string, Promise<void>>()
|
||||||
const inflightTodo = new Map<string, Promise<void>>()
|
const inflightTodo = new Map<string, Promise<void>>()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Persist, persisted } from "@/utils/persist"
|
|||||||
export type LocalPTY = {
|
export type LocalPTY = {
|
||||||
id: string
|
id: string
|
||||||
title: string
|
title: string
|
||||||
|
titleNumber: number
|
||||||
rows?: number
|
rows?: number
|
||||||
cols?: number
|
cols?: number
|
||||||
buffer?: string
|
buffer?: string
|
||||||
@@ -42,8 +43,20 @@ function createTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, id:
|
|||||||
all: createMemo(() => Object.values(store.all)),
|
all: createMemo(() => Object.values(store.all)),
|
||||||
active: createMemo(() => store.active),
|
active: createMemo(() => store.active),
|
||||||
new() {
|
new() {
|
||||||
|
const existingTitleNumbers = new Set(
|
||||||
|
store.all.map((pty) => {
|
||||||
|
const match = pty.titleNumber
|
||||||
|
return match
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
let nextNumber = 1
|
||||||
|
while (existingTitleNumbers.has(nextNumber)) {
|
||||||
|
nextNumber++
|
||||||
|
}
|
||||||
|
|
||||||
sdk.client.pty
|
sdk.client.pty
|
||||||
.create({ title: `Terminal ${store.all.length + 1}` })
|
.create({ title: `Terminal ${nextNumber}` })
|
||||||
.then((pty) => {
|
.then((pty) => {
|
||||||
const id = pty.data?.id
|
const id = pty.data?.id
|
||||||
if (!id) return
|
if (!id) return
|
||||||
@@ -52,6 +65,7 @@ function createTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, id:
|
|||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
title: pty.data?.title ?? "Terminal",
|
title: pty.data?.title ?? "Terminal",
|
||||||
|
titleNumber: nextNumber,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
setStore("active", id)
|
setStore("active", id)
|
||||||
|
|||||||
@@ -5,3 +5,7 @@
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*[data-tauri-drag-region] {
|
||||||
|
app-region: drag;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { LocalProvider } from "@/context/local"
|
|||||||
import { base64Decode } from "@opencode-ai/util/encode"
|
import { base64Decode } from "@opencode-ai/util/encode"
|
||||||
import { DataProvider } from "@opencode-ai/ui/context"
|
import { DataProvider } from "@opencode-ai/ui/context"
|
||||||
import { iife } from "@opencode-ai/util/iife"
|
import { iife } from "@opencode-ai/util/iife"
|
||||||
|
import type { QuestionAnswer } from "@opencode-ai/sdk/v2"
|
||||||
|
|
||||||
export default function Layout(props: ParentProps) {
|
export default function Layout(props: ParentProps) {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
@@ -27,6 +28,11 @@ export default function Layout(props: ParentProps) {
|
|||||||
response: "once" | "always" | "reject"
|
response: "once" | "always" | "reject"
|
||||||
}) => sdk.client.permission.respond(input)
|
}) => sdk.client.permission.respond(input)
|
||||||
|
|
||||||
|
const replyToQuestion = (input: { requestID: string; answers: QuestionAnswer[] }) =>
|
||||||
|
sdk.client.question.reply(input)
|
||||||
|
|
||||||
|
const rejectQuestion = (input: { requestID: string }) => sdk.client.question.reject(input)
|
||||||
|
|
||||||
const navigateToSession = (sessionID: string) => {
|
const navigateToSession = (sessionID: string) => {
|
||||||
navigate(`/${params.dir}/session/${sessionID}`)
|
navigate(`/${params.dir}/session/${sessionID}`)
|
||||||
}
|
}
|
||||||
@@ -36,6 +42,8 @@ export default function Layout(props: ParentProps) {
|
|||||||
data={sync.data}
|
data={sync.data}
|
||||||
directory={directory()}
|
directory={directory()}
|
||||||
onPermissionRespond={respond}
|
onPermissionRespond={respond}
|
||||||
|
onQuestionReply={replyToQuestion}
|
||||||
|
onQuestionReject={rejectQuestion}
|
||||||
onNavigateToSession={navigateToSession}
|
onNavigateToSession={navigateToSession}
|
||||||
>
|
>
|
||||||
<LocalProvider>{props.children}</LocalProvider>
|
<LocalProvider>{props.children}</LocalProvider>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { useGlobalSync } from "@/context/global-sync"
|
|
||||||
import { createMemo, For, Match, Show, Switch } from "solid-js"
|
import { createMemo, For, Match, Show, Switch } from "solid-js"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { Logo } from "@opencode-ai/ui/logo"
|
import { Logo } from "@opencode-ai/ui/logo"
|
||||||
@@ -12,6 +11,7 @@ import { useDialog } from "@opencode-ai/ui/context/dialog"
|
|||||||
import { DialogSelectDirectory } from "@/components/dialog-select-directory"
|
import { DialogSelectDirectory } from "@/components/dialog-select-directory"
|
||||||
import { DialogSelectServer } from "@/components/dialog-select-server"
|
import { DialogSelectServer } from "@/components/dialog-select-server"
|
||||||
import { useServer } from "@/context/server"
|
import { useServer } from "@/context/server"
|
||||||
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const sync = useGlobalSync()
|
const sync = useGlobalSync()
|
||||||
@@ -24,6 +24,7 @@ export default function Home() {
|
|||||||
|
|
||||||
function openProject(directory: string) {
|
function openProject(directory: string) {
|
||||||
layout.projects.open(directory)
|
layout.projects.open(directory)
|
||||||
|
server.projects.touch(directory)
|
||||||
navigate(`/${base64Encode(directory)}`)
|
navigate(`/${base64Encode(directory)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1048
-538
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@ import { useDialog } from "@opencode-ai/ui/context/dialog"
|
|||||||
import { DialogSelectFile } from "@/components/dialog-select-file"
|
import { DialogSelectFile } from "@/components/dialog-select-file"
|
||||||
import { DialogSelectModel } from "@/components/dialog-select-model"
|
import { DialogSelectModel } from "@/components/dialog-select-model"
|
||||||
import { DialogSelectMcp } from "@/components/dialog-select-mcp"
|
import { DialogSelectMcp } from "@/components/dialog-select-mcp"
|
||||||
|
import { DialogFork } from "@/components/dialog-fork"
|
||||||
import { useCommand } from "@/context/command"
|
import { useCommand } from "@/context/command"
|
||||||
import { useNavigate, useParams } from "@solidjs/router"
|
import { useNavigate, useParams } from "@solidjs/router"
|
||||||
import { UserMessage } from "@opencode-ai/sdk/v2"
|
import { UserMessage } from "@opencode-ai/sdk/v2"
|
||||||
@@ -418,7 +419,6 @@ export default function Page() {
|
|||||||
{
|
{
|
||||||
id: "session.new",
|
id: "session.new",
|
||||||
title: "New session",
|
title: "New session",
|
||||||
description: "Create a new session",
|
|
||||||
category: "Session",
|
category: "Session",
|
||||||
keybind: "mod+shift+s",
|
keybind: "mod+shift+s",
|
||||||
slash: "new",
|
slash: "new",
|
||||||
@@ -427,7 +427,7 @@ export default function Page() {
|
|||||||
{
|
{
|
||||||
id: "file.open",
|
id: "file.open",
|
||||||
title: "Open file",
|
title: "Open file",
|
||||||
description: "Search and open a file",
|
description: "Search files and commands",
|
||||||
category: "File",
|
category: "File",
|
||||||
keybind: "mod+p",
|
keybind: "mod+p",
|
||||||
slash: "open",
|
slash: "open",
|
||||||
@@ -436,7 +436,7 @@ export default function Page() {
|
|||||||
{
|
{
|
||||||
id: "terminal.toggle",
|
id: "terminal.toggle",
|
||||||
title: "Toggle terminal",
|
title: "Toggle terminal",
|
||||||
description: "Show or hide the terminal",
|
description: "",
|
||||||
category: "View",
|
category: "View",
|
||||||
keybind: "ctrl+`",
|
keybind: "ctrl+`",
|
||||||
slash: "terminal",
|
slash: "terminal",
|
||||||
@@ -445,7 +445,7 @@ export default function Page() {
|
|||||||
{
|
{
|
||||||
id: "review.toggle",
|
id: "review.toggle",
|
||||||
title: "Toggle review",
|
title: "Toggle review",
|
||||||
description: "Show or hide the review panel",
|
description: "",
|
||||||
category: "View",
|
category: "View",
|
||||||
keybind: "mod+shift+r",
|
keybind: "mod+shift+r",
|
||||||
onSelect: () => view().reviewPanel.toggle(),
|
onSelect: () => view().reviewPanel.toggle(),
|
||||||
@@ -645,6 +645,15 @@ export default function Page() {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "session.fork",
|
||||||
|
title: "Fork from message",
|
||||||
|
description: "Create a new session from a previous message",
|
||||||
|
category: "Session",
|
||||||
|
slash: "fork",
|
||||||
|
disabled: !params.id || visibleUserMessages().length === 0,
|
||||||
|
onSelect: () => dialog.show(() => <DialogFork />),
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
@@ -875,6 +884,19 @@ export default function Page() {
|
|||||||
window.history.replaceState(null, "", `#${anchor(id)}`)
|
window.history.replaceState(null, "", `#${anchor(id)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const scrollToElement = (el: HTMLElement, behavior: ScrollBehavior) => {
|
||||||
|
const root = scroller
|
||||||
|
if (!root) {
|
||||||
|
el.scrollIntoView({ behavior, block: "start" })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const a = el.getBoundingClientRect()
|
||||||
|
const b = root.getBoundingClientRect()
|
||||||
|
const top = a.top - b.top + root.scrollTop
|
||||||
|
root.scrollTo({ top, behavior })
|
||||||
|
}
|
||||||
|
|
||||||
const scrollToMessage = (message: UserMessage, behavior: ScrollBehavior = "smooth") => {
|
const scrollToMessage = (message: UserMessage, behavior: ScrollBehavior = "smooth") => {
|
||||||
setActiveMessage(message)
|
setActiveMessage(message)
|
||||||
|
|
||||||
@@ -886,7 +908,7 @@ export default function Page() {
|
|||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const el = document.getElementById(anchor(message.id))
|
const el = document.getElementById(anchor(message.id))
|
||||||
if (el) el.scrollIntoView({ behavior, block: "start" })
|
if (el) scrollToElement(el, behavior)
|
||||||
})
|
})
|
||||||
|
|
||||||
updateHash(message.id)
|
updateHash(message.id)
|
||||||
@@ -894,7 +916,7 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const el = document.getElementById(anchor(message.id))
|
const el = document.getElementById(anchor(message.id))
|
||||||
if (el) el.scrollIntoView({ behavior, block: "start" })
|
if (el) scrollToElement(el, behavior)
|
||||||
updateHash(message.id)
|
updateHash(message.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,7 +968,7 @@ export default function Page() {
|
|||||||
|
|
||||||
const hashTarget = document.getElementById(hash)
|
const hashTarget = document.getElementById(hash)
|
||||||
if (hashTarget) {
|
if (hashTarget) {
|
||||||
hashTarget.scrollIntoView({ behavior: "auto", block: "start" })
|
scrollToElement(hashTarget, "auto")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1069,7 +1091,7 @@ export default function Page() {
|
|||||||
file.load(path)
|
file.load(path)
|
||||||
}}
|
}}
|
||||||
classes={{
|
classes={{
|
||||||
root: "pb-[calc(var(--prompt-height,8rem)+32px)]",
|
root: "pb-[calc(var(--prompt-height,8rem)+24px)]",
|
||||||
header: "px-4",
|
header: "px-4",
|
||||||
container: "px-4",
|
container: "px-4",
|
||||||
}}
|
}}
|
||||||
@@ -1215,7 +1237,7 @@ export default function Page() {
|
|||||||
{/* Prompt input */}
|
{/* Prompt input */}
|
||||||
<div
|
<div
|
||||||
ref={(el) => (promptDock = el)}
|
ref={(el) => (promptDock = el)}
|
||||||
class="absolute inset-x-0 bottom-0 pt-12 pb-4 md:pb-8 flex flex-col justify-center items-center z-50 px-4 md:px-0 bg-gradient-to-t from-background-stronger via-background-stronger to-transparent pointer-events-none"
|
class="absolute inset-x-0 bottom-0 pt-12 pb-4 md:pb-6 flex flex-col justify-center items-center z-50 px-4 md:px-0 bg-gradient-to-t from-background-stronger via-background-stronger to-transparent pointer-events-none"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
classList={{
|
classList={{
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"typecheck": "tsgo --noEmit",
|
"typecheck": "tsgo --noEmit",
|
||||||
"dev": "vite dev --host 0.0.0.0",
|
"dev": "vite dev --host 0.0.0.0",
|
||||||
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
|
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai VITE_STRIPE_PUBLISHABLE_KEY=pk_test_51RtuLNE7fOCwHSD4mewwzFejyytjdGoSDK7CAvhbffwaZnPbNb2rwJICw6LTOXCmWO320fSNXvb5NzI08RZVkAxd00syfqrW7t bun sst shell --stage=dev bun dev",
|
||||||
"build": "./script/generate-sitemap.ts && vite build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
"build": "./script/generate-sitemap.ts && vite build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||||
"start": "vite start"
|
"start": "vite start"
|
||||||
},
|
},
|
||||||
@@ -20,17 +20,23 @@
|
|||||||
"@opencode-ai/console-mail": "workspace:*",
|
"@opencode-ai/console-mail": "workspace:*",
|
||||||
"@opencode-ai/console-resource": "workspace:*",
|
"@opencode-ai/console-resource": "workspace:*",
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
|
"@smithy/eventstream-codec": "4.2.7",
|
||||||
|
"@smithy/util-utf8": "4.2.0",
|
||||||
"@solidjs/meta": "catalog:",
|
"@solidjs/meta": "catalog:",
|
||||||
"@solidjs/router": "catalog:",
|
"@solidjs/router": "catalog:",
|
||||||
"@solidjs/start": "catalog:",
|
"@solidjs/start": "catalog:",
|
||||||
|
"@stripe/stripe-js": "8.6.1",
|
||||||
"chart.js": "4.5.1",
|
"chart.js": "4.5.1",
|
||||||
"nitro": "3.0.1-alpha.1",
|
"nitro": "3.0.1-alpha.1",
|
||||||
"solid-js": "catalog:",
|
"solid-js": "catalog:",
|
||||||
|
"solid-list": "0.3.0",
|
||||||
|
"solid-stripe": "0.8.1",
|
||||||
"vite": "catalog:",
|
"vite": "catalog:",
|
||||||
"zod": "catalog:"
|
"zod": "catalog:"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript/native-preview": "catalog:",
|
"@typescript/native-preview": "catalog:",
|
||||||
|
"@webgpu/types": "0.1.54",
|
||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
"wrangler": "4.50.0"
|
"wrangler": "4.50.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../ui/src/assets/images/social-share-black.png
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -24,6 +24,9 @@ export function Footer() {
|
|||||||
<div data-slot="cell">
|
<div data-slot="cell">
|
||||||
<a href="/docs">Docs</a>
|
<a href="/docs">Docs</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div data-slot="cell">
|
||||||
|
<a href="/changelog">Changelog</a>
|
||||||
|
</div>
|
||||||
<div data-slot="cell">
|
<div data-slot="cell">
|
||||||
<a href="/discord">Discord</a>
|
<a href="/discord">Discord</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
.spotlight-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 50dvh;
|
||||||
|
pointer-events: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spotlight-container canvas {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,820 @@
|
|||||||
|
import { createSignal, createEffect, onMount, onCleanup, Accessor } from "solid-js"
|
||||||
|
import "./spotlight.css"
|
||||||
|
|
||||||
|
export interface ParticlesConfig {
|
||||||
|
enabled: boolean
|
||||||
|
amount: number
|
||||||
|
size: [number, number]
|
||||||
|
speed: number
|
||||||
|
opacity: number
|
||||||
|
drift: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpotlightConfig {
|
||||||
|
placement: [number, number]
|
||||||
|
color: string
|
||||||
|
speed: number
|
||||||
|
spread: number
|
||||||
|
length: number
|
||||||
|
width: number
|
||||||
|
pulsating: false | [number, number]
|
||||||
|
distance: number
|
||||||
|
saturation: number
|
||||||
|
noiseAmount: number
|
||||||
|
distortion: number
|
||||||
|
opacity: number
|
||||||
|
particles: ParticlesConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultConfig: SpotlightConfig = {
|
||||||
|
placement: [0.5, -0.15],
|
||||||
|
color: "#ffffff",
|
||||||
|
speed: 0.8,
|
||||||
|
spread: 0.5,
|
||||||
|
length: 4.0,
|
||||||
|
width: 0.15,
|
||||||
|
pulsating: [0.95, 1.1],
|
||||||
|
distance: 3.5,
|
||||||
|
saturation: 0.35,
|
||||||
|
noiseAmount: 0.15,
|
||||||
|
distortion: 0.05,
|
||||||
|
opacity: 0.325,
|
||||||
|
particles: {
|
||||||
|
enabled: true,
|
||||||
|
amount: 70,
|
||||||
|
size: [1.25, 1.5],
|
||||||
|
speed: 0.75,
|
||||||
|
opacity: 0.9,
|
||||||
|
drift: 1.5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpotlightAnimationState {
|
||||||
|
time: number
|
||||||
|
intensity: number
|
||||||
|
pulseValue: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpotlightProps {
|
||||||
|
config: Accessor<SpotlightConfig>
|
||||||
|
class?: string
|
||||||
|
onAnimationFrame?: (state: SpotlightAnimationState) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const hexToRgb = (hex: string): [number, number, number] => {
|
||||||
|
const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||||||
|
return m ? [parseInt(m[1], 16) / 255, parseInt(m[2], 16) / 255, parseInt(m[3], 16) / 255] : [1, 1, 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAnchorAndDir = (
|
||||||
|
placement: [number, number],
|
||||||
|
w: number,
|
||||||
|
h: number,
|
||||||
|
): { anchor: [number, number]; dir: [number, number] } => {
|
||||||
|
const [px, py] = placement
|
||||||
|
const outside = 0.2
|
||||||
|
|
||||||
|
let anchorX = px * w
|
||||||
|
let anchorY = py * h
|
||||||
|
let dirX = 0
|
||||||
|
let dirY = 0
|
||||||
|
|
||||||
|
const centerX = 0.5
|
||||||
|
const centerY = 0.5
|
||||||
|
|
||||||
|
if (py <= 0.25) {
|
||||||
|
anchorY = -outside * h + py * h
|
||||||
|
dirY = 1
|
||||||
|
dirX = (centerX - px) * 0.5
|
||||||
|
} else if (py >= 0.75) {
|
||||||
|
anchorY = (1 + outside) * h - (1 - py) * h
|
||||||
|
dirY = -1
|
||||||
|
dirX = (centerX - px) * 0.5
|
||||||
|
} else if (px <= 0.25) {
|
||||||
|
anchorX = -outside * w + px * w
|
||||||
|
dirX = 1
|
||||||
|
dirY = (centerY - py) * 0.5
|
||||||
|
} else if (px >= 0.75) {
|
||||||
|
anchorX = (1 + outside) * w - (1 - px) * w
|
||||||
|
dirX = -1
|
||||||
|
dirY = (centerY - py) * 0.5
|
||||||
|
} else {
|
||||||
|
dirY = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const len = Math.sqrt(dirX * dirX + dirY * dirY)
|
||||||
|
if (len > 0) {
|
||||||
|
dirX /= len
|
||||||
|
dirY /= len
|
||||||
|
}
|
||||||
|
|
||||||
|
return { anchor: [anchorX, anchorY], dir: [dirX, dirY] }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UniformData {
|
||||||
|
iTime: number
|
||||||
|
iResolution: [number, number]
|
||||||
|
lightPos: [number, number]
|
||||||
|
lightDir: [number, number]
|
||||||
|
color: [number, number, number]
|
||||||
|
speed: number
|
||||||
|
lightSpread: number
|
||||||
|
lightLength: number
|
||||||
|
sourceWidth: number
|
||||||
|
pulsating: number
|
||||||
|
pulsatingMin: number
|
||||||
|
pulsatingMax: number
|
||||||
|
fadeDistance: number
|
||||||
|
saturation: number
|
||||||
|
noiseAmount: number
|
||||||
|
distortion: number
|
||||||
|
particlesEnabled: number
|
||||||
|
particleAmount: number
|
||||||
|
particleSizeMin: number
|
||||||
|
particleSizeMax: number
|
||||||
|
particleSpeed: number
|
||||||
|
particleOpacity: number
|
||||||
|
particleDrift: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const WGSL_SHADER = `
|
||||||
|
struct Uniforms {
|
||||||
|
iTime: f32,
|
||||||
|
_pad0: f32,
|
||||||
|
iResolution: vec2<f32>,
|
||||||
|
lightPos: vec2<f32>,
|
||||||
|
lightDir: vec2<f32>,
|
||||||
|
color: vec3<f32>,
|
||||||
|
speed: f32,
|
||||||
|
lightSpread: f32,
|
||||||
|
lightLength: f32,
|
||||||
|
sourceWidth: f32,
|
||||||
|
pulsating: f32,
|
||||||
|
pulsatingMin: f32,
|
||||||
|
pulsatingMax: f32,
|
||||||
|
fadeDistance: f32,
|
||||||
|
saturation: f32,
|
||||||
|
noiseAmount: f32,
|
||||||
|
distortion: f32,
|
||||||
|
particlesEnabled: f32,
|
||||||
|
particleAmount: f32,
|
||||||
|
particleSizeMin: f32,
|
||||||
|
particleSizeMax: f32,
|
||||||
|
particleSpeed: f32,
|
||||||
|
particleOpacity: f32,
|
||||||
|
particleDrift: f32,
|
||||||
|
_pad1: f32,
|
||||||
|
_pad2: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
@location(0) vUv: vec2<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
||||||
|
var positions = array<vec2<f32>, 3>(
|
||||||
|
vec2<f32>(-1.0, -1.0),
|
||||||
|
vec2<f32>(3.0, -1.0),
|
||||||
|
vec2<f32>(-1.0, 3.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
var output: VertexOutput;
|
||||||
|
let pos = positions[vertexIndex];
|
||||||
|
output.position = vec4<f32>(pos, 0.0, 1.0);
|
||||||
|
output.vUv = pos * 0.5 + 0.5;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash(p: vec2<f32>) -> f32 {
|
||||||
|
let p3 = fract(p.xyx * 0.1031);
|
||||||
|
return fract((p3.x + p3.y) * p3.z + dot(p3, p3.yzx + 33.33));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash2(p: vec2<f32>) -> vec2<f32> {
|
||||||
|
let n = sin(dot(p, vec2<f32>(41.0, 289.0)));
|
||||||
|
return fract(vec2<f32>(n * 262144.0, n * 32768.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fastNoise(st: vec2<f32>) -> f32 {
|
||||||
|
return fract(sin(dot(st, vec2<f32>(12.9898, 78.233))) * 43758.5453);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lightStrengthCombined(lightSource: vec2<f32>, lightRefDirection: vec2<f32>, coord: vec2<f32>) -> f32 {
|
||||||
|
let sourceToCoord = coord - lightSource;
|
||||||
|
let distSq = dot(sourceToCoord, sourceToCoord);
|
||||||
|
let distance = sqrt(distSq);
|
||||||
|
|
||||||
|
let baseSize = min(uniforms.iResolution.x, uniforms.iResolution.y);
|
||||||
|
let maxDistance = max(baseSize * uniforms.lightLength, 0.001);
|
||||||
|
if (distance > maxDistance) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let invDist = 1.0 / max(distance, 0.001);
|
||||||
|
let dirNorm = sourceToCoord * invDist;
|
||||||
|
let cosAngle = dot(dirNorm, lightRefDirection);
|
||||||
|
|
||||||
|
if (cosAngle < 0.0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let side = dot(dirNorm, vec2<f32>(-lightRefDirection.y, lightRefDirection.x));
|
||||||
|
let time = uniforms.iTime;
|
||||||
|
let speed = uniforms.speed;
|
||||||
|
|
||||||
|
let asymNoise = fastNoise(vec2<f32>(side * 6.0 + time * 0.12, distance * 0.004 + cosAngle * 2.0));
|
||||||
|
let asymShift = (asymNoise - 0.5) * uniforms.distortion * 0.6;
|
||||||
|
|
||||||
|
let distortPhase = time * 1.4 + distance * 0.006 + cosAngle * 4.5 + side * 1.7;
|
||||||
|
let distortedAngle = cosAngle + uniforms.distortion * sin(distortPhase) * 0.22 + asymShift;
|
||||||
|
|
||||||
|
let flickerSeed = cosAngle * 9.0 + side * 4.0 + time * speed * 0.35;
|
||||||
|
let flicker = 0.86 + fastNoise(vec2<f32>(flickerSeed, distance * 0.01)) * 0.28;
|
||||||
|
|
||||||
|
let asymSpread = max(uniforms.lightSpread * (0.9 + (asymNoise - 0.5) * 0.25), 0.001);
|
||||||
|
let spreadFactor = pow(max(distortedAngle, 0.0), 1.0 / asymSpread);
|
||||||
|
let lengthFalloff = clamp(1.0 - distance / maxDistance, 0.0, 1.0);
|
||||||
|
|
||||||
|
let fadeMaxDist = max(baseSize * uniforms.fadeDistance, 0.001);
|
||||||
|
let fadeFalloff = clamp((fadeMaxDist - distance) / fadeMaxDist, 0.0, 1.0);
|
||||||
|
|
||||||
|
var pulse: f32 = 1.0;
|
||||||
|
if (uniforms.pulsating > 0.5) {
|
||||||
|
let pulseCenter = (uniforms.pulsatingMin + uniforms.pulsatingMax) * 0.5;
|
||||||
|
let pulseAmplitude = (uniforms.pulsatingMax - uniforms.pulsatingMin) * 0.5;
|
||||||
|
pulse = pulseCenter + pulseAmplitude * sin(time * speed * 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeSpeed = time * speed;
|
||||||
|
let wave = 0.5
|
||||||
|
+ 0.25 * sin(cosAngle * 28.0 + side * 8.0 + timeSpeed * 1.2)
|
||||||
|
+ 0.18 * cos(cosAngle * 22.0 - timeSpeed * 0.95 + side * 6.0)
|
||||||
|
+ 0.12 * sin(cosAngle * 35.0 + timeSpeed * 1.6 + asymNoise * 3.0);
|
||||||
|
let minStrength = 0.14 + asymNoise * 0.06;
|
||||||
|
let baseStrength = max(clamp(wave * (0.85 + asymNoise * 0.3), 0.0, 1.0), minStrength);
|
||||||
|
|
||||||
|
let lightStrength = baseStrength * lengthFalloff * fadeFalloff * spreadFactor * pulse * flicker;
|
||||||
|
let ambientLight = (0.06 + asymNoise * 0.04) * lengthFalloff * fadeFalloff * spreadFactor;
|
||||||
|
|
||||||
|
return max(lightStrength, ambientLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn particle(coord: vec2<f32>, particlePos: vec2<f32>, size: f32) -> f32 {
|
||||||
|
let delta = coord - particlePos;
|
||||||
|
let distSq = dot(delta, delta);
|
||||||
|
let sizeSq = size * size;
|
||||||
|
|
||||||
|
if (distSq > sizeSq * 9.0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let d = sqrt(distSq);
|
||||||
|
let core = smoothstep(size, size * 0.35, d);
|
||||||
|
let glow = smoothstep(size * 3.0, 0.0, d) * 0.55;
|
||||||
|
return core + glow;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderParticles(coord: vec2<f32>, lightSource: vec2<f32>, lightDir: vec2<f32>) -> f32 {
|
||||||
|
if (uniforms.particlesEnabled < 0.5 || uniforms.particleAmount < 1.0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var particleSum: f32 = 0.0;
|
||||||
|
let particleCount = i32(uniforms.particleAmount);
|
||||||
|
let time = uniforms.iTime * uniforms.particleSpeed;
|
||||||
|
let perpDir = vec2<f32>(-lightDir.y, lightDir.x);
|
||||||
|
let baseSize = min(uniforms.iResolution.x, uniforms.iResolution.y);
|
||||||
|
let maxDist = max(baseSize * uniforms.lightLength, 1.0);
|
||||||
|
let spreadScale = uniforms.lightSpread * baseSize * 0.65;
|
||||||
|
let coneHalfWidth = uniforms.lightSpread * baseSize * 0.55;
|
||||||
|
|
||||||
|
for (var i: i32 = 0; i < particleCount; i = i + 1) {
|
||||||
|
let fi = f32(i);
|
||||||
|
let seed = vec2<f32>(fi * 127.1, fi * 311.7);
|
||||||
|
let rnd = hash2(seed);
|
||||||
|
|
||||||
|
let lifeDuration = 2.0 + hash(seed + vec2<f32>(19.0, 73.0)) * 3.0;
|
||||||
|
let lifeOffset = hash(seed + vec2<f32>(91.0, 37.0)) * lifeDuration;
|
||||||
|
let lifeProgress = fract((time + lifeOffset) / lifeDuration);
|
||||||
|
|
||||||
|
let fadeIn = smoothstep(0.0, 0.2, lifeProgress);
|
||||||
|
let fadeOut = 1.0 - smoothstep(0.8, 1.0, lifeProgress);
|
||||||
|
let lifeFade = fadeIn * fadeOut;
|
||||||
|
if (lifeFade < 0.01) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alongLight = rnd.x * maxDist * 0.8;
|
||||||
|
let perpOffset = (rnd.y - 0.5) * spreadScale;
|
||||||
|
|
||||||
|
let floatPhase = rnd.y * 6.28318 + fi * 0.37;
|
||||||
|
let floatSpeed = 0.35 + rnd.x * 0.9;
|
||||||
|
let drift = vec2<f32>(
|
||||||
|
sin(time * floatSpeed + floatPhase),
|
||||||
|
cos(time * floatSpeed * 0.85 + floatPhase * 1.3)
|
||||||
|
) * uniforms.particleDrift * baseSize * 0.08;
|
||||||
|
|
||||||
|
let wobble = vec2<f32>(
|
||||||
|
sin(time * 1.4 + floatPhase * 2.1),
|
||||||
|
cos(time * 1.1 + floatPhase * 1.6)
|
||||||
|
) * uniforms.particleDrift * baseSize * 0.03;
|
||||||
|
|
||||||
|
let flowOffset = (rnd.x - 0.5) * baseSize * 0.12 + fract(time * 0.06 + rnd.y) * baseSize * 0.1;
|
||||||
|
|
||||||
|
let basePos = lightSource + lightDir * (alongLight + flowOffset) + perpDir * perpOffset + drift + wobble;
|
||||||
|
|
||||||
|
let toParticle = basePos - lightSource;
|
||||||
|
let projLen = dot(toParticle, lightDir);
|
||||||
|
if (projLen < 0.0 || projLen > maxDist) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sideDist = abs(dot(toParticle, perpDir));
|
||||||
|
if (sideDist > coneHalfWidth) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = mix(uniforms.particleSizeMin, uniforms.particleSizeMax, rnd.x);
|
||||||
|
let twinkle = 0.7 + 0.3 * sin(time * (1.5 + rnd.y * 2.0) + floatPhase);
|
||||||
|
let distFade = 1.0 - smoothstep(maxDist * 0.2, maxDist * 0.95, projLen);
|
||||||
|
if (distFade < 0.01) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let p = particle(coord, basePos, size);
|
||||||
|
if (p > 0.0) {
|
||||||
|
particleSum = particleSum + p * lifeFade * twinkle * distFade * uniforms.particleOpacity;
|
||||||
|
if (particleSum >= 1.0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return min(particleSum, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fragmentMain(@builtin(position) fragCoord: vec4<f32>, @location(0) vUv: vec2<f32>) -> @location(0) vec4<f32> {
|
||||||
|
let coord = vec2<f32>(fragCoord.x, fragCoord.y);
|
||||||
|
|
||||||
|
let normalizedX = (coord.x / uniforms.iResolution.x) - 0.5;
|
||||||
|
let widthOffset = -normalizedX * uniforms.sourceWidth * uniforms.iResolution.x;
|
||||||
|
|
||||||
|
let perpDir = vec2<f32>(-uniforms.lightDir.y, uniforms.lightDir.x);
|
||||||
|
let adjustedLightPos = uniforms.lightPos + perpDir * widthOffset;
|
||||||
|
|
||||||
|
let lightValue = lightStrengthCombined(adjustedLightPos, uniforms.lightDir, coord);
|
||||||
|
|
||||||
|
if (lightValue < 0.001) {
|
||||||
|
let particles = renderParticles(coord, adjustedLightPos, uniforms.lightDir);
|
||||||
|
if (particles < 0.001) {
|
||||||
|
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
let particleBrightness = particles * 1.8;
|
||||||
|
return vec4<f32>(uniforms.color * particleBrightness, particles * 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fragColor = vec4<f32>(lightValue, lightValue, lightValue, lightValue);
|
||||||
|
|
||||||
|
if (uniforms.noiseAmount > 0.01) {
|
||||||
|
let n = fastNoise(coord * 0.5 + uniforms.iTime * 0.5);
|
||||||
|
let grain = mix(1.0, n, uniforms.noiseAmount * 0.5);
|
||||||
|
fragColor = vec4<f32>(fragColor.rgb * grain, fragColor.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
let brightness = 1.0 - (coord.y / uniforms.iResolution.y);
|
||||||
|
fragColor = vec4<f32>(
|
||||||
|
fragColor.x * (0.15 + brightness * 0.85),
|
||||||
|
fragColor.y * (0.35 + brightness * 0.65),
|
||||||
|
fragColor.z * (0.55 + brightness * 0.45),
|
||||||
|
fragColor.a
|
||||||
|
);
|
||||||
|
|
||||||
|
if (abs(uniforms.saturation - 1.0) > 0.01) {
|
||||||
|
let gray = dot(fragColor.rgb, vec3<f32>(0.299, 0.587, 0.114));
|
||||||
|
fragColor = vec4<f32>(mix(vec3<f32>(gray), fragColor.rgb, uniforms.saturation), fragColor.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColor = vec4<f32>(fragColor.rgb * uniforms.color, fragColor.a);
|
||||||
|
|
||||||
|
let particles = renderParticles(coord, adjustedLightPos, uniforms.lightDir);
|
||||||
|
if (particles > 0.001) {
|
||||||
|
let particleBrightness = particles * 1.8;
|
||||||
|
fragColor = vec4<f32>(fragColor.rgb + uniforms.color * particleBrightness, max(fragColor.a, particles * 0.9));
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragColor;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const UNIFORM_BUFFER_SIZE = 144
|
||||||
|
|
||||||
|
function updateUniformBuffer(buffer: Float32Array, data: UniformData): void {
|
||||||
|
buffer[0] = data.iTime
|
||||||
|
buffer[2] = data.iResolution[0]
|
||||||
|
buffer[3] = data.iResolution[1]
|
||||||
|
buffer[4] = data.lightPos[0]
|
||||||
|
buffer[5] = data.lightPos[1]
|
||||||
|
buffer[6] = data.lightDir[0]
|
||||||
|
buffer[7] = data.lightDir[1]
|
||||||
|
buffer[8] = data.color[0]
|
||||||
|
buffer[9] = data.color[1]
|
||||||
|
buffer[10] = data.color[2]
|
||||||
|
buffer[11] = data.speed
|
||||||
|
buffer[12] = data.lightSpread
|
||||||
|
buffer[13] = data.lightLength
|
||||||
|
buffer[14] = data.sourceWidth
|
||||||
|
buffer[15] = data.pulsating
|
||||||
|
buffer[16] = data.pulsatingMin
|
||||||
|
buffer[17] = data.pulsatingMax
|
||||||
|
buffer[18] = data.fadeDistance
|
||||||
|
buffer[19] = data.saturation
|
||||||
|
buffer[20] = data.noiseAmount
|
||||||
|
buffer[21] = data.distortion
|
||||||
|
buffer[22] = data.particlesEnabled
|
||||||
|
buffer[23] = data.particleAmount
|
||||||
|
buffer[24] = data.particleSizeMin
|
||||||
|
buffer[25] = data.particleSizeMax
|
||||||
|
buffer[26] = data.particleSpeed
|
||||||
|
buffer[27] = data.particleOpacity
|
||||||
|
buffer[28] = data.particleDrift
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Spotlight(props: SpotlightProps) {
|
||||||
|
let containerRef: HTMLDivElement | undefined
|
||||||
|
let canvasRef: HTMLCanvasElement | null = null
|
||||||
|
let deviceRef: GPUDevice | null = null
|
||||||
|
let contextRef: GPUCanvasContext | null = null
|
||||||
|
let pipelineRef: GPURenderPipeline | null = null
|
||||||
|
let uniformBufferRef: GPUBuffer | null = null
|
||||||
|
let bindGroupRef: GPUBindGroup | null = null
|
||||||
|
let animationIdRef: number | null = null
|
||||||
|
let cleanupFunctionRef: (() => void) | null = null
|
||||||
|
let uniformDataRef: UniformData | null = null
|
||||||
|
let uniformArrayRef: Float32Array | null = null
|
||||||
|
let configRef: SpotlightConfig = props.config()
|
||||||
|
let frameCount = 0
|
||||||
|
|
||||||
|
const [isVisible, setIsVisible] = createSignal(false)
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
configRef = props.config()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!containerRef) return
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
const entry = entries[0]
|
||||||
|
setIsVisible(entry.isIntersecting)
|
||||||
|
},
|
||||||
|
{ threshold: 0.1 },
|
||||||
|
)
|
||||||
|
|
||||||
|
observer.observe(containerRef)
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
observer.disconnect()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const visible = isVisible()
|
||||||
|
const config = props.config()
|
||||||
|
if (!visible || !containerRef) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanupFunctionRef) {
|
||||||
|
cleanupFunctionRef()
|
||||||
|
cleanupFunctionRef = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const initializeWebGPU = async () => {
|
||||||
|
if (!containerRef) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 10))
|
||||||
|
|
||||||
|
if (!containerRef) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!navigator.gpu) {
|
||||||
|
console.warn("WebGPU is not supported in this browser")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const adapter = await navigator.gpu.requestAdapter({
|
||||||
|
powerPreference: "high-performance",
|
||||||
|
})
|
||||||
|
if (!adapter) {
|
||||||
|
console.warn("Failed to get WebGPU adapter")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = await adapter.requestDevice()
|
||||||
|
deviceRef = device
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas")
|
||||||
|
canvas.style.width = "100%"
|
||||||
|
canvas.style.height = "100%"
|
||||||
|
canvasRef = canvas
|
||||||
|
|
||||||
|
while (containerRef.firstChild) {
|
||||||
|
containerRef.removeChild(containerRef.firstChild)
|
||||||
|
}
|
||||||
|
containerRef.appendChild(canvas)
|
||||||
|
|
||||||
|
const context = canvas.getContext("webgpu")
|
||||||
|
if (!context) {
|
||||||
|
console.warn("Failed to get WebGPU context")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
contextRef = context
|
||||||
|
|
||||||
|
const presentationFormat = navigator.gpu.getPreferredCanvasFormat()
|
||||||
|
context.configure({
|
||||||
|
device,
|
||||||
|
format: presentationFormat,
|
||||||
|
alphaMode: "premultiplied",
|
||||||
|
})
|
||||||
|
|
||||||
|
const shaderModule = device.createShaderModule({
|
||||||
|
code: WGSL_SHADER,
|
||||||
|
})
|
||||||
|
|
||||||
|
const uniformBuffer = device.createBuffer({
|
||||||
|
size: UNIFORM_BUFFER_SIZE,
|
||||||
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
||||||
|
})
|
||||||
|
uniformBufferRef = uniformBuffer
|
||||||
|
|
||||||
|
const bindGroupLayout = device.createBindGroupLayout({
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
binding: 0,
|
||||||
|
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
||||||
|
buffer: { type: "uniform" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const bindGroup = device.createBindGroup({
|
||||||
|
layout: bindGroupLayout,
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
binding: 0,
|
||||||
|
resource: { buffer: uniformBuffer },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
bindGroupRef = bindGroup
|
||||||
|
|
||||||
|
const pipelineLayout = device.createPipelineLayout({
|
||||||
|
bindGroupLayouts: [bindGroupLayout],
|
||||||
|
})
|
||||||
|
|
||||||
|
const pipeline = device.createRenderPipeline({
|
||||||
|
layout: pipelineLayout,
|
||||||
|
vertex: {
|
||||||
|
module: shaderModule,
|
||||||
|
entryPoint: "vertexMain",
|
||||||
|
},
|
||||||
|
fragment: {
|
||||||
|
module: shaderModule,
|
||||||
|
entryPoint: "fragmentMain",
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
format: presentationFormat,
|
||||||
|
blend: {
|
||||||
|
color: {
|
||||||
|
srcFactor: "src-alpha",
|
||||||
|
dstFactor: "one-minus-src-alpha",
|
||||||
|
operation: "add",
|
||||||
|
},
|
||||||
|
alpha: {
|
||||||
|
srcFactor: "one",
|
||||||
|
dstFactor: "one-minus-src-alpha",
|
||||||
|
operation: "add",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
primitive: {
|
||||||
|
topology: "triangle-list",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
pipelineRef = pipeline
|
||||||
|
|
||||||
|
const { clientWidth: wCSS, clientHeight: hCSS } = containerRef
|
||||||
|
const dpr = Math.min(window.devicePixelRatio, 2)
|
||||||
|
const w = wCSS * dpr
|
||||||
|
const h = hCSS * dpr
|
||||||
|
const { anchor, dir } = getAnchorAndDir(config.placement, w, h)
|
||||||
|
|
||||||
|
uniformDataRef = {
|
||||||
|
iTime: 0,
|
||||||
|
iResolution: [w, h],
|
||||||
|
lightPos: anchor,
|
||||||
|
lightDir: dir,
|
||||||
|
color: hexToRgb(config.color),
|
||||||
|
speed: config.speed,
|
||||||
|
lightSpread: config.spread,
|
||||||
|
lightLength: config.length,
|
||||||
|
sourceWidth: config.width,
|
||||||
|
pulsating: config.pulsating !== false ? 1.0 : 0.0,
|
||||||
|
pulsatingMin: config.pulsating !== false ? config.pulsating[0] : 1.0,
|
||||||
|
pulsatingMax: config.pulsating !== false ? config.pulsating[1] : 1.0,
|
||||||
|
fadeDistance: config.distance,
|
||||||
|
saturation: config.saturation,
|
||||||
|
noiseAmount: config.noiseAmount,
|
||||||
|
distortion: config.distortion,
|
||||||
|
particlesEnabled: config.particles.enabled ? 1.0 : 0.0,
|
||||||
|
particleAmount: config.particles.amount,
|
||||||
|
particleSizeMin: config.particles.size[0],
|
||||||
|
particleSizeMax: config.particles.size[1],
|
||||||
|
particleSpeed: config.particles.speed,
|
||||||
|
particleOpacity: config.particles.opacity,
|
||||||
|
particleDrift: config.particles.drift,
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatePlacement = () => {
|
||||||
|
if (!containerRef || !canvasRef || !uniformDataRef) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const dpr = Math.min(window.devicePixelRatio, 2)
|
||||||
|
const { clientWidth: wCSS, clientHeight: hCSS } = containerRef
|
||||||
|
const w = Math.floor(wCSS * dpr)
|
||||||
|
const h = Math.floor(hCSS * dpr)
|
||||||
|
|
||||||
|
canvasRef.width = w
|
||||||
|
canvasRef.height = h
|
||||||
|
|
||||||
|
uniformDataRef.iResolution = [w, h]
|
||||||
|
|
||||||
|
const { anchor, dir } = getAnchorAndDir(configRef.placement, w, h)
|
||||||
|
uniformDataRef.lightPos = anchor
|
||||||
|
uniformDataRef.lightDir = dir
|
||||||
|
}
|
||||||
|
|
||||||
|
const loop = (t: number) => {
|
||||||
|
if (!deviceRef || !contextRef || !pipelineRef || !uniformBufferRef || !bindGroupRef || !uniformDataRef) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeSeconds = t * 0.001
|
||||||
|
uniformDataRef.iTime = timeSeconds
|
||||||
|
frameCount++
|
||||||
|
|
||||||
|
if (props.onAnimationFrame && frameCount % 2 === 0) {
|
||||||
|
const pulsatingMin = configRef.pulsating !== false ? configRef.pulsating[0] : 1.0
|
||||||
|
const pulsatingMax = configRef.pulsating !== false ? configRef.pulsating[1] : 1.0
|
||||||
|
const pulseCenter = (pulsatingMin + pulsatingMax) * 0.5
|
||||||
|
const pulseAmplitude = (pulsatingMax - pulsatingMin) * 0.5
|
||||||
|
const pulseValue =
|
||||||
|
configRef.pulsating !== false
|
||||||
|
? pulseCenter + pulseAmplitude * Math.sin(timeSeconds * configRef.speed * 3.0)
|
||||||
|
: 1.0
|
||||||
|
|
||||||
|
const baseIntensity1 = 0.45 + 0.15 * Math.sin(timeSeconds * configRef.speed * 1.5)
|
||||||
|
const baseIntensity2 = 0.3 + 0.2 * Math.cos(timeSeconds * configRef.speed * 1.1)
|
||||||
|
const intensity = Math.max((baseIntensity1 + baseIntensity2) * pulseValue, 0.55)
|
||||||
|
|
||||||
|
props.onAnimationFrame({
|
||||||
|
time: timeSeconds,
|
||||||
|
intensity,
|
||||||
|
pulseValue: Math.max(pulseValue, 0.9),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!uniformArrayRef) {
|
||||||
|
uniformArrayRef = new Float32Array(36)
|
||||||
|
}
|
||||||
|
updateUniformBuffer(uniformArrayRef, uniformDataRef)
|
||||||
|
deviceRef.queue.writeBuffer(uniformBufferRef, 0, uniformArrayRef.buffer)
|
||||||
|
|
||||||
|
const commandEncoder = deviceRef.createCommandEncoder()
|
||||||
|
|
||||||
|
const textureView = contextRef.getCurrentTexture().createView()
|
||||||
|
|
||||||
|
const renderPass = commandEncoder.beginRenderPass({
|
||||||
|
colorAttachments: [
|
||||||
|
{
|
||||||
|
view: textureView,
|
||||||
|
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
||||||
|
loadOp: "clear",
|
||||||
|
storeOp: "store",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
renderPass.setPipeline(pipelineRef)
|
||||||
|
renderPass.setBindGroup(0, bindGroupRef)
|
||||||
|
renderPass.draw(3)
|
||||||
|
renderPass.end()
|
||||||
|
|
||||||
|
deviceRef.queue.submit([commandEncoder.finish()])
|
||||||
|
|
||||||
|
animationIdRef = requestAnimationFrame(loop)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("WebGPU rendering error:", error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", updatePlacement)
|
||||||
|
updatePlacement()
|
||||||
|
animationIdRef = requestAnimationFrame(loop)
|
||||||
|
|
||||||
|
cleanupFunctionRef = () => {
|
||||||
|
if (animationIdRef) {
|
||||||
|
cancelAnimationFrame(animationIdRef)
|
||||||
|
animationIdRef = null
|
||||||
|
}
|
||||||
|
|
||||||
|
window.removeEventListener("resize", updatePlacement)
|
||||||
|
|
||||||
|
if (uniformBufferRef) {
|
||||||
|
uniformBufferRef.destroy()
|
||||||
|
uniformBufferRef = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deviceRef) {
|
||||||
|
deviceRef.destroy()
|
||||||
|
deviceRef = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvasRef && canvasRef.parentNode) {
|
||||||
|
canvasRef.parentNode.removeChild(canvasRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
canvasRef = null
|
||||||
|
contextRef = null
|
||||||
|
pipelineRef = null
|
||||||
|
bindGroupRef = null
|
||||||
|
uniformDataRef = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeWebGPU()
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
if (cleanupFunctionRef) {
|
||||||
|
cleanupFunctionRef()
|
||||||
|
cleanupFunctionRef = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (!uniformDataRef || !containerRef) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = props.config()
|
||||||
|
|
||||||
|
uniformDataRef.color = hexToRgb(config.color)
|
||||||
|
uniformDataRef.speed = config.speed
|
||||||
|
uniformDataRef.lightSpread = config.spread
|
||||||
|
uniformDataRef.lightLength = config.length
|
||||||
|
uniformDataRef.sourceWidth = config.width
|
||||||
|
uniformDataRef.pulsating = config.pulsating !== false ? 1.0 : 0.0
|
||||||
|
uniformDataRef.pulsatingMin = config.pulsating !== false ? config.pulsating[0] : 1.0
|
||||||
|
uniformDataRef.pulsatingMax = config.pulsating !== false ? config.pulsating[1] : 1.0
|
||||||
|
uniformDataRef.fadeDistance = config.distance
|
||||||
|
uniformDataRef.saturation = config.saturation
|
||||||
|
uniformDataRef.noiseAmount = config.noiseAmount
|
||||||
|
uniformDataRef.distortion = config.distortion
|
||||||
|
uniformDataRef.particlesEnabled = config.particles.enabled ? 1.0 : 0.0
|
||||||
|
uniformDataRef.particleAmount = config.particles.amount
|
||||||
|
uniformDataRef.particleSizeMin = config.particles.size[0]
|
||||||
|
uniformDataRef.particleSizeMax = config.particles.size[1]
|
||||||
|
uniformDataRef.particleSpeed = config.particles.speed
|
||||||
|
uniformDataRef.particleOpacity = config.particles.opacity
|
||||||
|
uniformDataRef.particleDrift = config.particles.drift
|
||||||
|
|
||||||
|
const dpr = Math.min(window.devicePixelRatio, 2)
|
||||||
|
const { clientWidth: wCSS, clientHeight: hCSS } = containerRef
|
||||||
|
const { anchor, dir } = getAnchorAndDir(config.placement, wCSS * dpr, hCSS * dpr)
|
||||||
|
uniformDataRef.lightPos = anchor
|
||||||
|
uniformDataRef.lightDir = dir
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
class={`spotlight-container ${props.class ?? ""}`.trim()}
|
||||||
|
style={{ opacity: props.config().opacity }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -9,8 +9,8 @@ export const config = {
|
|||||||
github: {
|
github: {
|
||||||
repoUrl: "https://github.com/anomalyco/opencode",
|
repoUrl: "https://github.com/anomalyco/opencode",
|
||||||
starsFormatted: {
|
starsFormatted: {
|
||||||
compact: "50K",
|
compact: "70K",
|
||||||
full: "50,000",
|
full: "70,000",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ export const config = {
|
|||||||
// Static stats (used on landing page)
|
// Static stats (used on landing page)
|
||||||
stats: {
|
stats: {
|
||||||
contributors: "500",
|
contributors: "500",
|
||||||
commits: "6,500",
|
commits: "7,000",
|
||||||
monthlyUsers: "650,000",
|
monthlyUsers: "650,000",
|
||||||
},
|
},
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import { useSession } from "@solidjs/start/http"
|
|
||||||
|
|
||||||
export interface AuthSession {
|
|
||||||
account?: Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
id: string
|
|
||||||
email: string
|
|
||||||
}
|
|
||||||
>
|
|
||||||
current?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useAuthSession() {
|
|
||||||
return useSession<AuthSession>({
|
|
||||||
password: "0".repeat(32),
|
|
||||||
name: "auth",
|
|
||||||
maxAge: 60 * 60 * 24 * 365,
|
|
||||||
cookie: {
|
|
||||||
secure: false,
|
|
||||||
httpOnly: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,13 +5,38 @@ import { redirect } from "@solidjs/router"
|
|||||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||||
|
|
||||||
import { createClient } from "@openauthjs/openauth/client"
|
import { createClient } from "@openauthjs/openauth/client"
|
||||||
import { useAuthSession } from "./auth.session"
|
|
||||||
|
|
||||||
export const AuthClient = createClient({
|
export const AuthClient = createClient({
|
||||||
clientID: "app",
|
clientID: "app",
|
||||||
issuer: import.meta.env.VITE_AUTH_URL,
|
issuer: import.meta.env.VITE_AUTH_URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
import { useSession } from "@solidjs/start/http"
|
||||||
|
import { Resource } from "@opencode-ai/console-resource"
|
||||||
|
|
||||||
|
export interface AuthSession {
|
||||||
|
account?: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
id: string
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
>
|
||||||
|
current?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAuthSession() {
|
||||||
|
return useSession<AuthSession>({
|
||||||
|
password: Resource.ZEN_SESSION_SECRET.value,
|
||||||
|
name: "auth",
|
||||||
|
maxAge: 60 * 60 * 24 * 365,
|
||||||
|
cookie: {
|
||||||
|
secure: false,
|
||||||
|
httpOnly: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const getActor = async (workspace?: string): Promise<Actor.Info> => {
|
export const getActor = async (workspace?: string): Promise<Actor.Info> => {
|
||||||
"use server"
|
"use server"
|
||||||
const evt = getRequestEvent()
|
const evt = getRequestEvent()
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ export const github = query(async () => {
|
|||||||
fetch(`${apiBaseUrl}/releases`, { headers }).then((res) => res.json()),
|
fetch(`${apiBaseUrl}/releases`, { headers }).then((res) => res.json()),
|
||||||
fetch(`${apiBaseUrl}/contributors?per_page=1`, { headers }),
|
fetch(`${apiBaseUrl}/contributors?per_page=1`, { headers }),
|
||||||
])
|
])
|
||||||
|
if (!Array.isArray(releases) || releases.length === 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
const [release] = releases
|
const [release] = releases
|
||||||
const contributorCount = Number.parseInt(
|
const linkHeader = contributors.headers.get("Link")
|
||||||
contributors.headers
|
const contributorCount = linkHeader
|
||||||
.get("Link")!
|
? Number.parseInt(linkHeader.match(/&page=(\d+)>; rel="last"/)?.at(1) ?? "0")
|
||||||
.match(/&page=(\d+)>; rel="last"/)!
|
: 0
|
||||||
.at(1)!,
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
stars: meta.stargazers_count,
|
stars: meta.stargazers_count,
|
||||||
release: {
|
release: {
|
||||||
|
|||||||
+3
-2
@@ -1,10 +1,11 @@
|
|||||||
import { redirect } from "@solidjs/router"
|
import { redirect } from "@solidjs/router"
|
||||||
import type { APIEvent } from "@solidjs/start/server"
|
import type { APIEvent } from "@solidjs/start/server"
|
||||||
import { AuthClient } from "~/context/auth"
|
import { AuthClient } from "~/context/auth"
|
||||||
import { useAuthSession } from "~/context/auth.session"
|
import { useAuthSession } from "~/context/auth"
|
||||||
|
|
||||||
export async function GET(input: APIEvent) {
|
export async function GET(input: APIEvent) {
|
||||||
const url = new URL(input.request.url)
|
const url = new URL(input.request.url)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const code = url.searchParams.get("code")
|
const code = url.searchParams.get("code")
|
||||||
if (!code) throw new Error("No code found")
|
if (!code) throw new Error("No code found")
|
||||||
@@ -27,7 +28,7 @@ export async function GET(input: APIEvent) {
|
|||||||
current: id,
|
current: id,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return redirect("/auth")
|
return redirect(url.pathname === "/auth/callback" ? "/auth" : url.pathname.replace("/auth/callback", ""))
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@@ -2,6 +2,9 @@ import type { APIEvent } from "@solidjs/start/server"
|
|||||||
import { AuthClient } from "~/context/auth"
|
import { AuthClient } from "~/context/auth"
|
||||||
|
|
||||||
export async function GET(input: APIEvent) {
|
export async function GET(input: APIEvent) {
|
||||||
const result = await AuthClient.authorize(new URL("./callback", input.request.url).toString(), "code")
|
const url = new URL(input.request.url)
|
||||||
|
const cont = url.searchParams.get("continue") ?? ""
|
||||||
|
const callbackUrl = new URL(`./callback${cont}`, input.request.url)
|
||||||
|
const result = await AuthClient.authorize(callbackUrl.toString(), "code")
|
||||||
return Response.redirect(result.url, 302)
|
return Response.redirect(result.url, 302)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { redirect } from "@solidjs/router"
|
import { redirect } from "@solidjs/router"
|
||||||
import { APIEvent } from "@solidjs/start"
|
import { APIEvent } from "@solidjs/start"
|
||||||
import { useAuthSession } from "~/context/auth.session"
|
import { useAuthSession } from "~/context/auth"
|
||||||
|
|
||||||
export async function GET(event: APIEvent) {
|
export async function GET(event: APIEvent) {
|
||||||
const auth = await useAuthSession()
|
const auth = await useAuthSession()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { APIEvent } from "@solidjs/start"
|
import { APIEvent } from "@solidjs/start"
|
||||||
import { useAuthSession } from "~/context/auth.session"
|
import { useAuthSession } from "~/context/auth"
|
||||||
|
|
||||||
export async function GET(input: APIEvent) {
|
export async function GET(input: APIEvent) {
|
||||||
const session = await useAuthSession()
|
const session = await useAuthSession()
|
||||||
|
|||||||
@@ -0,0 +1,828 @@
|
|||||||
|
::view-transition-group(*) {
|
||||||
|
animation-duration: 250ms;
|
||||||
|
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(root),
|
||||||
|
::view-transition-new(root) {
|
||||||
|
animation-duration: 250ms;
|
||||||
|
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-image-pair(root) {
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(root) {
|
||||||
|
animation: none;
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-new(root) {
|
||||||
|
animation: none;
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-out {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in-up {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(8px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes reveal-terms {
|
||||||
|
from {
|
||||||
|
mask-position: 0% 200%;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
mask-position: 0% 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes hide-terms {
|
||||||
|
from {
|
||||||
|
mask-position: 0% 50%;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
mask-position: 0% 200%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(terms-20),
|
||||||
|
::view-transition-old(terms-100),
|
||||||
|
::view-transition-old(terms-200) {
|
||||||
|
mask-image: linear-gradient(to bottom, transparent, black 25% 75%, transparent);
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: 100% 200%;
|
||||||
|
animation: hide-terms 200ms cubic-bezier(0.25, 0, 0.5, 1) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-new(terms-20),
|
||||||
|
::view-transition-new(terms-100),
|
||||||
|
::view-transition-new(terms-200) {
|
||||||
|
mask-image: linear-gradient(to bottom, transparent, black 25% 75%, transparent);
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: 0% 200%;
|
||||||
|
mask-size: 100% 200%;
|
||||||
|
animation: reveal-terms 300ms cubic-bezier(0.25, 0, 0.5, 1) 50ms forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(actions-20),
|
||||||
|
::view-transition-old(actions-100),
|
||||||
|
::view-transition-old(actions-200) {
|
||||||
|
animation: fade-out 80ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-new(actions-20),
|
||||||
|
::view-transition-new(actions-100),
|
||||||
|
::view-transition-new(actions-200) {
|
||||||
|
animation: fade-in-up 300ms cubic-bezier(0.16, 1, 0.3, 1) 300ms forwards;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-group(card-20),
|
||||||
|
::view-transition-group(card-100),
|
||||||
|
::view-transition-group(card-200) {
|
||||||
|
animation-duration: 250ms;
|
||||||
|
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-page="black"] {
|
||||||
|
background: #000;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: stretch;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
[data-component="header-logo"] {
|
||||||
|
filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.25)) drop-shadow(0 4px 16px rgba(0, 0, 0, 0.1));
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-light-rays {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0 0 auto 0;
|
||||||
|
height: 30dvh;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="header"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="content"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
[data-slot="hero"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 40px;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.45;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.45;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="hero-black"] {
|
||||||
|
margin-top: 40px;
|
||||||
|
padding: 0 20px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 590px;
|
||||||
|
height: auto;
|
||||||
|
overflow: visible;
|
||||||
|
filter: drop-shadow(0 0 20px rgba(255, 255, 255, calc(0.1 + var(--hero-black-glow-intensity, 0) * 0.15)))
|
||||||
|
drop-shadow(0 -5px 30px rgba(255, 255, 255, calc(var(--hero-black-glow-intensity, 0) * 0.2)));
|
||||||
|
mask-image: linear-gradient(to bottom, black, transparent);
|
||||||
|
stroke-width: 1.5;
|
||||||
|
|
||||||
|
[data-slot="black-base"] {
|
||||||
|
fill: url(#hero-black-fill-gradient);
|
||||||
|
stroke: url(#hero-black-stroke-gradient);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="black-glow"] {
|
||||||
|
fill: url(#hero-black-top-glow);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="black-shimmer"] {
|
||||||
|
fill: url(#hero-black-shimmer-gradient);
|
||||||
|
pointer-events: none;
|
||||||
|
mix-blend-mode: overlay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="cta"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: -40px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="heading"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="subheading"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 160%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="button"] {
|
||||||
|
display: inline-flex;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 12px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.92);
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000;
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: normal;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="back-soon"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 20.8px */
|
||||||
|
}
|
||||||
|
[data-slot="follow-us"] {
|
||||||
|
display: inline-flex;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 12px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="pricing"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 660px;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="pricing-card"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
background: black;
|
||||||
|
background-clip: padding-box;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: border-color 0.15s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(:active) {
|
||||||
|
border-color: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="icon"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="price"] {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="amount"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="period"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="multiplier"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "·";
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="selected-plan"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 32px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 660px;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
background-color: rgba(0, 0, 0, 0.75);
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
margin: 0 20px;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="selected-card"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
[data-slot="icon"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="price"] {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="amount"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="period"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="multiplier"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "·";
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="terms"] {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
li {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding-left: 16px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "▪";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="actions"] {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
button,
|
||||||
|
a {
|
||||||
|
flex: 1;
|
||||||
|
display: inline-flex;
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 16px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="cancel"] {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
transition-property: background-color, border-color;
|
||||||
|
transition-duration: 150ms;
|
||||||
|
transition-timing-function: cubic-bezier(0.25, 0, 0.5, 1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.08);
|
||||||
|
border-color: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="continue"] {
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
color: rgb(0, 0, 0);
|
||||||
|
transition: background-color 150ms cubic-bezier(0.25, 0, 0.5, 1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="fine-print"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 20.8px */
|
||||||
|
font-style: italic;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subscribe page styles */
|
||||||
|
[data-slot="subscribe-form"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 32px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: -18px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 660px;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: 40px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="form-card"] {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.17);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="plan-header"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="title"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="icon"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
isolation: isolate;
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="price"] {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="amount"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="period"] {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="multiplier"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "·";
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="divider"] {
|
||||||
|
height: 1px;
|
||||||
|
background: rgba(255, 255, 255, 0.17);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="section-title"] {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="checkout-form"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="error"] {
|
||||||
|
color: #ff6b6b;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="submit-button"] {
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
background: rgba(255, 255, 255, 0.92);
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #000;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s ease;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="charge-notice"] {
|
||||||
|
color: #d4a500;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="loading"] {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="fine-print"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: italic;
|
||||||
|
view-transition-name: fine-print;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspace-picker"] {
|
||||||
|
[data-slot="workspace-list"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
outline: none;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 240px;
|
||||||
|
scrollbar-width: none;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspace-item"] {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
padding: 8px 12px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
[data-slot="selected-icon"] {
|
||||||
|
visibility: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "IBM Plex Mono", monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span:last-child {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&[data-active="true"] {
|
||||||
|
background: #161616;
|
||||||
|
|
||||||
|
[data-slot="selected-icon"] {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="footer"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="footer-content"] {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span,
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="github-stars"] {
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="anomaly"] {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="anomaly-alt"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
import { A, createAsync, RouteSectionProps } from "@solidjs/router"
|
||||||
|
import { Title, Meta, Link } from "@solidjs/meta"
|
||||||
|
import { createMemo, createSignal } from "solid-js"
|
||||||
|
import { github } from "~/lib/github"
|
||||||
|
import { config } from "~/config"
|
||||||
|
import Spotlight, { defaultConfig, type SpotlightAnimationState } from "~/component/spotlight"
|
||||||
|
import "./black.css"
|
||||||
|
|
||||||
|
export default function BlackLayout(props: RouteSectionProps) {
|
||||||
|
const githubData = createAsync(() => github())
|
||||||
|
const starCount = createMemo(() =>
|
||||||
|
githubData()?.stars
|
||||||
|
? new Intl.NumberFormat("en-US", {
|
||||||
|
notation: "compact",
|
||||||
|
compactDisplay: "short",
|
||||||
|
}).format(githubData()!.stars!)
|
||||||
|
: config.github.starsFormatted.compact,
|
||||||
|
)
|
||||||
|
|
||||||
|
const [spotlightAnimationState, setSpotlightAnimationState] = createSignal<SpotlightAnimationState>({
|
||||||
|
time: 0,
|
||||||
|
intensity: 0.5,
|
||||||
|
pulseValue: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const svgLightingValues = createMemo(() => {
|
||||||
|
const state = spotlightAnimationState()
|
||||||
|
const t = state.time
|
||||||
|
|
||||||
|
const wave1 = Math.sin(t * 1.5) * 0.5 + 0.5
|
||||||
|
const wave2 = Math.sin(t * 2.3 + 1.2) * 0.5 + 0.5
|
||||||
|
const wave3 = Math.sin(t * 0.8 + 2.5) * 0.5 + 0.5
|
||||||
|
|
||||||
|
const shimmerPos = Math.sin(t * 0.7) * 0.5 + 0.5
|
||||||
|
const glowIntensity = Math.max(state.intensity * state.pulseValue * 0.35, 0.15)
|
||||||
|
const fillOpacity = Math.max(0.1 + wave1 * 0.08 * state.pulseValue, 0.12)
|
||||||
|
const strokeBrightness = Math.max(55 + wave2 * 25 * state.pulseValue, 60)
|
||||||
|
|
||||||
|
const shimmerIntensity = Math.max(wave3 * 0.15 * state.pulseValue, 0.08)
|
||||||
|
|
||||||
|
return {
|
||||||
|
glowIntensity,
|
||||||
|
fillOpacity,
|
||||||
|
strokeBrightness,
|
||||||
|
shimmerPos,
|
||||||
|
shimmerIntensity,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const svgLightingStyle = createMemo(() => {
|
||||||
|
const values = svgLightingValues()
|
||||||
|
return {
|
||||||
|
"--hero-black-glow-intensity": values.glowIntensity.toFixed(3),
|
||||||
|
"--hero-black-stroke-brightness": `${values.strokeBrightness.toFixed(0)}%`,
|
||||||
|
} as Record<string, string>
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleAnimationFrame = (state: SpotlightAnimationState) => {
|
||||||
|
setSpotlightAnimationState(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
const spotlightConfig = () => defaultConfig
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-page="black">
|
||||||
|
<Title>OpenCode Black | Access all the world's best coding models</Title>
|
||||||
|
<Meta
|
||||||
|
name="description"
|
||||||
|
content="Get access to Claude, GPT, Gemini and more with OpenCode Black subscription plans."
|
||||||
|
/>
|
||||||
|
<Link rel="canonical" href={`${config.baseUrl}/black`} />
|
||||||
|
<Meta property="og:type" content="website" />
|
||||||
|
<Meta property="og:url" content={`${config.baseUrl}/black`} />
|
||||||
|
<Meta property="og:title" content="OpenCode Black | Access all the world's best coding models" />
|
||||||
|
<Meta
|
||||||
|
property="og:description"
|
||||||
|
content="Get access to Claude, GPT, Gemini and more with OpenCode Black subscription plans."
|
||||||
|
/>
|
||||||
|
<Meta property="og:image" content="/social-share-black.png" />
|
||||||
|
<Meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<Meta name="twitter:title" content="OpenCode Black | Access all the world's best coding models" />
|
||||||
|
<Meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="Get access to Claude, GPT, Gemini and more with OpenCode Black subscription plans."
|
||||||
|
/>
|
||||||
|
<Meta name="twitter:image" content="/social-share-black.png" />
|
||||||
|
|
||||||
|
<Spotlight config={spotlightConfig} class="header-spotlight" onAnimationFrame={handleAnimationFrame} />
|
||||||
|
|
||||||
|
<header data-component="header">
|
||||||
|
<A href="/" data-component="header-logo">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="179" height="32" viewBox="0 0 179 32" fill="none">
|
||||||
|
<title>opencode</title>
|
||||||
|
<g clip-path="url(#clip0_3654_210259)">
|
||||||
|
<mask
|
||||||
|
id="mask0_3654_210259"
|
||||||
|
style="mask-type:luminance"
|
||||||
|
maskUnits="userSpaceOnUse"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="179"
|
||||||
|
height="32"
|
||||||
|
>
|
||||||
|
<path d="M178.286 0H0V32H178.286V0Z" fill="white" />
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask0_3654_210259)">
|
||||||
|
<path d="M13.7132 22.8577H4.57031V13.7148H13.7132V22.8577Z" fill="#444444" />
|
||||||
|
<path
|
||||||
|
d="M13.7143 9.14174H4.57143V22.856H13.7143V9.14174ZM18.2857 27.4275H0V4.57031H18.2857V27.4275Z"
|
||||||
|
fill="#CDCDCD"
|
||||||
|
/>
|
||||||
|
<path d="M36.5725 22.8577H27.4297V13.7148H36.5725V22.8577Z" fill="#444444" />
|
||||||
|
<path
|
||||||
|
d="M27.4308 22.856H36.5737V9.14174H27.4308V22.856ZM41.1451 27.4275H27.4308V31.9989H22.8594V4.57031H41.1451V27.4275Z"
|
||||||
|
fill="#CDCDCD"
|
||||||
|
/>
|
||||||
|
<path d="M64.0033 18.2852V22.8566H50.2891V18.2852H64.0033Z" fill="#444444" />
|
||||||
|
<path
|
||||||
|
d="M63.9967 18.2846H50.2824V22.856H63.9967V27.4275H45.7109V4.57031H63.9967V18.2846ZM50.2824 13.7132H59.4252V9.14174H50.2824V13.7132Z"
|
||||||
|
fill="#CDCDCD"
|
||||||
|
/>
|
||||||
|
<path d="M82.2835 27.4291H73.1406V13.7148H82.2835V27.4291Z" fill="#444444" />
|
||||||
|
<path
|
||||||
|
d="M82.2846 9.14174H73.1417V27.4275H68.5703V4.57031H82.2846V9.14174ZM86.856 27.4275H82.2846V9.14174H86.856V27.4275Z"
|
||||||
|
fill="#CDCDCD"
|
||||||
|
/>
|
||||||
|
<path d="M109.714 22.8577H96V13.7148H109.714V22.8577Z" fill="#444444" />
|
||||||
|
<path
|
||||||
|
d="M109.715 9.14174H96.0011V22.856H109.715V27.4275H91.4297V4.57031H109.715V9.14174Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path d="M128.002 22.8577H118.859V13.7148H128.002V22.8577Z" fill="#444444" />
|
||||||
|
<path
|
||||||
|
d="M128.003 9.14174H118.86V22.856H128.003V9.14174ZM132.575 27.4275H114.289V4.57031H132.575V27.4275Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path d="M150.854 22.8577H141.711V13.7148H150.854V22.8577Z" fill="#444444" />
|
||||||
|
<path
|
||||||
|
d="M150.855 9.14286H141.712V22.8571H150.855V9.14286ZM155.426 27.4286H137.141V4.57143H150.855V0H155.426V27.4286Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path d="M178.285 18.2852V22.8566H164.57V18.2852H178.285Z" fill="#444444" />
|
||||||
|
<path
|
||||||
|
d="M164.571 9.14174V13.7132H173.714V9.14174H164.571ZM178.286 18.2846H164.571V22.856H178.286V27.4275H160V4.57031H178.286V18.2846Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_3654_210259">
|
||||||
|
<rect width="178.286" height="32" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</A>
|
||||||
|
</header>
|
||||||
|
<main data-component="content">
|
||||||
|
<div data-slot="hero">
|
||||||
|
<h1>Access all the world's best coding models</h1>
|
||||||
|
<p>Including Claude, GPT, Gemini and more</p>
|
||||||
|
</div>
|
||||||
|
<div data-slot="hero-black" style={svgLightingStyle()}>
|
||||||
|
<svg width="591" height="90" viewBox="0 0 591 90" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<linearGradient
|
||||||
|
id="hero-black-fill-gradient"
|
||||||
|
x1="290.82"
|
||||||
|
y1="1.57422"
|
||||||
|
x2="290.82"
|
||||||
|
y2="87.0326"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="white" />
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="hero-black-stroke-gradient"
|
||||||
|
x1="290.82"
|
||||||
|
y1="2.03255"
|
||||||
|
x2="290.82"
|
||||||
|
y2="87.0325"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color={`hsl(0 0% ${svgLightingValues().strokeBrightness}%)`} />
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="hero-black-shimmer-gradient"
|
||||||
|
x1="0"
|
||||||
|
y1="0"
|
||||||
|
x2="591"
|
||||||
|
y2="0"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop offset={Math.max(0, svgLightingValues().shimmerPos - 0.12)} stop-color="transparent" />
|
||||||
|
<stop
|
||||||
|
offset={svgLightingValues().shimmerPos}
|
||||||
|
stop-color={`rgba(255, 255, 255, ${svgLightingValues().shimmerIntensity})`}
|
||||||
|
/>
|
||||||
|
<stop offset={Math.min(1, svgLightingValues().shimmerPos + 0.12)} stop-color="transparent" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="hero-black-top-glow"
|
||||||
|
x1="290.82"
|
||||||
|
y1="0"
|
||||||
|
x2="290.82"
|
||||||
|
y2="45"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop offset="0" stop-color={`rgba(255, 255, 255, ${svgLightingValues().glowIntensity})`} />
|
||||||
|
<stop offset="1" stop-color="transparent" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="hero-black-shimmer-mask"
|
||||||
|
x1="290.82"
|
||||||
|
y1="0"
|
||||||
|
x2="290.82"
|
||||||
|
y2="50"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop offset="0" stop-color="white" />
|
||||||
|
<stop offset="0.8" stop-color="white" stop-opacity="0.5" />
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<mask id="shimmer-top-mask">
|
||||||
|
<rect x="0" y="0" width="591" height="90" fill="url(#hero-black-shimmer-mask)" />
|
||||||
|
</mask>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<path
|
||||||
|
d="M425.56 0.75C429.464 0.750017 432.877 1.27807 435.78 2.35645C438.656 3.42455 441.138 4.86975 443.215 6.69727C445.268 8.50382 446.995 10.5587 448.394 12.8604C449.77 15.0464 450.986 17.2741 452.04 19.5439L452.357 20.2275L451.672 20.542L443.032 24.502L442.311 24.833L442.021 24.0938C441.315 22.2906 440.494 20.6079 439.557 19.0459L439.552 19.0391L439.548 19.0322C438.626 17.419 437.517 16.0443 436.223 14.9023L436.206 14.8867L436.189 14.8701C434.989 13.6697 433.518 12.7239 431.766 12.0381L431.755 12.0342V12.0332C430.111 11.3607 428.053 11.0098 425.56 11.0098C419.142 11.0098 414.433 13.4271 411.308 18.2295C408.212 23.109 406.629 29.6717 406.629 37.9805V51.6602C406.629 59.9731 408.214 66.5377 411.312 71.418C414.438 76.2157 419.145 78.6299 425.56 78.6299C428.054 78.6299 430.111 78.2782 431.756 77.6055L431.766 77.6016L432.413 77.333C433.893 76.6811 435.154 75.8593 436.206 74.873C437.512 73.644 438.625 72.2626 439.548 70.7275C440.489 69.0801 441.314 67.3534 442.021 65.5469L442.311 64.8076L443.032 65.1387L451.672 69.0986L452.348 69.4082L452.044 70.0869C450.99 72.439 449.773 74.7099 448.395 76.8994C446.995 79.1229 445.266 81.1379 443.215 82.9434C441.138 84.7708 438.656 86.2151 435.78 87.2832C432.877 88.3616 429.464 88.8896 425.56 88.8896C415.111 88.8896 407.219 85.0777 402.019 77.4004L402.016 77.3965C396.939 69.7818 394.449 58.891 394.449 44.8203C394.449 30.7495 396.939 19.8589 402.016 12.2441L402.019 12.2393C407.219 4.56202 415.111 0.75 425.56 0.75ZM29.9404 2.19043C37.2789 2.19051 43.125 4.19131 47.3799 8.2793C51.6307 12.3635 53.7305 17.8115 53.7305 24.54C53.7305 29.6953 52.4605 33.8451 49.835 36.8994L49.8359 36.9004C47.7064 39.4558 45.0331 41.367 41.835 42.6445C45.893 43.8751 49.3115 45.9006 52.0703 48.7295C55.2954 51.9546 56.8496 56.6143 56.8496 62.5801C56.8496 66.0251 56.2751 69.2753 55.1211 72.3252C53.9689 75.3702 52.3185 78.014 50.1689 80.249L50.1699 80.25C48.0996 82.4858 45.6172 84.2628 42.7314 85.582L42.7227 85.5859C39.9002 86.8312 36.8362 87.4502 33.54 87.4502H0.75V2.19043H29.9404ZM148.123 2.19043V77.1904H187.843V87.4502H136.543V2.19043H148.123ZM298.121 2.19043L298.283 2.71973L323.963 86.4805L324.261 87.4502H312.006L311.848 86.9131L304.927 63.5703H276.646L269.726 86.9131L269.566 87.4502H257.552L257.85 86.4805L283.529 2.71973L283.691 2.19043H298.121ZM539.782 2.19043V44.9209L549.845 32.2344L549.851 32.2275L549.855 32.2207L574.575 2.46094L574.801 2.19043H588.874L587.849 3.41992L558.795 38.2832L588.749 86.3027L589.464 87.4502H575.934L575.714 87.0938L550.937 46.9316L539.782 60.0947V87.4502H528.202V2.19043H539.782ZM12.3301 77.1904H30.54C35.0749 77.1904 38.5307 76.1729 40.9961 74.2305C43.4059 72.3317 44.6699 69.3811 44.6699 65.2197V60.2998C44.6699 56.2239 43.4093 53.3106 40.9961 51.4092L40.9854 51.4004C38.5207 49.3838 35.0691 48.3301 30.54 48.3301H12.3301V77.1904ZM279.485 53.3096H302.087L290.786 14.4482L279.485 53.3096ZM12.3301 38.5498H28.8604C33 38.5498 36.1378 37.6505 38.3633 35.9443C40.5339 34.2015 41.6698 31.5679 41.6699 27.9004V23.2197C41.6699 19.5455 40.5299 16.9088 38.3516 15.166C36.1272 13.3865 32.9938 12.4502 28.8604 12.4502H12.3301V38.5498Z"
|
||||||
|
fill="url(#hero-black-fill-gradient)"
|
||||||
|
fill-opacity={svgLightingValues().fillOpacity}
|
||||||
|
stroke="url(#hero-black-stroke-gradient)"
|
||||||
|
stroke-width="1.5"
|
||||||
|
data-slot="black-base"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<path
|
||||||
|
d="M425.56 0.75C429.464 0.750017 432.877 1.27807 435.78 2.35645C438.656 3.42455 441.138 4.86975 443.215 6.69727C445.268 8.50382 446.995 10.5587 448.394 12.8604C449.77 15.0464 450.986 17.2741 452.04 19.5439L452.357 20.2275L451.672 20.542L443.032 24.502L442.311 24.833L442.021 24.0938C441.315 22.2906 440.494 20.6079 439.557 19.0459L439.552 19.0391L439.548 19.0322C438.626 17.419 437.517 16.0443 436.223 14.9023L436.206 14.8867L436.189 14.8701C434.989 13.6697 433.518 12.7239 431.766 12.0381L431.755 12.0342V12.0332C430.111 11.3607 428.053 11.0098 425.56 11.0098C419.142 11.0098 414.433 13.4271 411.308 18.2295C408.212 23.109 406.629 29.6717 406.629 37.9805V51.6602C406.629 59.9731 408.214 66.5377 411.312 71.418C414.438 76.2157 419.145 78.6299 425.56 78.6299C428.054 78.6299 430.111 78.2782 431.756 77.6055L431.766 77.6016L432.413 77.333C433.893 76.6811 435.154 75.8593 436.206 74.873C437.512 73.644 438.625 72.2626 439.548 70.7275C440.489 69.0801 441.314 67.3534 442.021 65.5469L442.311 64.8076L443.032 65.1387L451.672 69.0986L452.348 69.4082L452.044 70.0869C450.99 72.439 449.773 74.7099 448.395 76.8994C446.995 79.1229 445.266 81.1379 443.215 82.9434C441.138 84.7708 438.656 86.2151 435.78 87.2832C432.877 88.3616 429.464 88.8896 425.56 88.8896C415.111 88.8896 407.219 85.0777 402.019 77.4004L402.016 77.3965C396.939 69.7818 394.449 58.891 394.449 44.8203C394.449 30.7495 396.939 19.8589 402.016 12.2441L402.019 12.2393C407.219 4.56202 415.111 0.75 425.56 0.75ZM29.9404 2.19043C37.2789 2.19051 43.125 4.19131 47.3799 8.2793C51.6307 12.3635 53.7305 17.8115 53.7305 24.54C53.7305 29.6953 52.4605 33.8451 49.835 36.8994L49.8359 36.9004C47.7064 39.4558 45.0331 41.367 41.835 42.6445C45.893 43.8751 49.3115 45.9006 52.0703 48.7295C55.2954 51.9546 56.8496 56.6143 56.8496 62.5801C56.8496 66.0251 56.2751 69.2753 55.1211 72.3252C53.9689 75.3702 52.3185 78.014 50.1689 80.249L50.1699 80.25C48.0996 82.4858 45.6172 84.2628 42.7314 85.582L42.7227 85.5859C39.9002 86.8312 36.8362 87.4502 33.54 87.4502H0.75V2.19043H29.9404ZM148.123 2.19043V77.1904H187.843V87.4502H136.543V2.19043H148.123ZM298.121 2.19043L298.283 2.71973L323.963 86.4805L324.261 87.4502H312.006L311.848 86.9131L304.927 63.5703H276.646L269.726 86.9131L269.566 87.4502H257.552L257.85 86.4805L283.529 2.71973L283.691 2.19043H298.121ZM539.782 2.19043V44.9209L549.845 32.2344L549.851 32.2275L549.855 32.2207L574.575 2.46094L574.801 2.19043H588.874L587.849 3.41992L558.795 38.2832L588.749 86.3027L589.464 87.4502H575.934L575.714 87.0938L550.937 46.9316L539.782 60.0947V87.4502H528.202V2.19043H539.782ZM12.3301 77.1904H30.54C35.0749 77.1904 38.5307 76.1729 40.9961 74.2305C43.4059 72.3317 44.6699 69.3811 44.6699 65.2197V60.2998C44.6699 56.2239 43.4093 53.3106 40.9961 51.4092L40.9854 51.4004C38.5207 49.3838 35.0691 48.3301 30.54 48.3301H12.3301V77.1904ZM279.485 53.3096H302.087L290.786 14.4482L279.485 53.3096ZM12.3301 38.5498H28.8604C33 38.5498 36.1378 37.6505 38.3633 35.9443C40.5339 34.2015 41.6698 31.5679 41.6699 27.9004V23.2197C41.6699 19.5455 40.5299 16.9088 38.3516 15.166C36.1272 13.3865 32.9938 12.4502 28.8604 12.4502H12.3301V38.5498Z"
|
||||||
|
fill="url(#hero-black-top-glow)"
|
||||||
|
stroke="none"
|
||||||
|
data-slot="black-glow"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<path
|
||||||
|
d="M425.56 0.75C429.464 0.750017 432.877 1.27807 435.78 2.35645C438.656 3.42455 441.138 4.86975 443.215 6.69727C445.268 8.50382 446.995 10.5587 448.394 12.8604C449.77 15.0464 450.986 17.2741 452.04 19.5439L452.357 20.2275L451.672 20.542L443.032 24.502L442.311 24.833L442.021 24.0938C441.315 22.2906 440.494 20.6079 439.557 19.0459L439.552 19.0391L439.548 19.0322C438.626 17.419 437.517 16.0443 436.223 14.9023L436.206 14.8867L436.189 14.8701C434.989 13.6697 433.518 12.7239 431.766 12.0381L431.755 12.0342V12.0332C430.111 11.3607 428.053 11.0098 425.56 11.0098C419.142 11.0098 414.433 13.4271 411.308 18.2295C408.212 23.109 406.629 29.6717 406.629 37.9805V51.6602C406.629 59.9731 408.214 66.5377 411.312 71.418C414.438 76.2157 419.145 78.6299 425.56 78.6299C428.054 78.6299 430.111 78.2782 431.756 77.6055L431.766 77.6016L432.413 77.333C433.893 76.6811 435.154 75.8593 436.206 74.873C437.512 73.644 438.625 72.2626 439.548 70.7275C440.489 69.0801 441.314 67.3534 442.021 65.5469L442.311 64.8076L443.032 65.1387L451.672 69.0986L452.348 69.4082L452.044 70.0869C450.99 72.439 449.773 74.7099 448.395 76.8994C446.995 79.1229 445.266 81.1379 443.215 82.9434C441.138 84.7708 438.656 86.2151 435.78 87.2832C432.877 88.3616 429.464 88.8896 425.56 88.8896C415.111 88.8896 407.219 85.0777 402.019 77.4004L402.016 77.3965C396.939 69.7818 394.449 58.891 394.449 44.8203C394.449 30.7495 396.939 19.8589 402.016 12.2441L402.019 12.2393C407.219 4.56202 415.111 0.75 425.56 0.75ZM29.9404 2.19043C37.2789 2.19051 43.125 4.19131 47.3799 8.2793C51.6307 12.3635 53.7305 17.8115 53.7305 24.54C53.7305 29.6953 52.4605 33.8451 49.835 36.8994L49.8359 36.9004C47.7064 39.4558 45.0331 41.367 41.835 42.6445C45.893 43.8751 49.3115 45.9006 52.0703 48.7295C55.2954 51.9546 56.8496 56.6143 56.8496 62.5801C56.8496 66.0251 56.2751 69.2753 55.1211 72.3252C53.9689 75.3702 52.3185 78.014 50.1689 80.249L50.1699 80.25C48.0996 82.4858 45.6172 84.2628 42.7314 85.582L42.7227 85.5859C39.9002 86.8312 36.8362 87.4502 33.54 87.4502H0.75V2.19043H29.9404ZM148.123 2.19043V77.1904H187.843V87.4502H136.543V2.19043H148.123ZM298.121 2.19043L298.283 2.71973L323.963 86.4805L324.261 87.4502H312.006L311.848 86.9131L304.927 63.5703H276.646L269.726 86.9131L269.566 87.4502H257.552L257.85 86.4805L283.529 2.71973L283.691 2.19043H298.121ZM539.782 2.19043V44.9209L549.845 32.2344L549.851 32.2275L549.855 32.2207L574.575 2.46094L574.801 2.19043H588.874L587.849 3.41992L558.795 38.2832L588.749 86.3027L589.464 87.4502H575.934L575.714 87.0938L550.937 46.9316L539.782 60.0947V87.4502H528.202V2.19043H539.782ZM12.3301 77.1904H30.54C35.0749 77.1904 38.5307 76.1729 40.9961 74.2305C43.4059 72.3317 44.6699 69.3811 44.6699 65.2197V60.2998C44.6699 56.2239 43.4093 53.3106 40.9961 51.4092L40.9854 51.4004C38.5207 49.3838 35.0691 48.3301 30.54 48.3301H12.3301V77.1904ZM279.485 53.3096H302.087L290.786 14.4482L279.485 53.3096ZM12.3301 38.5498H28.8604C33 38.5498 36.1378 37.6505 38.3633 35.9443C40.5339 34.2015 41.6698 31.5679 41.6699 27.9004V23.2197C41.6699 19.5455 40.5299 16.9088 38.3516 15.166C36.1272 13.3865 32.9938 12.4502 28.8604 12.4502H12.3301V38.5498Z"
|
||||||
|
fill="url(#hero-black-shimmer-gradient)"
|
||||||
|
stroke="none"
|
||||||
|
data-slot="black-shimmer"
|
||||||
|
mask="url(#shimmer-top-mask)"
|
||||||
|
style={{ "mix-blend-mode": "overlay" }}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{props.children}
|
||||||
|
</main>
|
||||||
|
<footer data-component="footer">
|
||||||
|
<div data-slot="footer-content">
|
||||||
|
<span data-slot="anomaly">
|
||||||
|
©{new Date().getFullYear()} <a href="https://anoma.ly">Anomaly</a>
|
||||||
|
</span>
|
||||||
|
<a href={config.github.repoUrl} target="_blank">
|
||||||
|
GitHub <span data-slot="github-stars">[{starCount()}]</span>
|
||||||
|
</a>
|
||||||
|
<a href="/docs">Docs</a>
|
||||||
|
<span>
|
||||||
|
<A href="/legal/privacy-policy">Privacy</A>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<A href="/legal/terms-of-service">Terms</A>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span data-slot="anomaly-alt">
|
||||||
|
©{new Date().getFullYear()} <a href="https://anoma.ly">Anomaly</a>
|
||||||
|
</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { Match, Switch } from "solid-js"
|
||||||
|
|
||||||
|
export const plans = [
|
||||||
|
{ id: "20", multiplier: null },
|
||||||
|
{ id: "100", multiplier: "5x more usage than Black 20" },
|
||||||
|
{ id: "200", multiplier: "20x more usage than Black 20" },
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export type PlanID = (typeof plans)[number]["id"]
|
||||||
|
export type Plan = (typeof plans)[number]
|
||||||
|
|
||||||
|
export function PlanIcon(props: { plan: string }) {
|
||||||
|
return (
|
||||||
|
<Switch>
|
||||||
|
<Match when={props.plan === "20"}>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>Black 20 plan</title>
|
||||||
|
<rect x="0.5" y="0.5" width="23" height="23" stroke="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</Match>
|
||||||
|
<Match when={props.plan === "100"}>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>Black 100 plan</title>
|
||||||
|
<rect x="0.5" y="0.5" width="9" height="9" stroke="currentColor" />
|
||||||
|
<rect x="0.5" y="14.5" width="9" height="9" stroke="currentColor" />
|
||||||
|
<rect x="14.5" y="0.5" width="9" height="9" stroke="currentColor" />
|
||||||
|
<rect x="14.5" y="14.5" width="9" height="9" stroke="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</Match>
|
||||||
|
<Match when={props.plan === "200"}>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>Black 200 plan</title>
|
||||||
|
<rect x="0.5" y="0.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="0.5" y="5.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="0.5" y="10.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="0.5" y="15.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="0.5" y="20.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="5.5" y="0.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="5.5" y="5.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="5.5" y="10.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="5.5" y="15.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="5.5" y="20.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="10.5" y="0.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="10.5" y="5.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="10.5" y="10.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="10.5" y="15.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="10.5" y="20.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="15.5" y="0.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="15.5" y="5.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="15.5" y="10.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="15.5" y="15.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="15.5" y="20.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="20.5" y="0.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="20.5" y="5.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="20.5" y="10.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="20.5" y="15.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
<rect x="20.5" y="20.5" width="3" height="3" stroke="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import { A, useSearchParams } from "@solidjs/router"
|
||||||
|
import { Title } from "@solidjs/meta"
|
||||||
|
import { createMemo, createSignal, For, Match, onMount, Show, Switch } from "solid-js"
|
||||||
|
import { PlanIcon, plans } from "./common"
|
||||||
|
|
||||||
|
export default function Black() {
|
||||||
|
const [params] = useSearchParams()
|
||||||
|
const [selected, setSelected] = createSignal<string | null>((params.plan as string) || null)
|
||||||
|
const [mounted, setMounted] = createSignal(false)
|
||||||
|
const selectedPlan = createMemo(() => plans.find((p) => p.id === selected()))
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
requestAnimationFrame(() => setMounted(true))
|
||||||
|
})
|
||||||
|
|
||||||
|
const transition = (action: () => void) => {
|
||||||
|
if (mounted() && "startViewTransition" in document) {
|
||||||
|
;(document as any).startViewTransition(action)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
|
||||||
|
const select = (planId: string) => {
|
||||||
|
if (selected() === planId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
transition(() => setSelected(planId))
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
transition(() => setSelected(null))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title>opencode</Title>
|
||||||
|
<section data-slot="cta">
|
||||||
|
<Switch>
|
||||||
|
<Match when={!selected()}>
|
||||||
|
<div data-slot="pricing">
|
||||||
|
<For each={plans}>
|
||||||
|
{(plan) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => select(plan.id)}
|
||||||
|
data-slot="pricing-card"
|
||||||
|
style={{ "view-transition-name": `card-${plan.id}` }}
|
||||||
|
>
|
||||||
|
<div data-slot="icon">
|
||||||
|
<PlanIcon plan={plan.id} />
|
||||||
|
</div>
|
||||||
|
<p data-slot="price">
|
||||||
|
<span data-slot="amount">${plan.id}</span> <span data-slot="period">per month</span>
|
||||||
|
<Show when={plan.multiplier}>
|
||||||
|
<span data-slot="multiplier">{plan.multiplier}</span>
|
||||||
|
</Show>
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</Match>
|
||||||
|
<Match when={selectedPlan()}>
|
||||||
|
{(plan) => (
|
||||||
|
<div data-slot="selected-plan">
|
||||||
|
<div data-slot="selected-card" style={{ "view-transition-name": `card-${plan().id}` }}>
|
||||||
|
<div data-slot="icon">
|
||||||
|
<PlanIcon plan={plan().id} />
|
||||||
|
</div>
|
||||||
|
<p data-slot="price">
|
||||||
|
<span data-slot="amount">${plan().id}</span>{" "}
|
||||||
|
<span data-slot="period">per person billed monthly</span>
|
||||||
|
<Show when={plan().multiplier}>
|
||||||
|
<span data-slot="multiplier">{plan().multiplier}</span>
|
||||||
|
</Show>
|
||||||
|
</p>
|
||||||
|
<ul data-slot="terms" style={{ "view-transition-name": `terms-${plan().id}` }}>
|
||||||
|
<li>Your subscription will not start immediately</li>
|
||||||
|
<li>You will be added to the waitlist and activated soon</li>
|
||||||
|
<li>Your card will be only charged when your subscription is activated</li>
|
||||||
|
<li>Usage limits apply, heavily automated use may reach limits sooner</li>
|
||||||
|
<li>Subscriptions for individuals, contact Enterprise for teams</li>
|
||||||
|
<li>Limits may be adjusted and plans may be discontinued in the future</li>
|
||||||
|
<li>Cancel your subscription at anytime</li>
|
||||||
|
</ul>
|
||||||
|
<div data-slot="actions" style={{ "view-transition-name": `actions-${plan().id}` }}>
|
||||||
|
<button type="button" onClick={() => cancel()} data-slot="cancel">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<a href={`/black/subscribe/${plan().id}`} data-slot="continue">
|
||||||
|
Continue
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
<p data-slot="fine-print" style={{ "view-transition-name": "fine-print" }}>
|
||||||
|
Prices shown don't include applicable tax · <A href="/legal/terms-of-service">Terms of Service</A>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,451 @@
|
|||||||
|
import { A, createAsync, query, redirect, useParams } from "@solidjs/router"
|
||||||
|
import { Title } from "@solidjs/meta"
|
||||||
|
import { createEffect, createSignal, For, Match, Show, Switch } from "solid-js"
|
||||||
|
import { type Stripe, type PaymentMethod, loadStripe } from "@stripe/stripe-js"
|
||||||
|
import { Elements, PaymentElement, useStripe, useElements, AddressElement } from "solid-stripe"
|
||||||
|
import { PlanID, plans } from "../common"
|
||||||
|
import { getActor, useAuthSession } from "~/context/auth"
|
||||||
|
import { withActor } from "~/context/auth.withActor"
|
||||||
|
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||||
|
import { and, Database, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js"
|
||||||
|
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||||
|
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||||
|
import { createList } from "solid-list"
|
||||||
|
import { Modal } from "~/component/modal"
|
||||||
|
import { BillingTable } from "@opencode-ai/console-core/schema/billing.sql.js"
|
||||||
|
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||||
|
|
||||||
|
const plansMap = Object.fromEntries(plans.map((p) => [p.id, p])) as Record<PlanID, (typeof plans)[number]>
|
||||||
|
const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY!)
|
||||||
|
|
||||||
|
const getWorkspaces = query(async (plan: string) => {
|
||||||
|
"use server"
|
||||||
|
const actor = await getActor()
|
||||||
|
if (actor.type === "public") throw redirect("/auth/authorize?continue=/black/subscribe/" + plan)
|
||||||
|
return withActor(async () => {
|
||||||
|
return Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
id: WorkspaceTable.id,
|
||||||
|
name: WorkspaceTable.name,
|
||||||
|
slug: WorkspaceTable.slug,
|
||||||
|
billing: {
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
paymentMethodID: BillingTable.paymentMethodID,
|
||||||
|
paymentMethodType: BillingTable.paymentMethodType,
|
||||||
|
paymentMethodLast4: BillingTable.paymentMethodLast4,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
timeSubscriptionBooked: BillingTable.timeSubscriptionBooked,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.from(UserTable)
|
||||||
|
.innerJoin(WorkspaceTable, eq(UserTable.workspaceID, WorkspaceTable.id))
|
||||||
|
.innerJoin(BillingTable, eq(WorkspaceTable.id, BillingTable.workspaceID))
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(UserTable.accountID, Actor.account()),
|
||||||
|
isNull(WorkspaceTable.timeDeleted),
|
||||||
|
isNull(UserTable.timeDeleted),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}, "black.subscribe.workspaces")
|
||||||
|
|
||||||
|
const createSetupIntent = async (input: { plan: string; workspaceID: string }) => {
|
||||||
|
"use server"
|
||||||
|
const { plan, workspaceID } = input
|
||||||
|
|
||||||
|
if (!plan || !["20", "100", "200"].includes(plan)) return { error: "Invalid plan" }
|
||||||
|
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||||
|
|
||||||
|
return withActor(async () => {
|
||||||
|
const session = await useAuthSession()
|
||||||
|
const account = session.data.account?.[session.data.current ?? ""]
|
||||||
|
const email = account?.email
|
||||||
|
|
||||||
|
const customer = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
})
|
||||||
|
.from(BillingTable)
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (customer?.subscriptionID) {
|
||||||
|
return { error: "This workspace already has a subscription" }
|
||||||
|
}
|
||||||
|
|
||||||
|
let customerID = customer?.customerID
|
||||||
|
if (!customerID) {
|
||||||
|
const customer = await Billing.stripe().customers.create({
|
||||||
|
email,
|
||||||
|
metadata: {
|
||||||
|
workspaceID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
customerID = customer.id
|
||||||
|
await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
customerID,
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const intent = await Billing.stripe().setupIntents.create({
|
||||||
|
customer: customerID,
|
||||||
|
payment_method_types: ["card"],
|
||||||
|
metadata: {
|
||||||
|
workspaceID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return { clientSecret: intent.client_secret ?? undefined }
|
||||||
|
}, workspaceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
const bookSubscription = async (input: {
|
||||||
|
workspaceID: string
|
||||||
|
plan: PlanID
|
||||||
|
paymentMethodID: string
|
||||||
|
paymentMethodType: string
|
||||||
|
paymentMethodLast4?: string
|
||||||
|
}) => {
|
||||||
|
"use server"
|
||||||
|
return withActor(
|
||||||
|
() =>
|
||||||
|
Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
paymentMethodID: input.paymentMethodID,
|
||||||
|
paymentMethodType: input.paymentMethodType,
|
||||||
|
paymentMethodLast4: input.paymentMethodLast4,
|
||||||
|
subscriptionPlan: input.plan,
|
||||||
|
timeSubscriptionBooked: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, input.workspaceID)),
|
||||||
|
),
|
||||||
|
input.workspaceID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SuccessData {
|
||||||
|
plan: string
|
||||||
|
paymentMethodType: string
|
||||||
|
paymentMethodLast4?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function Failure(props: { message: string }) {
|
||||||
|
return (
|
||||||
|
<div data-slot="failure">
|
||||||
|
<p data-slot="message">Uh oh! {props.message}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Success(props: SuccessData) {
|
||||||
|
return (
|
||||||
|
<div data-slot="success">
|
||||||
|
<p data-slot="title">You're on the OpenCode Black waitlist</p>
|
||||||
|
<dl data-slot="details">
|
||||||
|
<div>
|
||||||
|
<dt>Subscription plan</dt>
|
||||||
|
<dd>OpenCode Black {props.plan}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Amount</dt>
|
||||||
|
<dd>${props.plan} per month</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Payment method</dt>
|
||||||
|
<dd>
|
||||||
|
<Show when={props.paymentMethodLast4} fallback={<span>{props.paymentMethodType}</span>}>
|
||||||
|
<span>
|
||||||
|
{props.paymentMethodType} - {props.paymentMethodLast4}
|
||||||
|
</span>
|
||||||
|
</Show>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Date joined</dt>
|
||||||
|
<dd>{new Date().toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
<p data-slot="charge-notice">Your card will be charged when your subscription is activated</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function IntentForm(props: { plan: PlanID; workspaceID: string; onSuccess: (data: SuccessData) => void }) {
|
||||||
|
const stripe = useStripe()
|
||||||
|
const elements = useElements()
|
||||||
|
const [error, setError] = createSignal<string | undefined>(undefined)
|
||||||
|
const [loading, setLoading] = createSignal(false)
|
||||||
|
|
||||||
|
const handleSubmit = async (e: Event) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (!stripe() || !elements()) return
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
setError(undefined)
|
||||||
|
|
||||||
|
const result = await elements()!.submit()
|
||||||
|
if (result.error) {
|
||||||
|
setError(result.error.message ?? "An error occurred")
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { error: confirmError, setupIntent } = await stripe()!.confirmSetup({
|
||||||
|
elements: elements()!,
|
||||||
|
confirmParams: {
|
||||||
|
expand: ["payment_method"],
|
||||||
|
payment_method_data: {
|
||||||
|
allow_redisplay: "always",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
redirect: "if_required",
|
||||||
|
})
|
||||||
|
|
||||||
|
if (confirmError) {
|
||||||
|
setError(confirmError.message ?? "An error occurred")
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
console.log(setupIntent)
|
||||||
|
if (setupIntent?.status === "succeeded") {
|
||||||
|
const pm = setupIntent.payment_method as PaymentMethod
|
||||||
|
|
||||||
|
await bookSubscription({
|
||||||
|
workspaceID: props.workspaceID,
|
||||||
|
plan: props.plan,
|
||||||
|
paymentMethodID: pm.id,
|
||||||
|
paymentMethodType: pm.type,
|
||||||
|
paymentMethodLast4: pm.card?.last4,
|
||||||
|
})
|
||||||
|
|
||||||
|
props.onSuccess({
|
||||||
|
plan: props.plan,
|
||||||
|
paymentMethodType: pm.type,
|
||||||
|
paymentMethodLast4: pm.card?.last4,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit} data-slot="checkout-form">
|
||||||
|
<PaymentElement />
|
||||||
|
<AddressElement options={{ mode: "billing" }} />
|
||||||
|
<Show when={error()}>
|
||||||
|
<p data-slot="error">{error()}</p>
|
||||||
|
</Show>
|
||||||
|
<button type="submit" disabled={loading() || !stripe() || !elements()} data-slot="submit-button">
|
||||||
|
{loading() ? "Processing..." : `Subscribe $${props.plan}`}
|
||||||
|
</button>
|
||||||
|
<p data-slot="charge-notice">You will only be charged when your subscription is activated</p>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BlackSubscribe() {
|
||||||
|
const params = useParams()
|
||||||
|
const planData = plansMap[(params.plan as PlanID) ?? "20"] ?? plansMap["20"]
|
||||||
|
const plan = planData.id
|
||||||
|
|
||||||
|
const workspaces = createAsync(() => getWorkspaces(plan))
|
||||||
|
const [selectedWorkspace, setSelectedWorkspace] = createSignal<string | undefined>(undefined)
|
||||||
|
const [success, setSuccess] = createSignal<SuccessData | undefined>(undefined)
|
||||||
|
const [failure, setFailure] = createSignal<string | undefined>(undefined)
|
||||||
|
const [clientSecret, setClientSecret] = createSignal<string | undefined>(undefined)
|
||||||
|
const [stripe, setStripe] = createSignal<Stripe | undefined>(undefined)
|
||||||
|
|
||||||
|
// Resolve stripe promise once
|
||||||
|
createEffect(() => {
|
||||||
|
stripePromise.then((s) => {
|
||||||
|
if (s) setStripe(s)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Auto-select if only one workspace
|
||||||
|
createEffect(() => {
|
||||||
|
const ws = workspaces()
|
||||||
|
if (ws?.length === 1 && !selectedWorkspace()) {
|
||||||
|
setSelectedWorkspace(ws[0].id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fetch setup intent when workspace is selected (unless workspace already has payment method)
|
||||||
|
createEffect(async () => {
|
||||||
|
const id = selectedWorkspace()
|
||||||
|
if (!id) return
|
||||||
|
|
||||||
|
const ws = workspaces()?.find((w) => w.id === id)
|
||||||
|
if (ws?.billing?.subscriptionID) {
|
||||||
|
setFailure("This workspace already has a subscription")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ws?.billing?.paymentMethodID) {
|
||||||
|
if (!ws?.billing?.timeSubscriptionBooked) {
|
||||||
|
await bookSubscription({
|
||||||
|
workspaceID: id,
|
||||||
|
plan: planData.id,
|
||||||
|
paymentMethodID: ws.billing.paymentMethodID!,
|
||||||
|
paymentMethodType: ws.billing.paymentMethodType!,
|
||||||
|
paymentMethodLast4: ws.billing.paymentMethodLast4 ?? undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setSuccess({
|
||||||
|
plan: planData.id,
|
||||||
|
paymentMethodType: ws.billing.paymentMethodType!,
|
||||||
|
paymentMethodLast4: ws.billing.paymentMethodLast4 ?? undefined,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await createSetupIntent({ plan, workspaceID: id })
|
||||||
|
if (result.error) {
|
||||||
|
setFailure(result.error)
|
||||||
|
} else if ("clientSecret" in result) {
|
||||||
|
setClientSecret(result.clientSecret)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Keyboard navigation for workspace picker
|
||||||
|
const { active, setActive, onKeyDown } = createList({
|
||||||
|
items: () => workspaces()?.map((w) => w.id) ?? [],
|
||||||
|
initialActive: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSelectWorkspace = (id: string) => {
|
||||||
|
setSelectedWorkspace(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
let listRef: HTMLUListElement | undefined
|
||||||
|
|
||||||
|
// Show workspace picker if multiple workspaces and none selected
|
||||||
|
const showWorkspacePicker = () => {
|
||||||
|
const ws = workspaces()
|
||||||
|
return ws && ws.length > 1 && !selectedWorkspace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title>Subscribe to OpenCode Black</Title>
|
||||||
|
<section data-slot="subscribe-form">
|
||||||
|
<div data-slot="form-card">
|
||||||
|
<Switch>
|
||||||
|
<Match when={success()}>{(data) => <Success {...data()} />}</Match>
|
||||||
|
<Match when={failure()}>{(data) => <Failure message={data()} />}</Match>
|
||||||
|
<Match when={true}>
|
||||||
|
<>
|
||||||
|
<div data-slot="plan-header">
|
||||||
|
<p data-slot="title">Subscribe to OpenCode Black</p>
|
||||||
|
<p data-slot="price">
|
||||||
|
<span data-slot="amount">${planData.id}</span> <span data-slot="period">per month</span>
|
||||||
|
<Show when={planData.multiplier}>
|
||||||
|
<span data-slot="multiplier">{planData.multiplier}</span>
|
||||||
|
</Show>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div data-slot="divider" />
|
||||||
|
<p data-slot="section-title">Payment method</p>
|
||||||
|
|
||||||
|
<Show
|
||||||
|
when={clientSecret() && selectedWorkspace() && stripe()}
|
||||||
|
fallback={
|
||||||
|
<div data-slot="loading">
|
||||||
|
<p>{selectedWorkspace() ? "Loading payment form..." : "Select a workspace to continue"}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Elements
|
||||||
|
stripe={stripe()!}
|
||||||
|
options={{
|
||||||
|
clientSecret: clientSecret()!,
|
||||||
|
appearance: {
|
||||||
|
theme: "night",
|
||||||
|
variables: {
|
||||||
|
colorPrimary: "#ffffff",
|
||||||
|
colorBackground: "#1a1a1a",
|
||||||
|
colorText: "#ffffff",
|
||||||
|
colorTextSecondary: "#999999",
|
||||||
|
colorDanger: "#ff6b6b",
|
||||||
|
fontFamily: "JetBrains Mono, monospace",
|
||||||
|
borderRadius: "4px",
|
||||||
|
spacingUnit: "4px",
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
".Input": {
|
||||||
|
backgroundColor: "#1a1a1a",
|
||||||
|
border: "1px solid rgba(255, 255, 255, 0.17)",
|
||||||
|
color: "#ffffff",
|
||||||
|
},
|
||||||
|
".Input:focus": {
|
||||||
|
borderColor: "rgba(255, 255, 255, 0.35)",
|
||||||
|
boxShadow: "none",
|
||||||
|
},
|
||||||
|
".Label": {
|
||||||
|
color: "rgba(255, 255, 255, 0.59)",
|
||||||
|
fontSize: "14px",
|
||||||
|
marginBottom: "8px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IntentForm plan={plan} workspaceID={selectedWorkspace()!} onSuccess={setSuccess} />
|
||||||
|
</Elements>
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Workspace picker modal */}
|
||||||
|
<Modal open={showWorkspacePicker() ?? false} onClose={() => {}} title="Select a workspace for this plan">
|
||||||
|
<div data-slot="workspace-picker">
|
||||||
|
<ul
|
||||||
|
ref={listRef}
|
||||||
|
data-slot="workspace-list"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" && active()) {
|
||||||
|
handleSelectWorkspace(active()!)
|
||||||
|
} else {
|
||||||
|
onKeyDown(e)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<For each={workspaces()}>
|
||||||
|
{(workspace) => (
|
||||||
|
<li
|
||||||
|
data-slot="workspace-item"
|
||||||
|
data-active={active() === workspace.id}
|
||||||
|
onMouseEnter={() => setActive(workspace.id)}
|
||||||
|
onClick={() => handleSelectWorkspace(workspace.id)}
|
||||||
|
>
|
||||||
|
<span data-slot="selected-icon">[*]</span>
|
||||||
|
<span>{workspace.name || workspace.slug}</span>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
<p data-slot="fine-print">
|
||||||
|
Prices shown don't include applicable tax · <A href="/legal/terms-of-service">Terms of Service</A>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
[data-page="black"] {
|
||||||
|
background: #000;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: stretch;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
[data-component="header-gradient"] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 288px;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="header"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
/* [data-component="header-logo"] { */
|
||||||
|
/* } */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="content"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
[data-slot="hero-black"] {
|
||||||
|
margin-top: 110px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin-top: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="select-workspace"] {
|
||||||
|
display: flex;
|
||||||
|
margin-top: -24px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 480px;
|
||||||
|
height: 305px;
|
||||||
|
padding: 32px 20px 0 20px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
border: 1px solid #303030;
|
||||||
|
background: #0a0a0a;
|
||||||
|
box-shadow:
|
||||||
|
0 100px 80px 0 rgba(0, 0, 0, 0.04),
|
||||||
|
0 41.778px 33.422px 0 rgba(0, 0, 0, 0.05),
|
||||||
|
0 22.336px 17.869px 0 rgba(0, 0, 0, 0.06),
|
||||||
|
0 12.522px 10.017px 0 rgba(0, 0, 0, 0.08),
|
||||||
|
0 6.65px 5.32px 0 rgba(0, 0, 0, 0.09),
|
||||||
|
0 2.767px 2.214px 0 rgba(0, 0, 0, 0.13);
|
||||||
|
|
||||||
|
[data-slot="select-workspace-title"] {
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: stretch;
|
||||||
|
color: rgba(255, 255, 255, 0.59);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 25.6px */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspaces"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
outline: none;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
scrollbar-width: none;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspace"] {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
padding: 8px 12px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
align-self: stretch;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
[data-slot="selected-icon"] {
|
||||||
|
visibility: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "IBM Plex Mono";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 25.6px */
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 160%; /* 25.6px */
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="workspace"]:hover,
|
||||||
|
[data-slot="workspace"][data-active="true"] {
|
||||||
|
background: #161616;
|
||||||
|
|
||||||
|
[data-slot="selected-icon"] {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="footer"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="footer-content"] {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span,
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="github-stars"] {
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="anomaly"] {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="anomaly-alt"] {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(255, 255, 255, 0.39);
|
||||||
|
font-family: "JetBrains Mono Nerd Font";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,477 @@
|
|||||||
|
::selection {
|
||||||
|
background: var(--color-background-interactive);
|
||||||
|
color: var(--color-text-strong);
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
background: var(--color-background-interactive);
|
||||||
|
color: var(--color-text-inverted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-page="changelog"] {
|
||||||
|
--color-background: hsl(0, 20%, 99%);
|
||||||
|
--color-background-weak: hsl(0, 8%, 97%);
|
||||||
|
--color-background-weak-hover: hsl(0, 8%, 94%);
|
||||||
|
--color-background-strong: hsl(0, 5%, 12%);
|
||||||
|
--color-background-strong-hover: hsl(0, 5%, 18%);
|
||||||
|
--color-background-interactive: hsl(62, 84%, 88%);
|
||||||
|
--color-background-interactive-weaker: hsl(64, 74%, 95%);
|
||||||
|
|
||||||
|
--color-text: hsl(0, 1%, 39%);
|
||||||
|
--color-text-weak: hsl(0, 1%, 60%);
|
||||||
|
--color-text-weaker: hsl(30, 2%, 81%);
|
||||||
|
--color-text-strong: hsl(0, 5%, 12%);
|
||||||
|
--color-text-inverted: hsl(0, 20%, 99%);
|
||||||
|
|
||||||
|
--color-border: hsl(30, 2%, 81%);
|
||||||
|
--color-border-weak: hsl(0, 1%, 85%);
|
||||||
|
|
||||||
|
--color-icon: hsl(0, 1%, 55%);
|
||||||
|
|
||||||
|
background: var(--color-background);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: var(--color-text);
|
||||||
|
padding-bottom: 5rem;
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
--color-background: hsl(0, 9%, 7%);
|
||||||
|
--color-background-weak: hsl(0, 6%, 10%);
|
||||||
|
--color-background-weak-hover: hsl(0, 6%, 15%);
|
||||||
|
--color-background-strong: hsl(0, 15%, 94%);
|
||||||
|
--color-background-strong-hover: hsl(0, 15%, 97%);
|
||||||
|
--color-background-interactive: hsl(62, 100%, 90%);
|
||||||
|
--color-background-interactive-weaker: hsl(60, 20%, 8%);
|
||||||
|
|
||||||
|
--color-text: hsl(0, 4%, 71%);
|
||||||
|
--color-text-weak: hsl(0, 2%, 49%);
|
||||||
|
--color-text-weaker: hsl(0, 3%, 28%);
|
||||||
|
--color-text-strong: hsl(0, 15%, 94%);
|
||||||
|
--color-text-inverted: hsl(0, 9%, 7%);
|
||||||
|
|
||||||
|
--color-border: hsl(0, 3%, 28%);
|
||||||
|
--color-border-weak: hsl(0, 4%, 23%);
|
||||||
|
|
||||||
|
--color-icon: hsl(10, 3%, 43%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header styles - copied from download */
|
||||||
|
[data-component="top"] {
|
||||||
|
padding: 24px 5rem;
|
||||||
|
height: 80px;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--color-background);
|
||||||
|
border-bottom: 1px solid var(--color-border-weak);
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
@media (max-width: 60rem) {
|
||||||
|
padding: 24px 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 34px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="nav-desktop"] {
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 48px;
|
||||||
|
|
||||||
|
@media (max-width: 55rem) {
|
||||||
|
gap: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 48rem) {
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
span {
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
text-decoration-thickness: 1px;
|
||||||
|
}
|
||||||
|
[data-slot="cta-button"] {
|
||||||
|
background: var(--color-background-strong);
|
||||||
|
color: var(--color-text-inverted);
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
@media (max-width: 55rem) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="cta-button"]:hover {
|
||||||
|
background: var(--color-background-strong-hover);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="nav-mobile"] {
|
||||||
|
button > svg {
|
||||||
|
color: var(--color-icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="nav-mobile-toggle"] {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
outline: none;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="nav-mobile-toggle"]:hover {
|
||||||
|
background: var(--color-background-weak);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="nav-mobile"] {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
[data-component="nav-mobile-icon"] {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="nav-mobile-menu-list"] {
|
||||||
|
position: fixed;
|
||||||
|
background: var(--color-background);
|
||||||
|
top: 80px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 20px 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 20px;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background: var(--color-background-weak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="logo dark"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
[data-slot="logo light"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
[data-slot="logo dark"] {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="footer"] {
|
||||||
|
border-top: 1px solid var(--color-border-weak);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-top: 4rem;
|
||||||
|
|
||||||
|
@media (max-width: 65rem) {
|
||||||
|
border-bottom: 1px solid var(--color-border-weak);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="cell"] {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 2rem 0;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background: var(--color-background-weak);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
text-decoration-thickness: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="cell"] + [data-slot="cell"] {
|
||||||
|
border-left: 1px solid var(--color-border-weak);
|
||||||
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 25rem) {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
[data-slot="cell"] {
|
||||||
|
flex: 1 0 100%;
|
||||||
|
border-left: none;
|
||||||
|
border-top: 1px solid var(--color-border-weak);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="cell"]:nth-child(1) {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="container"] {
|
||||||
|
max-width: 67.5rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
border: 1px solid var(--color-border-weak);
|
||||||
|
border-top: none;
|
||||||
|
|
||||||
|
@media (max-width: 65rem) {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="content"] {
|
||||||
|
padding: 6rem 5rem;
|
||||||
|
|
||||||
|
@media (max-width: 60rem) {
|
||||||
|
padding: 4rem 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="legal"] {
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem 5rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 32px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
@media (max-width: 60rem) {
|
||||||
|
padding: 2rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--color-text);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Changelog Hero */
|
||||||
|
[data-component="changelog-hero"] {
|
||||||
|
margin-bottom: 4rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
border-bottom: 1px solid var(--color-border-weak);
|
||||||
|
|
||||||
|
@media (max-width: 50rem) {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-text-strong);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Releases */
|
||||||
|
[data-component="releases"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="release"] {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 180px 1fr;
|
||||||
|
gap: 3rem;
|
||||||
|
padding: 2rem 0;
|
||||||
|
border-bottom: 1px solid var(--color-border-weak);
|
||||||
|
|
||||||
|
@media (max-width: 50rem) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
@media (max-width: 50rem) {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="version"] {
|
||||||
|
a {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-strong);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
text-decoration-thickness: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time {
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="content"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="section"] {
|
||||||
|
h3 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-strong);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
color: var(--color-text);
|
||||||
|
line-height: 1.5;
|
||||||
|
padding-left: 16px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "-";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="author"] {
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
font-size: 13px;
|
||||||
|
margin-left: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
text-decoration-thickness: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-component="contributors"] {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--color-text-weak);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
text-decoration-thickness: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-strong);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
text-decoration-thickness: 1px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration-thickness: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
import "./index.css"
|
||||||
|
import { Title, Meta, Link } from "@solidjs/meta"
|
||||||
|
import { createAsync, query } from "@solidjs/router"
|
||||||
|
import { Header } from "~/component/header"
|
||||||
|
import { Footer } from "~/component/footer"
|
||||||
|
import { Legal } from "~/component/legal"
|
||||||
|
import { config } from "~/config"
|
||||||
|
import { For, Show } from "solid-js"
|
||||||
|
|
||||||
|
type Release = {
|
||||||
|
tag_name: string
|
||||||
|
name: string
|
||||||
|
body: string
|
||||||
|
published_at: string
|
||||||
|
html_url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const getReleases = query(async () => {
|
||||||
|
"use server"
|
||||||
|
const response = await fetch("https://api.github.com/repos/anomalyco/opencode/releases?per_page=20", {
|
||||||
|
headers: {
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
"User-Agent": "OpenCode-Console",
|
||||||
|
},
|
||||||
|
cf: {
|
||||||
|
cacheTtl: 60 * 5,
|
||||||
|
cacheEverything: true,
|
||||||
|
},
|
||||||
|
} as any)
|
||||||
|
if (!response.ok) return []
|
||||||
|
return response.json() as Promise<Release[]>
|
||||||
|
}, "releases.get")
|
||||||
|
|
||||||
|
function formatDate(dateString: string) {
|
||||||
|
const date = new Date(dateString)
|
||||||
|
return date.toLocaleDateString("en-US", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "short",
|
||||||
|
day: "numeric",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMarkdown(body: string) {
|
||||||
|
const lines = body.split("\n")
|
||||||
|
const sections: { title: string; items: string[] }[] = []
|
||||||
|
let current: { title: string; items: string[] } | null = null
|
||||||
|
let skip = false
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith("## ")) {
|
||||||
|
if (current) sections.push(current)
|
||||||
|
const title = line.slice(3).trim()
|
||||||
|
current = { title, items: [] }
|
||||||
|
skip = false
|
||||||
|
} else if (line.startsWith("**Thank you")) {
|
||||||
|
skip = true
|
||||||
|
} else if (line.startsWith("- ") && !skip) {
|
||||||
|
current?.items.push(line.slice(2).trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current) sections.push(current)
|
||||||
|
|
||||||
|
return { sections }
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReleaseItem(props: { item: string }) {
|
||||||
|
const parts = () => {
|
||||||
|
const match = props.item.match(/^(.+?)(\s*\(@([\w-]+)\))?$/)
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
text: match[1],
|
||||||
|
username: match[3],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { text: props.item, username: undefined }
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<span>{parts().text}</span>
|
||||||
|
<Show when={parts().username}>
|
||||||
|
<a data-slot="author" href={`https://github.com/${parts().username}`} target="_blank" rel="noopener noreferrer">
|
||||||
|
(@{parts().username})
|
||||||
|
</a>
|
||||||
|
</Show>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Changelog() {
|
||||||
|
const releases = createAsync(() => getReleases())
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main data-page="changelog">
|
||||||
|
<Title>OpenCode | Changelog</Title>
|
||||||
|
<Link rel="canonical" href={`${config.baseUrl}/changelog`} />
|
||||||
|
<Meta name="description" content="OpenCode release notes and changelog" />
|
||||||
|
|
||||||
|
<div data-component="container">
|
||||||
|
<Header hideGetStarted />
|
||||||
|
|
||||||
|
<div data-component="content">
|
||||||
|
<section data-component="changelog-hero">
|
||||||
|
<h1>Changelog</h1>
|
||||||
|
<p>New updates and improvements to OpenCode</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section data-component="releases">
|
||||||
|
<For each={releases()}>
|
||||||
|
{(release) => {
|
||||||
|
const parsed = () => parseMarkdown(release.body || "")
|
||||||
|
return (
|
||||||
|
<article data-component="release">
|
||||||
|
<header>
|
||||||
|
<div data-slot="version">
|
||||||
|
<a href={release.html_url} target="_blank" rel="noopener noreferrer">
|
||||||
|
{release.tag_name}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<time dateTime={release.published_at}>{formatDate(release.published_at)}</time>
|
||||||
|
</header>
|
||||||
|
<div data-slot="content">
|
||||||
|
<For each={parsed().sections}>
|
||||||
|
{(section) => (
|
||||||
|
<div data-component="section">
|
||||||
|
<h3>{section.title}</h3>
|
||||||
|
<ul>
|
||||||
|
<For each={section.items}>{(item) => <ReleaseItem item={item} />}</For>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Legal />
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ export async function GET({ params: { platform } }: APIEvent) {
|
|||||||
const resp = await fetch(`https://github.com/anomalyco/opencode/releases/latest/download/${assetName}`, {
|
const resp = await fetch(`https://github.com/anomalyco/opencode/releases/latest/download/${assetName}`, {
|
||||||
cf: {
|
cf: {
|
||||||
// in case gh releases has rate limits
|
// in case gh releases has rate limits
|
||||||
cacheTtl: 60 * 60 * 24,
|
cacheTtl: 60 * 5,
|
||||||
cacheEverything: true,
|
cacheEverything: true,
|
||||||
},
|
},
|
||||||
} as any)
|
} as any)
|
||||||
|
|||||||
@@ -34,7 +34,6 @@
|
|||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
padding-bottom: 5rem;
|
padding-bottom: 5rem;
|
||||||
overflow-x: hidden;
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
--color-background: hsl(0, 9%, 7%);
|
--color-background: hsl(0, 9%, 7%);
|
||||||
|
|||||||
@@ -244,7 +244,8 @@ export default function Download() {
|
|||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div data-component="download-row">
|
{/* Disabled temporarily as it doesn't work */}
|
||||||
|
{/*<div data-component="download-row">
|
||||||
<div data-component="download-info">
|
<div data-component="download-info">
|
||||||
<span data-slot="icon">
|
<span data-slot="icon">
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -259,7 +260,7 @@ export default function Download() {
|
|||||||
<a href={getDownloadHref("linux-x64-appimage")} data-component="action-button">
|
<a href={getDownloadHref("linux-x64-appimage")} data-component="action-button">
|
||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>*/}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -440,7 +441,8 @@ export default function Download() {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Faq question="Can I only use OpenCode in the terminal?">
|
<Faq question="Can I only use OpenCode in the terminal?">
|
||||||
Not anymore! OpenCode is now available as an app for your desktop.
|
Not anymore! OpenCode is now available as an app for your <a href="/download">desktop</a> and{" "}
|
||||||
|
<a href="/docs/cli/#web">web</a>!
|
||||||
</Faq>
|
</Faq>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -522,7 +522,7 @@ body {
|
|||||||
[data-slot="content"] {
|
[data-slot="content"] {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 1ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-slot="text"] {
|
[data-slot="text"] {
|
||||||
|
|||||||
@@ -195,6 +195,12 @@ export default function Home() {
|
|||||||
<strong>Claude Pro</strong> Log in with Anthropic to use your Claude Pro or Max account
|
<strong>Claude Pro</strong> Log in with Anthropic to use your Claude Pro or Max account
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<span>[*]</span>
|
||||||
|
<div>
|
||||||
|
<strong>ChatGPT Plus/Pro</strong> Log in with OpenAI to use your ChatGPT Plus or Pro account
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span>[*]</span>
|
<span>[*]</span>
|
||||||
<div>
|
<div>
|
||||||
@@ -686,7 +692,8 @@ export default function Home() {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Faq question="Can I only use OpenCode in the terminal?">
|
<Faq question="Can I only use OpenCode in the terminal?">
|
||||||
Not anymore! OpenCode is now available as an app for your desktop.
|
Not anymore! OpenCode is now available as an app for your <a href="/download">desktop</a> and{" "}
|
||||||
|
<a href="/docs/web">web</a>!
|
||||||
</Faq>
|
</Faq>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -183,7 +183,12 @@ export async function POST(input: APIEvent) {
|
|||||||
.set({
|
.set({
|
||||||
customerID,
|
customerID,
|
||||||
subscriptionID,
|
subscriptionID,
|
||||||
subscriptionCouponID: couponID,
|
subscription: {
|
||||||
|
status: "subscribed",
|
||||||
|
coupon: couponID,
|
||||||
|
seats: 1,
|
||||||
|
plan: "200",
|
||||||
|
},
|
||||||
paymentMethodID: paymentMethod.id,
|
paymentMethodID: paymentMethod.id,
|
||||||
paymentMethodLast4: paymentMethod.card?.last4 ?? null,
|
paymentMethodLast4: paymentMethod.card?.last4 ?? null,
|
||||||
paymentMethodType: paymentMethod.type,
|
paymentMethodType: paymentMethod.type,
|
||||||
@@ -408,7 +413,7 @@ export async function POST(input: APIEvent) {
|
|||||||
await Database.transaction(async (tx) => {
|
await Database.transaction(async (tx) => {
|
||||||
await tx
|
await tx
|
||||||
.update(BillingTable)
|
.update(BillingTable)
|
||||||
.set({ subscriptionID: null, subscriptionCouponID: null })
|
.set({ subscriptionID: null, subscription: null })
|
||||||
.where(eq(BillingTable.workspaceID, workspaceID))
|
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||||
|
|
||||||
await tx.delete(SubscriptionTable).where(eq(SubscriptionTable.workspaceID, workspaceID))
|
await tx.delete(SubscriptionTable).where(eq(SubscriptionTable.workspaceID, workspaceID))
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ export default function Home() {
|
|||||||
<li>
|
<li>
|
||||||
<strong>Claude Pro</strong> Log in with Anthropic to use your Claude Pro or Max account
|
<strong>Claude Pro</strong> Log in with Anthropic to use your Claude Pro or Max account
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>ChatGPT Plus/Pro</strong> Log in with OpenAI to use your ChatGPT Plus or Pro account
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Use any model</strong> Supports 75+ LLM providers through{" "}
|
<strong>Use any model</strong> Supports 75+ LLM providers through{" "}
|
||||||
<a href="https://models.dev">Models.dev</a>, including local models
|
<a href="https://models.dev">Models.dev</a>, including local models
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { action } from "@solidjs/router"
|
import { action } from "@solidjs/router"
|
||||||
import { getRequestEvent } from "solid-js/web"
|
import { getRequestEvent } from "solid-js/web"
|
||||||
import { useAuthSession } from "~/context/auth.session"
|
import { useAuthSession } from "~/context/auth"
|
||||||
import { Dropdown } from "~/component/dropdown"
|
import { Dropdown } from "~/component/dropdown"
|
||||||
import "./user-menu.css"
|
import "./user-menu.css"
|
||||||
|
|
||||||
|
|||||||
@@ -5,4 +5,58 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-slot="usage"] {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-6);
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="usage-item"] {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="usage-header"] {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="usage-label"] {
|
||||||
|
font-size: var(--font-size-md);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="usage-value"] {
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="progress"] {
|
||||||
|
height: 8px;
|
||||||
|
background-color: var(--color-bg-surface);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="progress-bar"] {
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--color-accent);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="reset-time"] {
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,58 @@
|
|||||||
import { action, useParams, useAction, useSubmission, json } from "@solidjs/router"
|
import { action, useParams, useAction, useSubmission, json, query, createAsync } from "@solidjs/router"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
|
import { Show } from "solid-js"
|
||||||
import { Billing } from "@opencode-ai/console-core/billing.js"
|
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||||
|
import { Database, eq, and, isNull } from "@opencode-ai/console-core/drizzle/index.js"
|
||||||
|
import { SubscriptionTable } from "@opencode-ai/console-core/schema/billing.sql.js"
|
||||||
|
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||||
|
import { Black } from "@opencode-ai/console-core/black.js"
|
||||||
import { withActor } from "~/context/auth.withActor"
|
import { withActor } from "~/context/auth.withActor"
|
||||||
import { queryBillingInfo } from "../../common"
|
import { queryBillingInfo } from "../../common"
|
||||||
import styles from "./black-section.module.css"
|
import styles from "./black-section.module.css"
|
||||||
|
|
||||||
|
const querySubscription = query(async (workspaceID: string) => {
|
||||||
|
"use server"
|
||||||
|
return withActor(async () => {
|
||||||
|
const row = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
rollingUsage: SubscriptionTable.rollingUsage,
|
||||||
|
fixedUsage: SubscriptionTable.fixedUsage,
|
||||||
|
timeRollingUpdated: SubscriptionTable.timeRollingUpdated,
|
||||||
|
timeFixedUpdated: SubscriptionTable.timeFixedUpdated,
|
||||||
|
})
|
||||||
|
.from(SubscriptionTable)
|
||||||
|
.where(and(eq(SubscriptionTable.workspaceID, Actor.workspace()), isNull(SubscriptionTable.timeDeleted)))
|
||||||
|
.then((r) => r[0]),
|
||||||
|
)
|
||||||
|
if (!row) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
rollingUsage: Black.analyzeRollingUsage({
|
||||||
|
usage: row.rollingUsage ?? 0,
|
||||||
|
timeUpdated: row.timeRollingUpdated ?? new Date(),
|
||||||
|
}),
|
||||||
|
weeklyUsage: Black.analyzeWeeklyUsage({
|
||||||
|
usage: row.fixedUsage ?? 0,
|
||||||
|
timeUpdated: row.timeFixedUpdated ?? new Date(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}, workspaceID)
|
||||||
|
}, "subscription.get")
|
||||||
|
|
||||||
|
function formatResetTime(seconds: number) {
|
||||||
|
const days = Math.floor(seconds / 86400)
|
||||||
|
if (days >= 1) {
|
||||||
|
const hours = Math.floor((seconds % 86400) / 3600)
|
||||||
|
return `${days} ${days === 1 ? "day" : "days"} ${hours} ${hours === 1 ? "hour" : "hours"}`
|
||||||
|
}
|
||||||
|
const hours = Math.floor(seconds / 3600)
|
||||||
|
const minutes = Math.floor((seconds % 3600) / 60)
|
||||||
|
if (hours >= 1) return `${hours} ${hours === 1 ? "hour" : "hours"} ${minutes} ${minutes === 1 ? "minute" : "minutes"}`
|
||||||
|
if (minutes === 0) return "a few seconds"
|
||||||
|
return `${minutes} ${minutes === 1 ? "minute" : "minutes"}`
|
||||||
|
}
|
||||||
|
|
||||||
const createSessionUrl = action(async (workspaceID: string, returnUrl: string) => {
|
const createSessionUrl = action(async (workspaceID: string, returnUrl: string) => {
|
||||||
"use server"
|
"use server"
|
||||||
return json(
|
return json(
|
||||||
@@ -26,6 +74,7 @@ export function BlackSection() {
|
|||||||
const params = useParams()
|
const params = useParams()
|
||||||
const sessionAction = useAction(createSessionUrl)
|
const sessionAction = useAction(createSessionUrl)
|
||||||
const sessionSubmission = useSubmission(createSessionUrl)
|
const sessionSubmission = useSubmission(createSessionUrl)
|
||||||
|
const subscription = createAsync(() => querySubscription(params.id!))
|
||||||
const [store, setStore] = createStore({
|
const [store, setStore] = createStore({
|
||||||
sessionRedirecting: false,
|
sessionRedirecting: false,
|
||||||
})
|
})
|
||||||
@@ -53,6 +102,32 @@ export function BlackSection() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={subscription()}>
|
||||||
|
{(sub) => (
|
||||||
|
<div data-slot="usage">
|
||||||
|
<div data-slot="usage-item">
|
||||||
|
<div data-slot="usage-header">
|
||||||
|
<span data-slot="usage-label">5-hour Usage</span>
|
||||||
|
<span data-slot="usage-value">{sub().rollingUsage.usagePercent}%</span>
|
||||||
|
</div>
|
||||||
|
<div data-slot="progress">
|
||||||
|
<div data-slot="progress-bar" style={{ width: `${sub().rollingUsage.usagePercent}%` }} />
|
||||||
|
</div>
|
||||||
|
<span data-slot="reset-time">Resets in {formatResetTime(sub().rollingUsage.resetInSec)}</span>
|
||||||
|
</div>
|
||||||
|
<div data-slot="usage-item">
|
||||||
|
<div data-slot="usage-header">
|
||||||
|
<span data-slot="usage-label">Weekly Usage</span>
|
||||||
|
<span data-slot="usage-value">{sub().weeklyUsage.usagePercent}%</span>
|
||||||
|
</div>
|
||||||
|
<div data-slot="progress">
|
||||||
|
<div data-slot="progress-bar" style={{ width: `${sub().weeklyUsage.usagePercent}%` }} />
|
||||||
|
</div>
|
||||||
|
<span data-slot="reset-time">Resets in {formatResetTime(sub().weeklyUsage.resetInSec)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Show>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Actor } from "@opencode-ai/console-core/actor.js"
|
|||||||
import { action, json, query } from "@solidjs/router"
|
import { action, json, query } from "@solidjs/router"
|
||||||
import { withActor } from "~/context/auth.withActor"
|
import { withActor } from "~/context/auth.withActor"
|
||||||
import { Billing } from "@opencode-ai/console-core/billing.js"
|
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||||
import { User } from "@opencode-ai/console-core/user.js"
|
|
||||||
import { and, Database, desc, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js"
|
import { and, Database, desc, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js"
|
||||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||||
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||||
@@ -96,11 +95,22 @@ export const queryBillingInfo = query(async (workspaceID: string) => {
|
|||||||
return withActor(async () => {
|
return withActor(async () => {
|
||||||
const billing = await Billing.get()
|
const billing = await Billing.get()
|
||||||
return {
|
return {
|
||||||
...billing,
|
customerID: billing.customerID,
|
||||||
|
paymentMethodID: billing.paymentMethodID,
|
||||||
|
paymentMethodType: billing.paymentMethodType,
|
||||||
|
paymentMethodLast4: billing.paymentMethodLast4,
|
||||||
|
balance: billing.balance,
|
||||||
|
reload: billing.reload,
|
||||||
reloadAmount: billing.reloadAmount ?? Billing.RELOAD_AMOUNT,
|
reloadAmount: billing.reloadAmount ?? Billing.RELOAD_AMOUNT,
|
||||||
reloadAmountMin: Billing.RELOAD_AMOUNT_MIN,
|
reloadAmountMin: Billing.RELOAD_AMOUNT_MIN,
|
||||||
reloadTrigger: billing.reloadTrigger ?? Billing.RELOAD_TRIGGER,
|
reloadTrigger: billing.reloadTrigger ?? Billing.RELOAD_TRIGGER,
|
||||||
reloadTriggerMin: Billing.RELOAD_TRIGGER_MIN,
|
reloadTriggerMin: Billing.RELOAD_TRIGGER_MIN,
|
||||||
|
monthlyLimit: billing.monthlyLimit,
|
||||||
|
monthlyUsage: billing.monthlyUsage,
|
||||||
|
timeMonthlyUsageUpdated: billing.timeMonthlyUsageUpdated,
|
||||||
|
reloadError: billing.reloadError,
|
||||||
|
timeReloadError: billing.timeReloadError,
|
||||||
|
subscriptionID: billing.subscriptionID,
|
||||||
}
|
}
|
||||||
}, workspaceID)
|
}, workspaceID)
|
||||||
}, "billing.get")
|
}, "billing.get")
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { Billing } from "@opencode-ai/console-core/billing.js"
|
|||||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||||
import { ZenData } from "@opencode-ai/console-core/model.js"
|
import { ZenData } from "@opencode-ai/console-core/model.js"
|
||||||
import { BlackData } from "@opencode-ai/console-core/black.js"
|
import { Black, BlackData } from "@opencode-ai/console-core/black.js"
|
||||||
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||||
import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js"
|
import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js"
|
||||||
import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js"
|
import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js"
|
||||||
@@ -81,12 +81,13 @@ export async function handler(
|
|||||||
const isTrial = await trialLimiter?.isTrial()
|
const isTrial = await trialLimiter?.isTrial()
|
||||||
const rateLimiter = createRateLimiter(modelInfo.rateLimit, ip)
|
const rateLimiter = createRateLimiter(modelInfo.rateLimit, ip)
|
||||||
await rateLimiter?.check()
|
await rateLimiter?.check()
|
||||||
const stickyTracker = createStickyTracker(modelInfo.stickyProvider ?? false, sessionId)
|
const stickyTracker = createStickyTracker(modelInfo.stickyProvider, sessionId)
|
||||||
const stickyProvider = await stickyTracker?.get()
|
const stickyProvider = await stickyTracker?.get()
|
||||||
const authInfo = await authenticate(modelInfo)
|
const authInfo = await authenticate(modelInfo)
|
||||||
|
|
||||||
const retriableRequest = async (retry: RetryOptions = { excludeProviders: [], retryCount: 0 }) => {
|
const retriableRequest = async (retry: RetryOptions = { excludeProviders: [], retryCount: 0 }) => {
|
||||||
const providerInfo = selectProvider(
|
const providerInfo = selectProvider(
|
||||||
|
model,
|
||||||
zenData,
|
zenData,
|
||||||
authInfo,
|
authInfo,
|
||||||
modelInfo,
|
modelInfo,
|
||||||
@@ -101,7 +102,7 @@ export async function handler(
|
|||||||
logger.metric({ provider: providerInfo.id })
|
logger.metric({ provider: providerInfo.id })
|
||||||
|
|
||||||
const startTimestamp = Date.now()
|
const startTimestamp = Date.now()
|
||||||
const reqUrl = providerInfo.modifyUrl(providerInfo.api, providerInfo.model, isStream)
|
const reqUrl = providerInfo.modifyUrl(providerInfo.api, isStream)
|
||||||
const reqBody = JSON.stringify(
|
const reqBody = JSON.stringify(
|
||||||
providerInfo.modifyBody({
|
providerInfo.modifyBody({
|
||||||
...createBodyConverter(opts.format, providerInfo.format)(body),
|
...createBodyConverter(opts.format, providerInfo.format)(body),
|
||||||
@@ -135,7 +136,7 @@ export async function handler(
|
|||||||
// ie. openai 404 error: Item with id 'msg_0ead8b004a3b165d0069436a6b6834819896da85b63b196a3f' not found.
|
// ie. openai 404 error: Item with id 'msg_0ead8b004a3b165d0069436a6b6834819896da85b63b196a3f' not found.
|
||||||
res.status !== 404 &&
|
res.status !== 404 &&
|
||||||
// ie. cannot change codex model providers mid-session
|
// ie. cannot change codex model providers mid-session
|
||||||
!modelInfo.stickyProvider &&
|
modelInfo.stickyProvider !== "strict" &&
|
||||||
modelInfo.fallbackProvider &&
|
modelInfo.fallbackProvider &&
|
||||||
providerInfo.id !== modelInfo.fallbackProvider
|
providerInfo.id !== modelInfo.fallbackProvider
|
||||||
) {
|
) {
|
||||||
@@ -194,17 +195,19 @@ export async function handler(
|
|||||||
// Handle streaming response
|
// Handle streaming response
|
||||||
const streamConverter = createStreamPartConverter(providerInfo.format, opts.format)
|
const streamConverter = createStreamPartConverter(providerInfo.format, opts.format)
|
||||||
const usageParser = providerInfo.createUsageParser()
|
const usageParser = providerInfo.createUsageParser()
|
||||||
|
const binaryDecoder = providerInfo.createBinaryStreamDecoder()
|
||||||
const stream = new ReadableStream({
|
const stream = new ReadableStream({
|
||||||
start(c) {
|
start(c) {
|
||||||
const reader = res.body?.getReader()
|
const reader = res.body?.getReader()
|
||||||
const decoder = new TextDecoder()
|
const decoder = new TextDecoder()
|
||||||
const encoder = new TextEncoder()
|
const encoder = new TextEncoder()
|
||||||
|
|
||||||
let buffer = ""
|
let buffer = ""
|
||||||
let responseLength = 0
|
let responseLength = 0
|
||||||
|
|
||||||
function pump(): Promise<void> {
|
function pump(): Promise<void> {
|
||||||
return (
|
return (
|
||||||
reader?.read().then(async ({ done, value }) => {
|
reader?.read().then(async ({ done, value: rawValue }) => {
|
||||||
if (done) {
|
if (done) {
|
||||||
logger.metric({
|
logger.metric({
|
||||||
response_length: responseLength,
|
response_length: responseLength,
|
||||||
@@ -230,6 +233,10 @@ export async function handler(
|
|||||||
"timestamp.first_byte": now,
|
"timestamp.first_byte": now,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const value = binaryDecoder ? binaryDecoder(rawValue) : rawValue
|
||||||
|
if (!value) return
|
||||||
|
|
||||||
responseLength += value.length
|
responseLength += value.length
|
||||||
buffer += decoder.decode(value, { stream: true })
|
buffer += decoder.decode(value, { stream: true })
|
||||||
dataDumper?.provideStream(buffer)
|
dataDumper?.provideStream(buffer)
|
||||||
@@ -331,6 +338,7 @@ export async function handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function selectProvider(
|
function selectProvider(
|
||||||
|
reqModel: string,
|
||||||
zenData: ZenData,
|
zenData: ZenData,
|
||||||
authInfo: AuthInfo,
|
authInfo: AuthInfo,
|
||||||
modelInfo: ModelInfo,
|
modelInfo: ModelInfo,
|
||||||
@@ -339,7 +347,7 @@ export async function handler(
|
|||||||
retry: RetryOptions,
|
retry: RetryOptions,
|
||||||
stickyProvider: string | undefined,
|
stickyProvider: string | undefined,
|
||||||
) {
|
) {
|
||||||
const provider = (() => {
|
const modelProvider = (() => {
|
||||||
if (authInfo?.provider?.credentials) {
|
if (authInfo?.provider?.credentials) {
|
||||||
return modelInfo.providers.find((provider) => provider.id === modelInfo.byokProvider)
|
return modelInfo.providers.find((provider) => provider.id === modelInfo.byokProvider)
|
||||||
}
|
}
|
||||||
@@ -372,18 +380,19 @@ export async function handler(
|
|||||||
return providers[index || 0]
|
return providers[index || 0]
|
||||||
})()
|
})()
|
||||||
|
|
||||||
if (!provider) throw new ModelError("No provider available")
|
if (!modelProvider) throw new ModelError("No provider available")
|
||||||
if (!(provider.id in zenData.providers)) throw new ModelError(`Provider ${provider.id} not supported`)
|
if (!(modelProvider.id in zenData.providers)) throw new ModelError(`Provider ${modelProvider.id} not supported`)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...provider,
|
...modelProvider,
|
||||||
...zenData.providers[provider.id],
|
...zenData.providers[modelProvider.id],
|
||||||
...(() => {
|
...(() => {
|
||||||
const format = zenData.providers[provider.id].format
|
const format = zenData.providers[modelProvider.id].format
|
||||||
if (format === "anthropic") return anthropicHelper
|
const providerModel = modelProvider.model
|
||||||
if (format === "google") return googleHelper
|
if (format === "anthropic") return anthropicHelper({ reqModel, providerModel })
|
||||||
if (format === "openai") return openaiHelper
|
if (format === "google") return googleHelper({ reqModel, providerModel })
|
||||||
return oaCompatHelper
|
if (format === "openai") return openaiHelper({ reqModel, providerModel })
|
||||||
|
return oaCompatHelper({ reqModel, providerModel })
|
||||||
})(),
|
})(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -495,27 +504,28 @@ export async function handler(
|
|||||||
|
|
||||||
// Check weekly limit
|
// Check weekly limit
|
||||||
if (sub.fixedUsage && sub.timeFixedUpdated) {
|
if (sub.fixedUsage && sub.timeFixedUpdated) {
|
||||||
const week = getWeekBounds(now)
|
const result = Black.analyzeWeeklyUsage({
|
||||||
if (sub.timeFixedUpdated >= week.start && sub.fixedUsage >= centsToMicroCents(black.fixedLimit * 100)) {
|
usage: sub.fixedUsage,
|
||||||
const retryAfter = Math.ceil((week.end.getTime() - now.getTime()) / 1000)
|
timeUpdated: sub.timeFixedUpdated,
|
||||||
|
})
|
||||||
|
if (result.status === "rate-limited")
|
||||||
throw new SubscriptionError(
|
throw new SubscriptionError(
|
||||||
`Subscription quota exceeded. Retry in ${formatRetryTime(retryAfter)}.`,
|
`Subscription quota exceeded. Retry in ${formatRetryTime(result.resetInSec)}.`,
|
||||||
retryAfter,
|
result.resetInSec,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check rolling limit
|
// Check rolling limit
|
||||||
if (sub.rollingUsage && sub.timeRollingUpdated) {
|
if (sub.rollingUsage && sub.timeRollingUpdated) {
|
||||||
const rollingWindowMs = black.rollingWindow * 3600 * 1000
|
const result = Black.analyzeRollingUsage({
|
||||||
const windowStart = new Date(now.getTime() - rollingWindowMs)
|
usage: sub.rollingUsage,
|
||||||
if (sub.timeRollingUpdated >= windowStart && sub.rollingUsage >= centsToMicroCents(black.rollingLimit * 100)) {
|
timeUpdated: sub.timeRollingUpdated,
|
||||||
const retryAfter = Math.ceil((sub.timeRollingUpdated.getTime() + rollingWindowMs - now.getTime()) / 1000)
|
})
|
||||||
|
if (result.status === "rate-limited")
|
||||||
throw new SubscriptionError(
|
throw new SubscriptionError(
|
||||||
`Subscription quota exceeded. Retry in ${formatRetryTime(retryAfter)}.`,
|
`Subscription quota exceeded. Retry in ${formatRetryTime(result.resetInSec)}.`,
|
||||||
retryAfter,
|
result.resetInSec,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import { EventStreamCodec } from "@smithy/eventstream-codec"
|
||||||
import { ProviderHelper, CommonRequest, CommonResponse, CommonChunk } from "./provider"
|
import { ProviderHelper, CommonRequest, CommonResponse, CommonChunk } from "./provider"
|
||||||
|
import { fromUtf8, toUtf8 } from "@smithy/util-utf8"
|
||||||
|
|
||||||
type Usage = {
|
type Usage = {
|
||||||
cache_creation?: {
|
cache_creation?: {
|
||||||
@@ -14,65 +16,169 @@ type Usage = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const anthropicHelper = {
|
export const anthropicHelper: ProviderHelper = ({ reqModel, providerModel }) => {
|
||||||
format: "anthropic",
|
const isBedrockModelArn = providerModel.startsWith("arn:aws:bedrock:")
|
||||||
modifyUrl: (providerApi: string) => providerApi + "/messages",
|
const isBedrockModelID = providerModel.startsWith("global.anthropic.")
|
||||||
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
const isBedrock = isBedrockModelArn || isBedrockModelID
|
||||||
headers.set("x-api-key", apiKey)
|
const isSonnet = reqModel.includes("sonnet")
|
||||||
headers.set("anthropic-version", headers.get("anthropic-version") ?? "2023-06-01")
|
return {
|
||||||
if (body.model.startsWith("claude-sonnet-")) {
|
format: "anthropic",
|
||||||
headers.set("anthropic-beta", "context-1m-2025-08-07")
|
modifyUrl: (providerApi: string, isStream?: boolean) =>
|
||||||
}
|
isBedrock
|
||||||
},
|
? `${providerApi}/model/${isBedrockModelArn ? encodeURIComponent(providerModel) : providerModel}/${isStream ? "invoke-with-response-stream" : "invoke"}`
|
||||||
modifyBody: (body: Record<string, any>) => {
|
: providerApi + "/messages",
|
||||||
return {
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
|
if (isBedrock) {
|
||||||
|
headers.set("Authorization", `Bearer ${apiKey}`)
|
||||||
|
} else {
|
||||||
|
headers.set("x-api-key", apiKey)
|
||||||
|
headers.set("anthropic-version", headers.get("anthropic-version") ?? "2023-06-01")
|
||||||
|
if (body.model.startsWith("claude-sonnet-")) {
|
||||||
|
headers.set("anthropic-beta", "context-1m-2025-08-07")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifyBody: (body: Record<string, any>) => ({
|
||||||
...body,
|
...body,
|
||||||
service_tier: "standard_only",
|
...(isBedrock
|
||||||
}
|
? {
|
||||||
},
|
anthropic_version: "bedrock-2023-05-31",
|
||||||
streamSeparator: "\n\n",
|
anthropic_beta: isSonnet ? "context-1m-2025-08-07" : undefined,
|
||||||
createUsageParser: () => {
|
model: undefined,
|
||||||
let usage: Usage
|
stream: undefined,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
service_tier: "standard_only",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
createBinaryStreamDecoder: () => {
|
||||||
|
if (!isBedrock) return undefined
|
||||||
|
|
||||||
return {
|
const decoder = new TextDecoder()
|
||||||
parse: (chunk: string) => {
|
const encoder = new TextEncoder()
|
||||||
const data = chunk.split("\n")[1]
|
const codec = new EventStreamCodec(toUtf8, fromUtf8)
|
||||||
if (!data.startsWith("data: ")) return
|
let buffer = new Uint8Array(0)
|
||||||
|
return (value: Uint8Array) => {
|
||||||
|
const newBuffer = new Uint8Array(buffer.length + value.length)
|
||||||
|
newBuffer.set(buffer)
|
||||||
|
newBuffer.set(value, buffer.length)
|
||||||
|
buffer = newBuffer
|
||||||
|
|
||||||
let json
|
const messages = []
|
||||||
try {
|
while (buffer.length >= 4) {
|
||||||
json = JSON.parse(data.slice(6))
|
// first 4 bytes are the total length (big-endian)
|
||||||
} catch (e) {
|
const totalLength = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getUint32(0, false)
|
||||||
return
|
|
||||||
|
// wait for more chunks
|
||||||
|
if (buffer.length < totalLength) break
|
||||||
|
|
||||||
|
try {
|
||||||
|
const subView = buffer.subarray(0, totalLength)
|
||||||
|
const decoded = codec.decode(subView)
|
||||||
|
buffer = buffer.slice(totalLength)
|
||||||
|
|
||||||
|
/* Example of Bedrock data
|
||||||
|
```
|
||||||
|
{
|
||||||
|
bytes: 'eyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtb3B1cy00LTUtMjAyNTExMDEiLCJpZCI6Im1zZ19iZHJrXzAxMjVGdHRGb2lkNGlwWmZ4SzZMbktxeCIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwidXNhZ2UiOnsiaW5wdXRfdG9rZW5zIjo0LCJjYWNoZV9jcmVhdGlvbl9pbnB1dF90b2tlbnMiOjEsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjoxMTk2MywiY2FjaGVfY3JlYXRpb24iOnsiZXBoZW1lcmFsXzVtX2lucHV0X3Rva2VucyI6MSwiZXBoZW1lcmFsXzFoX2lucHV0X3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjF9fX0=',
|
||||||
|
p: '...'
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
const usageUpdate = json.usage ?? json.message?.usage
|
Decoded bytes
|
||||||
if (!usageUpdate) return
|
```
|
||||||
usage = {
|
{
|
||||||
...usage,
|
type: 'message_start',
|
||||||
...usageUpdate,
|
message: {
|
||||||
cache_creation: {
|
model: 'claude-opus-4-5-20251101',
|
||||||
...usage?.cache_creation,
|
id: 'msg_bdrk_0125FttFoid4ipZfxK6LnKqx',
|
||||||
...usageUpdate.cache_creation,
|
type: 'message',
|
||||||
},
|
role: 'assistant',
|
||||||
server_tool_use: {
|
content: [],
|
||||||
...usage?.server_tool_use,
|
stop_reason: null,
|
||||||
...usageUpdate.server_tool_use,
|
stop_sequence: null,
|
||||||
},
|
usage: {
|
||||||
|
input_tokens: 4,
|
||||||
|
cache_creation_input_tokens: 1,
|
||||||
|
cache_read_input_tokens: 11963,
|
||||||
|
cache_creation: [Object],
|
||||||
|
output_tokens: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
```
|
||||||
retrieve: () => usage,
|
*/
|
||||||
}
|
|
||||||
},
|
/* Example of Anthropic data
|
||||||
normalizeUsage: (usage: Usage) => ({
|
```
|
||||||
inputTokens: usage.input_tokens ?? 0,
|
event: message_delta
|
||||||
outputTokens: usage.output_tokens ?? 0,
|
data: {"type":"message_start","message":{"model":"claude-opus-4-5-20251101","id":"msg_01ETvwVWSKULxzPdkQ1xAnk2","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":11543,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":11543,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard"}}}
|
||||||
reasoningTokens: undefined,
|
```
|
||||||
cacheReadTokens: usage.cache_read_input_tokens ?? undefined,
|
*/
|
||||||
cacheWrite5mTokens: usage.cache_creation?.ephemeral_5m_input_tokens ?? undefined,
|
if (decoded.headers[":message-type"]?.value === "event") {
|
||||||
cacheWrite1hTokens: usage.cache_creation?.ephemeral_1h_input_tokens ?? undefined,
|
const data = decoder.decode(decoded.body, { stream: true })
|
||||||
}),
|
|
||||||
} satisfies ProviderHelper
|
const parsedDataResult = JSON.parse(data)
|
||||||
|
delete parsedDataResult.p
|
||||||
|
const binary = atob(parsedDataResult.bytes)
|
||||||
|
const uint8 = Uint8Array.from(binary, (c) => c.charCodeAt(0))
|
||||||
|
const bytes = decoder.decode(uint8)
|
||||||
|
const eventName = JSON.parse(bytes).type
|
||||||
|
messages.push([`event: ${eventName}`, "\n", `data: ${bytes}`, "\n\n"].join(""))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log("@@@EE@@@")
|
||||||
|
console.log(e)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return encoder.encode(messages.join(""))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
streamSeparator: "\n\n",
|
||||||
|
createUsageParser: () => {
|
||||||
|
let usage: Usage
|
||||||
|
|
||||||
|
return {
|
||||||
|
parse: (chunk: string) => {
|
||||||
|
const data = chunk.split("\n")[1]
|
||||||
|
if (!data.startsWith("data: ")) return
|
||||||
|
|
||||||
|
let json
|
||||||
|
try {
|
||||||
|
json = JSON.parse(data.slice(6))
|
||||||
|
} catch (e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const usageUpdate = json.usage ?? json.message?.usage
|
||||||
|
if (!usageUpdate) return
|
||||||
|
usage = {
|
||||||
|
...usage,
|
||||||
|
...usageUpdate,
|
||||||
|
cache_creation: {
|
||||||
|
...usage?.cache_creation,
|
||||||
|
...usageUpdate.cache_creation,
|
||||||
|
},
|
||||||
|
server_tool_use: {
|
||||||
|
...usage?.server_tool_use,
|
||||||
|
...usageUpdate.server_tool_use,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
retrieve: () => usage,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
normalizeUsage: (usage: Usage) => ({
|
||||||
|
inputTokens: usage.input_tokens ?? 0,
|
||||||
|
outputTokens: usage.output_tokens ?? 0,
|
||||||
|
reasoningTokens: undefined,
|
||||||
|
cacheReadTokens: usage.cache_read_input_tokens ?? undefined,
|
||||||
|
cacheWrite5mTokens: usage.cache_creation?.ephemeral_5m_input_tokens ?? undefined,
|
||||||
|
cacheWrite1hTokens: usage.cache_creation?.ephemeral_1h_input_tokens ?? undefined,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function fromAnthropicRequest(body: any): CommonRequest {
|
export function fromAnthropicRequest(body: any): CommonRequest {
|
||||||
if (!body || typeof body !== "object") return body
|
if (!body || typeof body !== "object") return body
|
||||||
|
|||||||
@@ -26,16 +26,17 @@ type Usage = {
|
|||||||
thoughtsTokenCount?: number
|
thoughtsTokenCount?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const googleHelper = {
|
export const googleHelper: ProviderHelper = ({ providerModel }) => ({
|
||||||
format: "google",
|
format: "google",
|
||||||
modifyUrl: (providerApi: string, model?: string, isStream?: boolean) =>
|
modifyUrl: (providerApi: string, isStream?: boolean) =>
|
||||||
`${providerApi}/models/${model}:${isStream ? "streamGenerateContent?alt=sse" : "generateContent"}`,
|
`${providerApi}/models/${providerModel}:${isStream ? "streamGenerateContent?alt=sse" : "generateContent"}`,
|
||||||
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
headers.set("x-goog-api-key", apiKey)
|
headers.set("x-goog-api-key", apiKey)
|
||||||
},
|
},
|
||||||
modifyBody: (body: Record<string, any>) => {
|
modifyBody: (body: Record<string, any>) => {
|
||||||
return body
|
return body
|
||||||
},
|
},
|
||||||
|
createBinaryStreamDecoder: () => undefined,
|
||||||
streamSeparator: "\r\n\r\n",
|
streamSeparator: "\r\n\r\n",
|
||||||
createUsageParser: () => {
|
createUsageParser: () => {
|
||||||
let usage: Usage
|
let usage: Usage
|
||||||
@@ -71,4 +72,4 @@ export const googleHelper = {
|
|||||||
cacheWrite1hTokens: undefined,
|
cacheWrite1hTokens: undefined,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
} satisfies ProviderHelper
|
})
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type Usage = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const oaCompatHelper = {
|
export const oaCompatHelper: ProviderHelper = () => ({
|
||||||
format: "oa-compat",
|
format: "oa-compat",
|
||||||
modifyUrl: (providerApi: string) => providerApi + "/chat/completions",
|
modifyUrl: (providerApi: string) => providerApi + "/chat/completions",
|
||||||
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
@@ -33,6 +33,7 @@ export const oaCompatHelper = {
|
|||||||
...(body.stream ? { stream_options: { include_usage: true } } : {}),
|
...(body.stream ? { stream_options: { include_usage: true } } : {}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
createBinaryStreamDecoder: () => undefined,
|
||||||
streamSeparator: "\n\n",
|
streamSeparator: "\n\n",
|
||||||
createUsageParser: () => {
|
createUsageParser: () => {
|
||||||
let usage: Usage
|
let usage: Usage
|
||||||
@@ -68,7 +69,7 @@ export const oaCompatHelper = {
|
|||||||
cacheWrite1hTokens: undefined,
|
cacheWrite1hTokens: undefined,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
} satisfies ProviderHelper
|
})
|
||||||
|
|
||||||
export function fromOaCompatibleRequest(body: any): CommonRequest {
|
export function fromOaCompatibleRequest(body: any): CommonRequest {
|
||||||
if (!body || typeof body !== "object") return body
|
if (!body || typeof body !== "object") return body
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ type Usage = {
|
|||||||
total_tokens?: number
|
total_tokens?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const openaiHelper = {
|
export const openaiHelper: ProviderHelper = () => ({
|
||||||
format: "openai",
|
format: "openai",
|
||||||
modifyUrl: (providerApi: string) => providerApi + "/responses",
|
modifyUrl: (providerApi: string) => providerApi + "/responses",
|
||||||
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
@@ -21,6 +21,7 @@ export const openaiHelper = {
|
|||||||
modifyBody: (body: Record<string, any>) => {
|
modifyBody: (body: Record<string, any>) => {
|
||||||
return body
|
return body
|
||||||
},
|
},
|
||||||
|
createBinaryStreamDecoder: () => undefined,
|
||||||
streamSeparator: "\n\n",
|
streamSeparator: "\n\n",
|
||||||
createUsageParser: () => {
|
createUsageParser: () => {
|
||||||
let usage: Usage
|
let usage: Usage
|
||||||
@@ -58,7 +59,7 @@ export const openaiHelper = {
|
|||||||
cacheWrite1hTokens: undefined,
|
cacheWrite1hTokens: undefined,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
} satisfies ProviderHelper
|
})
|
||||||
|
|
||||||
export function fromOpenaiRequest(body: any): CommonRequest {
|
export function fromOpenaiRequest(body: any): CommonRequest {
|
||||||
if (!body || typeof body !== "object") return body
|
if (!body || typeof body !== "object") return body
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ export type UsageInfo = {
|
|||||||
cacheWrite1hTokens?: number
|
cacheWrite1hTokens?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProviderHelper = {
|
export type ProviderHelper = (input: { reqModel: string; providerModel: string }) => {
|
||||||
format: ZenData.Format
|
format: ZenData.Format
|
||||||
modifyUrl: (providerApi: string, model?: string, isStream?: boolean) => string
|
modifyUrl: (providerApi: string, isStream?: boolean) => string
|
||||||
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => void
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => void
|
||||||
modifyBody: (body: Record<string, any>) => Record<string, any>
|
modifyBody: (body: Record<string, any>) => Record<string, any>
|
||||||
|
createBinaryStreamDecoder: () => ((chunk: Uint8Array) => Uint8Array | undefined) | undefined
|
||||||
streamSeparator: string
|
streamSeparator: string
|
||||||
createUsageParser: () => {
|
createUsageParser: () => {
|
||||||
parse: (chunk: string) => void
|
parse: (chunk: string) => void
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Resource } from "@opencode-ai/console-resource"
|
import { Resource } from "@opencode-ai/console-resource"
|
||||||
|
|
||||||
export function createStickyTracker(stickyProvider: boolean, session: string) {
|
export function createStickyTracker(stickyProvider: "strict" | "prefer" | undefined, session: string) {
|
||||||
if (!stickyProvider) return
|
if (!stickyProvider) return
|
||||||
if (!session) return
|
if (!session) return
|
||||||
const key = `sticky:${session}`
|
const key = `sticky:${session}`
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"types": ["vite/client"],
|
"types": ["vite/client", "@webgpu/types"],
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"~/*": ["./src/*"]
|
"~/*": ["./src/*"]
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `billing` ADD `time_subscription_booked` timestamp(3);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `billing` ADD `subscription_plan` enum('20','100','200');
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `billing` ADD `subscription` json;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `billing` DROP COLUMN `subscription_coupon_id`;
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -358,6 +358,34 @@
|
|||||||
"when": 1767931290031,
|
"when": 1767931290031,
|
||||||
"tag": "0050_bumpy_mephistopheles",
|
"tag": "0050_bumpy_mephistopheles",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 51,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1768341152722,
|
||||||
|
"tag": "0051_jazzy_green_goblin",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 52,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1768343920467,
|
||||||
|
"tag": "0052_aromatic_agent_zero",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 53,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1768599366758,
|
||||||
|
"tag": "0053_gigantic_hardball",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 54,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1768603665356,
|
||||||
|
"tag": "0054_numerous_annihilus",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.1.8",
|
"version": "1.1.25",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
"promote-models-to-dev": "script/promote-models.ts dev",
|
"promote-models-to-dev": "script/promote-models.ts dev",
|
||||||
"promote-models-to-prod": "script/promote-models.ts production",
|
"promote-models-to-prod": "script/promote-models.ts production",
|
||||||
"pull-models-from-dev": "script/pull-models.ts dev",
|
"pull-models-from-dev": "script/pull-models.ts dev",
|
||||||
|
"pull-models-from-prod": "script/pull-models.ts production",
|
||||||
"update-black": "script/update-black.ts",
|
"update-black": "script/update-black.ts",
|
||||||
"promote-black-to-dev": "script/promote-black.ts dev",
|
"promote-black-to-dev": "script/promote-black.ts dev",
|
||||||
"promote-black-to-prod": "script/promote-black.ts production",
|
"promote-black-to-prod": "script/promote-black.ts production",
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { subscribe } from "diagnostics_channel"
|
||||||
|
import { Billing } from "../src/billing.js"
|
||||||
|
import { and, Database, eq } from "../src/drizzle/index.js"
|
||||||
|
import { BillingTable, PaymentTable, SubscriptionTable } from "../src/schema/billing.sql.js"
|
||||||
|
|
||||||
|
const workspaceID = process.argv[2]
|
||||||
|
|
||||||
|
if (!workspaceID) {
|
||||||
|
console.error("Usage: bun script/foo.ts <workspaceID>")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Removing from Black waitlist`)
|
||||||
|
|
||||||
|
const billing = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
subscriptionPlan: BillingTable.subscriptionPlan,
|
||||||
|
timeSubscriptionBooked: BillingTable.timeSubscriptionBooked,
|
||||||
|
})
|
||||||
|
.from(BillingTable)
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!billing?.timeSubscriptionBooked) {
|
||||||
|
console.error(`Error: Workspace is not on the waitlist`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
subscriptionPlan: null,
|
||||||
|
timeSubscriptionBooked: null,
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID)),
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log(`Done`)
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import { Billing } from "../src/billing.js"
|
||||||
|
import { and, Database, eq, isNull, sql } from "../src/drizzle/index.js"
|
||||||
|
import { UserTable } from "../src/schema/user.sql.js"
|
||||||
|
import { BillingTable, PaymentTable, SubscriptionTable } from "../src/schema/billing.sql.js"
|
||||||
|
import { Identifier } from "../src/identifier.js"
|
||||||
|
import { centsToMicroCents } from "../src/util/price.js"
|
||||||
|
import { AuthTable } from "../src/schema/auth.sql.js"
|
||||||
|
|
||||||
|
const plan = "200"
|
||||||
|
const workspaceID = process.argv[2]
|
||||||
|
const seats = parseInt(process.argv[3])
|
||||||
|
|
||||||
|
console.log(`Gifting ${seats} seats of Black to workspace ${workspaceID}`)
|
||||||
|
|
||||||
|
if (!workspaceID || !seats) throw new Error("Usage: bun foo.ts <workspaceID> <seats>")
|
||||||
|
|
||||||
|
// Get workspace user
|
||||||
|
const users = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
id: UserTable.id,
|
||||||
|
role: UserTable.role,
|
||||||
|
email: AuthTable.subject,
|
||||||
|
})
|
||||||
|
.from(UserTable)
|
||||||
|
.leftJoin(AuthTable, and(eq(AuthTable.accountID, UserTable.accountID), eq(AuthTable.provider, "email")))
|
||||||
|
.where(and(eq(UserTable.workspaceID, workspaceID), isNull(UserTable.timeDeleted))),
|
||||||
|
)
|
||||||
|
if (users.length === 0) throw new Error(`Error: No users found in workspace ${workspaceID}`)
|
||||||
|
if (users.length !== seats)
|
||||||
|
throw new Error(`Error: Workspace ${workspaceID} has ${users.length} users, expected ${seats}`)
|
||||||
|
const adminUser = users.find((user) => user.role === "admin")
|
||||||
|
if (!adminUser) throw new Error(`Error: No admin user found in workspace ${workspaceID}`)
|
||||||
|
if (!adminUser.email) throw new Error(`Error: Admin user ${adminUser.id} has no email`)
|
||||||
|
|
||||||
|
// Get Billing
|
||||||
|
const billing = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
})
|
||||||
|
.from(BillingTable)
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (!billing) throw new Error(`Error: Workspace ${workspaceID} has no billing record`)
|
||||||
|
if (billing.subscriptionID) throw new Error(`Error: Workspace ${workspaceID} already has a subscription`)
|
||||||
|
|
||||||
|
// Look up the Stripe customer by email
|
||||||
|
const customerID =
|
||||||
|
billing.customerID ??
|
||||||
|
(await (() =>
|
||||||
|
Billing.stripe()
|
||||||
|
.customers.create({
|
||||||
|
email: adminUser.email,
|
||||||
|
metadata: {
|
||||||
|
workspaceID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((customer) => customer.id))())
|
||||||
|
console.log(`Customer ID: ${customerID}`)
|
||||||
|
|
||||||
|
const couponID = "JAIr0Pe1"
|
||||||
|
const subscription = await Billing.stripe().subscriptions.create({
|
||||||
|
customer: customerID!,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
price: `price_1SmfyI2StuRr0lbXovxJNeZn`,
|
||||||
|
discounts: [{ coupon: couponID }],
|
||||||
|
quantity: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
console.log(`Subscription ID: ${subscription.id}`)
|
||||||
|
|
||||||
|
await Database.transaction(async (tx) => {
|
||||||
|
// Set customer id, subscription id, and payment method on workspace billing
|
||||||
|
await tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
customerID,
|
||||||
|
subscriptionID: subscription.id,
|
||||||
|
subscription: { status: "subscribed", coupon: couponID, seats, plan },
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||||
|
|
||||||
|
// Create a row in subscription table
|
||||||
|
for (const user of users) {
|
||||||
|
await tx.insert(SubscriptionTable).values({
|
||||||
|
workspaceID,
|
||||||
|
id: Identifier.create("subscription"),
|
||||||
|
userID: user.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// // Create a row in payments table
|
||||||
|
// await tx.insert(PaymentTable).values({
|
||||||
|
// workspaceID,
|
||||||
|
// id: Identifier.create("payment"),
|
||||||
|
// amount: centsToMicroCents(amountInCents),
|
||||||
|
// customerID,
|
||||||
|
// invoiceID,
|
||||||
|
// paymentID,
|
||||||
|
// enrichment: {
|
||||||
|
// type: "subscription",
|
||||||
|
// couponID,
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`done`)
|
||||||
+8
-3
@@ -12,7 +12,7 @@ const email = process.argv[3]
|
|||||||
console.log(`Onboarding workspace ${workspaceID} for email ${email}`)
|
console.log(`Onboarding workspace ${workspaceID} for email ${email}`)
|
||||||
|
|
||||||
if (!workspaceID || !email) {
|
if (!workspaceID || !email) {
|
||||||
console.error("Usage: bun onboard-zen-black.ts <workspaceID> <email>")
|
console.error("Usage: bun foo.ts <workspaceID> <email>")
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ const existingSubscription = await Database.use((tx) =>
|
|||||||
tx
|
tx
|
||||||
.select({ workspaceID: BillingTable.workspaceID })
|
.select({ workspaceID: BillingTable.workspaceID })
|
||||||
.from(BillingTable)
|
.from(BillingTable)
|
||||||
.where(eq(BillingTable.subscriptionID, subscriptionID))
|
.where(sql`JSON_EXTRACT(${BillingTable.subscription}, '$.id') = ${subscriptionID}`)
|
||||||
.then((rows) => rows[0]),
|
.then((rows) => rows[0]),
|
||||||
)
|
)
|
||||||
if (existingSubscription) {
|
if (existingSubscription) {
|
||||||
@@ -128,10 +128,15 @@ await Database.transaction(async (tx) => {
|
|||||||
.set({
|
.set({
|
||||||
customerID,
|
customerID,
|
||||||
subscriptionID,
|
subscriptionID,
|
||||||
subscriptionCouponID: couponID,
|
|
||||||
paymentMethodID,
|
paymentMethodID,
|
||||||
paymentMethodLast4,
|
paymentMethodLast4,
|
||||||
paymentMethodType,
|
paymentMethodType,
|
||||||
|
subscription: {
|
||||||
|
status: "subscribed",
|
||||||
|
coupon: couponID,
|
||||||
|
seats: 1,
|
||||||
|
plan: "200",
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.where(eq(BillingTable.workspaceID, workspaceID))
|
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||||
|
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
import { Billing } from "../src/billing.js"
|
||||||
|
import { and, Database, desc, eq, isNotNull, lt, sql } from "../src/drizzle/index.js"
|
||||||
|
import { BillingTable, PaymentTable, SubscriptionTable } from "../src/schema/billing.sql.js"
|
||||||
|
|
||||||
|
const fromWrkID = process.argv[2]
|
||||||
|
const toWrkID = process.argv[3]
|
||||||
|
|
||||||
|
if (!fromWrkID || !toWrkID) {
|
||||||
|
console.error("Usage: bun foo.ts <fromWrkID> <toWrkID>")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Transferring subscription from ${fromWrkID} to ${toWrkID}`)
|
||||||
|
|
||||||
|
// Look up the FROM workspace billing
|
||||||
|
const fromBilling = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
subscription: BillingTable.subscription,
|
||||||
|
paymentMethodID: BillingTable.paymentMethodID,
|
||||||
|
paymentMethodType: BillingTable.paymentMethodType,
|
||||||
|
paymentMethodLast4: BillingTable.paymentMethodLast4,
|
||||||
|
})
|
||||||
|
.from(BillingTable)
|
||||||
|
.where(eq(BillingTable.workspaceID, fromWrkID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (!fromBilling) throw new Error(`Error: FROM workspace has no billing record`)
|
||||||
|
if (!fromBilling.customerID) throw new Error(`Error: FROM workspace has no Stripe customer ID`)
|
||||||
|
if (!fromBilling.subscriptionID) throw new Error(`Error: FROM workspace has no subscription`)
|
||||||
|
|
||||||
|
const fromSubscription = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({ userID: SubscriptionTable.userID })
|
||||||
|
.from(SubscriptionTable)
|
||||||
|
.where(eq(SubscriptionTable.workspaceID, fromWrkID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (!fromSubscription) throw new Error(`Error: FROM workspace has no subscription`)
|
||||||
|
|
||||||
|
// Look up the previous customer ID in FROM workspace
|
||||||
|
const subscriptionPayment = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: PaymentTable.customerID,
|
||||||
|
timeCreated: PaymentTable.timeCreated,
|
||||||
|
})
|
||||||
|
.from(PaymentTable)
|
||||||
|
.where(and(eq(PaymentTable.workspaceID, fromWrkID), sql`JSON_EXTRACT(enrichment, '$.type') = 'subscription'`))
|
||||||
|
.then((rows) => {
|
||||||
|
if (rows.length > 1) {
|
||||||
|
console.error(`Error: Multiple subscription payments found for workspace ${fromWrkID}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
return rows[0]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const fromPrevPayment = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({ customerID: PaymentTable.customerID })
|
||||||
|
.from(PaymentTable)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(PaymentTable.workspaceID, fromWrkID),
|
||||||
|
isNotNull(PaymentTable.customerID),
|
||||||
|
lt(PaymentTable.timeCreated, subscriptionPayment.timeCreated),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.orderBy(desc(PaymentTable.timeCreated))
|
||||||
|
.limit(1)
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (!fromPrevPayment?.customerID) throw new Error(`Error: FROM workspace has no previous Stripe customer to revert to`)
|
||||||
|
if (fromPrevPayment.customerID === fromBilling.customerID)
|
||||||
|
throw new Error(`Error: FROM workspace has the same Stripe customer ID as the current one`)
|
||||||
|
|
||||||
|
const fromPrevPaymentMethods = await Billing.stripe().customers.listPaymentMethods(fromPrevPayment.customerID, {})
|
||||||
|
if (fromPrevPaymentMethods.data.length === 0)
|
||||||
|
throw new Error(`Error: FROM workspace has no previous Stripe payment methods`)
|
||||||
|
|
||||||
|
// Look up the TO workspace billing
|
||||||
|
const toBilling = await Database.use((tx) =>
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
customerID: BillingTable.customerID,
|
||||||
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
|
})
|
||||||
|
.from(BillingTable)
|
||||||
|
.where(eq(BillingTable.workspaceID, toWrkID))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
if (!toBilling) throw new Error(`Error: TO workspace has no billing record`)
|
||||||
|
if (toBilling.subscriptionID) throw new Error(`Error: TO workspace already has a subscription`)
|
||||||
|
|
||||||
|
console.log(`FROM:`)
|
||||||
|
console.log(` Old Customer ID: ${fromBilling.customerID}`)
|
||||||
|
console.log(` New Customer ID: ${fromPrevPayment.customerID}`)
|
||||||
|
console.log(`TO:`)
|
||||||
|
console.log(` Old Customer ID: ${toBilling.customerID}`)
|
||||||
|
console.log(` New Customer ID: ${fromBilling.customerID}`)
|
||||||
|
|
||||||
|
// Clear workspaceID from Stripe customer metadata
|
||||||
|
await Billing.stripe().customers.update(fromPrevPayment.customerID, {
|
||||||
|
metadata: {
|
||||||
|
workspaceID: fromWrkID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await Billing.stripe().customers.update(fromBilling.customerID, {
|
||||||
|
metadata: {
|
||||||
|
workspaceID: toWrkID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await Database.transaction(async (tx) => {
|
||||||
|
await tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
customerID: fromPrevPayment.customerID,
|
||||||
|
subscriptionID: null,
|
||||||
|
subscription: null,
|
||||||
|
paymentMethodID: fromPrevPaymentMethods.data[0].id,
|
||||||
|
paymentMethodLast4: fromPrevPaymentMethods.data[0].card?.last4 ?? null,
|
||||||
|
paymentMethodType: fromPrevPaymentMethods.data[0].type,
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, fromWrkID))
|
||||||
|
|
||||||
|
await tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
customerID: fromBilling.customerID,
|
||||||
|
subscriptionID: fromBilling.subscriptionID,
|
||||||
|
subscription: fromBilling.subscription,
|
||||||
|
paymentMethodID: fromBilling.paymentMethodID,
|
||||||
|
paymentMethodLast4: fromBilling.paymentMethodLast4,
|
||||||
|
paymentMethodType: fromBilling.paymentMethodType,
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, toWrkID))
|
||||||
|
|
||||||
|
await tx
|
||||||
|
.update(SubscriptionTable)
|
||||||
|
.set({
|
||||||
|
workspaceID: toWrkID,
|
||||||
|
userID: fromSubscription.userID,
|
||||||
|
})
|
||||||
|
.where(eq(SubscriptionTable.workspaceID, fromWrkID))
|
||||||
|
|
||||||
|
await tx
|
||||||
|
.update(PaymentTable)
|
||||||
|
.set({
|
||||||
|
workspaceID: toWrkID,
|
||||||
|
})
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(PaymentTable.workspaceID, fromWrkID),
|
||||||
|
sql`JSON_EXTRACT(enrichment, '$.type') = 'subscription'`,
|
||||||
|
eq(PaymentTable.amount, 20000000000),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`done`)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user