Experiment to rebuild Diffuse using web applets.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: input-cache orchestrator

+443 -164
+2
deno.lock
··· 24 24 "npm:98.css@~0.1.21", 25 25 "npm:@atcute/cid@^2.2.2", 26 26 "npm:@picocss/pico@^2.1.1", 27 + "npm:@types/throttle-debounce@^5.0.2", 27 28 "npm:astro-purgecss@^5.2.2", 28 29 "npm:astro-scope@^3.0.1", 29 30 "npm:astro@^5.7.4", 30 31 "npm:iconoir@^7.11.0", 31 32 "npm:idb-keyval@^6.2.1", 33 + "npm:music-metadata@^11.2.3", 32 34 "npm:native-file-system-adapter@^3.0.1", 33 35 "npm:purgecss@^7.0.2", 34 36 "npm:query-string@^9.1.2",
+199 -35
package-lock.json
··· 7 7 "dependencies": { 8 8 "@atcute/cid": "^2.2.2", 9 9 "@picocss/pico": "^2.1.1", 10 - "@web-applets/sdk": "https://gitpkg.vercel.app/unternet-co/web-applets/sdk?feat/child-context&scripts.postinstall=npm%20i%20%40types%2Fnode%20%26%26%20npx%20tsc", 10 + "@web-applets/sdk": "https://gitpkg.vercel.app/unternet-co/web-applets/sdk?tokono.ma/experiment&scripts.postinstall=npm%20i%20%40types%2Fnode%20%26%26%20npx%20tsc", 11 11 "98.css": "^0.1.21", 12 12 "iconoir": "^7.11.0", 13 13 "idb-keyval": "^6.2.1", 14 + "music-metadata": "^11.2.3", 14 15 "native-file-system-adapter": "^3.0.1", 15 16 "query-string": "^9.1.2", 16 17 "spellcaster": "^6.0.0", ··· 20 21 "xxh32": "^2.0.5" 21 22 }, 22 23 "devDependencies": { 24 + "@types/throttle-debounce": "^5.0.2", 23 25 "astro": "^5.7.4", 24 26 "astro-purgecss": "^5.2.2", 25 27 "astro-scope": "^3.0.1", ··· 1710 1712 "tslib": "^2.8.0" 1711 1713 } 1712 1714 }, 1715 + "node_modules/@tokenizer/inflate": { 1716 + "version": "0.2.7", 1717 + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", 1718 + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", 1719 + "license": "MIT", 1720 + "dependencies": { 1721 + "debug": "^4.4.0", 1722 + "fflate": "^0.8.2", 1723 + "token-types": "^6.0.0" 1724 + }, 1725 + "engines": { 1726 + "node": ">=18" 1727 + }, 1728 + "funding": { 1729 + "type": "github", 1730 + "url": "https://github.com/sponsors/Borewit" 1731 + } 1732 + }, 1733 + "node_modules/@tokenizer/token": { 1734 + "version": "0.3.0", 1735 + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", 1736 + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", 1737 + "license": "MIT" 1738 + }, 1713 1739 "node_modules/@types/debug": { 1714 1740 "version": "4.1.12", 1715 1741 "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", ··· 1789 1815 "redux": "^4.0.0" 1790 1816 } 1791 1817 }, 1818 + "node_modules/@types/throttle-debounce": { 1819 + "version": "5.0.2", 1820 + "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-5.0.2.tgz", 1821 + "integrity": "sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==", 1822 + "dev": true, 1823 + "license": "MIT" 1824 + }, 1792 1825 "node_modules/@types/unist": { 1793 1826 "version": "3.0.3", 1794 1827 "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", ··· 1803 1836 }, 1804 1837 "node_modules/@web-applets/sdk": { 1805 1838 "version": "0.2.6", 1806 - "resolved": "https://gitpkg.vercel.app/unternet-co/web-applets/sdk?feat/child-context&scripts.postinstall=npm%20i%20%40types%2Fnode%20%26%26%20npx%20tsc", 1807 - "integrity": "sha512-O7wg0KnyfOtq6WSwDaezoenxi2wDkNDlTk3/R0OLKdcdnJbiv6zWO+HLbyMahSQ2S98V/9AO2pDMHpPPbUgIQg==", 1839 + "resolved": "https://gitpkg.vercel.app/unternet-co/web-applets/sdk?tokono.ma/experiment&scripts.postinstall=npm%20i%20%40types%2Fnode%20%26%26%20npx%20tsc", 1840 + "integrity": "sha512-aXG4XVRuiNm+HolucTHVQt7POcKaV/cYI+qqHDPb7zyJQGnBqm4EypHwl+c53LuOCRkw/+7E3078sECoJEJgEQ==", 1808 1841 "hasInstallScript": true, 1809 1842 "license": "MIT" 1810 1843 }, ··· 2520 2553 "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", 2521 2554 "dev": true 2522 2555 }, 2556 + "node_modules/content-type": { 2557 + "version": "1.0.5", 2558 + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 2559 + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 2560 + "license": "MIT", 2561 + "engines": { 2562 + "node": ">= 0.6" 2563 + } 2564 + }, 2523 2565 "node_modules/cookie": { 2524 2566 "version": "1.0.2", 2525 2567 "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", ··· 2613 2655 "license": "MIT" 2614 2656 }, 2615 2657 "node_modules/debug": { 2616 - "version": "4.4.0", 2617 - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 2618 - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 2658 + "version": "4.4.1", 2659 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", 2660 + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", 2661 + "license": "MIT", 2619 2662 "dependencies": { 2620 2663 "ms": "^2.1.3" 2621 2664 }, ··· 2992 3035 "node": "^12.20 || >= 14.13" 2993 3036 } 2994 3037 }, 3038 + "node_modules/fflate": { 3039 + "version": "0.8.2", 3040 + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", 3041 + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", 3042 + "license": "MIT" 3043 + }, 2995 3044 "node_modules/file-type": { 2996 - "version": "11.1.0", 2997 - "resolved": "https://registry.npmjs.org/file-type/-/file-type-11.1.0.tgz", 2998 - "integrity": "sha512-rM0UO7Qm9K7TWTtA6AShI/t7H5BPjDeGVDaNyg9BjHAj3PysKy7+8C8D137R88jnR3rFJZQB/tFgydl5sN5m7g==", 3045 + "version": "20.5.0", 3046 + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", 3047 + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", 2999 3048 "license": "MIT", 3049 + "dependencies": { 3050 + "@tokenizer/inflate": "^0.2.6", 3051 + "strtok3": "^10.2.0", 3052 + "token-types": "^6.0.0", 3053 + "uint8array-extras": "^1.4.0" 3054 + }, 3000 3055 "engines": { 3001 - "node": ">=6" 3056 + "node": ">=18" 3057 + }, 3058 + "funding": { 3059 + "url": "https://github.com/sindresorhus/file-type?sponsor=1" 3002 3060 } 3003 3061 }, 3004 3062 "node_modules/fill-range": { ··· 4071 4129 "dev": true 4072 4130 }, 4073 4131 "node_modules/media-typer": { 4074 - "version": "0.3.0", 4075 - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 4076 - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 4132 + "version": "1.1.0", 4133 + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", 4134 + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", 4077 4135 "license": "MIT", 4078 4136 "engines": { 4079 - "node": ">= 0.6" 4137 + "node": ">= 0.8" 4080 4138 } 4081 4139 }, 4082 4140 "node_modules/micromark": { ··· 4736 4794 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 4737 4795 }, 4738 4796 "node_modules/music-metadata": { 4739 - "version": "3.8.0", 4740 - "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-3.8.0.tgz", 4741 - "integrity": "sha512-aIADbp3uCS+ANr4nnFEHzTzMy81OT7PR7WBMW73SJ28Y7P94nnEugmTOj1ICP2JmxBBDlo+MeYVgiPnxVN69tg==", 4797 + "version": "11.2.3", 4798 + "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.2.3.tgz", 4799 + "integrity": "sha512-ReVxFoO12kaRiaNmqxkAdytul1Ntl2ersdIyw/CqWPysvOFpUrr19s8uOHEA4xjK69ETmpP71KezXWEE7r5Myg==", 4800 + "funding": [ 4801 + { 4802 + "type": "github", 4803 + "url": "https://github.com/sponsors/Borewit" 4804 + }, 4805 + { 4806 + "type": "buymeacoffee", 4807 + "url": "https://buymeacoffee.com/borewit" 4808 + } 4809 + ], 4742 4810 "license": "MIT", 4743 4811 "dependencies": { 4744 - "debug": "^4.1.0", 4745 - "file-type": "^11.0.0", 4746 - "media-typer": "0.3.0", 4747 - "strtok3": "^2.3.0", 4748 - "token-types": "^1.0.1" 4812 + "@tokenizer/token": "^0.3.0", 4813 + "content-type": "^1.0.5", 4814 + "debug": "^4.4.1", 4815 + "file-type": "^20.5.0", 4816 + "media-typer": "^1.1.0", 4817 + "strtok3": "^10.2.2", 4818 + "token-types": "^6.0.0", 4819 + "uint8array-extras": "^1.4.0" 4749 4820 }, 4750 4821 "engines": { 4751 - "node": "*" 4822 + "node": ">=18" 4752 4823 } 4753 4824 }, 4754 4825 "node_modules/music-metadata-browser": { ··· 4767 4838 "typedarray-to-buffer": "^3.1.5" 4768 4839 } 4769 4840 }, 4841 + "node_modules/music-metadata-browser/node_modules/file-type": { 4842 + "version": "11.1.0", 4843 + "resolved": "https://registry.npmjs.org/file-type/-/file-type-11.1.0.tgz", 4844 + "integrity": "sha512-rM0UO7Qm9K7TWTtA6AShI/t7H5BPjDeGVDaNyg9BjHAj3PysKy7+8C8D137R88jnR3rFJZQB/tFgydl5sN5m7g==", 4845 + "license": "MIT", 4846 + "engines": { 4847 + "node": ">=6" 4848 + } 4849 + }, 4850 + "node_modules/music-metadata-browser/node_modules/media-typer": { 4851 + "version": "0.3.0", 4852 + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 4853 + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 4854 + "license": "MIT", 4855 + "engines": { 4856 + "node": ">= 0.6" 4857 + } 4858 + }, 4859 + "node_modules/music-metadata-browser/node_modules/music-metadata": { 4860 + "version": "3.8.0", 4861 + "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-3.8.0.tgz", 4862 + "integrity": "sha512-aIADbp3uCS+ANr4nnFEHzTzMy81OT7PR7WBMW73SJ28Y7P94nnEugmTOj1ICP2JmxBBDlo+MeYVgiPnxVN69tg==", 4863 + "license": "MIT", 4864 + "dependencies": { 4865 + "debug": "^4.1.0", 4866 + "file-type": "^11.0.0", 4867 + "media-typer": "0.3.0", 4868 + "strtok3": "^2.3.0", 4869 + "token-types": "^1.0.1" 4870 + }, 4871 + "engines": { 4872 + "node": "*" 4873 + } 4874 + }, 4770 4875 "node_modules/music-metadata-browser/node_modules/readable-stream": { 4771 4876 "version": "3.6.2", 4772 4877 "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", ··· 4779 4884 }, 4780 4885 "engines": { 4781 4886 "node": ">= 6" 4887 + } 4888 + }, 4889 + "node_modules/music-metadata-browser/node_modules/strtok3": { 4890 + "version": "2.3.0", 4891 + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-2.3.0.tgz", 4892 + "integrity": "sha512-AA67/1atBh7X0fUTDevjW89by2ZkY9RZAnkwusx5Yc1COYf0ruUbpYOOIs03SnRA1CF9K3+BtRXKOEtKhAXVaQ==", 4893 + "license": "MIT", 4894 + "dependencies": { 4895 + "debug": "^4.1.0", 4896 + "then-read-stream": "^1.5.0", 4897 + "token-types": "^1.0.1" 4898 + }, 4899 + "engines": { 4900 + "node": ">=0.1.98" 4901 + } 4902 + }, 4903 + "node_modules/music-metadata-browser/node_modules/token-types": { 4904 + "version": "1.3.2", 4905 + "resolved": "https://registry.npmjs.org/token-types/-/token-types-1.3.2.tgz", 4906 + "integrity": "sha512-LemYprKRfZPUiwVEMIL8fIP/cvZBpMds1PklsyoQyLZdKk7SQlldNGzw4TTrg2MnWLGSkMM6gUa1EW0h1d72fg==", 4907 + "license": "MIT", 4908 + "dependencies": { 4909 + "ieee754": "^1.1.13" 4910 + }, 4911 + "engines": { 4912 + "node": ">=0.1.98" 4782 4913 } 4783 4914 }, 4784 4915 "node_modules/nanoid": { ··· 5106 5237 "dev": true, 5107 5238 "engines": { 5108 5239 "node": "20 || >=22" 5240 + } 5241 + }, 5242 + "node_modules/peek-readable": { 5243 + "version": "7.0.0", 5244 + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz", 5245 + "integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==", 5246 + "license": "MIT", 5247 + "engines": { 5248 + "node": ">=18" 5249 + }, 5250 + "funding": { 5251 + "type": "github", 5252 + "url": "https://github.com/sponsors/Borewit" 5109 5253 } 5110 5254 }, 5111 5255 "node_modules/picocolors": { ··· 6041 6185 } 6042 6186 }, 6043 6187 "node_modules/strtok3": { 6044 - "version": "2.3.0", 6045 - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-2.3.0.tgz", 6046 - "integrity": "sha512-AA67/1atBh7X0fUTDevjW89by2ZkY9RZAnkwusx5Yc1COYf0ruUbpYOOIs03SnRA1CF9K3+BtRXKOEtKhAXVaQ==", 6188 + "version": "10.2.2", 6189 + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz", 6190 + "integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==", 6047 6191 "license": "MIT", 6048 6192 "dependencies": { 6049 - "debug": "^4.1.0", 6050 - "then-read-stream": "^1.5.0", 6051 - "token-types": "^1.0.1" 6193 + "@tokenizer/token": "^0.3.0", 6194 + "peek-readable": "^7.0.0" 6052 6195 }, 6053 6196 "engines": { 6054 - "node": ">=0.1.98" 6197 + "node": ">=18" 6198 + }, 6199 + "funding": { 6200 + "type": "github", 6201 + "url": "https://github.com/sponsors/Borewit" 6055 6202 } 6056 6203 }, 6057 6204 "node_modules/then-read-stream": { ··· 6120 6267 } 6121 6268 }, 6122 6269 "node_modules/token-types": { 6123 - "version": "1.3.2", 6124 - "resolved": "https://registry.npmjs.org/token-types/-/token-types-1.3.2.tgz", 6125 - "integrity": "sha512-LemYprKRfZPUiwVEMIL8fIP/cvZBpMds1PklsyoQyLZdKk7SQlldNGzw4TTrg2MnWLGSkMM6gUa1EW0h1d72fg==", 6270 + "version": "6.0.0", 6271 + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", 6272 + "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", 6126 6273 "license": "MIT", 6127 6274 "dependencies": { 6128 - "ieee754": "^1.1.13" 6275 + "@tokenizer/token": "^0.3.0", 6276 + "ieee754": "^1.2.1" 6129 6277 }, 6130 6278 "engines": { 6131 - "node": ">=0.1.98" 6279 + "node": ">=14.16" 6280 + }, 6281 + "funding": { 6282 + "type": "github", 6283 + "url": "https://github.com/sponsors/Borewit" 6132 6284 } 6133 6285 }, 6134 6286 "node_modules/tr46": { ··· 6232 6384 "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", 6233 6385 "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", 6234 6386 "dev": true 6387 + }, 6388 + "node_modules/uint8array-extras": { 6389 + "version": "1.4.0", 6390 + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", 6391 + "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", 6392 + "license": "MIT", 6393 + "engines": { 6394 + "node": ">=18" 6395 + }, 6396 + "funding": { 6397 + "url": "https://github.com/sponsors/sindresorhus" 6398 + } 6235 6399 }, 6236 6400 "node_modules/ultrahtml": { 6237 6401 "version": "1.6.0",
+3 -1
package.json
··· 2 2 "dependencies": { 3 3 "@atcute/cid": "^2.2.2", 4 4 "@picocss/pico": "^2.1.1", 5 - "@web-applets/sdk": "https://gitpkg.vercel.app/unternet-co/web-applets/sdk?feat/child-context&scripts.postinstall=npm%20i%20%40types%2Fnode%20%26%26%20npx%20tsc", 5 + "@web-applets/sdk": "https://gitpkg.vercel.app/unternet-co/web-applets/sdk?tokono.ma/experiment&scripts.postinstall=npm%20i%20%40types%2Fnode%20%26%26%20npx%20tsc", 6 6 "98.css": "^0.1.21", 7 7 "iconoir": "^7.11.0", 8 8 "idb-keyval": "^6.2.1", 9 + "music-metadata": "^11.2.3", 9 10 "native-file-system-adapter": "^3.0.1", 10 11 "query-string": "^9.1.2", 11 12 "spellcaster": "^6.0.0", ··· 15 16 "xxh32": "^2.0.5" 16 17 }, 17 18 "devDependencies": { 19 + "@types/throttle-debounce": "^5.0.2", 18 20 "astro": "^5.7.4", 19 21 "astro-purgecss": "^5.2.2", 20 22 "astro-scope": "^3.0.1",
+4 -13
src/pages/configurator/input/_applet.astro
··· 32 32 <script> 33 33 import { applets } from "@web-applets/sdk"; 34 34 35 - import type { Output, Track } from "@applets/core/types.d.ts"; 35 + import type { Track } from "@applets/core/types.d.ts"; 36 36 import { applet } from "@scripts/theme"; 37 37 38 38 //////////////////////////////////////////// ··· 46 46 }; 47 47 48 48 // Register applet 49 - const context = applets.register<Output>(); 49 + const context = applets.register<Track[]>(); 50 50 51 51 //////////////////////////////////////////// 52 52 // ACTIONS ··· 67 67 async ([scheme, cachedTracksGroup]: [string, Track[]]) => { 68 68 switch (scheme) { 69 69 case input.nativeFs.manifest.input_properties.scheme: 70 - await input.nativeFs.sendAction("list", cachedTracksGroup); 71 - return input.nativeFs.data; 70 + return await input.nativeFs.sendAction("list", cachedTracksGroup); 72 71 73 72 default: 74 73 return cachedTracks; ··· 79 78 const nested = await Promise.all(promises); 80 79 const tracks = nested.flat(1); 81 80 82 - // TODO: Ideally promise returns data 83 - context.data = tracks as any; 84 - 85 81 return tracks; 86 82 }; 87 83 ··· 90 86 91 87 switch (scheme) { 92 88 case input.nativeFs.manifest.input_properties.scheme: 93 - await input.nativeFs.sendAction("resolve", fileUri); 94 - // TODO: Ideally promise returns data 95 - context.data = input.nativeFs.data as any; 96 - return input.nativeFs.data; 89 + return await input.nativeFs.sendAction("resolve", fileUri); 97 90 98 91 default: 99 - // TODO: Ideally promise returns data 100 - context.data = undefined as any; 101 92 return undefined; 102 93 } 103 94 };
+4 -9
src/pages/configurator/input/_manifest.json
··· 7 7 "title": "List", 8 8 "description": "List tracks from all inputs.", 9 9 "params_schema": { 10 - "type": "object", 11 - "properties": { 12 - "tracks": { 13 - "type": "array", 14 - "description": "A list of (cached) tracks", 15 - "items": { 16 - "type": "object" 17 - } 18 - } 10 + "type": "array", 11 + "description": "A list of (cached) tracks", 12 + "items": { 13 + "type": "object" 19 14 } 20 15 } 21 16 },
+17 -22
src/pages/configurator/output/_applet.astro
··· 63 63 //////////////////////////////////////////// 64 64 // SETUP 65 65 //////////////////////////////////////////// 66 - const context = applets.register(); 66 + const context = applets.register<{ ready: boolean }>(); 67 67 68 68 const container = document.createElement("div"); 69 69 container.id = "iframes"; ··· 87 87 }; 88 88 89 89 // Initial state 90 - context.data = undefined; 90 + context.data = { ready: false }; 91 91 92 92 // Signals 93 93 const stored = localStorage.getItem(LOCALSTORAGE_KEY); ··· 338 338 const get: OutputGetter = async (args) => { 339 339 let data: Uint8Array | undefined; 340 340 341 + console.log("------>", active()); 342 + 341 343 switch (active()) { 342 344 case "browser": { 343 - // TODO: Ideally promise returns data 344 - await storage.output.indexedDB.sendAction("get", args); 345 - data = storage.output.indexedDB.data as Uint8Array | undefined; 346 - break; 345 + return await storage.output.indexedDB.sendAction<Uint8Array | undefined>("get", args); 347 346 } 348 347 case "custom": 349 - // TODO: Ideally promise returns data 350 348 const a = await connectToCustomApplet(); 351 - await a.sendAction("get", args); 352 - data = a.data as Uint8Array | undefined; 353 - break; 349 + return await a.sendAction<Uint8Array | undefined>("get", args); 354 350 case "device": { 355 - // TODO: Ideally promise returns data 356 - await storage.output.nativeFs.sendAction("get", args); 357 - data = storage.output.nativeFs.data as Uint8Array | undefined; 358 - break; 351 + return await storage.output.nativeFs.sendAction<Uint8Array | undefined>("get", args); 352 + } 353 + default: { 354 + return undefined; 359 355 } 360 356 } 361 - 362 - if (data) { 363 - context.data = data; 364 - } else { 365 - context.data = undefined; 366 - } 367 - 368 - return data; 369 357 }; 370 358 371 359 const put: OutputSetter = async (args) => { ··· 383 371 } 384 372 }; 385 373 374 + console.log("BIND GET"); 375 + 386 376 context.setActionHandler("get", get); 387 377 context.setActionHandler("put", put); 378 + 379 + //////////////////////////////////////////// 380 + // 🚦 381 + //////////////////////////////////////////// 382 + context.data = { ready: true }; 388 383 </script>
+7 -1
src/pages/core/types.d.ts
··· 9 9 10 10 /* TRACKS */ 11 11 12 - export interface Track<Tags = TrackTags> { 12 + export interface Track<Tags = TrackTags, Stats = TrackStats> { 13 13 id: string; 14 + 15 + stats?: Stats; 14 16 tags?: Tags; 15 17 16 18 // NOTE: This is a "semi-permanent" URI. ··· 18 20 // Tracks are cached so you can't, for example, 19 21 // use an URL that expires in several hours. 20 22 uri: string; 23 + } 24 + 25 + export interface TrackStats { 26 + duration?: number; 21 27 } 22 28 23 29 export interface TrackTags {
+3 -3
src/pages/engine/audio/_applet.astro
··· 31 31 update({ 32 32 ...context.data, 33 33 items: { 34 - ...context.data.items, 35 - [trackId]: { ...context.data.items[trackId], ...partial }, 34 + ...(context.data?.items || {}), 35 + [trackId]: { ...(context.data?.items?.[trackId] || {}), ...partial }, 36 36 }, 37 37 }); 38 38 } ··· 156 156 const items = tracks.reduce((acc, track) => { 157 157 return { 158 158 ...acc, 159 - [track.id]: context.data.items[track.id] || { 159 + [track.id]: context.data?.items?.[track.id] || { 160 160 duration: 0, 161 161 id: track.id, 162 162 loadingState: "loading",
+1 -8
src/pages/input/native-fs/_applet.astro
··· 109 109 110 110 const list = async (cachedTracks: Track[] = []) => { 111 111 if (!isSupported()) { 112 - // TODO 113 - context.data = cachedTracks; 114 112 return cachedTracks; 115 113 } 116 114 ··· 160 158 return 0; 161 159 }); 162 160 163 - // TODO: Should be able to just return the data in the handler 164 - context.data = data; 161 + // Fin 165 162 return data; 166 163 }; 167 164 168 165 const resolve = async (fileUri: string) => { 169 166 if (!isSupported()) { 170 - // TODO 171 - context.data = undefined; 172 167 return undefined; 173 168 } 174 169 ··· 201 196 const file = await fileHandle.getFile(); 202 197 const url = URL.createObjectURL(file); 203 198 204 - // TODO: Should be able to just return the data in the handler 205 - context.data = url; 206 199 return url; 207 200 }; 208 201
+4 -9
src/pages/input/native-fs/_manifest.json
··· 18 18 "title": "List", 19 19 "description": "List tracks.", 20 20 "params_schema": { 21 - "type": "object", 22 - "properties": { 23 - "tracks": { 24 - "type": "array", 25 - "description": "A list of (cached) tracks with an uri matching the scheme", 26 - "items": { 27 - "type": "object" 28 - } 29 - } 21 + "type": "array", 22 + "description": "A list of (cached) tracks with an uri matching the scheme", 23 + "items": { 24 + "type": "object" 30 25 } 31 26 } 32 27 },
+57 -5
src/pages/orchestrator/input-cache/_applet.astro
··· 1 1 <script> 2 2 import { applets } from "@web-applets/sdk"; 3 3 4 - import type { Output, Track } from "@applets/core/types.d.ts"; 4 + import type { Output, Track, TrackStats, TrackTags } from "@applets/core/types.d.ts"; 5 5 import { applet } from "@scripts/theme"; 6 6 7 7 //////////////////////////////////////////// ··· 11 11 const context = applets.register<Output>(); 12 12 13 13 // Applet connections 14 + const configurator = { 15 + input: await applet("../../configurator/input", { context: self.parent }), 16 + }; 17 + 14 18 const orchestrator = { 15 - output: await applet("../../configurator/output", { context: self.parent }), 19 + output: await applet<Output>("../../orchestrator/output-management", { context: self.parent }), 16 20 }; 17 21 22 + const processor = { 23 + metadataFetcher: await applet("../../processor/metadata-fetcher", { context: self.parent }), 24 + }; 25 + 26 + console.log("🔮", orchestrator.output.data); 27 + 28 + orchestrator.output.ondata = () => console.log("🚀", orchestrator.output.data); 29 + 30 + orchestrator.output.addEventListener( 31 + "data", 32 + () => { 33 + console.log("BEFORE", orchestrator.output.data.tracks); 34 + processInputs(); 35 + }, 36 + { once: true }, 37 + ); 38 + 18 39 //////////////////////////////////////////// 19 40 // ACTIONS 20 41 //////////////////////////////////////////// 42 + async function processInputs() { 43 + const cachedTracks = orchestrator.output.data.tracks; 44 + const tracks = await configurator.input.sendAction<Track[]>("list", cachedTracks, { 45 + timeoutDuration: Infinity, 46 + }); 21 47 22 - //////////////////////////////////////////// 23 - // 🛠️ 24 - //////////////////////////////////////////// 48 + // Process 49 + tracks.reduce(async (promise: Promise<Track[]>, track: Track) => { 50 + const acc = await promise; 51 + if (track.tags) return [...acc, track]; 52 + 53 + await configurator.input.sendAction("resolve", track.uri); 54 + const url = configurator.input.data as string | undefined; 55 + 56 + await processor.metadataFetcher.sendAction("extract", url); 57 + const meta: any = processor.metadataFetcher.data; 58 + 59 + const stats: TrackStats = { 60 + duration: meta.format.duration, 61 + }; 62 + 63 + const tags: TrackTags = { 64 + album: meta.common.album, 65 + artist: meta.common.artist, 66 + title: meta.common.title, 67 + }; 68 + 69 + return [...acc, { ...track, stats, tags }]; 70 + }, Promise.resolve([])); 71 + 72 + console.log("AFTER", tracks); 73 + 74 + // Save 75 + await orchestrator.output.sendAction("saveTracks", tracks); 76 + } 25 77 </script>
+32 -11
src/pages/orchestrator/output-management/_applet.astro
··· 1 1 <!-- TODO: Button to export all user/output data. --><!-- TODO: Button to import data? --> 2 2 <script> 3 3 import { applets } from "@web-applets/sdk"; 4 + import { debounce } from "throttle-debounce"; 4 5 5 6 import type { Output, Track } from "@applets/core/types.d.ts"; 6 - import { applet } from "@scripts/theme"; 7 + import { applet, waitUntilAppletIsReady } from "@scripts/theme"; 7 8 8 9 //////////////////////////////////////////// 9 10 // SETUP ··· 39 40 tracks: await loadTracks(), 40 41 }; 41 42 43 + // State helpers 44 + function update(partial: Partial<Output>): void { 45 + context.data = { ...context.data, ...partial }; 46 + } 47 + 42 48 //////////////////////////////////////////// 43 49 // LOADERS 44 50 //////////////////////////////////////////// 45 51 async function loadTracks(): Promise<Track[]> { 52 + console.log("🚧🚧🚧 LOADING TRACKS"); 53 + 54 + await waitUntilAppletIsReady(configurator.output); 55 + 46 56 // TODO: This is not concurrency safe! 47 - await configurator.output.sendAction("get", { 48 - name: "tracks.json", 49 - }); 57 + await configurator.output.sendAction( 58 + "get", 59 + { 60 + name: "tracks.json", 61 + }, 62 + { 63 + timeoutDuration: 60000, 64 + }, 65 + ); 50 66 51 67 const data = configurator.output.data; 52 68 69 + console.log("🥝🥝🥝 TRACKS LOADED", data); 70 + 53 71 if (!data) { 54 - saveTracks(SAMPLE_TRACKS); 55 - return SAMPLE_TRACKS; 72 + return []; 56 73 } 57 74 58 75 return decode(data as Uint8Array); ··· 61 78 //////////////////////////////////////////// 62 79 // ACTIONS 63 80 //////////////////////////////////////////// 81 + const saveTracks = (tracks: Track[]) => { 82 + update({ tracks }); 83 + saveTracksToOutput(tracks); 84 + }; 64 85 65 - // UPDATE TRACKS: addTracks → SAVE_TRACKS 66 - 67 - function saveTracks(tracks: Track[]) { 86 + const saveTracksToOutput = debounce(5000, async function (tracks: Track[]) { 68 87 const data = encode(tracks); 69 88 70 - configurator.output.sendAction("put", { 89 + await configurator.output.sendAction("put", { 71 90 name: "tracks.json", 72 91 data, 73 92 }); 74 - } 93 + }); 94 + 95 + context.setActionHandler("saveTracks", saveTracks); 75 96 76 97 //////////////////////////////////////////// 77 98 // 🛠️
+5 -1
src/pages/orchestrator/output-management/_manifest.json
··· 2 2 "name": "diffuse/orchestrator/output-management", 3 3 "title": "Diffuse Orchestrator | Output management", 4 4 "entrypoint": "index.html", 5 - "actions": {} 5 + "actions": { 6 + "saveTracks": { 7 + "description": "Save tracks to output." 8 + } 9 + } 6 10 }
-1
src/pages/output/indexed-db/_applet.astro
··· 22 22 }; 23 23 24 24 const mount = async () => {}; 25 - 26 25 const unmount = async () => {}; 27 26 28 27 context.setActionHandler("get", get);
-3
src/pages/output/native-fs/_applet.astro
··· 24 24 const fileHandle = await handle.getFileHandle(name); 25 25 const file = await fileHandle.getFile(); 26 26 const data = await file.bytes(); 27 - // TODO: Should be able to just return the data in the handler 28 - context.data = data; 29 27 return data; 30 28 } catch (err) { 31 - context.data = undefined; 32 29 return undefined; 33 30 } 34 31 };
+22
src/pages/processor/metadata-fetcher/_applet.astro
··· 1 + <script> 2 + import { applets } from "@web-applets/sdk"; 3 + import { parseWebStream } from "music-metadata"; 4 + 5 + //////////////////////////////////////////// 6 + // SETUP 7 + //////////////////////////////////////////// 8 + const context = applets.register(); 9 + 10 + //////////////////////////////////////////// 11 + // ACTIONS 12 + //////////////////////////////////////////// 13 + context.setActionHandler("extract", extract); 14 + 15 + async function extract(url: string) { 16 + const resp = await fetch(url); 17 + const stream = resp.body; 18 + const metadata = await parseWebStream(stream); 19 + 20 + return metadata; 21 + } 22 + </script>
+15
src/pages/processor/metadata-fetcher/_manifest.json
··· 1 + { 2 + "name": "diffuse/processor/metadata-fetcher", 3 + "title": "Diffuse Processor | Metadata fetcher", 4 + "entrypoint": "index.html", 5 + "actions": { 6 + "extract": { 7 + "title": "Extract", 8 + "description": "Get the metadata for a given URL.", 9 + "params_schema": { 10 + "type": "string", 11 + "description": "URL" 12 + } 13 + } 14 + } 15 + }
+9
src/pages/processor/metadata-fetcher/index.astro
··· 1 + --- 2 + import Layout from "@layouts/applet.astro"; 3 + import Applet from "./_applet.astro"; 4 + import { title } from "./_manifest.json"; 5 + --- 6 + 7 + <Layout title={title}> 8 + <Applet /> 9 + </Layout>
+18
src/scripts/theme.ts
··· 126 126 export function isPrimitive(test: unknown) { 127 127 return test !== Object(test); 128 128 } 129 + 130 + export function waitUntilAppletIsReady(applet: Applet): Promise<void> { 131 + return new Promise((resolve) => { 132 + if (applet.data?.ready === true) { 133 + resolve(); 134 + return; 135 + } 136 + 137 + const callback = (event: AppletEvent) => { 138 + if (event.data?.ready === true) { 139 + applet.removeEventListener("data", callback); 140 + resolve(); 141 + } 142 + }; 143 + 144 + applet.addEventListener("data", callback); 145 + }); 146 + }
+2 -28
src/scripts/themes/pilot/index.ts
··· 28 28 }; 29 29 30 30 const _orchestrator = { 31 + input: await applet<Output>("../../orchestrator/input-cache"), 32 + output: await applet<Output>("../../orchestrator/output-management"), 31 33 queue: await applet("../../orchestrator/single-queue"), 32 - output: await applet<Output>("../../orchestrator/output-management"), 33 34 }; 34 35 35 36 const ui = { ··· 95 96 } 96 97 }, 97 98 ); 98 - 99 - // TODO: 100 - document.onclick = async () => { 101 - await input.nativeFs.sendAction("mount"); 102 - await input.nativeFs.sendAction("list"); 103 - const list = input.nativeFs.data as Track[]; 104 - console.log(list); 105 - await input.nativeFs.sendAction("resolve", list[0].uri); 106 - const url = input.nativeFs.data; 107 - console.log(url); 108 - 109 - // const id = crypto.randomUUID(); 110 - // 111 - // await engine.audio.sendAction("render", { 112 - // tracks: [ 113 - // { 114 - // id, 115 - // isPreload: false, 116 - // url, 117 - // }, 118 - // ], 119 - // play: { 120 - // trackId: id, 121 - // volume: 0.5, 122 - // }, 123 - // }); 124 - };
+39 -14
src/scripts/themes/webamp/index.ts
··· 1 1 import Webamp from "webamp"; 2 + import { URLTrack } from "webamp"; 2 3 3 - import type { Track } from "@applets/core/types.d.ts"; 4 + import type { Output, Track } from "@applets/core/types.d.ts"; 4 5 import { applet } from "../../theme.ts"; 5 6 6 7 //////////////////////////////////////////// ··· 16 17 input: await applet("../../configurator/input"), 17 18 }; 18 19 19 - // const orchestrator = { 20 - // output: await applet<Output>("../../orchestrator/output-management"), 21 - // }; 20 + const orchestrator = { 21 + output: await applet<Output>("../../orchestrator/output-management"), 22 + }; 23 + 24 + // setTimeout(async () => { 25 + // await applet("../../orchestrator/input-cache"); 26 + // }, 15000); 27 + 28 + // await applet("../../orchestrator/input-cache"); 22 29 23 30 //////////////////////////////////////////// 24 31 // ⚡ 25 32 //////////////////////////////////////////// 26 33 const amp = new Webamp({ 27 - initialTracks: await makeList(), 34 + initialTracks: [], 28 35 }); 29 36 30 37 const ampNode = document.createElement("div"); ··· 32 39 document.body.appendChild(ampNode); 33 40 amp.renderWhenReady(ampNode); 34 41 42 + // orchestrator.output.ondata = async () => { 43 + // console.log("DATA CHANGED"); 44 + // const tracks = await loadTracks(); 45 + // amp.setTracksToPlay(tracks); 46 + // }; 47 + 35 48 //////////////////////////////////////////// 36 49 // 🛠️ 37 50 //////////////////////////////////////////// 51 + async function loadTracks(): Promise<URLTrack[]> { 52 + return await orchestrator.output.data.tracks.reduce( 53 + async (promise: Promise<URLTrack[]>, track: Track) => { 54 + const acc = await promise; 38 55 39 - async function makeList() { 40 - await configurator.input.sendAction("list", []); 41 - const list = configurator.input.data as Track[]; 56 + await configurator.input.sendAction("resolve", track.uri); 57 + const url = configurator.input.data as string | undefined; 58 + if (!url) return acc; 42 59 43 - return list.reduce(async (acc: Promise<Array<{ url: string }>>, track: Track) => { 44 - await configurator.input.sendAction("resolve", track.uri); 45 - const url = configurator.input.data as string | undefined; 46 - if (url) return [...(await acc), { url }]; 47 - return await acc; 48 - }, Promise.resolve([])); 60 + const urlTrack: URLTrack = { 61 + url, 62 + metaData: { 63 + title: track.tags?.title || "", 64 + artist: track.tags?.artist || "", 65 + album: track.tags?.album, 66 + }, 67 + duration: track.stats?.duration, 68 + }; 69 + 70 + return [...acc, urlTrack]; 71 + }, 72 + Promise.resolve([]), 73 + ); 49 74 }