diff --git a/apps/desktop/assets/emojis.zip b/apps/desktop/assets/emojis.zip new file mode 100644 index 00000000..c4f06491 Binary files /dev/null and b/apps/desktop/assets/emojis.zip differ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a52577f1..1e8b0100 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -521,6 +521,28 @@ importers: specifier: ^1.6.0 version: 1.6.0(@types/node@22.9.0) + scripts: + dependencies: + '@emoji-mart/data': + specifier: ^1.2.1 + version: 1.2.1 + archiver: + specifier: ^7.0.1 + version: 7.0.1 + ulid: + specifier: ^2.3.0 + version: 2.3.0 + devDependencies: + '@types/archiver': + specifier: ^6.0.3 + version: 6.0.3 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@22.9.0)(typescript@5.6.3) + typescript: + specifier: ^5.6.3 + version: 5.6.3 + packages: 7zip-bin@5.2.0: @@ -861,6 +883,9 @@ packages: '@emnapi/runtime@1.3.1': resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@emoji-mart/data@1.2.1': + resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==} + '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} @@ -2417,6 +2442,9 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@types/archiver@6.0.3': + resolution: {integrity: sha512-a6wUll6k3zX6qs5KlxIggs1P1JcYJaTCx2gnlr+f0S1yd2DoaEwoIK10HmBaLnZwWneBz+JBm0dwcZu0zECBcQ==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -2543,6 +2571,9 @@ packages: '@types/react@18.3.12': resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} + '@types/readdir-glob@1.1.5': + resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==} + '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} @@ -2717,6 +2748,10 @@ packages: abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -2798,10 +2833,18 @@ packages: resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} engines: {node: '>= 10'} + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + archiver@5.3.2: resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} engines: {node: '>= 10'} + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + are-we-there-yet@2.0.0: resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} engines: {node: '>=10'} @@ -2898,9 +2941,15 @@ packages: axios@1.7.7: resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} + b4a@1.6.7: + resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + bare-events@2.5.0: + resolution: {integrity: sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -2956,6 +3005,10 @@ packages: buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + buffer-equal@1.0.1: resolution: {integrity: sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==} engines: {node: '>=0.4'} @@ -2966,6 +3019,9 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + builder-util-runtime@9.2.10: resolution: {integrity: sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==} engines: {node: '>=12.0.0'} @@ -3124,6 +3180,10 @@ packages: resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} engines: {node: '>= 10'} + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -3181,6 +3241,10 @@ packages: resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} engines: {node: '>= 10'} + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + crc@3.8.0: resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==} @@ -3604,6 +3668,14 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -3631,6 +3703,9 @@ packages: fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -4074,6 +4149,10 @@ packages: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4909,6 +4988,10 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -5006,6 +5089,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + quick-lru@5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} @@ -5156,6 +5242,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdir-glob@1.1.3: resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} @@ -5404,6 +5494,9 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} + streamx@2.20.1: + resolution: {integrity: sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -5511,6 +5604,9 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -5518,6 +5614,9 @@ packages: temp-file@3.4.0: resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} + text-decoder@1.2.1: + resolution: {integrity: sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -6006,6 +6105,10 @@ packages: resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} engines: {node: '>= 10'} + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} + zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} @@ -6665,7 +6768,6 @@ snapshots: '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - optional: true '@develar/schema-utils@2.6.5': dependencies: @@ -6765,6 +6867,8 @@ snapshots: tslib: 2.8.1 optional: true + '@emoji-mart/data@1.2.1': {} + '@esbuild/aix-ppc64@0.21.5': optional: true @@ -7073,7 +7177,6 @@ snapshots: dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - optional: true '@malept/cross-spawn-promise@1.1.1': dependencies: @@ -8231,17 +8334,17 @@ snapshots: '@tootallnate/once@2.0.0': {} - '@tsconfig/node10@1.0.11': - optional: true + '@tsconfig/node10@1.0.11': {} - '@tsconfig/node12@1.0.11': - optional: true + '@tsconfig/node12@1.0.11': {} - '@tsconfig/node14@1.0.3': - optional: true + '@tsconfig/node14@1.0.3': {} - '@tsconfig/node16@1.0.4': - optional: true + '@tsconfig/node16@1.0.4': {} + + '@types/archiver@6.0.3': + dependencies: + '@types/readdir-glob': 1.1.5 '@types/babel__core@7.20.5': dependencies: @@ -8400,6 +8503,10 @@ snapshots: '@types/prop-types': 15.7.13 csstype: 3.1.3 + '@types/readdir-glob@1.1.5': + dependencies: + '@types/node': 22.9.0 + '@types/responselike@1.0.3': dependencies: '@types/node': 22.9.0 @@ -8644,6 +8751,10 @@ snapshots: abbrev@1.1.1: {} + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -8761,6 +8872,16 @@ snapshots: normalize-path: 3.0.0 readable-stream: 3.6.2 + archiver-utils@5.0.2: + dependencies: + glob: 10.4.5 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.17.21 + normalize-path: 3.0.0 + readable-stream: 4.5.2 + archiver@5.3.2: dependencies: archiver-utils: 2.1.0 @@ -8771,13 +8892,22 @@ snapshots: tar-stream: 2.2.0 zip-stream: 4.1.1 + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.5.2 + readdir-glob: 1.1.3 + tar-stream: 3.1.7 + zip-stream: 6.0.1 + are-we-there-yet@2.0.0: dependencies: delegates: 1.0.0 readable-stream: 3.6.2 - arg@4.1.3: - optional: true + arg@4.1.3: {} arg@5.0.2: {} @@ -8894,8 +9024,13 @@ snapshots: transitivePeerDependencies: - debug + b4a@1.6.7: {} + balanced-match@1.0.2: {} + bare-events@2.5.0: + optional: true + base64-js@1.5.1: {} bcrypt@5.1.1(encoding@0.1.13): @@ -8973,6 +9108,8 @@ snapshots: buffer-crc32@0.2.13: {} + buffer-crc32@1.0.0: {} + buffer-equal@1.0.1: {} buffer-from@1.1.2: {} @@ -8982,6 +9119,11 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + builder-util-runtime@9.2.10: dependencies: debug: 4.3.7 @@ -9184,6 +9326,14 @@ snapshots: normalize-path: 3.0.0 readable-stream: 3.6.2 + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.5.2 + concat-map@0.0.1: {} concat-stream@1.6.2: @@ -9233,13 +9383,17 @@ snapshots: crc-32: 1.2.2 readable-stream: 3.6.2 + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.5.2 + crc@3.8.0: dependencies: buffer: 5.7.1 optional: true - create-require@1.1.1: - optional: true + create-require@1.1.1: {} crelt@1.0.6: {} @@ -9348,8 +9502,7 @@ snapshots: diff-sequences@29.6.3: {} - diff@4.0.2: - optional: true + diff@4.0.2: {} diff@7.0.0: {} @@ -9833,6 +9986,10 @@ snapshots: etag@1.8.1: {} + event-target-shim@5.0.1: {} + + events@3.3.0: {} + execa@8.0.1: dependencies: cross-spawn: 7.0.3 @@ -9900,6 +10057,8 @@ snapshots: fast-diff@1.3.0: {} + fast-fifo@1.3.2: {} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -10372,6 +10531,8 @@ snapshots: dependencies: call-bind: 1.0.7 + is-stream@2.0.1: {} + is-stream@3.0.0: {} is-string@1.0.7: @@ -10590,8 +10751,7 @@ snapshots: dependencies: semver: 6.3.1 - make-error@1.3.6: - optional: true + make-error@1.3.6: {} markdown-it@14.1.0: dependencies: @@ -11076,6 +11236,8 @@ snapshots: process-nextick-args@2.0.1: {} + process@0.11.10: {} + progress@2.0.3: {} promise-retry@2.0.1: @@ -11216,6 +11378,8 @@ snapshots: queue-microtask@1.2.3: {} + queue-tick@1.0.1: {} + quick-lru@5.1.1: {} range-parser@1.2.1: {} @@ -11367,6 +11531,14 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readable-stream@4.5.2: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + readdir-glob@1.1.3: dependencies: minimatch: 5.1.6 @@ -11678,6 +11850,14 @@ snapshots: streamsearch@1.1.0: {} + streamx@2.20.1: + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + text-decoder: 1.2.1 + optionalDependencies: + bare-events: 2.5.0 + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -11838,6 +12018,12 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + tar-stream@3.1.7: + dependencies: + b4a: 1.6.7 + fast-fifo: 1.3.2 + streamx: 2.20.1 + tar@6.2.1: dependencies: chownr: 2.0.0 @@ -11852,6 +12038,8 @@ snapshots: async-exit-hook: 2.0.1 fs-extra: 10.1.0 + text-decoder@1.2.1: {} + text-table@0.2.0: {} thenify-all@1.6.0: @@ -11932,6 +12120,24 @@ snapshots: yn: 3.1.1 optional: true + ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.9.0 + acorn: 8.14.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.6.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + ts-pattern@5.5.0: {} tsconfig-paths@3.15.0: @@ -12120,8 +12326,7 @@ snapshots: uuid@9.0.1: {} - v8-compile-cache-lib@3.0.1: - optional: true + v8-compile-cache-lib@3.0.1: {} vary@1.1.2: {} @@ -12329,8 +12534,7 @@ snapshots: dependencies: lib0: 0.2.98 - yn@3.1.1: - optional: true + yn@3.1.1: {} yocto-queue@0.1.0: {} @@ -12342,4 +12546,10 @@ snapshots: compress-commons: 4.1.2 readable-stream: 3.6.2 + zip-stream@6.0.1: + dependencies: + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.5.2 + zod@3.23.8: {} diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 00000000..70970139 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,19 @@ +{ + "name": "@colanode/scripts", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "generate:emojis": "node --loader ts-node/esm src/generate-emojis.ts" + }, + "devDependencies": { + "@types/archiver": "^6.0.3", + "ts-node": "^10.9.2", + "typescript": "^5.6.3" + }, + "dependencies": { + "@emoji-mart/data": "^1.2.1", + "archiver": "^7.0.1", + "ulid": "^2.3.0" + } +} diff --git a/scripts/src/generate-emojis.ts b/scripts/src/generate-emojis.ts new file mode 100644 index 00000000..524b7283 --- /dev/null +++ b/scripts/src/generate-emojis.ts @@ -0,0 +1,213 @@ +import fs from 'fs'; +import path from 'path'; +import { EmojiMartData } from '@emoji-mart/data'; +import { monotonicFactory } from 'ulid'; +import archiver from 'archiver'; + +const ulid = monotonicFactory(); + +type EmojiMartI18n = { + categories: Record; +}; + +const emojiMartDataPath = 'node_modules/@emoji-mart/data/sets/15/twitter.json'; +const emojiMartData = JSON.parse( + fs.readFileSync(emojiMartDataPath, 'utf-8') +) as EmojiMartData; + +const i18nPath = 'node_modules/@emoji-mart/data/i18n/en.json'; +const i18nData = JSON.parse( + fs.readFileSync(i18nPath, 'utf-8') +) as EmojiMartI18n; + +const EMOJIS_DIR_PATH = 'emojis'; +const EMOJIS_METADATA_FILE_PATH = 'emojis/emojis.json'; +const ZIP_FILE_PATH = 'emojis.zip'; + +type EmojiMetadata = { + categories: EmojiCategory[]; + emojis: Record; +}; + +type Emoji = { + id: string; + code: string; + name: string; + tags: string[]; + emoticons: string[] | undefined; + skins: EmojiSkin[]; +}; + +type EmojiCategory = { + id: string; + name: string; + emojis: string[]; +}; + +type EmojiSkin = { + id: string; + unified: string; +}; + +const generateEmojiId = () => { + return ulid().toLowerCase() + 'em'; +}; + +const generateEmojiSkinId = () => { + return ulid().toLowerCase() + 'es'; +}; + +const getEmojiUrl = (unified: string) => { + let file = unified; + + // Fix for "copyright" and "trademark" emojis + if (file.substring(0, 2) == '00') { + file = file.substring(2); + + // Fix for keycap emojis + const regex = /-fe0f/i; + file = file.replace(regex, ''); + } + + if (file.startsWith('1f441')) { + const regex = /-fe0f/gi; + file = file.replace(regex, ''); + } + + if (file.endsWith('-fe0f')) { + const parts = file.split('-'); + if (parts.length === 2 && parts[0]) { + file = parts[0]; + } + } + + return `https://cdn.jsdelivr.net/gh/jdecked/twemoji@15.1.0/assets/svg/${file}.svg`; +}; + +const generateMetadata = () => { + const existingEmojis: EmojiMetadata = fs.existsSync(EMOJIS_METADATA_FILE_PATH) + ? (JSON.parse( + fs.readFileSync(EMOJIS_METADATA_FILE_PATH, 'utf-8') + ) as EmojiMetadata) + : { emojis: {}, categories: [] }; + + const result: EmojiMetadata = { + categories: [], + emojis: {}, + }; + + const idMap: Record = {}; + + for (const emojiMartItem of Object.values(emojiMartData.emojis)) { + const existingEmoji = Object.values(existingEmojis.emojis).find( + (emoji) => emoji.code === emojiMartItem.id + ); + + const emojiId = existingEmoji ? existingEmoji.id : generateEmojiId(); + const emoji: Emoji = { + id: emojiId, + code: emojiMartItem.id, + name: emojiMartItem.name, + tags: emojiMartItem.keywords, + emoticons: emojiMartItem.emoticons, + skins: emojiMartItem.skins.map((skin) => ({ + id: + existingEmoji?.skins.find((s) => s.unified === skin.unified)?.id ?? + generateEmojiSkinId(), + unified: skin.unified, + })), + }; + + idMap[emoji.code] = emoji.id; + result.emojis[emojiId] = emoji; + } + + for (const emojiMartCategory of Object.values(emojiMartData.categories)) { + const i18nCategory = i18nData.categories[emojiMartCategory.id]; + + if (!i18nCategory) { + throw new Error( + `Category ${emojiMartCategory.id} not found in i18n data` + ); + } + + const categoryEmojis: string[] = []; + for (const emoji of emojiMartCategory.emojis) { + if (!idMap[emoji]) { + throw new Error(`Emoji ${emoji} not found in idMap`); + } + + categoryEmojis.push(idMap[emoji]); + } + + const category: EmojiCategory = { + id: emojiMartCategory.id, + name: i18nCategory, + emojis: categoryEmojis, + }; + + result.categories.push(category); + } + + fs.writeFileSync(EMOJIS_METADATA_FILE_PATH, JSON.stringify(result, null, 2)); +}; + +const generateImages = async () => { + if (!fs.existsSync(EMOJIS_DIR_PATH)) { + fs.mkdirSync(EMOJIS_DIR_PATH); + } + + const emojis = JSON.parse( + fs.readFileSync(EMOJIS_METADATA_FILE_PATH, 'utf-8') + ) as EmojiMetadata; + + for (const emoji of Object.values(emojis.emojis)) { + for (const skin of emoji.skins) { + const fileName = `${skin.id}.svg`; + const filePath = path.join(EMOJIS_DIR_PATH, fileName); + if (fs.existsSync(filePath)) { + continue; + } + + const url = getEmojiUrl(skin.unified); + console.log(`Downloading ${url} to ${filePath}`); + + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to download ${url}`); + } + + const svg = await response.text(); + fs.writeFileSync(filePath, svg); + + // Rate limit + await new Promise((resolve) => setTimeout(resolve, 10)); + } + } +}; + +const zipEmojis = async () => { + await new Promise((resolve, reject) => { + const output = fs.createWriteStream(ZIP_FILE_PATH); + const archive = archiver('zip', { zlib: { level: 9 } }); + + output.on('close', () => { + console.log(`Zipped ${archive.pointer()} total bytes`); + resolve(void 0); + }); + + archive.on('error', (err) => reject(err)); + + archive.pipe(output); + archive.directory(EMOJIS_DIR_PATH, false); + archive.finalize(); + }); +}; + +const generateEmojis = async () => { + generateMetadata(); + await generateImages(); + await zipEmojis(); +}; + +generateEmojis(); diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json new file mode 100644 index 00000000..9ed484a0 --- /dev/null +++ b/scripts/tsconfig.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Scripts", + "extends": "../tsconfig.base.json", + "compilerOptions": { + "resolveJsonModule": true + } +}