diff --git a/STATS.md b/STATS.md index b6e03b01b0..e09c57e8f4 100644 --- a/STATS.md +++ b/STATS.md @@ -1,203 +1,204 @@ # Download Stats -| Date | GitHub Downloads | npm Downloads | Total | -| ---------- | -------------------- | ------------------- | -------------------- | -| 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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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 | 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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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) | +| Date | GitHub Downloads | npm Downloads | Total | +| ---------- | -------------------- | -------------------- | -------------------- | +| 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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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 | 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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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) | diff --git a/bun.lock b/bun.lock index 5b604702c9..5474eb220a 100644 --- a/bun.lock +++ b/bun.lock @@ -379,7 +379,7 @@ "name": "@opencode-ai/sdk", "version": "1.1.23", "devDependencies": { - "@hey-api/openapi-ts": "0.88.1", + "@hey-api/openapi-ts": "0.90.4", "@tsconfig/node22": "catalog:", "@types/node": "catalog:", "@typescript/native-preview": "catalog:", @@ -922,11 +922,11 @@ "@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/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=="], @@ -2094,7 +2094,7 @@ "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=="], @@ -3494,7 +3494,7 @@ "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=="], @@ -4280,6 +4280,8 @@ "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/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=="], @@ -4302,6 +4304,8 @@ "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=="], "compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], @@ -4318,6 +4322,8 @@ "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=="], @@ -4344,6 +4350,8 @@ "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=="], + "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=="], "globby/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], @@ -4358,6 +4366,8 @@ "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=="], "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=="], @@ -4442,6 +4452,8 @@ "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/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], @@ -4920,6 +4932,8 @@ "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=="], "drizzle-kit/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="], diff --git a/nix/hashes.json b/nix/hashes.json index 688f47f3f6..29a102ae66 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-qjXrRkNAJsarbUBMiEL18lGkr65w74YvCsFVjrSCQHI=", + "x86_64-linux": "sha256-07XxcHLuToM4QfWVyaPLACxjPZ93ZM7gtpX2o08Lp18=", "aarch64-linux": "sha256-E6lyYFApS1cw3jE7ISx5QZxDDJ9V3HU0ICYFdY+aIBw=", - "aarch64-darwin": "sha256-7UajHu40n7JKqurU/+CGlitErsVFA2qDneUytI8+/zQ=", + "aarch64-darwin": "sha256-U2UvE70nM0OI0VhIku8qnX+ptPbA+Q/y1BGXbFMcyt4=", "x86_64-darwin": "sha256-LxBsYdq5AzInQJzF89taXvS2vigew5C5hjaIEH8rTb8=" } } diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 4ba5413dfd..4b083771fb 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -54,17 +54,22 @@ export function SessionHeader() { @@ -1496,7 +1512,7 @@ export default function Layout(props: ParentProps) { "hidden xl:block": true, "relative shrink-0": true, }} - style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : "64px" }} + style={{ width: layout.sidebar.opened() ? `${Math.max(layout.sidebar.width(), 244)}px` : "64px" }} >
@@ -1505,9 +1521,9 @@ export default function Layout(props: ParentProps) { @@ -1516,7 +1532,7 @@ export default function Layout(props: ParentProps) {
{props.children} diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index ca5e73a9be..585156afa7 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -26,6 +26,7 @@ import { useSync } from "@/context/sync" import { useTerminal, type LocalPTY } from "@/context/terminal" import { useLayout } from "@/context/layout" import { Terminal } from "@/components/terminal" +import { TerminalSplit } from "@/components/terminal-split" import { checksum, base64Encode, base64Decode } from "@opencode-ai/util/encode" import { useDialog } from "@opencode-ai/ui/context/dialog" import { DialogSelectFile } from "@/components/dialog-select-file" @@ -170,6 +171,7 @@ export default function Page() { const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) const tabs = createMemo(() => layout.tabs(sessionKey())) const view = createMemo(() => layout.view(sessionKey())) + const activeTerminal = createMemo(() => terminal.active()) if (import.meta.env.DEV) { createEffect( @@ -380,7 +382,7 @@ export default function Page() { createEffect(() => { if (!view().terminal.opened()) return if (!terminal.ready()) return - if (terminal.all().length !== 0) return + if (terminal.tabs().length !== 0) return terminal.new() }) @@ -459,6 +461,30 @@ export default function Page() { keybind: "ctrl+shift+`", onSelect: () => terminal.new(), }, + { + id: "terminal.split.vertical", + title: "Split terminal right", + description: "Split the current terminal vertically", + category: "Terminal", + keybind: "mod+d", + disabled: !terminal.active(), + onSelect: () => { + const active = terminal.active() + if (active) terminal.split(active, "vertical") + }, + }, + { + id: "terminal.split.horizontal", + title: "Split terminal down", + description: "Split the current terminal horizontally", + category: "Terminal", + keybind: "mod+shift+d", + disabled: !terminal.active(), + onSelect: () => { + const active = terminal.active() + if (active) terminal.split(active, "horizontal") + }, + }, { id: "steps.toggle", title: "Toggle steps", @@ -707,7 +733,7 @@ export default function Page() { const handleTerminalDragOver = (event: DragEvent) => { const { draggable, droppable } = event if (draggable && droppable) { - const terminals = terminal.all() + const terminals = terminal.tabs() const fromIndex = terminals.findIndex((t: LocalPTY) => t.id === draggable.id.toString()) const toIndex = terminals.findIndex((t: LocalPTY) => t.id === droppable.id.toString()) if (fromIndex !== -1 && toIndex !== -1 && fromIndex !== toIndex) { @@ -1009,7 +1035,7 @@ export default function Page() { createEffect(() => { if (!terminal.ready()) return - handoff.terminals = terminal.all().map((t) => t.title) + handoff.terminals = terminal.tabs().map((t) => t.title) }) createEffect(() => { @@ -1666,10 +1692,10 @@ export default function Page() { > - + - t.id)}> - {(pty) => } + t.id)}> + {(pty) => }
- + {(pty) => ( - - terminal.clone(pty.id)} /> + + )} @@ -1692,7 +1718,7 @@ export default function Page() { {(draggedId) => { - const pty = createMemo(() => terminal.all().find((t: LocalPTY) => t.id === draggedId())) + const pty = createMemo(() => terminal.tabs().find((t: LocalPTY) => t.id === draggedId())) return ( {(t) => ( diff --git a/packages/console/app/src/component/light-rays.css b/packages/console/app/src/component/light-rays.css deleted file mode 100644 index b688e6d9e3..0000000000 --- a/packages/console/app/src/component/light-rays.css +++ /dev/null @@ -1,186 +0,0 @@ -.light-rays-container { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - overflow: hidden; -} - -.light-rays-container canvas { - display: block; - width: 100%; - height: 100%; -} - -.light-rays-controls { - position: fixed; - top: 16px; - right: 16px; - z-index: 9999; - font-family: var(--font-mono, monospace); - font-size: 12px; - color: #fff; -} - -.light-rays-controls-toggle { - background: rgba(0, 0, 0, 0.8); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 4px; - padding: 8px 12px; - color: #fff; - cursor: pointer; - font-family: inherit; - font-size: inherit; - width: 100%; - text-align: left; -} - -.light-rays-controls-toggle:hover { - background: rgba(0, 0, 0, 0.9); - border-color: rgba(255, 255, 255, 0.3); -} - -.light-rays-controls-panel { - background: rgba(0, 0, 0, 0.85); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 4px; - padding: 12px; - margin-top: 4px; - display: flex; - flex-direction: column; - gap: 10px; - min-width: 240px; - max-height: calc(100vh - 100px); - overflow-y: auto; - backdrop-filter: blur(8px); -} - -.control-group { - display: flex; - flex-direction: column; - gap: 4px; -} - -.control-group label { - color: rgba(255, 255, 255, 0.7); - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.control-group.checkbox { - flex-direction: row; - align-items: center; -} - -.control-group.checkbox label { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; - text-transform: none; -} - -.control-group input[type="range"] { - -webkit-appearance: none; - appearance: none; - width: 100%; - height: 4px; - background: rgba(255, 255, 255, 0.2); - border-radius: 2px; - outline: none; -} - -.control-group input[type="range"]::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 14px; - height: 14px; - background: #fff; - border-radius: 50%; - cursor: pointer; - transition: transform 0.1s; -} - -.control-group input[type="range"]::-webkit-slider-thumb:hover { - transform: scale(1.1); -} - -.control-group input[type="range"]::-moz-range-thumb { - width: 14px; - height: 14px; - background: #fff; - border-radius: 50%; - cursor: pointer; - border: none; -} - -.control-group input[type="color"] { - -webkit-appearance: none; - appearance: none; - width: 100%; - height: 32px; - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 4px; - background: transparent; - cursor: pointer; - padding: 2px; -} - -.control-group input[type="color"]::-webkit-color-swatch-wrapper { - padding: 0; -} - -.control-group input[type="color"]::-webkit-color-swatch { - border: none; - border-radius: 2px; -} - -.control-group select { - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 4px; - padding: 6px 8px; - color: #fff; - font-family: inherit; - font-size: inherit; - cursor: pointer; - outline: none; -} - -.control-group select:hover { - border-color: rgba(255, 255, 255, 0.3); -} - -.control-group select option { - background: #1a1a1a; - color: #fff; -} - -.control-group input[type="checkbox"] { - width: 16px; - height: 16px; - accent-color: #fff; - cursor: pointer; -} - -.reset-button { - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 4px; - padding: 8px 12px; - color: rgba(255, 255, 255, 0.7); - cursor: pointer; - font-family: inherit; - font-size: inherit; - margin-top: 4px; - transition: all 0.15s; -} - -.reset-button:hover { - background: rgba(255, 255, 255, 0.15); - border-color: rgba(255, 255, 255, 0.3); - color: #fff; -} diff --git a/packages/console/app/src/component/light-rays.tsx b/packages/console/app/src/component/light-rays.tsx deleted file mode 100644 index 53daeb2f34..0000000000 --- a/packages/console/app/src/component/light-rays.tsx +++ /dev/null @@ -1,924 +0,0 @@ -import { createSignal, createEffect, onMount, onCleanup, Show, For, Accessor, Setter } from "solid-js" -import "./light-rays.css" - -export type RaysOrigin = - | "top-center" - | "top-left" - | "top-right" - | "right" - | "left" - | "bottom-center" - | "bottom-right" - | "bottom-left" - -export interface LightRaysConfig { - raysOrigin: RaysOrigin - raysColor: string - raysSpeed: number - lightSpread: number - rayLength: number - sourceWidth: number - pulsating: boolean - pulsatingMin: number - pulsatingMax: number - fadeDistance: number - saturation: number - followMouse: boolean - mouseInfluence: number - noiseAmount: number - distortion: number - opacity: number -} - -export const defaultConfig: LightRaysConfig = { - raysOrigin: "top-center", - raysColor: "#ffffff", - raysSpeed: 1.0, - lightSpread: 1.2, - rayLength: 4.5, - sourceWidth: 0.1, - pulsating: true, - pulsatingMin: 0.9, - pulsatingMax: 1.05, - fadeDistance: 1.25, - saturation: 0.35, - followMouse: false, - mouseInfluence: 0.05, - noiseAmount: 0.5, - distortion: 0.0, - opacity: 0.35, -} - -export interface LightRaysAnimationState { - time: number - intensity: number - pulseValue: number -} - -interface LightRaysProps { - config: Accessor - class?: string - onAnimationFrame?: (state: LightRaysAnimationState) => 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 = ( - origin: RaysOrigin, - w: number, - h: number, -): { anchor: [number, number]; dir: [number, number] } => { - const outside = 0.2 - switch (origin) { - case "top-left": - return { anchor: [0, -outside * h], dir: [0, 1] } - case "top-right": - return { anchor: [w, -outside * h], dir: [0, 1] } - case "left": - return { anchor: [-outside * w, 0.5 * h], dir: [1, 0] } - case "right": - return { anchor: [(1 + outside) * w, 0.5 * h], dir: [-1, 0] } - case "bottom-left": - return { anchor: [0, (1 + outside) * h], dir: [0, -1] } - case "bottom-center": - return { anchor: [0.5 * w, (1 + outside) * h], dir: [0, -1] } - case "bottom-right": - return { anchor: [w, (1 + outside) * h], dir: [0, -1] } - default: // "top-center" - return { anchor: [0.5 * w, -outside * h], dir: [0, 1] } - } -} - -interface UniformData { - iTime: number - iResolution: [number, number] - rayPos: [number, number] - rayDir: [number, number] - raysColor: [number, number, number] - raysSpeed: number - lightSpread: number - rayLength: number - sourceWidth: number - pulsating: number - pulsatingMin: number - pulsatingMax: number - fadeDistance: number - saturation: number - mousePos: [number, number] - mouseInfluence: number - noiseAmount: number - distortion: number -} - -const WGSL_SHADER = ` - struct Uniforms { - iTime: f32, - _pad0: f32, - iResolution: vec2, - rayPos: vec2, - rayDir: vec2, - raysColor: vec3, - raysSpeed: f32, - lightSpread: f32, - rayLength: f32, - sourceWidth: f32, - pulsating: f32, - pulsatingMin: f32, - pulsatingMax: f32, - fadeDistance: f32, - saturation: f32, - mousePos: vec2, - mouseInfluence: f32, - noiseAmount: f32, - distortion: f32, - _pad1: f32, - _pad2: f32, - _pad3: f32, - }; - - @group(0) @binding(0) var uniforms: Uniforms; - - struct VertexOutput { - @builtin(position) position: vec4, - @location(0) vUv: vec2, - }; - - @vertex - fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { - var positions = array, 3>( - vec2(-1.0, -1.0), - vec2(3.0, -1.0), - vec2(-1.0, 3.0) - ); - - var output: VertexOutput; - let pos = positions[vertexIndex]; - output.position = vec4(pos, 0.0, 1.0); - output.vUv = pos * 0.5 + 0.5; - return output; - } - - fn noise(st: vec2) -> f32 { - return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453123); - } - - fn rayStrength(raySource: vec2, rayRefDirection: vec2, coord: vec2, - seedA: f32, seedB: f32, speed: f32) -> f32 { - let sourceToCoord = coord - raySource; - let dirNorm = normalize(sourceToCoord); - let cosAngle = dot(dirNorm, rayRefDirection); - - let distortedAngle = cosAngle + uniforms.distortion * sin(uniforms.iTime * 2.0 + length(sourceToCoord) * 0.01) * 0.2; - - let spreadFactor = pow(max(distortedAngle, 0.0), 1.0 / max(uniforms.lightSpread, 0.001)); - - let distance = length(sourceToCoord); - let maxDistance = uniforms.iResolution.x * uniforms.rayLength; - let lengthFalloff = clamp((maxDistance - distance) / maxDistance, 0.0, 1.0); - - let fadeFalloff = clamp((uniforms.iResolution.x * uniforms.fadeDistance - distance) / (uniforms.iResolution.x * uniforms.fadeDistance), 0.5, 1.0); - let pulseCenter = (uniforms.pulsatingMin + uniforms.pulsatingMax) * 0.5; - let pulseAmplitude = (uniforms.pulsatingMax - uniforms.pulsatingMin) * 0.5; - var pulse: f32; - if (uniforms.pulsating > 0.5) { - pulse = pulseCenter + pulseAmplitude * sin(uniforms.iTime * speed * 3.0); - } else { - pulse = 1.0; - } - - let baseStrength = clamp( - (0.45 + 0.15 * sin(distortedAngle * seedA + uniforms.iTime * speed)) + - (0.3 + 0.2 * cos(-distortedAngle * seedB + uniforms.iTime * speed)), - 0.0, 1.0 - ); - - return baseStrength * lengthFalloff * fadeFalloff * spreadFactor * pulse; - } - - @fragment - fn fragmentMain(@builtin(position) fragCoord: vec4, @location(0) vUv: vec2) -> @location(0) vec4 { - let coord = vec2(fragCoord.x, fragCoord.y); - - let normalizedX = (coord.x / uniforms.iResolution.x) - 0.5; - let widthOffset = -normalizedX * uniforms.sourceWidth * uniforms.iResolution.x; - - let perpDir = vec2(-uniforms.rayDir.y, uniforms.rayDir.x); - let adjustedRayPos = uniforms.rayPos + perpDir * widthOffset; - - var finalRayDir = uniforms.rayDir; - if (uniforms.mouseInfluence > 0.0) { - let mouseScreenPos = uniforms.mousePos * uniforms.iResolution; - let mouseDirection = normalize(mouseScreenPos - adjustedRayPos); - finalRayDir = normalize(mix(uniforms.rayDir, mouseDirection, uniforms.mouseInfluence)); - } - - let rays1 = vec4(1.0) * - rayStrength(adjustedRayPos, finalRayDir, coord, 36.2214, 21.11349, - 1.5 * uniforms.raysSpeed); - let rays2 = vec4(1.0) * - rayStrength(adjustedRayPos, finalRayDir, coord, 22.3991, 18.0234, - 1.1 * uniforms.raysSpeed); - - var fragColor = rays1 * 0.5 + rays2 * 0.4; - - if (uniforms.noiseAmount > 0.0) { - let n = noise(coord * 0.01 + uniforms.iTime * 0.1); - fragColor = vec4(fragColor.rgb * (1.0 - uniforms.noiseAmount + uniforms.noiseAmount * n), fragColor.a); - } - - let brightness = 1.0 - (coord.y / uniforms.iResolution.y); - fragColor.x = fragColor.x * (0.1 + brightness * 0.8); - fragColor.y = fragColor.y * (0.3 + brightness * 0.6); - fragColor.z = fragColor.z * (0.5 + brightness * 0.5); - - if (uniforms.saturation != 1.0) { - let gray = dot(fragColor.rgb, vec3(0.299, 0.587, 0.114)); - fragColor = vec4(mix(vec3(gray), fragColor.rgb, uniforms.saturation), fragColor.a); - } - - fragColor = vec4(fragColor.rgb * uniforms.raysColor, fragColor.a); - - return fragColor; - } -` - -const UNIFORM_BUFFER_SIZE = 96 - -function createUniformBuffer(data: UniformData): Float32Array { - const buffer = new Float32Array(24) - buffer[0] = data.iTime - buffer[1] = 0 - buffer[2] = data.iResolution[0] - buffer[3] = data.iResolution[1] - buffer[4] = data.rayPos[0] - buffer[5] = data.rayPos[1] - buffer[6] = data.rayDir[0] - buffer[7] = data.rayDir[1] - buffer[8] = data.raysColor[0] - buffer[9] = data.raysColor[1] - buffer[10] = data.raysColor[2] - buffer[11] = data.raysSpeed - buffer[12] = data.lightSpread - buffer[13] = data.rayLength - 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.mousePos[0] - buffer[21] = data.mousePos[1] - buffer[22] = data.mouseInfluence - buffer[23] = data.noiseAmount - return buffer -} - -const UNIFORM_BUFFER_SIZE_CORRECTED = 112 - -function createUniformBufferCorrected(data: UniformData): Float32Array { - const buffer = new Float32Array(28) - buffer[0] = data.iTime - buffer[1] = 0 - buffer[2] = data.iResolution[0] - buffer[3] = data.iResolution[1] - buffer[4] = data.rayPos[0] - buffer[5] = data.rayPos[1] - buffer[6] = data.rayDir[0] - buffer[7] = data.rayDir[1] - buffer[8] = data.raysColor[0] - buffer[9] = data.raysColor[1] - buffer[10] = data.raysColor[2] - buffer[11] = data.raysSpeed - buffer[12] = data.lightSpread - buffer[13] = data.rayLength - 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.mousePos[0] - buffer[21] = data.mousePos[1] - buffer[22] = data.mouseInfluence - buffer[23] = data.noiseAmount - buffer[24] = data.distortion - buffer[25] = 0 - buffer[26] = 0 - buffer[27] = 0 - return buffer -} - -export default function LightRays(props: LightRaysProps) { - 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 - - const mouseRef = { x: 0.5, y: 0.5 } - const smoothMouseRef = { x: 0.5, y: 0.5 } - - const [isVisible, setIsVisible] = createSignal(false) - - 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() - 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_CORRECTED, - 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.raysOrigin, w, h) - - uniformDataRef = { - iTime: 0, - iResolution: [w, h], - rayPos: anchor, - rayDir: dir, - raysColor: hexToRgb(config.raysColor), - raysSpeed: config.raysSpeed, - lightSpread: config.lightSpread, - rayLength: config.rayLength, - sourceWidth: config.sourceWidth, - pulsating: config.pulsating ? 1.0 : 0.0, - pulsatingMin: config.pulsatingMin, - pulsatingMax: config.pulsatingMax, - fadeDistance: config.fadeDistance, - saturation: config.saturation, - mousePos: [0.5, 0.5], - mouseInfluence: config.mouseInfluence, - noiseAmount: config.noiseAmount, - distortion: config.distortion, - } - - 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 currentConfig = props.config() - const { anchor, dir } = getAnchorAndDir(currentConfig.raysOrigin, w, h) - uniformDataRef.rayPos = anchor - uniformDataRef.rayDir = dir - } - - const loop = (t: number) => { - if (!deviceRef || !contextRef || !pipelineRef || !uniformBufferRef || !bindGroupRef || !uniformDataRef) { - return - } - - const currentConfig = props.config() - const timeSeconds = t * 0.001 - uniformDataRef.iTime = timeSeconds - - if (currentConfig.followMouse && currentConfig.mouseInfluence > 0.0) { - const smoothing = 0.92 - - smoothMouseRef.x = smoothMouseRef.x * smoothing + mouseRef.x * (1 - smoothing) - smoothMouseRef.y = smoothMouseRef.y * smoothing + mouseRef.y * (1 - smoothing) - - uniformDataRef.mousePos = [smoothMouseRef.x, smoothMouseRef.y] - } - - if (props.onAnimationFrame) { - const pulseCenter = (currentConfig.pulsatingMin + currentConfig.pulsatingMax) * 0.5 - const pulseAmplitude = (currentConfig.pulsatingMax - currentConfig.pulsatingMin) * 0.5 - const pulseValue = currentConfig.pulsating - ? pulseCenter + pulseAmplitude * Math.sin(timeSeconds * currentConfig.raysSpeed * 3.0) - : 1.0 - - const baseIntensity1 = 0.45 + 0.15 * Math.sin(timeSeconds * currentConfig.raysSpeed * 1.5) - const baseIntensity2 = 0.3 + 0.2 * Math.cos(timeSeconds * currentConfig.raysSpeed * 1.1) - const intensity = (baseIntensity1 + baseIntensity2) * pulseValue - - props.onAnimationFrame({ - time: timeSeconds, - intensity, - pulseValue, - }) - } - - try { - const uniformData = createUniformBufferCorrected(uniformDataRef) - deviceRef.queue.writeBuffer(uniformBufferRef, 0, uniformData.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.raysColor = hexToRgb(config.raysColor) - uniformDataRef.raysSpeed = config.raysSpeed - uniformDataRef.lightSpread = config.lightSpread - uniformDataRef.rayLength = config.rayLength - uniformDataRef.sourceWidth = config.sourceWidth - uniformDataRef.pulsating = config.pulsating ? 1.0 : 0.0 - uniformDataRef.pulsatingMin = config.pulsatingMin - uniformDataRef.pulsatingMax = config.pulsatingMax - uniformDataRef.fadeDistance = config.fadeDistance - uniformDataRef.saturation = config.saturation - uniformDataRef.mouseInfluence = config.mouseInfluence - uniformDataRef.noiseAmount = config.noiseAmount - uniformDataRef.distortion = config.distortion - - const dpr = Math.min(window.devicePixelRatio, 2) - const { clientWidth: wCSS, clientHeight: hCSS } = containerRef - const { anchor, dir } = getAnchorAndDir(config.raysOrigin, wCSS * dpr, hCSS * dpr) - uniformDataRef.rayPos = anchor - uniformDataRef.rayDir = dir - }) - - createEffect(() => { - const config = props.config() - if (!config.followMouse) { - return - } - - const handleMouseMove = (e: MouseEvent) => { - if (!containerRef) { - return - } - const rect = containerRef.getBoundingClientRect() - const x = (e.clientX - rect.left) / rect.width - const y = (e.clientY - rect.top) / rect.height - mouseRef.x = x - mouseRef.y = y - } - - window.addEventListener("mousemove", handleMouseMove) - - onCleanup(() => { - window.removeEventListener("mousemove", handleMouseMove) - }) - }) - - return ( -
- ) -} - -interface LightRaysControlsProps { - config: Accessor - setConfig: Setter -} - -export function LightRaysControls(props: LightRaysControlsProps) { - const [isOpen, setIsOpen] = createSignal(true) - - const updateConfig = (key: K, value: LightRaysConfig[K]) => { - props.setConfig((prev) => ({ ...prev, [key]: value })) - } - - const origins: RaysOrigin[] = [ - "top-center", - "top-left", - "top-right", - "left", - "right", - "bottom-center", - "bottom-left", - "bottom-right", - ] - - return ( -
- - -
-
- - -
- -
- - updateConfig("raysColor", e.currentTarget.value)} - /> -
- -
- - updateConfig("raysSpeed", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("lightSpread", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("rayLength", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("sourceWidth", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("fadeDistance", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("saturation", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("mouseInfluence", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("noiseAmount", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("distortion", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("opacity", parseFloat(e.currentTarget.value))} - /> -
- -
- -
- - -
- - updateConfig("pulsatingMin", parseFloat(e.currentTarget.value))} - /> -
- -
- - updateConfig("pulsatingMax", parseFloat(e.currentTarget.value))} - /> -
-
- -
- -
- - -
-
-
- ) -} diff --git a/packages/console/app/src/component/spotlight.css b/packages/console/app/src/component/spotlight.css new file mode 100644 index 0000000000..4b311c3d02 --- /dev/null +++ b/packages/console/app/src/component/spotlight.css @@ -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%; +} diff --git a/packages/console/app/src/component/spotlight.tsx b/packages/console/app/src/component/spotlight.tsx new file mode 100644 index 0000000000..7043069905 --- /dev/null +++ b/packages/console/app/src/component/spotlight.tsx @@ -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 + 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, + lightPos: vec2, + lightDir: vec2, + color: vec3, + 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 uniforms: Uniforms; + + struct VertexOutput { + @builtin(position) position: vec4, + @location(0) vUv: vec2, + }; + + @vertex + fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { + var positions = array, 3>( + vec2(-1.0, -1.0), + vec2(3.0, -1.0), + vec2(-1.0, 3.0) + ); + + var output: VertexOutput; + let pos = positions[vertexIndex]; + output.position = vec4(pos, 0.0, 1.0); + output.vUv = pos * 0.5 + 0.5; + return output; + } + + fn hash(p: vec2) -> 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) -> vec2 { + let n = sin(dot(p, vec2(41.0, 289.0))); + return fract(vec2(n * 262144.0, n * 32768.0)); + } + + fn fastNoise(st: vec2) -> f32 { + return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453); + } + + fn lightStrengthCombined(lightSource: vec2, lightRefDirection: vec2, coord: vec2) -> 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(-lightRefDirection.y, lightRefDirection.x)); + let time = uniforms.iTime; + let speed = uniforms.speed; + + let asymNoise = fastNoise(vec2(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(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, particlePos: vec2, 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, lightSource: vec2, lightDir: vec2) -> 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(-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(fi * 127.1, fi * 311.7); + let rnd = hash2(seed); + + let lifeDuration = 2.0 + hash(seed + vec2(19.0, 73.0)) * 3.0; + let lifeOffset = hash(seed + vec2(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( + sin(time * floatSpeed + floatPhase), + cos(time * floatSpeed * 0.85 + floatPhase * 1.3) + ) * uniforms.particleDrift * baseSize * 0.08; + + let wobble = vec2( + 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, @location(0) vUv: vec2) -> @location(0) vec4 { + let coord = vec2(fragCoord.x, fragCoord.y); + + let normalizedX = (coord.x / uniforms.iResolution.x) - 0.5; + let widthOffset = -normalizedX * uniforms.sourceWidth * uniforms.iResolution.x; + + let perpDir = vec2(-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(0.0, 0.0, 0.0, 0.0); + } + let particleBrightness = particles * 1.8; + return vec4(uniforms.color * particleBrightness, particles * 0.9); + } + + var fragColor = vec4(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(fragColor.rgb * grain, fragColor.a); + } + + let brightness = 1.0 - (coord.y / uniforms.iResolution.y); + fragColor = vec4( + 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(0.299, 0.587, 0.114)); + fragColor = vec4(mix(vec3(gray), fragColor.rgb, uniforms.saturation), fragColor.a); + } + + fragColor = vec4(fragColor.rgb * uniforms.color, fragColor.a); + + let particles = renderParticles(coord, adjustedLightPos, uniforms.lightDir); + if (particles > 0.001) { + let particleBrightness = particles * 1.8; + fragColor = vec4(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 ( +
+ ) +} diff --git a/packages/console/app/src/routes/black.tsx b/packages/console/app/src/routes/black.tsx index 36c9d1eaf0..b991a60a77 100644 --- a/packages/console/app/src/routes/black.tsx +++ b/packages/console/app/src/routes/black.tsx @@ -3,7 +3,7 @@ import { Title, Meta, Link } from "@solidjs/meta" import { createMemo, createSignal } from "solid-js" import { github } from "~/lib/github" import { config } from "~/config" -import LightRays, { defaultConfig, type LightRaysConfig, type LightRaysAnimationState } from "~/component/light-rays" +import Spotlight, { defaultConfig, type SpotlightAnimationState } from "~/component/spotlight" import "./black.css" export default function BlackLayout(props: RouteSectionProps) { @@ -17,15 +17,14 @@ export default function BlackLayout(props: RouteSectionProps) { : config.github.starsFormatted.compact, ) - const [lightRaysConfig, setLightRaysConfig] = createSignal(defaultConfig) - const [rayAnimationState, setRayAnimationState] = createSignal({ + const [spotlightAnimationState, setSpotlightAnimationState] = createSignal({ time: 0, intensity: 0.5, pulseValue: 1, }) const svgLightingValues = createMemo(() => { - const state = rayAnimationState() + const state = spotlightAnimationState() const t = state.time const wave1 = Math.sin(t * 1.5) * 0.5 + 0.5 @@ -33,11 +32,11 @@ export default function BlackLayout(props: RouteSectionProps) { 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 = state.intensity * state.pulseValue * 0.35 - const fillOpacity = 0.1 + wave1 * 0.08 * state.pulseValue - const strokeBrightness = 55 + wave2 * 25 * state.pulseValue + 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 = wave3 * 0.15 * state.pulseValue + const shimmerIntensity = Math.max(wave3 * 0.15 * state.pulseValue, 0.08) return { glowIntensity, @@ -56,10 +55,12 @@ export default function BlackLayout(props: RouteSectionProps) { } as Record }) - const handleAnimationFrame = (state: LightRaysAnimationState) => { - setRayAnimationState(state) + const handleAnimationFrame = (state: SpotlightAnimationState) => { + setSpotlightAnimationState(state) } + const spotlightConfig = () => defaultConfig + return (
OpenCode Black | Access all the world's best coding models @@ -84,7 +85,7 @@ export default function BlackLayout(props: RouteSectionProps) { /> - +
diff --git a/packages/console/app/src/routes/zen/util/provider/anthropic.ts b/packages/console/app/src/routes/zen/util/provider/anthropic.ts index 87344c0deb..2546ad3ef1 100644 --- a/packages/console/app/src/routes/zen/util/provider/anthropic.ts +++ b/packages/console/app/src/routes/zen/util/provider/anthropic.ts @@ -64,23 +64,21 @@ export const anthropicHelper: ProviderHelper = ({ reqModel, providerModel }) => newBuffer.set(value, buffer.length) buffer = newBuffer - if (buffer.length < 4) return - // The first 4 bytes are the total length (big-endian). - const totalLength = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getUint32(0, false) + const messages = [] - // If we don't have the full message yet, wait for more chunks. - if (buffer.length < totalLength) return + while (buffer.length >= 4) { + // first 4 bytes are the total length (big-endian) + const totalLength = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getUint32(0, false) - try { - // Decode exactly the sub-slice for this event. - const subView = buffer.subarray(0, totalLength) - const decoded = codec.decode(subView) + // wait for more chunks + if (buffer.length < totalLength) break - // Slice the used bytes out of the buffer, removing this message. - buffer = buffer.slice(totalLength) + try { + const subView = buffer.subarray(0, totalLength) + const decoded = codec.decode(subView) + buffer = buffer.slice(totalLength) - // Process message - /* Example of Bedrock data + /* Example of Bedrock data ``` { bytes: 'eyJ0eXBlIjoibWVzc2FnZV9zdGFydCIsIm1lc3NhZ2UiOnsibW9kZWwiOiJjbGF1ZGUtb3B1cy00LTUtMjAyNTExMDEiLCJpZCI6Im1zZ19iZHJrXzAxMjVGdHRGb2lkNGlwWmZ4SzZMbktxeCIsInR5cGUiOiJtZXNzYWdlIiwicm9sZSI6ImFzc2lzdGFudCIsImNvbnRlbnQiOltdLCJzdG9wX3JlYXNvbiI6bnVsbCwic3RvcF9zZXF1ZW5jZSI6bnVsbCwidXNhZ2UiOnsiaW5wdXRfdG9rZW5zIjo0LCJjYWNoZV9jcmVhdGlvbl9pbnB1dF90b2tlbnMiOjEsImNhY2hlX3JlYWRfaW5wdXRfdG9rZW5zIjoxMTk2MywiY2FjaGVfY3JlYXRpb24iOnsiZXBoZW1lcmFsXzVtX2lucHV0X3Rva2VucyI6MSwiZXBoZW1lcmFsXzFoX2lucHV0X3Rva2VucyI6MH0sIm91dHB1dF90b2tlbnMiOjF9fX0=', @@ -112,22 +110,28 @@ export const anthropicHelper: ProviderHelper = ({ reqModel, providerModel }) => ``` */ - /* Example of Anthropic data + /* Example of Anthropic data ``` event: message_delta 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"}}} ``` */ - if (decoded.headers[":message-type"]?.value !== "event") return - const data = decoder.decode(decoded.body, { stream: true }) + if (decoded.headers[":message-type"]?.value === "event") { + const data = decoder.decode(decoded.body, { stream: true }) - const parsedDataResult = JSON.parse(data) - delete parsedDataResult.p - const utf8 = atob(parsedDataResult.bytes) - return encoder.encode(["event: message_start", "\n", "data: " + utf8, "\n\n"].join("")) - } catch (e) { - console.log(e) + const parsedDataResult = JSON.parse(data) + delete parsedDataResult.p + const bytes = atob(parsedDataResult.bytes) + 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", diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index a2cad5bd87..355b3ba001 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -20,7 +20,6 @@ import { Installation } from "@/installation" import { ConfigMarkdown } from "./markdown" import { existsSync } from "fs" import { Bus } from "@/bus" -import { Session } from "@/session" export namespace Config { const log = Log.create({ service: "config" }) @@ -233,10 +232,11 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item).catch((err) => { + const md = await ConfigMarkdown.parse(item).catch(async (err) => { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse command ${item}` + const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load command", { command: item, err }) return undefined @@ -272,10 +272,11 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item).catch((err) => { + const md = await ConfigMarkdown.parse(item).catch(async (err) => { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse agent ${item}` + const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load agent", { agent: item, err }) return undefined @@ -310,10 +311,11 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item).catch((err) => { + const md = await ConfigMarkdown.parse(item).catch(async (err) => { const message = ConfigMarkdown.FrontmatterError.isInstance(err) ? err.data.message : `Failed to parse mode ${item}` + const { Session } = await import("@/session") Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) log.error("failed to load mode", { mode: item, err }) return undefined @@ -942,7 +944,7 @@ export namespace Config { }) .catchall(Agent) .optional() - .describe("Agent configuration, see https://opencode.ai/docs/agent"), + .describe("Agent configuration, see https://opencode.ai/docs/agents"), provider: z .record(z.string(), Provider) .optional() diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index cdb65c79af..4566fc1de2 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -24,15 +24,23 @@ export namespace ProviderTransform { // Strip openai itemId metadata following what codex does if (model.api.npm === "@ai-sdk/openai" || options.store === false) { msgs = msgs.map((msg) => { - if (msg.providerOptions?.openai) { - delete msg.providerOptions.openai["itemId"] + if (msg.providerOptions) { + for (const options of Object.values(msg.providerOptions)) { + if (options && typeof options === "object") { + delete options["itemId"] + } + } } if (!Array.isArray(msg.content)) { return msg } const content = msg.content.map((part) => { - if (part.providerOptions?.openai) { - delete part.providerOptions.openai["itemId"] + if (part.providerOptions) { + for (const options of Object.values(part.providerOptions)) { + if (options && typeof options === "object") { + delete options["itemId"] + } + } } return part }) diff --git a/packages/opencode/src/pty/index.ts b/packages/opencode/src/pty/index.ts index 39ccebf96b..b76160d506 100644 --- a/packages/opencode/src/pty/index.ts +++ b/packages/opencode/src/pty/index.ts @@ -146,6 +146,10 @@ export namespace Pty { ptyProcess.onExit(({ exitCode }) => { log.info("session exited", { id, exitCode }) session.info.status = "exited" + for (const ws of session.subscribers) { + ws.close() + } + session.subscribers.clear() Bus.publish(Event.Exited, { id, exitCode }) state().delete(id) }) diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index c9afb4906e..f6f8fe0a1b 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -1,14 +1,7 @@ import { BusEvent } from "@/bus/bus-event" import z from "zod" import { NamedError } from "@opencode-ai/util/error" -import { - APICallError, - convertToModelMessages, - LoadAPIKeyError, - type ModelMessage, - type UIMessage, - type ToolSet, -} from "ai" +import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai" import { Identifier } from "../id/id" import { LSP } from "../lsp" import { Snapshot } from "@/snapshot" @@ -439,7 +432,7 @@ export namespace MessageV2 { }) export type WithParts = z.infer - export function toModelMessage(input: WithParts[], options?: { tools?: ToolSet }): ModelMessage[] { + export function toModelMessage(input: WithParts[]): ModelMessage[] { const result: UIMessage[] = [] for (const msg of input) { @@ -510,6 +503,24 @@ export namespace MessageV2 { }) if (part.type === "tool") { if (part.state.status === "completed") { + if (part.state.attachments?.length) { + result.push({ + id: Identifier.ascending("message"), + role: "user", + parts: [ + { + type: "text", + text: `Tool ${part.tool} returned an attachment:`, + }, + ...part.state.attachments.map((attachment) => ({ + type: "file" as const, + url: attachment.url, + mediaType: attachment.mime, + filename: attachment.filename, + })), + ], + }) + } assistantMessage.parts.push({ type: ("tool-" + part.tool) as `tool-${string}`, state: "output-available", @@ -558,12 +569,7 @@ export namespace MessageV2 { } } - return convertToModelMessages( - result.filter((msg) => msg.parts.some((part) => part.type !== "step-start")), - { - tools: options?.tools, - }, - ) + return convertToModelMessages(result.filter((msg) => msg.parts.some((part) => part.type !== "step-start"))) } export const stream = fn(Identifier.schema("session"), async function* (sessionID) { diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 2b49561e66..50d8180a04 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -597,7 +597,7 @@ export namespace SessionPrompt { sessionID, system: [...(await SystemPrompt.environment()), ...(await SystemPrompt.custom())], messages: [ - ...MessageV2.toModelMessage(sessionMessages, { tools }), + ...MessageV2.toModelMessage(sessionMessages), ...(isLastStep ? [ { @@ -721,15 +721,8 @@ export namespace SessionPrompt { if (typeof result === "string") return { type: "text", value: result } if (!result.attachments?.length) return { type: "text", value: result.output } return { - type: "content", - value: [ - { type: "text", text: result.output }, - ...result.attachments.map((a) => ({ - type: "media" as const, - data: a.url.slice(a.url.indexOf(",") + 1), - mediaType: a.mime, - })), - ], + type: "text", + value: result.output, } }, }) @@ -821,15 +814,8 @@ export namespace SessionPrompt { if (typeof result === "string") return { type: "text", value: result } if (!result.attachments?.length) return { type: "text", value: result.output } return { - type: "content", - value: [ - { type: "text", text: result.output }, - ...result.attachments.map((a) => ({ - type: "media" as const, - data: a.url.slice(a.url.indexOf(",") + 1), - mediaType: a.mime, - })), - ], + type: "text", + value: result.output, } } tools[key] = item diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 170619b25a..33047b5bcb 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -805,6 +805,82 @@ describe("ProviderTransform.message - strip openai metadata when store=false", ( expect(result[0].content[0].providerOptions?.openai?.itemId).toBeUndefined() }) + test("strips metadata using providerID key when store is false", () => { + const opencodeModel = { + ...openaiModel, + providerID: "opencode", + api: { + id: "opencode-test", + url: "https://api.opencode.ai", + npm: "@ai-sdk/openai-compatible", + }, + } + const msgs = [ + { + role: "assistant", + content: [ + { + type: "text", + text: "Hello", + providerOptions: { + opencode: { + itemId: "msg_123", + otherOption: "value", + }, + }, + }, + ], + }, + ] as any[] + + const result = ProviderTransform.message(msgs, opencodeModel, { store: false }) as any[] + + expect(result[0].content[0].providerOptions?.opencode?.itemId).toBeUndefined() + expect(result[0].content[0].providerOptions?.opencode?.otherOption).toBe("value") + }) + + test("strips itemId across all providerOptions keys", () => { + const opencodeModel = { + ...openaiModel, + providerID: "opencode", + api: { + id: "opencode-test", + url: "https://api.opencode.ai", + npm: "@ai-sdk/openai-compatible", + }, + } + const msgs = [ + { + role: "assistant", + providerOptions: { + openai: { itemId: "msg_root" }, + opencode: { itemId: "msg_opencode" }, + extra: { itemId: "msg_extra" }, + }, + content: [ + { + type: "text", + text: "Hello", + providerOptions: { + openai: { itemId: "msg_openai_part" }, + opencode: { itemId: "msg_opencode_part" }, + extra: { itemId: "msg_extra_part" }, + }, + }, + ], + }, + ] as any[] + + const result = ProviderTransform.message(msgs, opencodeModel, { store: false }) as any[] + + expect(result[0].providerOptions?.openai?.itemId).toBeUndefined() + expect(result[0].providerOptions?.opencode?.itemId).toBeUndefined() + expect(result[0].providerOptions?.extra?.itemId).toBeUndefined() + expect(result[0].content[0].providerOptions?.openai?.itemId).toBeUndefined() + expect(result[0].content[0].providerOptions?.opencode?.itemId).toBeUndefined() + expect(result[0].content[0].providerOptions?.extra?.itemId).toBeUndefined() + }) + test("does not strip metadata for non-openai packages when store is not false", () => { const anthropicModel = { ...openaiModel, diff --git a/packages/opencode/test/session/message-v2.test.ts b/packages/opencode/test/session/message-v2.test.ts index 4b9f3bf19b..f069f6ba68 100644 --- a/packages/opencode/test/session/message-v2.test.ts +++ b/packages/opencode/test/session/message-v2.test.ts @@ -264,6 +264,18 @@ describe("session.message-v2.toModelMessage", () => { role: "user", content: [{ type: "text", text: "run tool" }], }, + { + role: "user", + content: [ + { type: "text", text: "Tool bash returned an attachment:" }, + { + type: "file", + mediaType: "image/png", + filename: "attachment.png", + data: "https://example.com/attachment.png", + }, + ], + }, { role: "assistant", content: [ @@ -285,21 +297,7 @@ describe("session.message-v2.toModelMessage", () => { type: "tool-result", toolCallId: "call-1", toolName: "bash", - output: { - type: "json", - value: { - output: "ok", - attachments: [ - { - ...basePart(assistantID, "file-1"), - type: "file", - mime: "image/png", - filename: "attachment.png", - url: "https://example.com/attachment.png", - }, - ], - }, - }, + output: { type: "text", value: "ok" }, providerOptions: { openai: { tool: "meta" } }, }, ], diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index ec9391a49f..37b324f349 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -20,7 +20,7 @@ "dist" ], "devDependencies": { - "@hey-api/openapi-ts": "0.88.1", + "@hey-api/openapi-ts": "0.90.4", "@tsconfig/node22": "catalog:", "@types/node": "catalog:", "typescript": "catalog:", diff --git a/packages/sdk/js/src/v2/gen/client/client.gen.ts b/packages/sdk/js/src/v2/gen/client/client.gen.ts index 47f1403429..627e98ec42 100644 --- a/packages/sdk/js/src/v2/gen/client/client.gen.ts +++ b/packages/sdk/js/src/v2/gen/client/client.gen.ts @@ -162,10 +162,16 @@ export const createClient = (config: Config = {}): Client => { case "arrayBuffer": case "blob": case "formData": - case "json": case "text": data = await response[parseAs]() break + case "json": { + // Some servers return 200 with no Content-Length and empty body. + // response.json() would throw; read as text and parse if non-empty. + const text = await response.text() + data = text ? JSON.parse(text) : {} + break + } case "stream": return opts.responseStyle === "data" ? response.body @@ -244,6 +250,7 @@ export const createClient = (config: Config = {}): Client => { } return request }, + serializedBody: getValidRequestBody(opts) as BodyInit | null | undefined, url, }) } diff --git a/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts b/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts index 09ef3fb393..056a812593 100644 --- a/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts +++ b/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts @@ -151,6 +151,8 @@ export const createSseClient = ({ const { done, value } = await reader.read() if (done) break buffer += value + // Normalize line endings: CRLF -> LF, then CR -> LF + buffer = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n") const chunks = buffer.split("\n\n") buffer = chunks.pop() ?? "" diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 697dac7eef..09b193c7c4 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -7,7 +7,7 @@ import type { AppAgentsResponses, AppLogErrors, AppLogResponses, - Auth as Auth2, + Auth as Auth3, AuthSetErrors, AuthSetResponses, CommandListResponses, @@ -2023,7 +2023,10 @@ export class Provider extends HeyApiClient { }) } - oauth = new Oauth({ client: this.client }) + private _oauth?: Oauth + get oauth(): Oauth { + return (this._oauth ??= new Oauth({ client: this.client })) + } } export class Find extends HeyApiClient { @@ -2398,43 +2401,6 @@ export class Auth extends HeyApiClient { }, ) } - - /** - * Set auth credentials - * - * Set authentication credentials - */ - public set( - parameters: { - providerID: string - directory?: string - auth?: Auth2 - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "path", key: "providerID" }, - { in: "query", key: "directory" }, - { key: "auth", map: "body" }, - ], - }, - ], - ) - return (options?.client ?? this.client).put({ - url: "/auth/{providerID}", - ...options, - ...params, - headers: { - "Content-Type": "application/json", - ...options?.headers, - ...params.headers, - }, - }) - } } export class Mcp extends HeyApiClient { @@ -2550,7 +2516,10 @@ export class Mcp extends HeyApiClient { }) } - auth = new Auth({ client: this.client }) + private _auth?: Auth + get auth(): Auth { + return (this._auth ??= new Auth({ client: this.client })) + } } export class Resource extends HeyApiClient { @@ -2575,7 +2544,10 @@ export class Resource extends HeyApiClient { } export class Experimental extends HeyApiClient { - resource = new Resource({ client: this.client }) + private _resource?: Resource + get resource(): Resource { + return (this._resource ??= new Resource({ client: this.client })) + } } export class Lsp extends HeyApiClient { @@ -2952,7 +2924,49 @@ export class Tui extends HeyApiClient { }) } - control = new Control({ client: this.client }) + private _control?: Control + get control(): Control { + return (this._control ??= new Control({ client: this.client })) + } +} + +export class Auth2 extends HeyApiClient { + /** + * Set auth credentials + * + * Set authentication credentials + */ + public set( + parameters: { + providerID: string + directory?: string + auth?: Auth3 + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "providerID" }, + { in: "query", key: "directory" }, + { key: "auth", map: "body" }, + ], + }, + ], + ) + return (options?.client ?? this.client).put({ + url: "/auth/{providerID}", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } } export class Event extends HeyApiClient { @@ -2984,53 +2998,128 @@ export class OpencodeClient extends HeyApiClient { OpencodeClient.__registry.set(this, args?.key) } - global = new Global({ client: this.client }) + private _global?: Global + get global(): Global { + return (this._global ??= new Global({ client: this.client })) + } - project = new Project({ client: this.client }) + private _project?: Project + get project(): Project { + return (this._project ??= new Project({ client: this.client })) + } - pty = new Pty({ client: this.client }) + private _pty?: Pty + get pty(): Pty { + return (this._pty ??= new Pty({ client: this.client })) + } - config = new Config({ client: this.client }) + private _config?: Config + get config(): Config { + return (this._config ??= new Config({ client: this.client })) + } - tool = new Tool({ client: this.client }) + private _tool?: Tool + get tool(): Tool { + return (this._tool ??= new Tool({ client: this.client })) + } - instance = new Instance({ client: this.client }) + private _instance?: Instance + get instance(): Instance { + return (this._instance ??= new Instance({ client: this.client })) + } - path = new Path({ client: this.client }) + private _path?: Path + get path(): Path { + return (this._path ??= new Path({ client: this.client })) + } - worktree = new Worktree({ client: this.client }) + private _worktree?: Worktree + get worktree(): Worktree { + return (this._worktree ??= new Worktree({ client: this.client })) + } - vcs = new Vcs({ client: this.client }) + private _vcs?: Vcs + get vcs(): Vcs { + return (this._vcs ??= new Vcs({ client: this.client })) + } - session = new Session({ client: this.client }) + private _session?: Session + get session(): Session { + return (this._session ??= new Session({ client: this.client })) + } - part = new Part({ client: this.client }) + private _part?: Part + get part(): Part { + return (this._part ??= new Part({ client: this.client })) + } - permission = new Permission({ client: this.client }) + private _permission?: Permission + get permission(): Permission { + return (this._permission ??= new Permission({ client: this.client })) + } - question = new Question({ client: this.client }) + private _question?: Question + get question(): Question { + return (this._question ??= new Question({ client: this.client })) + } - command = new Command({ client: this.client }) + private _command?: Command + get command(): Command { + return (this._command ??= new Command({ client: this.client })) + } - provider = new Provider({ client: this.client }) + private _provider?: Provider + get provider(): Provider { + return (this._provider ??= new Provider({ client: this.client })) + } - find = new Find({ client: this.client }) + private _find?: Find + get find(): Find { + return (this._find ??= new Find({ client: this.client })) + } - file = new File({ client: this.client }) + private _file?: File + get file(): File { + return (this._file ??= new File({ client: this.client })) + } - app = new App({ client: this.client }) + private _app?: App + get app(): App { + return (this._app ??= new App({ client: this.client })) + } - mcp = new Mcp({ client: this.client }) + private _mcp?: Mcp + get mcp(): Mcp { + return (this._mcp ??= new Mcp({ client: this.client })) + } - experimental = new Experimental({ client: this.client }) + private _experimental?: Experimental + get experimental(): Experimental { + return (this._experimental ??= new Experimental({ client: this.client })) + } - lsp = new Lsp({ client: this.client }) + private _lsp?: Lsp + get lsp(): Lsp { + return (this._lsp ??= new Lsp({ client: this.client })) + } - formatter = new Formatter({ client: this.client }) + private _formatter?: Formatter + get formatter(): Formatter { + return (this._formatter ??= new Formatter({ client: this.client })) + } - tui = new Tui({ client: this.client }) + private _tui?: Tui + get tui(): Tui { + return (this._tui ??= new Tui({ client: this.client })) + } - auth = new Auth({ client: this.client }) + private _auth?: Auth2 + get auth(): Auth2 { + return (this._auth ??= new Auth2({ client: this.client })) + } - event = new Event({ client: this.client }) + private _event?: Event + get event(): Event { + return (this._event ??= new Event({ client: this.client })) + } } diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 95c09eb6f3..65db19131f 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1666,7 +1666,7 @@ export type Config = { [key: string]: AgentConfig | undefined } /** - * Agent configuration, see https://opencode.ai/docs/agent + * Agent configuration, see https://opencode.ai/docs/agents */ agent?: { plan?: AgentConfig diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 3775cc0fb6..010e87118e 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -9316,7 +9316,7 @@ } }, "agent": { - "description": "Agent configuration, see https://opencode.ai/docs/agent", + "description": "Agent configuration, see https://opencode.ai/docs/agents", "type": "object", "properties": { "plan": { diff --git a/packages/ui/src/components/avatar.tsx b/packages/ui/src/components/avatar.tsx index ab7b0d0e2a..9d124b56a7 100644 --- a/packages/ui/src/components/avatar.tsx +++ b/packages/ui/src/components/avatar.tsx @@ -37,7 +37,7 @@ export function Avatar(props: AvatarProps) { }} > - {(src) => } + {(src) => }
) diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index a3e24c990f..2d680b28bb 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -63,6 +63,7 @@ const icons = { edit: ``, help: ``, "settings-gear": ``, + dash: ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 5724bc9f7f..6ff19635d4 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -58,6 +58,7 @@ 0 16px 48px -6px light-dark(hsl(0 0% 0% / 0.05), hsl(0 0% 0% / 0.15)), 0 6px 12px -2px light-dark(hsl(0 0% 0% / 0.025), hsl(0 0% 0% / 0.1)), 0 1px 2.5px light-dark(hsl(0 0% 0% / 0.025), hsl(0 0% 0% / 0.1)); + --shadow-xxs-border: 0 0 0 0.5px var(--border-weak-base, rgba(0, 0, 0, 0.07)); --shadow-xs-border: 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.2)), 0 1px 2px -1px rgba(19, 16, 16, 0.04), 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08);