Compare commits
768 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f54d93a00 | |||
| 284f6293a2 | |||
| a2b99f94bd | |||
| 8a0f9c6e36 | |||
| d38a8ab1fa | |||
| 6a451be50c | |||
| dac2996b3d | |||
| d689d71ee6 | |||
| a4a6321afb | |||
| 8b13badb67 | |||
| 15a6747b2c | |||
| a96ff66f63 | |||
| 123323e32b | |||
| 8ca850f9fe | |||
| d77725cf1d | |||
| b016d115f5 | |||
| 1a57a86497 | |||
| d433e68fb2 | |||
| c26f6b72d9 | |||
| 6cd3cc96a3 | |||
| c138cf0b76 | |||
| c90bc80e6d | |||
| d3c4fd70dc | |||
| 87cab96893 | |||
| 0248d15470 | |||
| d4801c545e | |||
| ce28acaf18 | |||
| 8edb086178 | |||
| 0f74b5a4b1 | |||
| 6de2feff05 | |||
| a9811ee955 | |||
| 98b6c11a82 | |||
| 3df2929d12 | |||
| 30d1f423cb | |||
| e0c5566f35 | |||
| c5a2ae47d7 | |||
| 17654b885b | |||
| acaf0ca0af | |||
| 6a201be1fd | |||
| 8617b9164e | |||
| 991e5f1b3c | |||
| 55c533c947 | |||
| d17543d5d9 | |||
| 73169563c4 | |||
| bf0336404d | |||
| 7c3d8e5c8d | |||
| 3dad55e266 | |||
| d351cb9f7f | |||
| 251be60630 | |||
| 18d7222804 | |||
| ff1c37d361 | |||
| 3abe6db3de | |||
| b9ca9d3b06 | |||
| 9bbc04843d | |||
| ddf61a6438 | |||
| ba35a81e9b | |||
| 13133583ca | |||
| e91b981740 | |||
| 1458ee64ec | |||
| 4e2a5e1663 | |||
| c98b0e9687 | |||
| 4e25990de9 | |||
| 4b51f1a3fc | |||
| 5490b01004 | |||
| 43176e1f1d | |||
| 3d9ac37efe | |||
| d4619362bd | |||
| fffd3c07ca | |||
| 952bd38e70 | |||
| 8589f5cc89 | |||
| ec594d52f8 | |||
| 848cbf9c60 | |||
| e02be7dd9a | |||
| 33d35e47a6 | |||
| 18d62b32c2 | |||
| 58ed51b49c | |||
| 9f9fe529e4 | |||
| aad721890a | |||
| e63a5d10c8 | |||
| 15681262de | |||
| b5d3aa81f9 | |||
| a688061616 | |||
| f788d347ec | |||
| a33ce67dd4 | |||
| 8a22c5627a | |||
| 7b529a30f4 | |||
| 4910b34f26 | |||
| 6a73b43e3a | |||
| b59b26460d | |||
| aa324ae25d | |||
| ff412f6396 | |||
| ac148b2f32 | |||
| 6f5d096e6d | |||
| ff1e6714c6 | |||
| 41d82eaadf | |||
| be6b011204 | |||
| 65e48c05f0 | |||
| cee08ea67d | |||
| 35ef26c013 | |||
| b08a0b8102 | |||
| 220342154b | |||
| ef7182cd76 | |||
| 6059c69995 | |||
| 892d8ba152 | |||
| da11069f75 | |||
| bf72908dc9 | |||
| 81ab5c78e2 | |||
| de503c7d63 | |||
| 487ccb90a1 | |||
| 4bfeaa47bf | |||
| 9a4d8036a6 | |||
| a50bd6375d | |||
| 14508e91a9 | |||
| 2d5064bd5b | |||
| fdc4819e39 | |||
| efddbb616b | |||
| db4d0f26a8 | |||
| 06cffbc31b | |||
| 3cfb0f4c76 | |||
| 436ba067bc | |||
| f8724bf47e | |||
| 1f7e293204 | |||
| 86d2652258 | |||
| f5c0cc2e2d | |||
| 4463d4a417 | |||
| df35a5c9b1 | |||
| dda93b3c13 | |||
| 5de40b6bf5 | |||
| dcbe799d25 | |||
| a5641aef03 | |||
| 668f97a4c8 | |||
| e9d577496b | |||
| 66fba947ac | |||
| 18c7e02609 | |||
| 7e1351507a | |||
| 032acbd27c | |||
| 8b51f0fbd2 | |||
| ead5942ef0 | |||
| cfe369d9fe | |||
| 2750a3c130 | |||
| a7f6d6ecac | |||
| 3680282450 | |||
| 3b3435c999 | |||
| e2ae6278c2 | |||
| 37d6723e3c | |||
| 961bbf6f55 | |||
| fc71d349ed | |||
| 97c8ab0f71 | |||
| 28534ea547 | |||
| 1a75b79a33 | |||
| 75dfb0af35 | |||
| 073fa09648 | |||
| 3015e7b203 | |||
| 7ac0cdc0f9 | |||
| 49234183a3 | |||
| ed7763df92 | |||
| b818a02f76 | |||
| 886f8449fd | |||
| 5de6643604 | |||
| 6283273d81 | |||
| 5506d18bc5 | |||
| c8f8f02a46 | |||
| 4c5d6f1d6c | |||
| f349e36345 | |||
| 1574c4ab13 | |||
| 0dc9bc3ab6 | |||
| 0006d57a4a | |||
| 70f7e73b9a | |||
| b67b049802 | |||
| ff99dbcd22 | |||
| 0198697737 | |||
| a742a71f67 | |||
| 8f6c1531fa | |||
| d9f56f11cb | |||
| 7ff877464f | |||
| 7c367eb1e8 | |||
| 20848aac09 | |||
| 7716574076 | |||
| 89cd0c769b | |||
| a21b3d788f | |||
| 7994ba8b38 | |||
| 81b4cb2f16 | |||
| 14af1df6a3 | |||
| ac3bd7d7db | |||
| 632e8e9634 | |||
| 1e3f09f69b | |||
| f3f18ffca7 | |||
| ceb3aa0627 | |||
| 014555fe5d | |||
| fdb76867a7 | |||
| 57157fde9c | |||
| 4a3540cf2b | |||
| 540bd938f2 | |||
| c6fbebd996 | |||
| 18d3a7e23a | |||
| cbae355896 | |||
| 924e8a764a | |||
| 40e629cb0e | |||
| 99ec261f79 | |||
| 9daea5bbc2 | |||
| 4704c03d48 | |||
| 9dbf52bedb | |||
| cfd04ab7fa | |||
| 33dbf75e3b | |||
| 8ffaeeb171 | |||
| 451828e5d4 | |||
| 176877f2b8 | |||
| 61b0d08b5f | |||
| f50a6ce1ae | |||
| 8549d6ea8b | |||
| 90a90a87a5 | |||
| 760bdea521 | |||
| 0fd783e83f | |||
| 42caa9058b | |||
| 9ab315ac2c | |||
| a08ad07537 | |||
| c6ce763f7c | |||
| 70d1eb17a7 | |||
| 0393205eda | |||
| 5c05a5a63e | |||
| b01d9f849b | |||
| 0ab904275c | |||
| 5f82a86801 | |||
| 13a5d8de86 | |||
| f024b71808 | |||
| cd35a17904 | |||
| be3f68ed06 | |||
| 64b8fd9584 | |||
| b001d1d0f1 | |||
| 64ad75c79e | |||
| 12eb361d69 | |||
| dd51bd0a2f | |||
| 0fb478a7c8 | |||
| 9694ecf55b | |||
| fe80478b8a | |||
| a253c26bee | |||
| a500f983f5 | |||
| 3579b6f8f3 | |||
| 14cb954213 | |||
| 6170eef441 | |||
| ddb9af4d57 | |||
| 6db2c0bf93 | |||
| 46f70e6a5e | |||
| 0b994a1b0f | |||
| 0326244956 | |||
| ea1b2252fc | |||
| 0da25ea7cc | |||
| 3209afa4ff | |||
| 33e2631e5d | |||
| f02a41b675 | |||
| f497d7b212 | |||
| 0f18f5eb81 | |||
| 8da81104aa | |||
| 97cbdc6b0f | |||
| 7f53f4e36b | |||
| c350f262e6 | |||
| 3e727893bf | |||
| 2a2c7437e4 | |||
| 4f0885d3af | |||
| 5656cd2fc6 | |||
| b6e42876ad | |||
| 3671344867 | |||
| 30b149053c | |||
| cdd40df9d3 | |||
| 9d661808ba | |||
| 6a5d849b3c | |||
| 25bbd73a90 | |||
| 3d27a5f8c7 | |||
| 44ef372399 | |||
| fd09dff381 | |||
| 10580a0bec | |||
| f3f87b0a46 | |||
| ac606bcf71 | |||
| bb50df07e7 | |||
| f7ac033e1c | |||
| a1043352c1 | |||
| e15fecf6cf | |||
| 3e99f345ef | |||
| 0a77770ef3 | |||
| 379b8392dd | |||
| 0fdae70461 | |||
| 087423c754 | |||
| ce64c98fb7 | |||
| e11a8a9dbb | |||
| ef4a68a24d | |||
| 0ec65c85d3 | |||
| 74326f226d | |||
| 554e0d7a39 | |||
| 1b99ac92dd | |||
| ff0b77908a | |||
| 83d19638ba | |||
| a7d484b1a8 | |||
| d87beb37b8 | |||
| dcdb383960 | |||
| 478a7e95ea | |||
| f7b3b582cc | |||
| d1719dc277 | |||
| 9d8c812aa3 | |||
| 2e390acce9 | |||
| e04797d891 | |||
| fb6195b081 | |||
| c6491a3ccd | |||
| 014ae2107e | |||
| 1457eb9d19 | |||
| 6f5f8cc7d8 | |||
| 9ec3fee455 | |||
| 2d52168c26 | |||
| aa5dd7920f | |||
| d60956e82a | |||
| 91fd3fb5ca | |||
| 46f3b662a7 | |||
| 54a61dcdae | |||
| b58f5232c7 | |||
| 9a5bda862c | |||
| 7f67adb639 | |||
| 48fd943622 | |||
| 08350d00be | |||
| 84bf4a2686 | |||
| 2823456e96 | |||
| a373629fc2 | |||
| 0aa2f72a3a | |||
| c032f6dde1 | |||
| f448d8f334 | |||
| a5d4a43753 | |||
| 52e01321ec | |||
| 19692e0122 | |||
| cd1da3cb81 | |||
| ffaa8f4ab3 | |||
| f3da9d680d | |||
| 6118bd24f2 | |||
| fddbb2e2ac | |||
| cb7156b091 | |||
| b5914a3f12 | |||
| 348d576ff3 | |||
| 7507654749 | |||
| c25aad0c4a | |||
| 4aa32415cd | |||
| cde23bbf98 | |||
| 555fe0b629 | |||
| be44c28fd3 | |||
| 657091307e | |||
| 458dc6e07d | |||
| cfaecf9fcb | |||
| 902f722d4c | |||
| ea2d8b2773 | |||
| b319754dc3 | |||
| 35f2040717 | |||
| 4121fc926e | |||
| fd9ab987e6 | |||
| 6c3a235b08 | |||
| 07cb861385 | |||
| 217e348fc3 | |||
| ad917afba4 | |||
| 9539c6d317 | |||
| e946e1e01e | |||
| fea1e71a99 | |||
| 9731eb8c5b | |||
| e058ae54ef | |||
| ba6714f384 | |||
| 0a0a761e23 | |||
| 88e8368022 | |||
| 468ccc6288 | |||
| 77e0237148 | |||
| ffdfb07f72 | |||
| 203ed88024 | |||
| 9923bcd16f | |||
| 0179ec051f | |||
| 05b6353355 | |||
| 47c6ed8829 | |||
| ff9ebd64e3 | |||
| cd2b098b81 | |||
| 7000a4cead | |||
| 5aa983b45b | |||
| 800989419b | |||
| 362679954a | |||
| 2732d64beb | |||
| 88684d6d9e | |||
| 9afddf0850 | |||
| a2d7d6e81f | |||
| 49bcb5e19f | |||
| 3744ea6218 | |||
| b2c6d87633 | |||
| e4f538b674 | |||
| ea0e0b4396 | |||
| 1ac784dc7a | |||
| d980c9ca4c | |||
| 6a2a60df0d | |||
| ee9b89b6d5 | |||
| 68cc109933 | |||
| 1015bb0fdc | |||
| 7da94dd109 | |||
| 171808cf2c | |||
| 78d14cdfea | |||
| 19982a2ffb | |||
| 79904560fc | |||
| 28aee1c58d | |||
| d4512f360b | |||
| 48d0ffe868 | |||
| 62af12f3b9 | |||
| efa4bfef2f | |||
| fc3ead4c01 | |||
| ca9c813b40 | |||
| e3b0f47c40 | |||
| bf13e42092 | |||
| 4345fda9bc | |||
| 4059e9faa9 | |||
| 95fcce1bcf | |||
| 86cde9716a | |||
| 6e315a50ed | |||
| 500c0b6534 | |||
| db75dca118 | |||
| d5f37e2cbe | |||
| a120a9895b | |||
| e9c1db020f | |||
| 08cd319a0b | |||
| 896ec69329 | |||
| 32e64bbb6e | |||
| 0974540e5f | |||
| e06963e616 | |||
| a675815529 | |||
| 3d48135b32 | |||
| 8f09e7f097 | |||
| e8b09f1124 | |||
| 57ba353594 | |||
| b695e090cd | |||
| 69ecb7b54f | |||
| f57fe182ed | |||
| 10ee11c886 | |||
| 45842b1f05 | |||
| 9ca5f9ad48 | |||
| bb00036a30 | |||
| 4a2a540500 | |||
| 09217216dc | |||
| 22a64c4931 | |||
| 93f520b941 | |||
| c84714dc3a | |||
| a7aa09a13e | |||
| efa1cdfcb2 | |||
| 70695daae8 | |||
| 62fdba0600 | |||
| 5733e00908 | |||
| e56abaefd7 | |||
| c71e893e8e | |||
| 3992356517 | |||
| dbb84ef129 | |||
| 8781fa2e33 | |||
| 605a1365d3 | |||
| ebb1bbc966 | |||
| d4518f7a6e | |||
| 68c9008019 | |||
| cafde1e910 | |||
| 41f5f3fe6e | |||
| cf12ecfa34 | |||
| 4fa668927f | |||
| 24d2b5b4bd | |||
| 4da2e003e1 | |||
| 205debde9c | |||
| ba6c7b0901 | |||
| 8e5d02fd25 | |||
| 11d3251c67 | |||
| 9ec5787b4d | |||
| 027c669ad7 | |||
| 405c12ba3f | |||
| 64fd53a8e5 | |||
| 069d63e3cf | |||
| 66049e0e25 | |||
| db7ef01dbb | |||
| 1a6b649fd6 | |||
| 548619775e | |||
| 4b79a75f39 | |||
| 28912fff97 | |||
| 98a83ddcc0 | |||
| e9d965ce7a | |||
| b7624813cc | |||
| 734e451bd9 | |||
| 7a96436085 | |||
| 670af8fb1b | |||
| 0abd062e5c | |||
| 1286468d5a | |||
| 0f3da5be4a | |||
| 9170269e06 | |||
| 8bb460701f | |||
| c0a7173ca4 | |||
| 949e15d8c6 | |||
| 0caedeb481 | |||
| 83ca8d95be | |||
| 8483868783 | |||
| 6961388545 | |||
| 8ef1ae5202 | |||
| 26b8f93cea | |||
| a576523976 | |||
| cf21c4085b | |||
| 204f655d6d | |||
| fb14865785 | |||
| 5cd052cc59 | |||
| af49c4117c | |||
| dd736130a0 | |||
| cee8bda676 | |||
| 6420a72ceb | |||
| 380e7c7899 | |||
| 16b4058a8b | |||
| e3fba0257f | |||
| cb8b65f6f3 | |||
| 3944a8ed54 | |||
| 2ceafe6379 | |||
| 9f058bd88a | |||
| 1dd0ff13d8 | |||
| 6061f10230 | |||
| ea59fafb52 | |||
| 2ff71126de | |||
| 6d522e1743 | |||
| b37f3c819d | |||
| 1b41994e5f | |||
| 4fd4b0d713 | |||
| 7b43ed702e | |||
| 56cd77942b | |||
| 37dc723b4b | |||
| d7ea37c917 | |||
| c1d2bf9b9f | |||
| 36f837730a | |||
| b8f0a1dd82 | |||
| 2d1b62a978 | |||
| 0792d0d59d | |||
| 8c6da1a658 | |||
| 26be10f937 | |||
| ef4d9a13ed | |||
| 320d987ede | |||
| 51a4c4d13b | |||
| aaf3ae5f9c | |||
| 858f4f74b3 | |||
| 3feed90039 | |||
| b46330fc7a | |||
| 5ca7ce1f75 | |||
| d1d9b37ec7 | |||
| 823115c4ec | |||
| 8fe3fda27f | |||
| 63fffd7493 | |||
| 243a350e2b | |||
| 8c09e7ba36 | |||
| d89502f91f | |||
| 847602f933 | |||
| 8fcf8fe39c | |||
| d59eec9296 | |||
| b9122ebc34 | |||
| 38e066ee49 | |||
| 232a04a1a2 | |||
| dcb145635c | |||
| c9b73c68a6 | |||
| 08f7925ea9 | |||
| ed79ec7940 | |||
| 869a44dd04 | |||
| 6a9e9ebc4c | |||
| f18a2a11fa | |||
| 9aaaa755c7 | |||
| 818cd9044b | |||
| 006ca12b6d | |||
| b5f9ab7c20 | |||
| 0981f95f16 | |||
| f64ef32eaa | |||
| 7a19659c9e | |||
| b1202fcd55 | |||
| b8f991eddb | |||
| d0013f55a2 | |||
| 7efaff4f6f | |||
| bc7a84e462 | |||
| 61657bd1b1 | |||
| fc63f4f89c | |||
| 2c44c252d7 | |||
| 613fc6e3ef | |||
| 0a9604d10e | |||
| 1cabedc65e | |||
| 4e5a0d9e02 | |||
| bf373d894d | |||
| e6bbad84c1 | |||
| 4de477d46c | |||
| 264cab3c9b | |||
| 7f9ef59bea | |||
| 8ebbef4cbb | |||
| 0c98a84242 | |||
| 2bbdd21b2f | |||
| 794cfe949b | |||
| 6a5010a2d6 | |||
| 61559fee9d | |||
| 2118a20408 | |||
| b0bc20cb83 | |||
| 2cd77b075b | |||
| 03450c6085 | |||
| 5d3c522da2 | |||
| bf1ba6efc0 | |||
| 67ad82d52e | |||
| f437d976f8 | |||
| c94443faaa | |||
| c6d57f6a56 | |||
| 09b06da403 | |||
| df73ce8a60 | |||
| 8b22c2e04f | |||
| b4f4d6cea2 | |||
| cdf7b2acf1 | |||
| 94651731a5 | |||
| 56424d29a3 | |||
| c5083b2aef | |||
| 1b06795ffe | |||
| 6679cd60ce | |||
| 843a3c16ba | |||
| df40204a57 | |||
| 210910eb6e | |||
| ef427758da | |||
| def6bbf683 | |||
| 5f37a4a21c | |||
| dc869987f9 | |||
| b98d2201e5 | |||
| 8dd6440bb8 | |||
| a2d83c48eb | |||
| a0ae8f4a07 | |||
| 719dcbcd4d | |||
| caa207e868 | |||
| 306004ecd2 | |||
| bcac3c5b84 | |||
| ccc90108df | |||
| b9781915a4 | |||
| 1890e65b75 | |||
| 2f4da3ed52 | |||
| 27a6ac0c0b | |||
| b81a66f07e | |||
| bc43fc9585 | |||
| 4f7c013c78 | |||
| 09b15a07c6 | |||
| 4340f024d3 | |||
| 98e9780819 | |||
| a45d63595f | |||
| 5fbd1453b4 | |||
| f827201ee0 | |||
| 485a96962a | |||
| 98a104f731 | |||
| ff987f14d7 | |||
| 4dd08268dc | |||
| c18a13d4fc | |||
| 7a79995727 | |||
| 01eca562c5 | |||
| f491d0c0c4 | |||
| edf2287e13 | |||
| fcaab633c1 | |||
| 33b6579b5d | |||
| 72a8e4bc22 | |||
| c67b02b7b0 | |||
| 486cd00384 | |||
| caea7bb39d | |||
| e8fb7ee683 | |||
| e4ee4211c8 | |||
| 3fa3ebee14 | |||
| 8e531f400e | |||
| 5727c0dc8a | |||
| f599088cdf | |||
| d78da55f54 | |||
| 9052ff71e6 | |||
| 713da887ca | |||
| ad5950697e | |||
| 85d0d06cab | |||
| be6ec558e3 | |||
| dd2f60810a | |||
| 6c0b86d8f4 | |||
| 517ada6dbf | |||
| 0a098e3008 | |||
| c40078ffc9 | |||
| 729e0afdbe | |||
| eab86840d3 | |||
| 43aa2247f1 | |||
| afe17a507e | |||
| e621020474 | |||
| ac2aa7f288 | |||
| 805bd7e930 | |||
| f9bf2122b7 | |||
| 4e3e533e13 | |||
| 611c429912 | |||
| e2898ee5bf | |||
| e1a532500e | |||
| 4cf412b93d | |||
| 54c9c434e0 | |||
| de19f3e5fe | |||
| 8304993481 | |||
| 4c838aeaca | |||
| 4ec1145dce | |||
| 547d925065 | |||
| 874633051b | |||
| 2293f1ba15 | |||
| 483c3f61b5 | |||
| 83263968f2 | |||
| f279c61792 | |||
| 9717c696d8 | |||
| 94a594c777 | |||
| f1ae8a17cf | |||
| add9c43827 | |||
| d3f6a5c296 | |||
| 4aea78b7c0 | |||
| 4313fe670d | |||
| 2e8a1b67ea | |||
| e73044c533 | |||
| 418f09e364 | |||
| 08f207a396 | |||
| 4f57060708 | |||
| 1507724ecd | |||
| 4bc2f64b24 | |||
| 839ba31dfb | |||
| 2341452d91 | |||
| 7ab263b514 | |||
| 45b5b5960f | |||
| 1b8ec1f01c | |||
| c92cb1c096 | |||
| 8f31fbe275 | |||
| aa76419eda | |||
| 21503c0c38 | |||
| d957b19f55 | |||
| 122c4242ce | |||
| e769ed2fd8 | |||
| c722904831 | |||
| c93faa9564 | |||
| ce6495a46d | |||
| 04f2b905af | |||
| b668a2c0d2 | |||
| 5fc8f10602 | |||
| a27f399a9e | |||
| ff5d14fb63 | |||
| 27fb2d2d5b | |||
| 0b9d59bf0e | |||
| 23e059a065 | |||
| 45c7b29a96 | |||
| 3e6c3fda60 | |||
| 1a1363ef77 | |||
| b5c51e231b | |||
| 22711dd7be | |||
| 1ba4d3d5d1 | |||
| 823ab73585 | |||
| 66c444efac | |||
| b688b8921a | |||
| 00550ddab5 | |||
| 42fedec088 | |||
| ff3bc0e8c2 | |||
| dde7dff870 | |||
| 25ffc9cf96 | |||
| eca92cf5b5 | |||
| 372bdf8be7 | |||
| c7c90b0647 | |||
| 14c2da4fa2 | |||
| 64275cfd22 | |||
| f0b099d9f7 | |||
| c99d957bc8 | |||
| 51b14d55ec | |||
| f92d9994df | |||
| 431e1b87bd | |||
| 3ec2fc6030 | |||
| 99f416bf6b | |||
| 89acc4870b | |||
| 314c739c3a | |||
| 30b18fc997 | |||
| 168ac60853 | |||
| dff0c0c3b0 | |||
| ea3e9de7ab | |||
| c93fd900bc | |||
| a761aa9a37 | |||
| f37eb92d93 | |||
| 2c7cc5d325 | |||
| 16659c82d2 | |||
| 4ed3ca4128 | |||
| 9b2899c8fb | |||
| fada7fc9cc | |||
| 2be5bca42b | |||
| ad3a324719 | |||
| 8c9e178206 |
@@ -0,0 +1,20 @@
|
||||
krill-parser.js
|
||||
krill.pegjs
|
||||
.eslintrc.json
|
||||
server.js
|
||||
tidal-sniffer.js
|
||||
*.jsx
|
||||
tunejs.js
|
||||
out/**
|
||||
postcss.config.js
|
||||
postcss.config.cjs
|
||||
tailwind.config.js
|
||||
tailwind.config.cjs
|
||||
vite.config.js
|
||||
/**/dist/**/*
|
||||
!**/*.mjs
|
||||
**/*.tsx
|
||||
**/*.ts
|
||||
**/*.json
|
||||
**/dev-dist
|
||||
**/dist
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": ["eslint:recommended"],
|
||||
"overrides": [],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["import"],
|
||||
"rules": {
|
||||
"no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }],
|
||||
"import/no-extraneous-dependencies": ["error", {"devDependencies": true}]
|
||||
}
|
||||
}
|
||||
@@ -22,15 +22,18 @@ jobs:
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: "npm"
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
- name: Install Dependencies
|
||||
run: npm ci && cd repl && npm ci && cd ../tutorial && npm ci
|
||||
run: pnpm install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
run: pnpm build
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
@@ -39,7 +42,7 @@ jobs:
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
# Upload entire repository
|
||||
path: "./out"
|
||||
path: "./website/dist"
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
|
||||
@@ -10,10 +10,15 @@ jobs:
|
||||
node-version: [18]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
- uses: actions/checkout@v2
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
- run: pnpm install
|
||||
- run: pnpm run format-check
|
||||
- run: pnpm run lint
|
||||
- run: pnpm test
|
||||
|
||||
@@ -37,4 +37,6 @@ talk/public/EmuSP12
|
||||
talk/public/samples
|
||||
server/samples/old
|
||||
repl/stats.html
|
||||
coverage
|
||||
coverage
|
||||
public/icons/apple-splash-*
|
||||
dev-dist
|
||||
@@ -0,0 +1,12 @@
|
||||
*.md
|
||||
*.json
|
||||
*.yml
|
||||
*.
|
||||
**/out
|
||||
**/dist
|
||||
packages/mini/krill-parser.js
|
||||
packages/xen/tunejs.js
|
||||
paper
|
||||
pnpm-lock.yaml
|
||||
pnpm-workspace.yaml
|
||||
**/dev-dist
|
||||
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": false,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"proseWrap": "preserve",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
|
||||
"printWidth": 120,
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": false,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"proseWrap": "preserve",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
],
|
||||
"yaml.schemas": {
|
||||
"https://json.schemastore.org/github-workflow.json": "file:///home/felix/projects/strudel/.github/workflows/deploy.yml"
|
||||
}
|
||||
},
|
||||
"testing.automaticallyOpenPeekView": "never"
|
||||
}
|
||||
@@ -12,8 +12,8 @@ To get in touch with the contributors, either
|
||||
|
||||
## Ask a Question
|
||||
|
||||
If you have any questions about strudel, make sure you've read the
|
||||
[tutorial](https://strudel.tidalcycles.org/tutorial/) to find out if it answers your question.
|
||||
If you have any questions about strudel, make sure you've glanced through the
|
||||
[docs](https://strudel.tidalcycles.org/learn/) to find out if it answers your question.
|
||||
If not, use one of the Communication Channels above!
|
||||
|
||||
Don't be afraid to ask! Your question might be of great value for other people too.
|
||||
@@ -29,12 +29,10 @@ If you made some music with strudel, you can give back some love and share what
|
||||
Your creation could also be part of the random selection in the REPL if you want.
|
||||
Use one of the Communication Channels listed above.
|
||||
|
||||
## Improve the Tutorial
|
||||
## Improve the Docs
|
||||
|
||||
If you find some weak spots in the [tutorial](https://strudel.tidalcycles.org/),
|
||||
you are welcome to improve them by editing [this file](https://github.com/tidalcycles/strudel/blob/main/tutorial/tutorial.mdx).
|
||||
|
||||
This will even work without setting up a development environment, only a github account is required.
|
||||
If you find some weak spots in the [docs](https://strudel.tidalcycles.org/learn/getting-started),
|
||||
you can edit each file directly on github via the "Edit this page" link located in the right sidebar.
|
||||
|
||||
## Propose a Feature
|
||||
|
||||
@@ -60,23 +58,22 @@ To fix a bug that has been reported,
|
||||
## Write Tests
|
||||
|
||||
There are still many tests that have not been written yet! Reading and writing tests is a great opportunity to get familiar with the codebase.
|
||||
You can find the tests in each package in the `test` folder. To run all tests, run `npm test` from the root folder.
|
||||
You can find the tests in each package in the `test` folder. To run all tests, run `pnpm test` from the root folder.
|
||||
|
||||
## Project Setup
|
||||
|
||||
To get the project up and running for development, make sure you have installed:
|
||||
|
||||
- git
|
||||
- node, preferably v16
|
||||
- [git](https://git-scm.com/)
|
||||
- [node](https://nodejs.org/en/) >= 18
|
||||
- [pnpm](https://pnpm.io/) (`npm i pnpm -g`)
|
||||
|
||||
then, do the following:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/tidalcycles/strudel.git && cd strudel
|
||||
npm i # install at root to symlink packages
|
||||
npx lerna bootstrap # install all dependencies in packages
|
||||
cd repl && npm i # install repl dependencies
|
||||
npm run start # start repl
|
||||
pnpm i # install at root to symlink packages
|
||||
pnpm start # start repl
|
||||
```
|
||||
|
||||
Those commands might look slightly different for your OS.
|
||||
@@ -85,6 +82,10 @@ Please report any problems you've had with the setup instructions!
|
||||
## Code Style
|
||||
|
||||
To make sure the code changes only where it should, we are using prettier to unify the code style.
|
||||
|
||||
- You can format all files at once by running `pnpm prettier` from the project root
|
||||
- Run `pnpm format-check` from the project root to check if all files are well formatted
|
||||
|
||||
If you use VSCode, you can
|
||||
|
||||
1. install [the prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
|
||||
@@ -92,12 +93,29 @@ If you use VSCode, you can
|
||||
3. Choose "Configure Default Formatter..."
|
||||
4. Select prettier
|
||||
|
||||
## ESLint
|
||||
|
||||
To prevent unwanted runtime errors, this project uses [eslint](https://eslint.org/).
|
||||
|
||||
- You can check for lint errors by running `pnpm lint`
|
||||
|
||||
There are also eslint extensions / plugins for most editors.
|
||||
|
||||
## Running Tests
|
||||
|
||||
- Run all tests with `pnpm test`
|
||||
- Run all tests with UI using `pnpm test-ui`
|
||||
|
||||
## Running all CI Checks
|
||||
|
||||
When opening a PR, the CI runner will automatically check the code style and eslint, as well as run all tests.
|
||||
You can run the same check with `pnpm check`
|
||||
|
||||
## Package Workflow
|
||||
|
||||
The project is split into multiple [packages](https://github.com/tidalcycles/strudel/tree/main/packages) with independent versioning.
|
||||
When you run `npm i` on the root folder, [npm workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces) will symlink all packages
|
||||
in the `node_modules` folder. This will allow any js file to import `@strudel.cycles/<package-name>` to get the local version,
|
||||
which allows developing multiple packages at the same time
|
||||
When you run `pnpm i` on the root folder, [pnpm workspaces](https://pnpm.io/workspaces) will install all dependencies of all subpackages. This will allow any js file to import `@strudel.cycles/<package-name>` to get the local version,
|
||||
allowing to develop multiple packages at the same time.
|
||||
|
||||
## Package Publishing
|
||||
|
||||
@@ -105,15 +123,25 @@ To publish all packages that have been changed since the last release, run:
|
||||
|
||||
```sh
|
||||
npm login
|
||||
npx lerna publish
|
||||
|
||||
# this will increment all the versions in package.json files of non private packages to selected versions
|
||||
npx lerna version --no-private
|
||||
|
||||
# publish all packages inside /packages using pnpm! don't use lerna to publish!!
|
||||
pnpm --filter "./packages/**" publish --dry-run
|
||||
|
||||
# the last command was only a dry-run, make sure everything looks ok, if yes, run the same command without flag
|
||||
```
|
||||
|
||||
To manually publish a single package, increase the version in the `package.json`, then run `pnpm publish`.
|
||||
Important: Always publish with `pnpm`, as `npm` does not support overriding main files in `publishConfig`, which is done in all the packages.
|
||||
|
||||
### New Packages
|
||||
|
||||
To add a new package, you have to publish it manually the first time, using:
|
||||
|
||||
```sh
|
||||
cd packages/<package-name> && npm publish --access public
|
||||
cd packages/<package-name> && pnpm publish --access public
|
||||
```
|
||||
|
||||
## Have Fun
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This software is slowly stabilising, but please continue to tread carefully.
|
||||
|
||||
- Try it here: <https://strudel.tidalcycles.org/>
|
||||
- Tutorial: <https://strudel.tidalcycles.org/tutorial/>
|
||||
- Docs: <https://strudel.tidalcycles.org/learn/>
|
||||
- Technical Blog Post: <https://loophole-letters.vercel.app/strudel>
|
||||
- 1 Year of Strudel Blog Post: <https://loophole-letters.vercel.app/strudel1year>
|
||||
|
||||
## Running Locally
|
||||
|
||||
After cloning the project, you can run the REPL locally:
|
||||
|
||||
```bash
|
||||
npm run setup
|
||||
npm run repl
|
||||
pnpm run setup
|
||||
pnpm run repl
|
||||
```
|
||||
|
||||
## Using Strudel In Your Project
|
||||
|
||||
@@ -23,5 +23,4 @@ dist-ssr
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
oldtunes.mjs
|
||||
public/samples/EMU World/
|
||||
public/samples
|
||||
@@ -0,0 +1,22 @@
|
||||
# Usage
|
||||
|
||||
```sh
|
||||
cd iclc2023/slides
|
||||
pnpm i
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
- ctrl+click or ctrl+(left|right) to switch slides
|
||||
|
||||
## Samples
|
||||
|
||||
To make sure all the samples are available, clone:
|
||||
|
||||
```sh
|
||||
cd public/samples
|
||||
git clone https://github.com/ritchse/tidal-drum-machines.git
|
||||
git clone https://github.com/tidalcycles/Dirt-Samples.git
|
||||
git clone https://github.com/felixroos/webaudiofontdata.git
|
||||
```
|
||||
|
||||
or use symlinks..
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Slides</title>
|
||||
</head>
|
||||
<body style="background-color: black">
|
||||
<div id="root">
|
||||
<span style="color: white">loading... </span>
|
||||
</div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "slides",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdx-js/rollup": "^2.3.0",
|
||||
"@nanostores/persistent": "^0.7.0",
|
||||
"@nanostores/react": "^0.4.1",
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/react": "workspace:*",
|
||||
"@strudel.cycles/soundfonts": "workspace:*",
|
||||
"@strudel.cycles/tonal": "workspace:*",
|
||||
"@strudel.cycles/csound": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*",
|
||||
"@uiw/codemirror-themes-all": "^4.19.8",
|
||||
"escodegen": "^2.0.0",
|
||||
"nanostores": "^0.7.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"vite-plugin-top-level-await": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@vitejs/plugin-react": "^3.1.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.21",
|
||||
"tailwindcss": "^3.2.7",
|
||||
"vite": "^4.2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 244 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 103 KiB |
@@ -0,0 +1,46 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { atomone } from '@uiw/codemirror-themes-all';
|
||||
import { CodeMirror, flash, useKeydown } from '@strudel.cycles/react';
|
||||
import { initAudioOnFirstClick, webaudioOutput } from '@strudel.cycles/webaudio';
|
||||
import { TimeSpan } from '@strudel.cycles/core';
|
||||
|
||||
const initAudio = initAudioOnFirstClick();
|
||||
|
||||
function EventEditor({ code: initialCode }) {
|
||||
const [code, setCode] = useState(initialCode);
|
||||
const [view, setView] = useState();
|
||||
useKeydown(
|
||||
useCallback(
|
||||
async (e) => {
|
||||
if (view?.hasFocus) {
|
||||
if (e.ctrlKey || e.altKey) {
|
||||
if (e.code === 'Enter') {
|
||||
/* if (getAudioContext().state !== 'running') {
|
||||
alert('please click play to initialize the audio. you can use shortcuts after that!');
|
||||
return;
|
||||
} */
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
let json = JSON.parse(code);
|
||||
if (!Array.isArray(json)) {
|
||||
json = [json];
|
||||
}
|
||||
await initAudio;
|
||||
json.forEach((value) => {
|
||||
const hap = new Hap(new TimeSpan(0, 1), new TimeSpan(0, 1), value);
|
||||
webaudioOutput(hap, 0.01, 1, 1);
|
||||
});
|
||||
} else if (e.key === '.') {
|
||||
stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[code, view],
|
||||
),
|
||||
);
|
||||
return <CodeMirror value={code} onChange={setCode} theme={atomone} onViewChanged={(v) => setView(v)} fontSize={32} />;
|
||||
}
|
||||
|
||||
export default EventEditor;
|
||||
@@ -0,0 +1,39 @@
|
||||
import EventEditor from './EventEditor.jsx';
|
||||
import Stepper from './Stepper.jsx';
|
||||
|
||||
const snippets = [
|
||||
[`{ "s": "cp", "crush": 4 }`, 'Samples'],
|
||||
[`{ "s": "sawtooth", "note": "e3" }`, 'Oscillators'],
|
||||
[
|
||||
`[
|
||||
{ "s": "gm_epiano2", "note": "c4" },
|
||||
{ "s": "gm_epiano2", "note": "eb4" },
|
||||
{ "s": "gm_epiano2", "note": "g4" }
|
||||
]`,
|
||||
'Soundfonts',
|
||||
],
|
||||
[
|
||||
`{
|
||||
"s": "toys",
|
||||
"cutoff": 2000,
|
||||
"delay": 0.5,
|
||||
"crush": 4
|
||||
}`,
|
||||
'Effects',
|
||||
],
|
||||
];
|
||||
|
||||
function EventsDemo() {
|
||||
return (
|
||||
<Stepper
|
||||
steps={snippets.map(([code, label]) => (
|
||||
<div className="py-4 space-y-4">
|
||||
<h3>{label}</h3>
|
||||
<EventEditor code={code} />
|
||||
</div>
|
||||
))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default EventsDemo;
|
||||
@@ -0,0 +1,21 @@
|
||||
//import {Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
//import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
//import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import { atomOneDark } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import js from 'react-syntax-highlighter/dist/esm/languages/hljs/javascript';
|
||||
import hs from 'react-syntax-highlighter/dist/esm/languages/hljs/haskell';
|
||||
SyntaxHighlighter.registerLanguage('javascript', js);
|
||||
SyntaxHighlighter.registerLanguage('haskell', hs);
|
||||
|
||||
function Highlight({ code, language = 'javascript' }) {
|
||||
return (
|
||||
<div className="text-[32px] leading-10 rounded-xl overflow-hidden">
|
||||
<SyntaxHighlighter language={language} style={atomOneDark} customStyle={{ padding: '8px 10px' }}>
|
||||
{code}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Highlight;
|
||||
@@ -0,0 +1,38 @@
|
||||
import { SlideRepl } from './SlideRepl.jsx';
|
||||
import Stepper from './Stepper.jsx';
|
||||
|
||||
const snippets = [
|
||||
[`sound("bd [hh sd]")`, `sound(seq("bd", ["hh", "sd"]))`, 'Nested Sequences'], //
|
||||
[`sound("<bd sd>")`, `sound(cat("bd", "sd"))`, 'Cyclewise Sequences'], //
|
||||
[`sound("bd,hh")`, `sound(stack("bd", "hh"))`, 'Stack'], //
|
||||
[`sound("bd@3 sd@1")`, `sound(timeCat([3, "bd"], [1, "sd"]))`, 'Weighted Sequence'], //
|
||||
[`sound("{lt ht mt, bd sd}")`, `sound(polymeter(["lt","ht","mt"], ["bd", "sd"]))`, 'Polymeter'], //
|
||||
[`sound("{bd hh sd ht}%2")`, `sound(polymeterSteps(2, ["bd", "hh", "sd", "ht"]))`, 'Steps per Cycle'],
|
||||
];
|
||||
|
||||
function MiniComparison() {
|
||||
return (
|
||||
<>
|
||||
<h1>User Code: Mini Notation -> JavaScript</h1>
|
||||
<Stepper
|
||||
steps={snippets.map(([a, b, label]) => (
|
||||
<div className="py-4">
|
||||
<h3 className="pb-4">{label}</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="flex space-x-2">
|
||||
<img src="./img/tidalcycles.svg" className={`h-10 mt-2`} />
|
||||
<SlideRepl tune={a} hideHeader />
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<img src="./img/js.jpg" className={`h-10 mt-2`} />
|
||||
<SlideRepl tune={b} hideHeader />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default MiniComparison;
|
||||
@@ -0,0 +1,50 @@
|
||||
import { SlideRepl } from './SlideRepl.jsx';
|
||||
import Highlight from './Highlight.jsx';
|
||||
import Stepper from './Stepper.jsx';
|
||||
|
||||
const snippets = [
|
||||
[
|
||||
<>
|
||||
{' '}
|
||||
<SlideRepl tune={`s("bd [hh sd]").crush(4).log()`} hideHeader />
|
||||
</>,
|
||||
'Logging Events',
|
||||
],
|
||||
[
|
||||
<>
|
||||
<Highlight
|
||||
code={`let pattern = s("bd [hh sd]").crush(4);
|
||||
let events = pattern.queryArc(0, 1);
|
||||
console.log(events.map(e => e.show()))`}
|
||||
language="javascript"
|
||||
/>
|
||||
<Highlight
|
||||
language="json"
|
||||
code={`[
|
||||
"0/1 -> 1/2 | s:bd crush:4",
|
||||
"1/2 -> 3/4 | s:hh crush:4",
|
||||
"3/4 -> 1/1 | s:sd crush:4"
|
||||
]`}
|
||||
/>
|
||||
</>,
|
||||
'Querying Events Manually',
|
||||
],
|
||||
];
|
||||
|
||||
function QueryDemo() {
|
||||
return (
|
||||
<>
|
||||
<img src="./img/queryflow.png" className="w-[800px]" />
|
||||
<Stepper
|
||||
steps={snippets.map(([snippet, label]) => (
|
||||
<div className="py-4 space-y-4">
|
||||
<h3>{label}</h3>
|
||||
{snippet}
|
||||
</div>
|
||||
))}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default QueryDemo;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { evalScope, controls } from '@strudel.cycles/core';
|
||||
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
|
||||
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
|
||||
import { prebake } from './prebake';
|
||||
import { atomone } from '@uiw/codemirror-themes-all';
|
||||
import blackscreen from '@strudel.cycles/react/src/themes/blackscreen';
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
await evalScope(
|
||||
controls,
|
||||
import('@strudel.cycles/core'),
|
||||
import('@strudel.cycles/tonal'),
|
||||
import('@strudel.cycles/mini'),
|
||||
import('@strudel.cycles/webaudio'),
|
||||
import('@strudel.cycles/soundfonts'),
|
||||
import('@strudel.cycles/csound'),
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
initAudioOnFirstClick();
|
||||
prebake();
|
||||
}
|
||||
|
||||
export function SlideRepl({
|
||||
tune,
|
||||
drawTime,
|
||||
punchcard,
|
||||
canvasHeight = 100,
|
||||
hideHeader = false,
|
||||
fontSize = 32,
|
||||
disabled = false,
|
||||
}) {
|
||||
return (
|
||||
<div className="not-prose rounded-xl overflow-hidden">
|
||||
<_MiniRepl
|
||||
hideHeader={hideHeader}
|
||||
tune={tune}
|
||||
hideOutsideView={true}
|
||||
drawTime={drawTime}
|
||||
punchcard={punchcard}
|
||||
canvasHeight={canvasHeight}
|
||||
fontSize={fontSize}
|
||||
theme={disabled ? blackscreen : atomone}
|
||||
// theme={themes[theme]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import { persistentAtom } from '@nanostores/persistent';
|
||||
import { useStore } from '@nanostores/react'; // or '@nanostores/preact'
|
||||
import useEvent from './useEvent';
|
||||
|
||||
function cx(...classes) {
|
||||
return classes.filter(Boolean).join(' ');
|
||||
}
|
||||
// https://vitejs.dev/guide/features.html#glob-import
|
||||
const slideImports = import.meta.glob('./slides/*.mdx');
|
||||
//const slideImports = import.meta.glob('./slides/*.mdx', { eager: true });
|
||||
console.log('load slides...', slideImports);
|
||||
|
||||
const loadedMDXFiles = await Promise.all(
|
||||
Object.entries(slideImports).map(async ([path, load]) => {
|
||||
//Object.entries(slideImports).map(async ([path, m]) => {
|
||||
const segments = path.split('/');
|
||||
const filename = segments[segments.length - 1].slice(0, -4); // expects .mdx at the end
|
||||
return [filename, (await load()).default];
|
||||
//return [filename, m.default];
|
||||
}),
|
||||
);
|
||||
console.log('load done!');
|
||||
|
||||
const order = [
|
||||
'cover',
|
||||
'whatIsStrudel',
|
||||
'history',
|
||||
'hs2js',
|
||||
|
||||
'usage',
|
||||
'swatch',
|
||||
'repl',
|
||||
//'repl-features',
|
||||
'viz',
|
||||
|
||||
'replflow',
|
||||
|
||||
// User Code
|
||||
'transpilation',
|
||||
'mininotation',
|
||||
// 'mini-comparison', // remove?
|
||||
// Patterns
|
||||
'patterns-events', // remove?
|
||||
// scheduler?
|
||||
// Output
|
||||
'events-audio', // remove?
|
||||
'outputs',
|
||||
// 'csound', // remove?
|
||||
//
|
||||
// pattern alignment?
|
||||
'bad',
|
||||
'good',
|
||||
'future',
|
||||
'end',
|
||||
];
|
||||
// examples swatch
|
||||
// draw examples!!
|
||||
|
||||
const slideEntries = order.map((name) => loadedMDXFiles.find(([file]) => file === name));
|
||||
|
||||
// current slide index is persisted, so it's safe to refresh the browser
|
||||
export const slideIndex = persistentAtom('slideIndex', '0');
|
||||
|
||||
export const prev = () => slideIndex.set((parseInt(slideIndex.get()) - 1 + slideEntries.length) % slideEntries.length);
|
||||
export const next = () => slideIndex.set((parseInt(slideIndex.get()) + 1) % slideEntries.length);
|
||||
|
||||
function Slides() {
|
||||
const activeIndex = parseInt(useStore(slideIndex));
|
||||
|
||||
/*useEvent('click', (e) => {
|
||||
if (!e.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
const leftHalf = e.clientX / document.body.clientWidth < 0.5;
|
||||
if (leftHalf) {
|
||||
prev();
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});*/
|
||||
/*useEvent('keydown', (e) => {
|
||||
if (e.key === 'Home') {
|
||||
prev();
|
||||
} else if (e.key === 'End') {
|
||||
next();
|
||||
}
|
||||
});*/
|
||||
|
||||
return (
|
||||
<div className="w-screen h-screen overflow-hidden relative">
|
||||
{slideEntries.map(([path, Slide], i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={cx(
|
||||
`bg-slate-900 w-full h-full flex justify-center absolute top-0 transition-all ease-in-out duration-500 overflow-auto`,
|
||||
i < activeIndex && '-translate-x-full',
|
||||
i > activeIndex && 'translate-x-full',
|
||||
)}
|
||||
>
|
||||
<div className="prose prose-invert w-[1400px] p-12 max-w-full" style={{ fontSize: '2em' }}>
|
||||
{Math.abs(i - activeIndex) <= 1 && <Slide />}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className="text-white text-2xl text-bold cursor-pointer select-none">
|
||||
<div className="fixed left-0 p-4 h-full items-start flex hover:bg-[#00000050]" onClick={() => prev()}>
|
||||
<
|
||||
</div>
|
||||
<div className="fixed right-0 p-4 h-full items-start flex hover:bg-[#00000050]" onClick={() => next()}>
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-0 w-full">
|
||||
<div className="h-[5px] bg-slate-500 w-full z-100">
|
||||
<div
|
||||
style={{ width: ((activeIndex / (slideEntries.length - 1)) * 100).toFixed(0) + '%' }}
|
||||
className="h-full bg-yellow-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Slides;
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
function Stepper({ steps }) {
|
||||
const [step, setStep] = useState(1);
|
||||
return (
|
||||
<div className="not-prose">
|
||||
{steps.slice(0, step).map((snippet, i) => {
|
||||
const isActive = i === step - 1;
|
||||
return (
|
||||
<div key={i} className={`border-l-4 pl-8 border-gray-500 ${isActive ? 'border-yellow-500' : ''}`}>
|
||||
{snippet}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{step < steps.length && <button onClick={() => setStep((r) => r + 1)}>next</button>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Stepper;
|
||||
@@ -0,0 +1,73 @@
|
||||
import { SlideRepl } from './SlideRepl.jsx';
|
||||
import Highlight from './Highlight.jsx';
|
||||
import Stepper from './Stepper.jsx';
|
||||
|
||||
const snippets = [
|
||||
[`sound "bd ~ [sd cp]"`, [`sound("bd ~ [sd cp]")`], 'Mini Notation'], //
|
||||
[`sound "bd ~ [sd cp]" # speed "1 2"`, [`sound("bd ~ [sd cp]").speed("1 2")`], 'Composing Patterns'],
|
||||
[
|
||||
`fast 2 $ sound "bd ~ [sd cp]"`,
|
||||
[`fast(2, sound("bd ~ [sd cp]"))`, `sound("bd ~ [sd cp]").fast(2)`],
|
||||
'Patterns Transformations',
|
||||
],
|
||||
[
|
||||
`speed "1 2 3" + "4 5" # s "bd"`,
|
||||
['speed("1 2 3".add("4 5")).s("bd")', '"1 2 3".add("4 5").speed().s("bd")'],
|
||||
'Pattern Arithmetic',
|
||||
],
|
||||
[
|
||||
`jux rev
|
||||
$ every 3 (fast 2)
|
||||
$ sound "bd sd"`,
|
||||
[
|
||||
`sound("bd sd")
|
||||
.every(3, fast("2"))
|
||||
.jux(rev)`,
|
||||
],
|
||||
'Higher order transformations (with partial application)',
|
||||
],
|
||||
[
|
||||
`jux rev
|
||||
$ every "<3 5>" (fast "1 2")
|
||||
$ sound "bd sd cp mt"`,
|
||||
[
|
||||
`sound("bd sd cp mt")
|
||||
.every("<3 5>", fast("1 2"))
|
||||
.jux(rev)`,
|
||||
],
|
||||
'Patterns all the way down',
|
||||
],
|
||||
];
|
||||
|
||||
function SyntaxComparison() {
|
||||
return (
|
||||
<>
|
||||
<h1>Haskell -> JavaScript</h1>
|
||||
<Stepper
|
||||
steps={snippets
|
||||
.map(([hs, js, label]) => (
|
||||
<div className="py-4 space-y-4">
|
||||
<h3>{label}</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="flex space-x-4">
|
||||
<img src="./img/haskell.png" className="h-10 mt-2" />
|
||||
<Highlight language="haskell" code={hs} />
|
||||
</div>
|
||||
<div>
|
||||
{js.map((c, j) => (
|
||||
<div className="flex space-x-4" key={j}>
|
||||
<img src="./img/js.jpg" className={`h-10 mt-2`} />
|
||||
<SlideRepl tune={c} hideHeader />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
.concat([<>Fluent Interface: Just chain all the things!</>])}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SyntaxComparison;
|
||||
@@ -0,0 +1,84 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { atomone } from '@uiw/codemirror-themes-all';
|
||||
import { CodeMirror, flash, useKeydown } from '@strudel.cycles/react';
|
||||
import { transpiler } from '@strudel.cycles/transpiler';
|
||||
import Highlight from './Highlight';
|
||||
import Stepper from './Stepper';
|
||||
import escodegen from 'escodegen';
|
||||
|
||||
function getTranspiled(code) {
|
||||
return transpiler(code, {
|
||||
addReturn: false,
|
||||
codegenOptions: {
|
||||
format: escodegen.FORMAT_MINIFY,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function TranspilationEditor({ code: initialCode }) {
|
||||
const [code, setCode] = useState(initialCode);
|
||||
const [transpiled, setTranspiled] = useState(getTranspiled(initialCode));
|
||||
const [view, setView] = useState();
|
||||
useKeydown(
|
||||
useCallback(
|
||||
async (e) => {
|
||||
if (view?.hasFocus) {
|
||||
if (e.ctrlKey || e.altKey) {
|
||||
if (e.code === 'Enter') {
|
||||
/* if (getAudioContext().state !== 'running') {
|
||||
alert('please click play to initialize the audio. you can use shortcuts after that!');
|
||||
return;
|
||||
} */
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
const t = getTranspiled(code);
|
||||
setTranspiled(t);
|
||||
console.log('transpiled', t);
|
||||
} else if (e.key === '.') {
|
||||
stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[code, view],
|
||||
),
|
||||
);
|
||||
return (
|
||||
<div className="space-y-2 py-2">
|
||||
<CodeMirror
|
||||
value={code}
|
||||
onChange={(v) => {
|
||||
setCode(v);
|
||||
}}
|
||||
theme={atomone}
|
||||
onViewChanged={(v) => setView(v)}
|
||||
fontSize={32}
|
||||
/>
|
||||
<span>⬇️ transpiles to</span>
|
||||
<Highlight language="javascript" code={transpiled} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TranspilationDemo() {
|
||||
return (
|
||||
<div className="not-prose space-y-8">
|
||||
<Stepper
|
||||
steps={[
|
||||
<>
|
||||
<img src="./img/transpileflow.png" className="h-[500px]" />
|
||||
<ul className="list-decimal pl-8">
|
||||
<li>Parse JS Code with acorn to get AST</li>
|
||||
<li>Modify AST with estree-walker</li>
|
||||
<li>Convert modified AST back to JS with escodegen</li>
|
||||
</ul>
|
||||
</>,
|
||||
<TranspilationEditor code={`"bd [hh sd]"`} />,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TranspilationDemo;
|
||||
@@ -0,0 +1,21 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.cm-activeLine {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.cm-theme {
|
||||
background-color: var(--background);
|
||||
border: 1px solid var(--lineHighlight);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.cm-gutters {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
img {
|
||||
@apply rounded-xl overflow-hidden;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import Slides from './Slides';
|
||||
import './index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<Slides />
|
||||
</React.StrictMode>,
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
import { registerSynthSounds, samples } from '@strudel.cycles/webaudio';
|
||||
import { registerSoundfonts } from '@strudel.cycles/soundfonts';
|
||||
|
||||
export async function prebake() {
|
||||
// https://archive.org/details/SalamanderGrandPianoV3
|
||||
// License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm
|
||||
await Promise.all([
|
||||
registerSynthSounds(),
|
||||
registerSoundfonts(`./samples/webaudiofontdata/sound/`),
|
||||
/* samples(`./piano.json`, `./piano/`, { prebake: true }),
|
||||
samples(`./vcsl.json`, 'github:sgossner/VCSL/master/', { prebake: true }),
|
||||
*/
|
||||
//samples(`./tidal-drum-machines.json`, 'github:ritchse/tidal-drum-machines/main/machines/', {
|
||||
samples(`./tidal-drum-machines.json`, './samples/tidal-drum-machines/machines/', {
|
||||
prebake: true,
|
||||
tag: 'drum-machines',
|
||||
}) /*
|
||||
samples(`./EmuSP12.json`, `./EmuSP12/`, { prebake: true, tag: 'drum-machines' }), */,
|
||||
// samples('github:tidalcycles/Dirt-Samples/master'),
|
||||
samples('./Dirt-Samples.json', './samples/Dirt-Samples/'),
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# What's bad
|
||||
|
||||
- Dynamic Typing in JS
|
||||
- Fractions and Hot Paths
|
||||
- Verbose function composition
|
||||
- Method-chaining tradeoffs
|
||||
@@ -0,0 +1,7 @@
|
||||
<div className="flex flex-col justify-between h-full overflow-hidden max-h-screen space-y-8">
|
||||
<h2 className="m-0">Strudel: live coding patterns on the Web</h2>
|
||||
<div className="justify-center flex overflow-hidden not-prose">
|
||||
<img src="./img/strudel_icon.png" className="h-full" />
|
||||
</div>
|
||||
<span>Felix Roos, Alex McLean</span>
|
||||
</div>
|
||||
@@ -0,0 +1,65 @@
|
||||
import { SlideRepl } from '../SlideRepl.jsx';
|
||||
|
||||
# CSound Example
|
||||
|
||||
## Loading ORC instruments
|
||||
|
||||
<SlideRepl
|
||||
tune={`// livecode.orc by Steven Yi
|
||||
await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')
|
||||
|
||||
stack(
|
||||
note("<C^7 A7 Dm7 Fm7>/2".voicings('lefthand'))
|
||||
.cutoff(sine.range(500,2000).round().slow(16))
|
||||
.euclidLegato(3,8).csound('FM1'),
|
||||
|
||||
note("<C2 A1 D2 F2>/2").ply(8).csound('Bass')
|
||||
.gain("1 4 1 4"),
|
||||
|
||||
note("0 7 [4 3] 2".fast(2/3)
|
||||
.off(".25 .125", add("<2 4 -3 -1>"))
|
||||
.slow(2).scale('A4 minor'))
|
||||
.legato(.25).csound('SynHarp'),
|
||||
|
||||
s("bd*2,[~ hh]*2,~ cp").bank('RolandTR909')
|
||||
)`}
|
||||
hideHeader
|
||||
/>
|
||||
|
||||
## Synthesis
|
||||
|
||||
<SlideRepl
|
||||
tune={`await loadCsound\`
|
||||
instr CoolSynth
|
||||
iduration = p3
|
||||
ifreq = p4
|
||||
igain = p5
|
||||
ioct = octcps(ifreq)
|
||||
|
||||
kpwm = oscili(.05, 8)
|
||||
asig = vco2(igain, ifreq, 4, .5 + kpwm)
|
||||
asig += vco2(igain, ifreq * 2)
|
||||
|
||||
idepth = 2
|
||||
acut = transegr:a(0, .005, 0, idepth, .06, -4.2, 0.001, .01, -4.2, 0) ; filter envelope
|
||||
asig = zdf_2pole(asig, cpsoct(ioct + acut + 2), 0.5)
|
||||
|
||||
iattack = .01
|
||||
isustain = .5
|
||||
idecay = .1
|
||||
irelease = .1
|
||||
asig *= linsegr:a(0, iattack, 1, idecay, isustain, iduration, isustain, irelease, 0)
|
||||
|
||||
out(asig, asig)
|
||||
|
||||
endin\`
|
||||
|
||||
"<0 2 [4 6](3,4,2) 3\*2>"
|
||||
.off(1/4, add(2))
|
||||
.off(1/2, add(6))
|
||||
.scale('D minor')
|
||||
.note()
|
||||
//.pianoroll()
|
||||
.csound('CoolSynth')`}
|
||||
hideHeader
|
||||
/>
|
||||
@@ -0,0 +1 @@
|
||||
# Strudel Docs
|
||||
@@ -0,0 +1,4 @@
|
||||
# Thanks
|
||||
|
||||
- ICLC organizers
|
||||
- Tidal Community
|
||||
@@ -0,0 +1,7 @@
|
||||
import { SlideRepl } from '../SlideRepl.jsx';
|
||||
import Highlight from '../Highlight.jsx';
|
||||
import EventsDemo from '../EventsDemo.jsx';
|
||||
|
||||
# Webaudio Output: Values -> Sound
|
||||
|
||||
<EventsDemo />
|
||||
@@ -0,0 +1,15 @@
|
||||
import { SlideRepl } from '../SlideRepl.jsx';
|
||||
|
||||
# Pattern Example
|
||||
|
||||
<SlideRepl
|
||||
tune={`"<0 2 [4 6](3,4,1) 3>"
|
||||
.off(1/4, add(2))
|
||||
.off(1/2, add(6))
|
||||
.scale('D minor')
|
||||
.legato(.25)
|
||||
.note().s("sawtooth square")
|
||||
.delay(.8).delaytime(.125)`}
|
||||
hideHeader
|
||||
punchcard
|
||||
/>
|
||||
@@ -0,0 +1,10 @@
|
||||
# Future Outlook
|
||||
|
||||
- (slow) community growth
|
||||
- block based evaluation
|
||||
- live collaboration
|
||||
- more audio backends
|
||||
- interface experiments, e.g.:
|
||||
- alternative syntax, visual notations
|
||||
- time scrubbing
|
||||
- ui controls
|
||||
@@ -0,0 +1,9 @@
|
||||
# What's good
|
||||
|
||||
- Zero-install, multi-platform
|
||||
- works (surprisingly) well on mobile devices
|
||||
- visual feedback good for teaching + learning
|
||||
- loads of potential contributors
|
||||
- instant pattern sharing
|
||||
- two-way flow of features between strudel and tidal
|
||||
- full mini-notation in hydra and gibber
|
||||
@@ -0,0 +1,3 @@
|
||||
# The journey
|
||||
|
||||

|
||||
@@ -0,0 +1,3 @@
|
||||
import SyntaxComparison from '../SyntaxComparison.jsx';
|
||||
|
||||
<SyntaxComparison />
|
||||
@@ -0,0 +1,5 @@
|
||||
import { SlideRepl } from '../SlideRepl.jsx';
|
||||
import Highlight from '../Highlight.jsx';
|
||||
import MiniComparison from '../MiniComparison.jsx';
|
||||
|
||||
<MiniComparison />
|
||||
@@ -0,0 +1,29 @@
|
||||
import Highlight from '../Highlight';
|
||||
import Stepper from '../Stepper';
|
||||
|
||||
# User Code: Mini Notation
|
||||
|
||||
<img src="./img/pegflow.png" />
|
||||
|
||||
<Stepper
|
||||
steps={[
|
||||
<ul className="list-decimal pl-8">
|
||||
<li>Parse Mini Notation to get AST</li>
|
||||
<li>walk AST and construct function calls</li>
|
||||
</ul>,
|
||||
<div className="space-y-2 py-2">
|
||||
<Highlight code={`mini('bd [hh sd]')`} />
|
||||
<span>⬇️ resulting calls</span>
|
||||
<Highlight
|
||||
code={`seq(
|
||||
reify('bd').withLocation([1,1,1], [1,4,4]),
|
||||
seq(
|
||||
reify('hh').withLocation([1,5,5], [1,8,8]),
|
||||
reify('sd').withLocation([1,8,8], [1,10,10]),
|
||||
)
|
||||
)`}
|
||||
/>
|
||||
<span>➡️ location can be used for highlighting</span>
|
||||
</div>,
|
||||
]}
|
||||
/>
|
||||
@@ -0,0 +1,7 @@
|
||||
# What can I pattern?
|
||||
|
||||
- Default: Web Audio API
|
||||
- OSC via osc-js, e.g. SuperDirt
|
||||
- Csound via the Csound WebAssembly build
|
||||
- MIDI via WebMIDI
|
||||
- Serial via WebSerial, e.g. for robot dancing
|
||||
@@ -0,0 +1,5 @@
|
||||
import QueryDemo from '../QueryDemo.jsx';
|
||||
|
||||
# Querying: From Patterns to Events
|
||||
|
||||
<QueryDemo />
|
||||
@@ -0,0 +1,12 @@
|
||||
# REPL: Features
|
||||
|
||||
- Visual Feedback
|
||||
- Example shuffler (Hydra-style)
|
||||
- Tutorial + reference
|
||||
- Shareable links
|
||||
- Sound Browser
|
||||
- Offline Support (+ installable PWA)
|
||||
- Outputs: Web Audio, MIDI, OSC, CSound, Serial, ..
|
||||
- Themes
|
||||
|
||||

|
||||
@@ -0,0 +1,7 @@
|
||||
# REPL - live coding editor
|
||||
|
||||
[strudel.tidalcycles.org](https://strudel.tidalcycles.org/)
|
||||
|
||||
{/* <iframe className="w-full h-[600px]" src="https://strudel.tidalcycles.org/#c2FtcGxlcyh7CiAgYmQ6IFsnYmQvQlQwQUFEQS53YXYnLCdiZC9CVDBBQUQwLndhdicsJ2JkL0JUMEEwREEud2F2JywnYmQvQlQwQTBEMy53YXYnLCdiZC9CVDBBMEQwLndhdicsJ2JkL0JUMEEwQTcud2F2J10sCiAgc2Q6IFsnc2Qvcnl0bS0wMS1jbGFzc2ljLndhdicsJ3NkL3J5dG0tMDAtaGFyZC53YXYnXSwKICBoaDogWydoaDI3LzAwMF9oaDI3Y2xvc2VkaGgud2F2JywnaGgvMDAwX2hoM2Nsb3NlZGhoLndhdiddLAp9LCAnZ2l0aHViOnRpZGFsY3ljbGVzL0RpcnQtU2FtcGxlcy9tYXN0ZXIvJyk7CnN0YWNrKAogIHMoImJkLFt%2BIDxzZCEzIHNkKDMsNCwyKT5dLGhoKjQiKSAvLyBkcnVtcwogIC5zcGVlZChwZXJsaW4ucmFuZ2UoLjcsMSkpIC8vIHJhbmRvbSBzYW1wbGUgc3BlZWQgdmFyaWF0aW9uCiAgLy8uaHVzaCgpCiAgLCI8YTEgYjEqMiBhMSgzLDgpIGUyPiIgLy8gYmFzc2xpbmUKICAub2ZmKDEvOCx4PT54LmFkZCgxMikuZGVncmFkZUJ5KC41KSkgLy8gcmFuZG9tIG9jdGF2ZSBqdW1wcwogIC5hZGQocGVybGluLnJhbmdlKDAsLjUpKSAvLyByYW5kb20gcGl0Y2ggdmFyaWF0aW9uCiAgLnN1cGVyaW1wb3NlKGFkZCguMDUpKSAvLyBhZGQgc2Vjb25kLCBzbGlnaHRseSBkZXR1bmVkIHZvaWNlCiAgLm4oKSAvLyB3cmFwIGluICJuIgogIC5kZWNheSguMTUpLnN1c3RhaW4oMCkgLy8gbWFrZSBlYWNoIG5vdGUgb2YgZXF1YWwgbGVuZ3RoCiAgLnMoJ3Nhd3Rvb3RoJykgLy8gd2F2ZWZvcm0KICAuZ2FpbiguNCkgLy8gdHVybiBkb3duCiAgLmN1dG9mZihzaW5lLnNsb3coNykucmFuZ2UoMzAwLDUwMDApKSAvLyBhdXRvbWF0ZSBjdXRvZmYKICAvLy5odXNoKCkKICAsIjxBbTchMyA8RW03IEU3YjEzIEVtNyBFYm03YjU%2BPiIudm9pY2luZ3MoJ2xlZnRoYW5kJykgLy8gY2hvcmRzCiAgLnN1cGVyaW1wb3NlKHg9PnguYWRkKC4wNCkpIC8vIGFkZCBzZWNvbmQsIHNsaWdodGx5IGRldHVuZWQgdm9pY2UKICAuYWRkKHBlcmxpbi5yYW5nZSgwLC41KSkgLy8gcmFuZG9tIHBpdGNoIHZhcmlhdGlvbgogIC5uKCkgLy8gd3JhcCBpbiAibiIKICAucygnc2F3dG9vdGgnKSAvLyB3YXZlZm9ybQogIC5nYWluKC4xNikgLy8gdHVybiBkb3duCiAgLmN1dG9mZig1MDApIC8vIGZpeGVkIGN1dG9mZgogIC5hdHRhY2soMSkgLy8gc2xvd2x5IGZhZGUgaW4KICAvLy5odXNoKCkKICAsImE0IGM1IDxlNiBhNj4iLnN0cnVjdCgieCg1LDgpIikKICAuc3VwZXJpbXBvc2UoeD0%2BeC5hZGQoLjA0KSkgLy8gYWRkIHNlY29uZCwgc2xpZ2h0bHkgZGV0dW5lZCB2b2ljZQogIC5hZGQocGVybGluLnJhbmdlKDAsLjUpKSAvLyByYW5kb20gcGl0Y2ggdmFyaWF0aW9uCiAgLm4oKSAvLyB3cmFwIGluICJuIgogIC5kZWNheSguMSkuc3VzdGFpbigwKSAvLyBtYWtlIG5vdGVzIHNob3J0CiAgLnMoJ3RyaWFuZ2xlJykgLy8gd2F2ZWZvcm0KICAuZGVncmFkZUJ5KHBlcmxpbi5yYW5nZSgwLC41KSkgLy8gcmFuZG9tbHkgY29udHJvbGxlZCByYW5kb20gcmVtb3ZhbCA6KQogIC5lY2hvV2l0aCg0LC4xMjUsKHgsbik9PnguZ2FpbiguMTUqMS8obisxKSkpIC8vIGVjaG8gbm90ZXMKICAvLy5odXNoKCkKKQogIC5zbG93KDEuNCk%3D" /> */}
|
||||
|
||||

|
||||
@@ -0,0 +1,5 @@
|
||||
# REPL: System Overview
|
||||
|
||||
<center>
|
||||
<img src="./img/strudelflow.png" className="w-[900px]" />
|
||||
</center>
|
||||
@@ -0,0 +1,3 @@
|
||||
# Strudel Docs
|
||||
|
||||

|
||||
@@ -0,0 +1,5 @@
|
||||
# Pattern Swatches
|
||||
|
||||
fork strudel - publish your own patterns with a fixed version
|
||||
|
||||

|
||||
@@ -0,0 +1,5 @@
|
||||
import TranspilationDemo from '../TranspilationDemo';
|
||||
|
||||
# User Code: Transpilation
|
||||
|
||||
<TranspilationDemo />
|
||||
@@ -0,0 +1,14 @@
|
||||
import Highlight from '../Highlight.jsx';
|
||||
|
||||
# Ways to use Strudel
|
||||
|
||||
- "Strudel REPL" at [strudel.tidalcycles.org](https://strudel.tidalcycles.org)
|
||||
- strudel on [flok.cc](https://flok.cc/) for collaboration
|
||||
- `@strudel.cycles/*` npm packages for your own projects
|
||||
- Embeddable [Web Component](https://github.com/tidalcycles/strudel/tree/main/packages/embed#strudelcyclesembed)
|
||||
- Browser Plugin for [Mastodon](https://github.com/mortuosplango/topfenstrudel) + Discourse Plugin:
|
||||
|
||||
<div className="flex">
|
||||
<img src="./img/topfenstrudel.png" />
|
||||
<img src="./img/discourse.png" />
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
import { SlideRepl } from '../SlideRepl.jsx';
|
||||
|
||||
# REPL: Visual Feedback
|
||||
|
||||
Highlighting + Piano Roll
|
||||
|
||||
<SlideRepl
|
||||
hideHeader
|
||||
punchcard
|
||||
canvasHeight={300}
|
||||
fontSize={24}
|
||||
tune={`// Steve Reich - Piano Phase (1967)
|
||||
note("E4 F#4 B4 C#5 D5 F#4 E4 C#5 B4 F#4 D5 C#5")
|
||||
.layer(
|
||||
x=>x.color('#00ff0050').pan(0).fast(1), // left speaker
|
||||
x=>x.color('#ff00ff50').pan(1).fast(1.01) // right speaker
|
||||
)
|
||||
.slow(2).room(.5)
|
||||
`}
|
||||
/>
|
||||
@@ -0,0 +1,28 @@
|
||||
import { SlideRepl } from '../SlideRepl.jsx';
|
||||
|
||||
# What is Strudel
|
||||
|
||||
- TidalCycles faithfully ported to JavaScript
|
||||
- zero-install live coding environment for the browser
|
||||
- free & open source, modular, hackable, sharable, embeddable:
|
||||
|
||||
<SlideRepl
|
||||
hideHeader
|
||||
fontSize={24}
|
||||
tune={`// froos - freakuency - 2023/03/29
|
||||
stack(
|
||||
stack(
|
||||
s("bd, ~ <rim cp>,~ hh*2").off(1/8, x=>x.speed(2).gain(.5)).fast(2),
|
||||
s("oh*6").end(.01).speed(.5).room(.5).fast("1 2")
|
||||
).bank('RolandTR909'),
|
||||
//
|
||||
note("[c a f e](<3 5>,8,<0 1>)".add("0,.1").sub(24)).s('sawtooth').lpf(500),
|
||||
//
|
||||
"[~ <Cm7 Fm7 Bb7 Db7>]*2".voicings('lefthand').note().legato(.2)
|
||||
.echoWith(6, .1, (p,n)=>p.add(note(n*.6)))
|
||||
.lpf(sine.range(300,1000).slow(8)).lpq(6)
|
||||
.add(note(perlin.range(0,.8))).s('gm_epiano2')
|
||||
.mask("<0 1>")
|
||||
).restart("<1@3 1*[4 [8 16]]>")
|
||||
.slow(2)`}
|
||||
/>
|
||||
@@ -0,0 +1,12 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
function useEvent(name, onTrigger, useCapture = false) {
|
||||
useEffect(() => {
|
||||
document.addEventListener(name, onTrigger, useCapture);
|
||||
return () => {
|
||||
document.removeEventListener(name, onTrigger, useCapture);
|
||||
};
|
||||
}, [onTrigger]);
|
||||
}
|
||||
|
||||
export default useEvent;
|
||||
@@ -0,0 +1,26 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./index.html',
|
||||
'./src/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'../../packages/react/src/**/*.{html,js,jsx,md,mdx,ts,tsx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
// codemirror-theme settings
|
||||
background: 'var(--background)',
|
||||
lineBackground: 'var(--lineBackground)',
|
||||
foreground: 'var(--foreground)',
|
||||
caret: 'var(--caret)',
|
||||
selection: 'var(--selection)',
|
||||
selectionMatch: 'var(--selectionMatch)',
|
||||
gutterBackground: 'var(--gutterBackground)',
|
||||
gutterForeground: 'var(--gutterForeground)',
|
||||
gutterBorder: 'var(--gutterBorder)',
|
||||
lineHighlight: 'var(--lineHighlight)',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require('@tailwindcss/typography')],
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import mdx from '@mdx-js/rollup';
|
||||
import gfm from 'remark-gfm';
|
||||
import topLevelAwait from 'vite-plugin-top-level-await';
|
||||
|
||||
const options = {
|
||||
// See https://mdxjs.com/advanced/plugins
|
||||
remarkPlugins: [gfm],
|
||||
rehypePlugins: [],
|
||||
};
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
mdx(options),
|
||||
topLevelAwait({
|
||||
// The export name of top-level await promise for each chunk module
|
||||
promiseExportName: '__tla',
|
||||
// The function to generate import names of top-level await promise in each chunk module
|
||||
promiseImportName: (i) => `__tla_${i}`,
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
target: 'esnext', //browsers can handle the latest ES features
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
// this barrel export is currently only used to find undocumented exports
|
||||
export * from './packages/core/index.mjs';
|
||||
export * from './packages/csound/index.mjs';
|
||||
export * from './packages/embed/index.mjs';
|
||||
export * from './packages/eval/index.mjs';
|
||||
export * from './packages/midi/index.mjs';
|
||||
export * from './packages/mini/index.mjs';
|
||||
export * from './packages/osc/index.mjs';
|
||||
export * from './packages/react/index.mjs';
|
||||
export * from './packages/serial/index.mjs';
|
||||
export * from './packages/soundfonts/index.mjs';
|
||||
export * from './packages/tonal/index.mjs';
|
||||
export * from './packages/tone/index.mjs';
|
||||
export * from './packages/transpiler/index.mjs';
|
||||
export * from './packages/webaudio/index.mjs';
|
||||
export * from './packages/webdirt/index.mjs';
|
||||
export * from './packages/xen/index.mjs';
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"source": {
|
||||
"includePattern": ".+\\.(js(doc|x)?|mjs)$",
|
||||
"excludePattern": "node_modules|shift-parser|shift-reducer|shift-traverser"
|
||||
"excludePattern": "node_modules|shift-parser|shift-reducer|shift-traverser|dist"
|
||||
},
|
||||
"plugins": ["plugins/markdown"],
|
||||
"opts": {
|
||||
|
||||
@@ -2,5 +2,7 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "independent"
|
||||
"version": "independent",
|
||||
"npmClient": "pnpm",
|
||||
"useWorkspaces": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# my-patterns
|
||||
|
||||
This directory can be used to save your own patterns, which then get
|
||||
made into a pattern swatch.
|
||||
|
||||
Example: <https://felixroos.github.io/strudel/swatch/>
|
||||
|
||||
## deploy
|
||||
|
||||
### 1. fork the [strudel repo on github](https://github.com/tidalcycles/strudel.git)
|
||||
|
||||
### 2. clone your fork to your machine `git clone https://github.com/<your-username>/strudel.git strudel && cd strudel`
|
||||
|
||||
### 3. create a separate branch like `git branch patternuary && git checkout patternuary`
|
||||
|
||||
### 4. save one or more .txt files in the my-patterns folder
|
||||
|
||||
### 5. edit `website/public/CNAME` to contain `<your-username>.github.io/strudel`
|
||||
|
||||
### 6. edit `website/astro.config.mjs` to use site: `https://<your-username>.github.io` and base `/strudel`, like this
|
||||
|
||||
```js
|
||||
const site = 'https://<your-username>.github.io';
|
||||
const base = '/strudel';
|
||||
```
|
||||
|
||||
### 7. commit & push the changes
|
||||
|
||||
```sh
|
||||
git add . && git commit -m "site config" && git push --set-upstream origin
|
||||
```
|
||||
|
||||
### 8. deploy to github pages
|
||||
|
||||
- go to settings -> pages and select "Github Actions" as source
|
||||
- go to settings -> environments -> github-pages and press the edit button next to `main` and type in `patternuary` (under "Deployment branches")
|
||||
- go to Actions -> `Build and Deploy` and click `Run workflow` with branch `patternuary`
|
||||
|
||||
### 9. view your patterns at `<your-username>.github.io/strudel/swatch/`
|
||||
|
||||
Alternatively, github pages allows you to use a custom domain, like https://mycooldomain.org/swatch/. [See their documentation for details](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site).
|
||||
|
||||
### 10. optional: automatic deployment
|
||||
|
||||
If you want to automatically deploy your site on push, go to `deploy.yml` and change `workflow_dispatch` to `push`.
|
||||
|
||||
## running locally
|
||||
|
||||
- install dependencies with `npm run setup`
|
||||
- run dev server with `npm run repl` and open `http://localhost:3000/strudel/swatch/`
|
||||
|
||||
## tests fail?
|
||||
|
||||
Your tests might fail if the code does not follow prettiers format.
|
||||
In that case, run `npm run codeformat`. To disable that, remove `npm run format-check` from `test.yml`
|
||||
|
||||
## updating your fork
|
||||
|
||||
To update your fork, you can pull the main branch and merge it into your `patternuary` branch.
|
||||
@@ -1,27 +1,33 @@
|
||||
{
|
||||
"name": "@strudel.cycles/monorepo",
|
||||
"version": "0.0.4",
|
||||
"version": "0.5.0",
|
||||
"private": true,
|
||||
"description": "Port of tidalcycles to javascript",
|
||||
"scripts": {
|
||||
"pretest": "cd tutorial && npm run jsdoc-json",
|
||||
"test": "vitest run --version",
|
||||
"test-ui": "vitest --ui",
|
||||
"test-coverage": "vitest --coverage",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
"setup": "npm i && npm run bootstrap && cd repl && npm i && cd ../tutorial && npm i",
|
||||
"snapshot": "cd repl && npm run snapshot",
|
||||
"repl": "cd repl && npm run dev",
|
||||
"setup": "pnpm i",
|
||||
"pretest": "npm run jsdoc-json",
|
||||
"prebuild": "npm run jsdoc-json",
|
||||
"prestart": "npm run jsdoc-json",
|
||||
"test": "npm run pretest && vitest run --version",
|
||||
"test-ui": "npm run pretest && vitest --ui",
|
||||
"test-coverage": "npm run pretest && vitest --coverage",
|
||||
"snapshot": "npm run pretest && vitest run -u --silent",
|
||||
"repl": "npm run prestart && cd website && npm run dev",
|
||||
"start": "npm run prestart && cd website && npm run dev",
|
||||
"dev": "npm run prestart && cd website && npm run dev",
|
||||
"build": "npm run prebuild && cd website && npm run build",
|
||||
"preview": "cd website && npm run preview",
|
||||
"osc": "cd packages/osc && npm run server",
|
||||
"build": "rm -rf out && cd repl && npm run build && cd ../tutorial && npm run build",
|
||||
"preview": "npx serve ./out",
|
||||
"deploy": "NODE_DEBUG=gh-pages gh-pages -d out",
|
||||
"jsdoc": "jsdoc packages/ -c jsdoc.config.json",
|
||||
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json"
|
||||
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json",
|
||||
"lint": "eslint . --ext mjs,js --quiet",
|
||||
"codeformat": "prettier --write .",
|
||||
"format-check": "prettier --check .",
|
||||
"report-undocumented": "npm run jsdoc-json && node undocumented.mjs > undocumented.json",
|
||||
"check": "npm run format-check && npm run lint && npm run test",
|
||||
"iclc": "cd paper && pandoc --template=pandoc/iclc.html --citeproc --number-sections iclc2023.md -o iclc2023.html && pandoc --template=pandoc/iclc.latex --citeproc --number-sections iclc2023.md -o iclc2023.pdf"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tidalcycles/strudel.git"
|
||||
@@ -39,16 +45,30 @@
|
||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||
},
|
||||
"homepage": "https://strudel.tidalcycles.org",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/mini": "workspace:*",
|
||||
"@strudel.cycles/tonal": "workspace:*",
|
||||
"@strudel.cycles/transpiler": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*",
|
||||
"@strudel.cycles/xen": "workspace:*",
|
||||
"acorn": "^8.8.1",
|
||||
"dependency-tree": "^9.0.0",
|
||||
"vitest": "^0.25.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitest/ui": "^0.21.1",
|
||||
"@vitest/ui": "^0.25.7",
|
||||
"c8": "^7.12.0",
|
||||
"canvas": "^2.11.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"events": "^3.3.0",
|
||||
"gh-pages": "^4.0.0",
|
||||
"jsdoc": "^3.6.10",
|
||||
"jsdoc-json": "^2.0.2",
|
||||
"jsdoc-to-markdown": "^7.1.1",
|
||||
"lerna": "^4.0.0",
|
||||
"rollup-plugin-visualizer": "^5.8.1",
|
||||
"vitest": "^0.21.1"
|
||||
"lerna": "^6.5.1",
|
||||
"prettier": "^2.8.1",
|
||||
"rollup-plugin-visualizer": "^5.8.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ b: 3/2 - 7/4
|
||||
c: 7/4 - 2
|
||||
```
|
||||
|
||||
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-qmz6qr?file=/src/index.js).
|
||||
- [open color pattern example](https://raw.githack.com/tidalcycles/strudel/package-examples/packages/core/examples/canvas.html)
|
||||
- [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/package-examples/packages/core/examples/metro.html)
|
||||
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js).
|
||||
- [open color pattern example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/canvas.html)
|
||||
- [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/vanilla.html)
|
||||
- [open minimal vite example](./examples/vite-vanilla-repl/)
|
||||
@@ -0,0 +1,67 @@
|
||||
import { controls, Pattern, getDrawContext, silence, register, pure } from './index.mjs';
|
||||
const { createParams } = controls;
|
||||
|
||||
let clearColor = '#22222210';
|
||||
|
||||
Pattern.prototype.animate = function ({ callback, sync = false, smear = 0.5 } = {}) {
|
||||
window.frame && cancelAnimationFrame(window.frame);
|
||||
const ctx = getDrawContext();
|
||||
const { clientWidth: ww, clientHeight: wh } = ctx.canvas;
|
||||
let smearPart = smear === 0 ? '99' : Number((1 - smear) * 100).toFixed(0);
|
||||
smearPart = smearPart.length === 1 ? `0${smearPart}` : smearPart;
|
||||
clearColor = `#200010${smearPart}`;
|
||||
const render = (t) => {
|
||||
let frame;
|
||||
/* if (sync) {
|
||||
t = scheduler.now();
|
||||
frame = this.queryArc(t, t);
|
||||
} else { */
|
||||
t = Math.round(t);
|
||||
frame = this.slow(1000).queryArc(t, t);
|
||||
// }
|
||||
ctx.fillStyle = clearColor;
|
||||
ctx.fillRect(0, 0, ww, wh);
|
||||
frame.forEach((f) => {
|
||||
let { x, y, w, h, s, r, angle = 0, fill = 'darkseagreen' } = f.value;
|
||||
w *= ww;
|
||||
h *= wh;
|
||||
if (r !== undefined && angle !== undefined) {
|
||||
const radians = angle * 2 * Math.PI;
|
||||
const [cx, cy] = [(ww - w) / 2, (wh - h) / 2];
|
||||
x = cx + Math.cos(radians) * r * cx;
|
||||
y = cy + Math.sin(radians) * r * cy;
|
||||
} else {
|
||||
x *= ww - w;
|
||||
y *= wh - h;
|
||||
}
|
||||
const val = { ...f.value, x, y, w, h };
|
||||
ctx.fillStyle = fill;
|
||||
if (s === 'rect') {
|
||||
ctx.fillRect(x, y, w, h);
|
||||
} else if (s === 'ellipse') {
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(x + w / 2, y + h / 2, w / 2, h / 2, 0, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
}
|
||||
callback && callback(ctx, val, f);
|
||||
});
|
||||
window.frame = requestAnimationFrame(render);
|
||||
};
|
||||
window.frame = requestAnimationFrame(render);
|
||||
return silence;
|
||||
};
|
||||
|
||||
export const { x, y, w, h, angle, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'angle', 'r', 'fill', 'smear');
|
||||
|
||||
export const rescale = register('rescale', function (f, pat) {
|
||||
return pat.mul(x(f).w(f).y(f).h(f));
|
||||
});
|
||||
|
||||
export const moveXY = register('moveXY', function (dx, dy, pat) {
|
||||
return pat.add(x(dx).y(dy));
|
||||
});
|
||||
|
||||
export const zoomIn = register('zoomIn', function (f, pat) {
|
||||
const d = pure(1).sub(f).div(2);
|
||||
return pat.rescale(f).move(d, d);
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
export const colorMap = {
|
||||
aliceblue: '#f0f8ff',
|
||||
antiquewhite: '#faebd7',
|
||||
aqua: '#00ffff',
|
||||
aquamarine: '#7fffd4',
|
||||
azure: '#f0ffff',
|
||||
beige: '#f5f5dc',
|
||||
bisque: '#ffe4c4',
|
||||
black: '#000000',
|
||||
blanchedalmond: '#ffebcd',
|
||||
blue: '#0000ff',
|
||||
blueviolet: '#8a2be2',
|
||||
brown: '#a52a2a',
|
||||
burlywood: '#deb887',
|
||||
cadetblue: '#5f9ea0',
|
||||
chartreuse: '#7fff00',
|
||||
chocolate: '#d2691e',
|
||||
coral: '#ff7f50',
|
||||
cornflowerblue: '#6495ed',
|
||||
cornsilk: '#fff8dc',
|
||||
crimson: '#dc143c',
|
||||
cyan: '#00ffff',
|
||||
darkblue: '#00008b',
|
||||
darkcyan: '#008b8b',
|
||||
darkgoldenrod: '#b8860b',
|
||||
darkgray: '#a9a9a9',
|
||||
darkgreen: '#006400',
|
||||
darkgrey: '#a9a9a9',
|
||||
darkkhaki: '#bdb76b',
|
||||
darkmagenta: '#8b008b',
|
||||
darkolivegreen: '#556b2f',
|
||||
darkorange: '#ff8c00',
|
||||
darkorchid: '#9932cc',
|
||||
darkred: '#8b0000',
|
||||
darksalmon: '#e9967a',
|
||||
darkseagreen: '#8fbc8f',
|
||||
darkslateblue: '#483d8b',
|
||||
darkslategray: '#2f4f4f',
|
||||
darkslategrey: '#2f4f4f',
|
||||
darkturquoise: '#00ced1',
|
||||
darkviolet: '#9400d3',
|
||||
deeppink: '#ff1493',
|
||||
deepskyblue: '#00bfff',
|
||||
dimgray: '#696969',
|
||||
dimgrey: '#696969',
|
||||
dodgerblue: '#1e90ff',
|
||||
firebrick: '#b22222',
|
||||
floralwhite: '#fffaf0',
|
||||
forestgreen: '#228b22',
|
||||
fuchsia: '#ff00ff',
|
||||
gainsboro: '#dcdcdc',
|
||||
ghostwhite: '#f8f8ff',
|
||||
gold: '#ffd700',
|
||||
goldenrod: '#daa520',
|
||||
gray: '#808080',
|
||||
green: '#008000',
|
||||
greenyellow: '#adff2f',
|
||||
grey: '#808080',
|
||||
honeydew: '#f0fff0',
|
||||
hotpink: '#ff69b4',
|
||||
indianred: '#cd5c5c',
|
||||
indigo: '#4b0082',
|
||||
ivory: '#fffff0',
|
||||
khaki: '#f0e68c',
|
||||
lavender: '#e6e6fa',
|
||||
lavenderblush: '#fff0f5',
|
||||
lawngreen: '#7cfc00',
|
||||
lemonchiffon: '#fffacd',
|
||||
lightblue: '#add8e6',
|
||||
lightcoral: '#f08080',
|
||||
lightcyan: '#e0ffff',
|
||||
lightgoldenrodyellow: '#fafad2',
|
||||
lightgray: '#d3d3d3',
|
||||
lightgreen: '#90ee90',
|
||||
lightgrey: '#d3d3d3',
|
||||
lightpink: '#ffb6c1',
|
||||
lightsalmon: '#ffa07a',
|
||||
lightseagreen: '#20b2aa',
|
||||
lightskyblue: '#87cefa',
|
||||
lightslategray: '#778899',
|
||||
lightslategrey: '#778899',
|
||||
lightsteelblue: '#b0c4de',
|
||||
lightyellow: '#ffffe0',
|
||||
lime: '#00ff00',
|
||||
limegreen: '#32cd32',
|
||||
linen: '#faf0e6',
|
||||
magenta: '#ff00ff',
|
||||
maroon: '#800000',
|
||||
mediumaquamarine: '#66cdaa',
|
||||
mediumblue: '#0000cd',
|
||||
mediumorchid: '#ba55d3',
|
||||
mediumpurple: '#9370db',
|
||||
mediumseagreen: '#3cb371',
|
||||
mediumslateblue: '#7b68ee',
|
||||
mediumspringgreen: '#00fa9a',
|
||||
mediumturquoise: '#48d1cc',
|
||||
mediumvioletred: '#c71585',
|
||||
midnightblue: '#191970',
|
||||
mintcream: '#f5fffa',
|
||||
mistyrose: '#ffe4e1',
|
||||
moccasin: '#ffe4b5',
|
||||
navajowhite: '#ffdead',
|
||||
navy: '#000080',
|
||||
oldlace: '#fdf5e6',
|
||||
olive: '#808000',
|
||||
olivedrab: '#6b8e23',
|
||||
orange: '#ffa500',
|
||||
orangered: '#ff4500',
|
||||
orchid: '#da70d6',
|
||||
palegoldenrod: '#eee8aa',
|
||||
palegreen: '#98fb98',
|
||||
paleturquoise: '#afeeee',
|
||||
palevioletred: '#db7093',
|
||||
papayawhip: '#ffefd5',
|
||||
peachpuff: '#ffdab9',
|
||||
peru: '#cd853f',
|
||||
pink: '#ffc0cb',
|
||||
plum: '#dda0dd',
|
||||
powderblue: '#b0e0e6',
|
||||
purple: '#800080',
|
||||
red: '#ff0000',
|
||||
rosybrown: '#bc8f8f',
|
||||
royalblue: '#4169e1',
|
||||
saddlebrown: '#8b4513',
|
||||
salmon: '#fa8072',
|
||||
sandybrown: '#f4a460',
|
||||
seagreen: '#2e8b57',
|
||||
seashell: '#fff5ee',
|
||||
sienna: '#a0522d',
|
||||
silver: '#c0c0c0',
|
||||
skyblue: '#87ceeb',
|
||||
slateblue: '#6a5acd',
|
||||
slategray: '#708090',
|
||||
slategrey: '#708090',
|
||||
snow: '#fffafa',
|
||||
springgreen: '#00ff7f',
|
||||
steelblue: '#4682b4',
|
||||
tan: '#d2b48c',
|
||||
teal: '#008080',
|
||||
thistle: '#d8bfd8',
|
||||
tomato: '#ff6347',
|
||||
turquoise: '#40e0d0',
|
||||
violet: '#ee82ee',
|
||||
wheat: '#f5deb3',
|
||||
white: '#ffffff',
|
||||
whitesmoke: '#f5f5f5',
|
||||
yellow: '#ffff00',
|
||||
yellowgreen: '#9acd32',
|
||||
};
|
||||
|
||||
export function convertColorToNumber(color) {
|
||||
// Convert color to lowercase for easier matching
|
||||
color = color.toLowerCase();
|
||||
|
||||
// If the color is a hex code, convert it to a number
|
||||
if (color[0] === '#') {
|
||||
return convertHexToNumber(color);
|
||||
}
|
||||
|
||||
// If the color is a named color, return the corresponding number
|
||||
if (colorMap[color] !== undefined) {
|
||||
return convertHexToNumber(colorMap[color]);
|
||||
}
|
||||
|
||||
// If the color is not recognized, return null
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function convertHexToNumber(hex) {
|
||||
// Remove the leading '#' from the hex code
|
||||
hex = hex.slice(1);
|
||||
|
||||
// Convert the hex code to a number
|
||||
return parseInt(hex, 16);
|
||||
}
|
||||
@@ -5,65 +5,77 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import createClock from './zyklus.mjs';
|
||||
import { logger } from './logger.mjs';
|
||||
|
||||
export class Cyclist {
|
||||
worker;
|
||||
pattern;
|
||||
started = false;
|
||||
cps = 1; // TODO
|
||||
getTime;
|
||||
phase = 0;
|
||||
constructor({ interval, onTrigger, onError, getTime, latency = 0.1 }) {
|
||||
this.getTime = getTime;
|
||||
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
|
||||
this.started = false;
|
||||
this.cps = 1;
|
||||
this.lastTick = 0; // absolute time when last tick (clock callback) happened
|
||||
this.lastBegin = 0; // query begin of last tick
|
||||
this.lastEnd = 0; // query end of last tick
|
||||
this.getTime = getTime; // get absolute time
|
||||
this.onToggle = onToggle;
|
||||
this.latency = latency; // fixed trigger time offset
|
||||
const round = (x) => Math.round(x * 1000) / 1000;
|
||||
this.clock = createClock(
|
||||
getTime,
|
||||
// called slightly before each cycle
|
||||
(phase, duration, tick) => {
|
||||
if (tick === 0) {
|
||||
this.origin = phase;
|
||||
}
|
||||
const begin = round(phase - this.origin);
|
||||
this.phase = begin - latency;
|
||||
const end = round(begin + duration);
|
||||
const time = getTime();
|
||||
try {
|
||||
const haps = this.pattern.queryArc(begin, end); // get Haps
|
||||
// console.log('haps', haps.map((hap) => hap.value.n).join(' '));
|
||||
const time = getTime();
|
||||
const begin = this.lastEnd;
|
||||
this.lastBegin = begin;
|
||||
const end = round(begin + duration * this.cps);
|
||||
this.lastEnd = end;
|
||||
const haps = this.pattern.queryArc(begin, end);
|
||||
const tickdeadline = phase - time; // time left till phase begins
|
||||
this.lastTick = time + tickdeadline;
|
||||
|
||||
haps.forEach((hap) => {
|
||||
// console.log('hap', hap.value.n, hap.part.begin);
|
||||
if (hap.part.begin.equals(hap.whole.begin)) {
|
||||
const deadline = hap.whole.begin + this.origin - time + latency;
|
||||
const duration = hap.duration * 1;
|
||||
onTrigger?.(hap, deadline, duration);
|
||||
const deadline = (hap.whole.begin - begin) / this.cps + tickdeadline + latency;
|
||||
const duration = hap.duration / this.cps;
|
||||
onTrigger?.(hap, deadline, duration, this.cps);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('scheduler error', e);
|
||||
logger(`[cyclist] error: ${e.message}`);
|
||||
onError?.(e);
|
||||
}
|
||||
}, // called slightly before each cycle
|
||||
},
|
||||
interval, // duration of each cycle
|
||||
);
|
||||
}
|
||||
getPhase() {
|
||||
return this.phase;
|
||||
now() {
|
||||
const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration;
|
||||
return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency;
|
||||
}
|
||||
setStarted(v) {
|
||||
this.started = v;
|
||||
this.onToggle?.(v);
|
||||
}
|
||||
start() {
|
||||
if (!this.pattern) {
|
||||
throw new Error('Scheduler: no pattern set! call .setPattern first.');
|
||||
}
|
||||
logger('[cyclist] start');
|
||||
this.clock.start();
|
||||
this.started = true;
|
||||
this.setStarted(true);
|
||||
}
|
||||
pause() {
|
||||
this.clock.stop();
|
||||
delete this.origin;
|
||||
this.started = false;
|
||||
logger('[cyclist] pause');
|
||||
this.clock.pause();
|
||||
this.setStarted(false);
|
||||
}
|
||||
stop() {
|
||||
delete this.origin;
|
||||
logger('[cyclist] stop');
|
||||
this.clock.stop();
|
||||
this.started = false;
|
||||
this.lastEnd = 0;
|
||||
this.setStarted(false);
|
||||
}
|
||||
setPattern(pat, autostart = false) {
|
||||
this.pattern = pat;
|
||||
|
||||
@@ -4,8 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Tone } from './tone.mjs';
|
||||
import { Pattern } from '@strudel.cycles/core';
|
||||
import { Pattern, getTime, State, TimeSpan } from './index.mjs';
|
||||
|
||||
export const getDrawContext = (id = 'test-canvas') => {
|
||||
let canvas = document.querySelector('#' + id);
|
||||
@@ -28,7 +27,7 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery }) {
|
||||
let cycle,
|
||||
events = [];
|
||||
const animate = (time) => {
|
||||
const t = Tone.getTransport().seconds;
|
||||
const t = getTime();
|
||||
if (from !== undefined && to !== undefined) {
|
||||
const currentCycle = Math.floor(t);
|
||||
if (cycle !== currentCycle) {
|
||||
@@ -50,9 +49,9 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery }) {
|
||||
return this;
|
||||
};
|
||||
|
||||
export const cleanupDraw = () => {
|
||||
export const cleanupDraw = (clearScreen = true) => {
|
||||
const ctx = getDrawContext();
|
||||
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
|
||||
clearScreen && ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
|
||||
if (window.strudelAnimation) {
|
||||
cancelAnimationFrame(window.strudelAnimation);
|
||||
}
|
||||
@@ -60,3 +59,9 @@ export const cleanupDraw = () => {
|
||||
clearInterval(window.strudelScheduler);
|
||||
}
|
||||
};
|
||||
|
||||
Pattern.prototype.onPaint = function (onPaint) {
|
||||
// this is evil! TODO: add pattern.context
|
||||
this.context = { onPaint };
|
||||
return this;
|
||||
};
|
||||
@@ -1,47 +1,91 @@
|
||||
/*
|
||||
euclid.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/euclid.mjs>
|
||||
euclid.mjs - Bjorklund/Euclidean/Diaspora rhythms
|
||||
Copyright (C) 2023 Rohan Drape and strudel contributors
|
||||
|
||||
See <https://github.com/tidalcycles/strudel/blob/main/packages/core/euclid.mjs> for authors of this file.
|
||||
|
||||
The Bjorklund algorithm implementation is ported from the Haskell Music Theory Haskell module by Rohan Drape -
|
||||
https://rohandrape.net/?t=hmt
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Pattern, timeCat } from './pattern.mjs';
|
||||
import bjork from 'bjork';
|
||||
import { rotate } from './util.mjs';
|
||||
import { Pattern, timeCat, register, silence } from './pattern.mjs';
|
||||
import { rotate, flatten, splitAt, zipWith } from './util.mjs';
|
||||
import Fraction from './fraction.mjs';
|
||||
|
||||
const euclid = (pulses, steps, rotation = 0) => {
|
||||
const b = bjork(steps, pulses);
|
||||
if (rotation) {
|
||||
return rotate(b, -rotation);
|
||||
}
|
||||
return b;
|
||||
const left = function (n, x) {
|
||||
const [ons, offs] = n;
|
||||
const [xs, ys] = x;
|
||||
const [_xs, __xs] = splitAt(offs, xs);
|
||||
return [
|
||||
[offs, ons - offs],
|
||||
[zipWith((a, b) => a.concat(b), _xs, ys), __xs],
|
||||
];
|
||||
};
|
||||
|
||||
const right = function (n, x) {
|
||||
const [ons, offs] = n;
|
||||
const [xs, ys] = x;
|
||||
const [_ys, __ys] = splitAt(ons, ys);
|
||||
const result = [
|
||||
[ons, offs - ons],
|
||||
[zipWith((a, b) => a.concat(b), xs, _ys), __ys],
|
||||
];
|
||||
return result;
|
||||
};
|
||||
|
||||
const _bjork = function (n, x) {
|
||||
const [ons, offs] = n;
|
||||
return Math.min(ons, offs) <= 1 ? [n, x] : _bjork(...(ons > offs ? left(n, x) : right(n, x)));
|
||||
};
|
||||
|
||||
export const bjork = function (ons, steps) {
|
||||
const offs = steps - ons;
|
||||
const x = Array(ons).fill([1]);
|
||||
const y = Array(offs).fill([0]);
|
||||
const result = _bjork([ons, offs], [x, y]);
|
||||
return flatten(result[1][0]).concat(flatten(result[1][1]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the structure of the pattern to form an euclidean rhythm.
|
||||
* Euclidian rhythms are rhythms obtained using the greatest common divisor of two numbers.
|
||||
* They were described in 2004 by Godfried Toussaint, a canadian computer scientist.
|
||||
* Euclidian rhythms are really useful for computer/algorithmic music because they can accurately
|
||||
* describe a large number of rhythms used in the most important music world traditions.
|
||||
* Euclidian rhythms are rhythms obtained using the greatest common
|
||||
* divisor of two numbers. They were described in 2004 by Godfried
|
||||
* Toussaint, a canadian computer scientist. Euclidian rhythms are
|
||||
* really useful for computer/algorithmic music because they can
|
||||
* describe a large number of rhythms with a couple of numbers.
|
||||
*
|
||||
* @memberof Pattern
|
||||
* @name euclid
|
||||
* @param {number} pulses the number of onsets / beats
|
||||
* @param {number} steps the number of steps to fill
|
||||
* @param {number} rotation (optional) offset in steps
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* // The Cuban tresillo pattern.
|
||||
* note("c3").euclid(3,8)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Like `euclid`, but has an additional parameter for 'rotating' the resulting sequence.
|
||||
* @memberof Pattern
|
||||
* @name euclidRot
|
||||
* @param {number} pulses the number of onsets / beats
|
||||
* @param {number} steps the number of steps to fill
|
||||
* @param {number} rotation offset in steps
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* // A Samba rhythm necklace from Brazil
|
||||
* note("c3").euclidRot(3,16,14)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @example // A thirteenth century Persian rhythm called Khafif-e-ramal.
|
||||
* note("c3").euclid(2,5)
|
||||
* @example // The archetypal pattern of the Cumbia from Colombia, as well as a Calypso rhythm from Trinidad.
|
||||
* note("c3").euclid(3,4)
|
||||
* @example // Another thirteenth century Persian rhythm by the name of Khafif-e-ramal, as well as a Rumanian folk-dance rhythm.
|
||||
* note("c3").euclid(3,5,2)
|
||||
* note("c3").euclidRot(3,5,2)
|
||||
* @example // A Ruchenitza rhythm used in a Bulgarian folk-dance.
|
||||
* note("c3").euclid(3,7)
|
||||
* @example // The Cuban tresillo pattern.
|
||||
@@ -71,34 +115,57 @@ const euclid = (pulses, steps, rotation = 0) => {
|
||||
* @example // A common West African bell pattern.
|
||||
* note("c3").euclid(7,12)
|
||||
* @example // A Samba rhythm necklace from Brazil.
|
||||
* note("c3").euclid(7,16,14)
|
||||
* note("c3").euclidRot(7,16,14)
|
||||
* @example // A rhythm necklace used in the Central African Republic.
|
||||
* note("c3").euclid(9,16)
|
||||
* @example // A rhythm necklace of the Aka Pygmies of Central Africa.
|
||||
* note("c3").euclid(11,24,14)
|
||||
* note("c3").euclidRot(11,24,14)
|
||||
* @example // Another rhythm necklace of the Aka Pygmies of the upper Sangha.
|
||||
* note("c3").euclid(13,24,5)
|
||||
* note("c3").euclidRot(13,24,5)
|
||||
*/
|
||||
Pattern.prototype.euclid = function (pulses, steps, rotation = 0) {
|
||||
return this.struct(euclid(pulses, steps, rotation));
|
||||
|
||||
const _euclidRot = function (pulses, steps, rotation) {
|
||||
const b = bjork(pulses, steps);
|
||||
if (rotation) {
|
||||
return rotate(b, -rotation);
|
||||
}
|
||||
return b;
|
||||
};
|
||||
|
||||
export const euclid = register('euclid', function (pulses, steps, pat) {
|
||||
return pat.struct(_euclidRot(pulses, steps, 0));
|
||||
});
|
||||
|
||||
export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], function (pulses, steps, rotation, pat) {
|
||||
return pat.struct(_euclidRot(pulses, steps, rotation));
|
||||
});
|
||||
|
||||
/**
|
||||
* Similar to `.euclid`, but each pulse is held until the next pulse, so there will be no gaps.
|
||||
* Similar to `euclid`, but each pulse is held until the next pulse,
|
||||
* so there will be no gaps.
|
||||
* @name euclidLegato
|
||||
* @memberof Pattern
|
||||
* @example
|
||||
* n("g2").decay(.1).sustain(.3).euclidLegato(3,8)
|
||||
*/
|
||||
Pattern.prototype.euclidLegato = function (pulses, steps, rotation = 0) {
|
||||
const bin_pat = euclid(pulses, steps, rotation);
|
||||
const firstOne = bin_pat.indexOf(1);
|
||||
const gapless = rotate(bin_pat, firstOne)
|
||||
|
||||
const _euclidLegato = function (pulses, steps, rotation, pat) {
|
||||
if (pulses < 1) {
|
||||
return silence;
|
||||
}
|
||||
const bin_pat = _euclidRot(pulses, steps, rotation);
|
||||
const gapless = bin_pat
|
||||
.join('')
|
||||
.split('1')
|
||||
.slice(1)
|
||||
.map((s) => [s.length + 1, true]);
|
||||
return this.struct(timeCat(...gapless)).late(Fraction(firstOne).div(steps));
|
||||
return pat.struct(timeCat(...gapless));
|
||||
};
|
||||
|
||||
export default euclid;
|
||||
export const euclidLegato = register(['euclidLegato'], function (pulses, steps, pat) {
|
||||
return _euclidLegato(pulses, steps, 0, pat);
|
||||
});
|
||||
|
||||
export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, steps, rotation, pat) {
|
||||
return _euclidLegato(pulses, steps, rotation, pat);
|
||||
});
|
||||
|
||||
@@ -4,16 +4,9 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
import { isPattern } from './index.mjs';
|
||||
|
||||
const { isPattern, Pattern } = strudel;
|
||||
|
||||
let scoped = false;
|
||||
export const evalScope = async (...args) => {
|
||||
if (scoped) {
|
||||
console.warn('evalScope was called more than once.');
|
||||
}
|
||||
scoped = true;
|
||||
const results = await Promise.allSettled(args);
|
||||
const modules = results.filter((result) => result.status === 'fulfilled').map((r) => r.value);
|
||||
results.forEach((result, i) => {
|
||||
@@ -21,21 +14,35 @@ export const evalScope = async (...args) => {
|
||||
console.warn(`evalScope: module with index ${i} could not be loaded:`, result.reason);
|
||||
}
|
||||
});
|
||||
Object.assign(globalThis, ...modules, Pattern.prototype.bootstrap());
|
||||
// Object.assign(globalThis, ...modules);
|
||||
// below is a fix for above commented out line
|
||||
// same error as https://github.com/vitest-dev/vitest/issues/1807 when running this on astro server
|
||||
modules.forEach((module) => {
|
||||
Object.entries(module).forEach(([name, value]) => {
|
||||
globalThis[name] = value;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function safeEval(str) {
|
||||
return Function('"use strict";return (' + str + ')')();
|
||||
function safeEval(str, options = {}) {
|
||||
const { wrapExpression = true, wrapAsync = true } = options;
|
||||
if (wrapExpression) {
|
||||
str = `{${str}}`;
|
||||
}
|
||||
if (wrapAsync) {
|
||||
str = `(async ()=>${str})()`;
|
||||
}
|
||||
const body = `"use strict";return (${str})`;
|
||||
return Function(body)();
|
||||
}
|
||||
|
||||
export const evaluate = async (code, transpiler) => {
|
||||
if (!scoped) {
|
||||
await evalScope(); // at least scope Pattern.prototype.boostrap
|
||||
}
|
||||
if (transpiler) {
|
||||
code = transpiler(code); // transform syntactically correct js code to semantically usable code
|
||||
}
|
||||
let evaluated = await safeEval(code);
|
||||
// if no transpiler is given, we expect a single instruction (!wrapExpression)
|
||||
const options = { wrapExpression: !!transpiler };
|
||||
let evaluated = await safeEval(code, options);
|
||||
if (!isPattern(evaluated)) {
|
||||
console.log('evaluated', evaluated);
|
||||
const message = `got "${typeof evaluated}" instead of pattern`;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<input
|
||||
type="text"
|
||||
id="text"
|
||||
value="cat('a', 'b')"
|
||||
value="seq('a', ['b', 'c'])"
|
||||
style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px"
|
||||
spellcheck="false"
|
||||
/>
|
||||
<div id="output"></div>
|
||||
<script type="module">
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
|
||||
Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval
|
||||
const input = document.getElementById('text');
|
||||
const getEvents = () => {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<input
|
||||
type="text"
|
||||
id="text"
|
||||
value="cat('orange', 'indigo')"
|
||||
value="seq('tomato', 'indigo', ['white', 'steelblue']).fast(4)"
|
||||
style="width: 100%; font-size: 2em; background: black; color: white; outline: none; position: absolute; top: 0"
|
||||
spellcheck="false"
|
||||
/>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script type="module">
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
|
||||
// this adds all strudel functions to the global scope, to be used by eval
|
||||
Object.assign(window, strudel);
|
||||
// setup elements
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
<div style="position: absolute; bottom: 0; right: 0; padding: 4px; width: 100vw; display: flex">
|
||||
<button id="start" style="font-size: 2em">start</button>
|
||||
<button id="stop" style="font-size: 2em">stop</button>
|
||||
<button id="slower" style="font-size: 2em">slower</button>
|
||||
<button id="faster" style="font-size: 2em">faster</button>
|
||||
</div>
|
||||
<textarea
|
||||
style="font-size: 2em; background: #052b49; color: #fff; height: 100%; width: 100%; outline: none; border: 0"
|
||||
id="text"
|
||||
spellcheck="false"
|
||||
>
|
||||
Loading...</textarea
|
||||
>
|
||||
<script type="module">
|
||||
document.body.style = 'margin: 0';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
|
||||
const { cat, State, TimeSpan, Scheduler, getPlayableNoteValue, getFreq } = strudel;
|
||||
Object.assign(window, strudel); // add strudel to eval scope
|
||||
|
||||
const ctx = new AudioContext();
|
||||
const scheduler = new Scheduler({
|
||||
// audioContext: getAudioContext(),
|
||||
interval: 0.1,
|
||||
onTrigger: (hap, time, duration) => {
|
||||
const freq = getFrequency(hap);
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = 0.2;
|
||||
osc.frequency.value = freq;
|
||||
osc.type = 'triangle';
|
||||
const onset = ctx.currentTime + time;
|
||||
const offset = onset + duration;
|
||||
const attack = 0.05;
|
||||
const release = 0.05;
|
||||
osc.start(onset);
|
||||
osc.stop(offset + release);
|
||||
|
||||
const g = ctx.createGain();
|
||||
g.gain.setValueAtTime(gain, onset);
|
||||
g.gain.linearRampToValueAtTime(gain, onset + attack);
|
||||
g.gain.setValueAtTime(gain, offset - release);
|
||||
g.gain.linearRampToValueAtTime(0, offset);
|
||||
osc.connect(g);
|
||||
g.connect(ctx.destination);
|
||||
},
|
||||
});
|
||||
|
||||
let initialCode = `stack('c4','e4',cat('g4','a4','b4','a4'))
|
||||
.add(cat(0,1,2,3,4,3,2,1).slow(8))
|
||||
.fast(2)
|
||||
.cps(tri.range(1,8).slow(32))`;
|
||||
|
||||
try {
|
||||
const base64 = decodeURIComponent(window.location.href.split('#')[1]);
|
||||
initialCode = atob(base64);
|
||||
} catch (err) {
|
||||
console.warn('failed to decode', err);
|
||||
}
|
||||
const input = document.getElementById('text');
|
||||
input.value = initialCode;
|
||||
const evaluate = () => {
|
||||
try {
|
||||
const pattern = eval(input.value);
|
||||
scheduler.setPattern(pattern);
|
||||
window.location.hash = '#' + encodeURIComponent(btoa(input.value)); // update url hash
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
};
|
||||
evaluate();
|
||||
input.addEventListener('input', () => evaluate());
|
||||
document.getElementById('start').addEventListener('click', async () => {
|
||||
await ctx.resume();
|
||||
scheduler.start();
|
||||
});
|
||||
document.getElementById('stop').addEventListener('click', () => scheduler.stop());
|
||||
document.getElementById('slower').addEventListener('click', () => scheduler.setCps(scheduler.cps - 0.1));
|
||||
document.getElementById('faster').addEventListener('click', () => scheduler.setCps(scheduler.cps + 0.1));
|
||||
</script>
|
||||
<!--
|
||||
sequence(1,2).mul(55/2) // frequencies
|
||||
.mul(slowcat(1,2))
|
||||
.mul(slowcat(1,3/2,4/3,5/3).slow(8))
|
||||
.fast(3)
|
||||
.freq()
|
||||
.velocity(.5)
|
||||
.s('sawtooth')
|
||||
.cutoff(800)
|
||||
.out()
|
||||
-->
|
||||
@@ -1,44 +0,0 @@
|
||||
<input
|
||||
type="text"
|
||||
id="text"
|
||||
value="seq('c3','eb3','g3').note().s('sawtooth').out()"
|
||||
style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px"
|
||||
spellcheck="false"
|
||||
/>
|
||||
<button id="start">play</button>
|
||||
<div id="output"></div>
|
||||
<script type="module">
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@latest');
|
||||
|
||||
const controls = await import('https://cdn.skypack.dev/@strudel.cycles/core@latest/controls.mjs');
|
||||
const { getAudioContext, Scheduler } = await import('https://cdn.skypack.dev/@strudel.cycles/webaudio@latest');
|
||||
let scheduler;
|
||||
|
||||
const audioContext = getAudioContext();
|
||||
const latency = 0.2;
|
||||
|
||||
Object.assign(window, strudel);
|
||||
Object.assign(window, controls.default);
|
||||
scheduler = new Scheduler({
|
||||
audioContext,
|
||||
interval: 0.1,
|
||||
latency,
|
||||
onEvent: (hap) => {
|
||||
if (!hap.context.onTrigger) {
|
||||
console.warn('no output chosen. use one of .out() .webdirt() .osc()');
|
||||
}
|
||||
},
|
||||
});
|
||||
let started;
|
||||
document.getElementById('start').addEventListener('click', async () => {
|
||||
const code = document.getElementById('text').value;
|
||||
const pattern = eval(code);
|
||||
const events = pattern._firstCycleValues;
|
||||
console.log(code, '->', events);
|
||||
scheduler.setPattern(pattern);
|
||||
if (!started) {
|
||||
scheduler.start();
|
||||
started = true;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Buildless Vanilla Strudel REPL</title>
|
||||
</head>
|
||||
<body style="margin: 0; background: #222">
|
||||
<div style="display: grid; height: 100vh; grid-template-rows: 32px auto">
|
||||
<button id="start" style="width: 100vw; height: 32px">evaluate</button>
|
||||
<textarea
|
||||
id="text"
|
||||
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
</div>
|
||||
<div id="output"></div>
|
||||
<script type="module">
|
||||
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.6.8';
|
||||
import { mini } from 'https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0';
|
||||
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.6.0';
|
||||
import {
|
||||
getAudioContext,
|
||||
webaudioOutput,
|
||||
initAudioOnFirstClick,
|
||||
} from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0';
|
||||
|
||||
initAudioOnFirstClick();
|
||||
const ctx = getAudioContext();
|
||||
const input = document.getElementById('text');
|
||||
input.innerHTML = getTune();
|
||||
|
||||
evalScope(
|
||||
controls,
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'),
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0'),
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.6.0'),
|
||||
import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0'),
|
||||
);
|
||||
|
||||
const { evaluate } = repl({
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime: () => ctx.currentTime,
|
||||
transpiler,
|
||||
});
|
||||
document.getElementById('start').addEventListener('click', () => evaluate(input.value));
|
||||
|
||||
function getTune() {
|
||||
return `await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
|
||||
stack(
|
||||
// amen
|
||||
n("0 1 2 3 4 5 6 7")
|
||||
.sometimes(x=>x.ply(2))
|
||||
.rarely(x=>x.speed("2 | -2"))
|
||||
.sometimesBy(.4, x=>x.delay(".5"))
|
||||
.s("amencutup")
|
||||
.slow(2)
|
||||
.room(.5)
|
||||
,
|
||||
// bass
|
||||
sine.add(saw.slow(4)).range(0,7).segment(8)
|
||||
.superimpose(x=>x.add(.1))
|
||||
.scale('G0 minor').note()
|
||||
.s("sawtooth").decay(.1).sustain(0)
|
||||
.gain(.4).cutoff(perlin.range(300,3000).slow(8)).resonance(10)
|
||||
.degradeBy("0 0.1 .5 .1")
|
||||
.rarely(add(note("12")))
|
||||
,
|
||||
// chord
|
||||
note("Bb3,D4".superimpose(x=>x.add(.2)))
|
||||
.s('sawtooth').cutoff(1000).struct("<~@3 [~ x]>")
|
||||
.decay(.05).sustain(.0).delay(.8).delaytime(.125).room(.8)
|
||||
,
|
||||
// alien
|
||||
s("breath").room(1).shape(.6).chop(16).rev().mask("<x ~@7>")
|
||||
,
|
||||
n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5))
|
||||
).reset("<x@7 x(5,8)>")`;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
import{b as s,h as i,m,a as n,c as p,p as t}from"./index.4cbc0a10.js";export{s as SyntaxError,i as h,m as mini,n as minify,p as parse,t as patternifyAST};
|
||||
@@ -1 +0,0 @@
|
||||
import{g as s,b as d,e as t,j as u,l as o,f as r,p as f,i as p,d as g,h as i,w as l,a as m}from"./index.f88145a1.js";export{s as getAudioContext,d as getCachedBuffer,t as getLoadedBuffer,u as getLoadedSamples,o as loadBuffer,r as loadGithubSamples,f as panic,p as resetLoadedSamples,g as reverseBuffer,i as samples,l as webaudioOutput,m as webaudioOutputTrigger};
|
||||
@@ -0,0 +1 @@
|
||||
import{g as s,f as d,i as t,n as r,l as u,j as o,d as f,k as p,r as g,s as i,w as l,e as m}from"./index.4cbc0a10.js";export{s as getAudioContext,d as getCachedBuffer,t as getLoadedBuffer,r as getLoadedSamples,u as loadBuffer,o as loadGithubSamples,f as panic,p as resetLoadedSamples,g as reverseBuffer,i as samples,l as webaudioOutput,m as webaudioOutputTrigger};
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite Vanilla Strudel REPL</title>
|
||||
<script type="module" crossorigin src="/tidalcycles/strudel/general-purpose-scheduler/packages/core/examples/vite-vanilla-repl/dist/assets/index.f88145a1.js"></script>
|
||||
<script type="module" crossorigin src="/tidalcycles/strudel/use-acorn/packages/core/examples/vite-vanilla-repl/dist/assets/index.4cbc0a10.js"></script>
|
||||
</head>
|
||||
<body style="margin: 0; background: #222">
|
||||
<div style="display: grid; height: 100vh">
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { controls, repl, evalScope } from '@strudel.cycles/core';
|
||||
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
|
||||
import shapeshifter from '@strudel.cycles/eval/shapeshifter.mjs';
|
||||
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel.cycles/webaudio';
|
||||
import { transpiler } from '@strudel.cycles/transpiler';
|
||||
import tune from './tune.mjs';
|
||||
|
||||
const ctx = getAudioContext();
|
||||
const input = document.getElementById('text');
|
||||
input.innerHTML = tune;
|
||||
initAudioOnFirstClick();
|
||||
|
||||
evalScope(
|
||||
controls,
|
||||
@@ -18,9 +19,10 @@ evalScope(
|
||||
const { evaluate } = repl({
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime: () => ctx.currentTime,
|
||||
transpiler: shapeshifter,
|
||||
transpiler,
|
||||
});
|
||||
document.getElementById('start').addEventListener('click', () => {
|
||||
ctx.resume();
|
||||
console.log('eval', input.value);
|
||||
evaluate(input.value);
|
||||
});
|
||||
|
||||
@@ -1,885 +0,0 @@
|
||||
{
|
||||
"name": "vite-vanilla-repl",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "vite-vanilla-repl",
|
||||
"version": "0.0.0",
|
||||
"devDependencies": {
|
||||
"vite": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
|
||||
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
|
||||
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
|
||||
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/android-arm": "0.15.13",
|
||||
"@esbuild/linux-loong64": "0.15.13",
|
||||
"esbuild-android-64": "0.15.13",
|
||||
"esbuild-android-arm64": "0.15.13",
|
||||
"esbuild-darwin-64": "0.15.13",
|
||||
"esbuild-darwin-arm64": "0.15.13",
|
||||
"esbuild-freebsd-64": "0.15.13",
|
||||
"esbuild-freebsd-arm64": "0.15.13",
|
||||
"esbuild-linux-32": "0.15.13",
|
||||
"esbuild-linux-64": "0.15.13",
|
||||
"esbuild-linux-arm": "0.15.13",
|
||||
"esbuild-linux-arm64": "0.15.13",
|
||||
"esbuild-linux-mips64le": "0.15.13",
|
||||
"esbuild-linux-ppc64le": "0.15.13",
|
||||
"esbuild-linux-riscv64": "0.15.13",
|
||||
"esbuild-linux-s390x": "0.15.13",
|
||||
"esbuild-netbsd-64": "0.15.13",
|
||||
"esbuild-openbsd-64": "0.15.13",
|
||||
"esbuild-sunos-64": "0.15.13",
|
||||
"esbuild-windows-32": "0.15.13",
|
||||
"esbuild-windows-64": "0.15.13",
|
||||
"esbuild-windows-arm64": "0.15.13"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-android-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
|
||||
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-android-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-darwin-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
|
||||
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-darwin-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-freebsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-freebsd-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-32": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
|
||||
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
|
||||
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-arm": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
|
||||
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-mips64le": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
|
||||
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-ppc64le": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
|
||||
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-riscv64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
|
||||
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-linux-s390x": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
|
||||
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-netbsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-openbsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-sunos-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
|
||||
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-windows-32": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
|
||||
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-windows-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
|
||||
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-windows-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has": "^1.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.18",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
|
||||
"integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.4",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.9.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "2.79.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
||||
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.2.tgz",
|
||||
"integrity": "sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.15.9",
|
||||
"postcss": "^8.4.18",
|
||||
"resolve": "^1.22.1",
|
||||
"rollup": "^2.79.1"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"less": "*",
|
||||
"sass": "*",
|
||||
"stylus": "*",
|
||||
"sugarss": "*",
|
||||
"terser": "^5.4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"less": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"stylus": {
|
||||
"optional": true
|
||||
},
|
||||
"sugarss": {
|
||||
"optional": true
|
||||
},
|
||||
"terser": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@esbuild/android-arm": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
|
||||
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-loong64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
|
||||
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
|
||||
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@esbuild/android-arm": "0.15.13",
|
||||
"@esbuild/linux-loong64": "0.15.13",
|
||||
"esbuild-android-64": "0.15.13",
|
||||
"esbuild-android-arm64": "0.15.13",
|
||||
"esbuild-darwin-64": "0.15.13",
|
||||
"esbuild-darwin-arm64": "0.15.13",
|
||||
"esbuild-freebsd-64": "0.15.13",
|
||||
"esbuild-freebsd-arm64": "0.15.13",
|
||||
"esbuild-linux-32": "0.15.13",
|
||||
"esbuild-linux-64": "0.15.13",
|
||||
"esbuild-linux-arm": "0.15.13",
|
||||
"esbuild-linux-arm64": "0.15.13",
|
||||
"esbuild-linux-mips64le": "0.15.13",
|
||||
"esbuild-linux-ppc64le": "0.15.13",
|
||||
"esbuild-linux-riscv64": "0.15.13",
|
||||
"esbuild-linux-s390x": "0.15.13",
|
||||
"esbuild-netbsd-64": "0.15.13",
|
||||
"esbuild-openbsd-64": "0.15.13",
|
||||
"esbuild-sunos-64": "0.15.13",
|
||||
"esbuild-windows-32": "0.15.13",
|
||||
"esbuild-windows-64": "0.15.13",
|
||||
"esbuild-windows-arm64": "0.15.13"
|
||||
}
|
||||
},
|
||||
"esbuild-android-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
|
||||
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-android-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-darwin-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
|
||||
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-darwin-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-freebsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-freebsd-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-32": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
|
||||
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
|
||||
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-arm": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
|
||||
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-mips64le": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
|
||||
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-ppc64le": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
|
||||
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-riscv64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
|
||||
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-linux-s390x": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
|
||||
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-netbsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-openbsd-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
|
||||
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-sunos-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
|
||||
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-windows-32": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
|
||||
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-windows-64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
|
||||
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"esbuild-windows-arm64": {
|
||||
"version": "0.15.13",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
|
||||
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.18",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
|
||||
"integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.9.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.79.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
||||
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true
|
||||
},
|
||||
"vite": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.2.tgz",
|
||||
"integrity": "sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esbuild": "^0.15.9",
|
||||
"fsevents": "~2.3.2",
|
||||
"postcss": "^8.4.18",
|
||||
"resolve": "^1.22.1",
|
||||
"rollup": "^2.79.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||