From d1fefb88a16d54ff0cd1fbd6e92b3e9c2e00f40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Fri, 13 Sep 2024 21:35:52 +0200 Subject: [PATCH] feat: matrix --- icons.config.js | 6 + package.json | 2 + pnpm-lock.yaml | 215 +++++++++++ src/env.d.ts | 1 + src/lib/charrecorder/CharRecorder.svelte | 3 + src/lib/charrecorder/core/player.ts | 16 +- src/lib/charrecorder/core/recorder.ts | 26 +- src/lib/chat/MatrixRoomMembers.svelte | 71 ++++ src/lib/chat/MatrixRooms.svelte | 73 ++++ src/lib/chat/MatrixTimeline.svelte | 251 ++++++++++++ src/lib/chat/chat.ts | 35 ++ src/lib/chat/events/MatrixEvent.svelte | 357 ++++++++++++++++++ src/lib/chat/events/MatrixMessageEvent.svelte | 56 +++ src/lib/learn/stats.ts | 11 + src/lib/preferences.ts | 9 +- src/routes/(app)/PageTransition.svelte | 6 + src/routes/(app)/chat/+page.svelte | 185 ++++++++- src/routes/(app)/config/chords/+page.svelte | 6 +- .../(app)/config/chords/ChordEdit.svelte | 76 ++-- .../config/chords/ChordEditActions.svelte | 21 ++ src/routes/(app)/learn/Pick.svelte | 0 src/routes/(app)/stats/+page.svelte | 0 static/.htaccess | 12 - vite.config.ts | 7 +- 24 files changed, 1386 insertions(+), 59 deletions(-) create mode 100644 src/lib/chat/MatrixRoomMembers.svelte create mode 100644 src/lib/chat/MatrixRooms.svelte create mode 100644 src/lib/chat/MatrixTimeline.svelte create mode 100644 src/lib/chat/chat.ts create mode 100644 src/lib/chat/events/MatrixEvent.svelte create mode 100644 src/lib/chat/events/MatrixMessageEvent.svelte create mode 100644 src/lib/learn/stats.ts create mode 100644 src/routes/(app)/config/chords/ChordEditActions.svelte create mode 100644 src/routes/(app)/learn/Pick.svelte create mode 100644 src/routes/(app)/stats/+page.svelte delete mode 100644 static/.htaccess diff --git a/icons.config.js b/icons.config.js index 11ea748f..af1a3e2f 100644 --- a/icons.config.js +++ b/icons.config.js @@ -65,6 +65,8 @@ const config = { "bolt", "undo", "redo", + "replay", + "reply", "navigate_before", "navigate_next", "print", @@ -91,6 +93,10 @@ const config = { "upload_2", "stat_minus_2", "stat_2", + "send", + "more_horiz", + "add_reaction", + "stop", "description", "add_circle", "refresh", diff --git a/package.json b/package.json index 2e04f62c..d35a4477 100644 --- a/package.json +++ b/package.json @@ -65,10 +65,12 @@ "fontkit": "^2.0.2", "glob": "^11.0.0", "jsdom": "^24.1.1", + "matrix-js-sdk": "^34.4.0", "npm-run-all": "^4.1.5", "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.2.6", "sass": "^1.77.8", + "socket.io-client": "^4.7.5", "stylelint": "^16.8.1", "stylelint-config-clean-order": "^6.1.0", "stylelint-config-html": "^1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df85f565..972a7a1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -101,6 +101,9 @@ importers: jsdom: specifier: ^24.1.1 version: 24.1.1 + matrix-js-sdk: + specifier: ^34.4.0 + version: 34.4.0 npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -113,6 +116,9 @@ importers: sass: specifier: ^1.77.8 version: 1.77.8 + socket.io-client: + specifier: ^4.7.5 + version: 4.7.5 stylelint: specifier: ^16.8.1 version: 16.8.1(typescript@5.5.4) @@ -1015,6 +1021,13 @@ packages: '@material/material-color-utilities@0.3.0': resolution: {integrity: sha512-ztmtTd6xwnuh2/xu+Vb01btgV8SQWYCaK56CkRK8gEkWe5TuDyBcYJ0wgkMRn+2VcE9KUmhvkz+N9GHrqw/C0g==} + '@matrix-org/matrix-sdk-crypto-wasm@7.0.0': + resolution: {integrity: sha512-MOencXiW/gI5MuTtCNsuojjwT5DXCrjMqv9xOslJC9h2tPdLFFFMGr58dY5Lis4DRd9MRWcgrGowUIHOqieWTA==} + engines: {node: '>= 10'} + + '@matrix-org/olm@3.2.15': + resolution: {integrity: sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q==} + '@melt-ui/pp@0.3.2': resolution: {integrity: sha512-xKkPvaIAFinklLXcQOpwZ8YSpqAFxykjWf8Y/fSJQwsixV/0rcFs07hJ49hJjPy5vItvw5Qa0uOjzFUbXzBypQ==} peerDependencies: @@ -1234,6 +1247,9 @@ packages: cpu: [x64] os: [win32] + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@surma/rollup-plugin-off-main-thread@2.2.3': resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} @@ -1356,6 +1372,9 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/events@3.0.3': + resolution: {integrity: sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==} + '@types/flexsearch@0.7.6': resolution: {integrity: sha512-H5IXcRn96/gaDmo+rDl2aJuIJsob8dgOXDqf8K0t8rWZd1AFNaaspmRsElESiU+EWE33qfbFPgI0OC/B1g9FCA==} @@ -1368,6 +1387,9 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + '@types/sinonjs__fake-timers@8.1.1': resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} @@ -1436,6 +1458,9 @@ packages: ajv@8.16.0: resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + another-json@0.2.0: + resolution: {integrity: sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -1558,6 +1583,9 @@ packages: balanced-match@2.0.0: resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + base-x@5.0.0: + resolution: {integrity: sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1595,6 +1623,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -1726,6 +1757,10 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -2044,6 +2079,13 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + engine.io-client@6.5.4: + resolution: {integrity: sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} @@ -2121,6 +2163,10 @@ packages: eventemitter2@6.4.7: resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + execa@4.1.0: resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} engines: {node: '>=10'} @@ -2674,6 +2720,10 @@ packages: resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} engines: {'0': node >=0.6.0} + jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -2741,6 +2791,10 @@ packages: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + loupe@3.1.1: resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} @@ -2760,6 +2814,16 @@ packages: mathml-tag-names@2.1.3: resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + matrix-events-sdk@0.0.1: + resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} + + matrix-js-sdk@34.4.0: + resolution: {integrity: sha512-bI5xJZS3/qhjPQqQL5HhOQ1iBvnHxiqhS2zgzk9SarEuXiH08wbVl9gAAuDqOYE3miNGs4WQQJ19MoaUEOnNwg==} + engines: {node: '>=20.0.0'} + + matrix-widget-api@1.9.0: + resolution: {integrity: sha512-au8mqralNDqrEvaVAkU37bXOb8I9SCe+ACdPk11QWw58FKstVq31q2wRz+qWA6J+42KJ6s1DggWbG/S3fEs3jw==} + mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} @@ -2893,6 +2957,10 @@ packages: resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} + oidc-client-ts@3.0.1: + resolution: {integrity: sha512-xX8unZNtmtw3sOz4FPSqDhkLFnxCDsdo2qhFEH2opgWnF/iXMFoYdBQzkwCxAZVgt3FT3DnuBY3k80EZHT0RYg==} + engines: {node: '>=18'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -2911,6 +2979,10 @@ packages: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} @@ -3173,6 +3245,10 @@ packages: restructure@3.0.2: resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -3243,6 +3319,10 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + sdp-transform@2.14.2: + resolution: {integrity: sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==} + hasBin: true + semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true @@ -3322,6 +3402,14 @@ packages: smob@1.5.0: resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} + socket.io-client@4.7.5: + resolution: {integrity: sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + sorcery@0.11.1: resolution: {integrity: sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ==} hasBin: true @@ -3748,6 +3836,9 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + unhomoglyph@1.0.6: + resolution: {integrity: sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg==} + unicode-canonical-property-names-ecmascript@2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} @@ -3808,6 +3899,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -4021,6 +4116,18 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -4040,6 +4147,10 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xmlhttprequest-ssl@2.0.0: + resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} + engines: {node: '>=0.4.0'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -5069,6 +5180,10 @@ snapshots: '@material/material-color-utilities@0.3.0': {} + '@matrix-org/matrix-sdk-crypto-wasm@7.0.0': {} + + '@matrix-org/olm@3.2.15': {} + '@melt-ui/pp@0.3.2(@melt-ui/svelte@0.83.0(svelte@5.0.0-next.221))(svelte@5.0.0-next.221)': dependencies: '@melt-ui/svelte': 0.83.0(svelte@5.0.0-next.221) @@ -5274,6 +5389,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.18.0': optional: true + '@socket.io/component-emitter@3.1.2': {} + '@surma/rollup-plugin-off-main-thread@2.2.3': dependencies: ejs: 3.1.10 @@ -5392,6 +5509,8 @@ snapshots: '@types/estree@1.0.5': {} + '@types/events@3.0.3': {} + '@types/flexsearch@0.7.6': {} '@types/node@20.14.10': @@ -5403,6 +5522,8 @@ snapshots: '@types/resolve@1.20.2': {} + '@types/retry@0.12.0': {} + '@types/sinonjs__fake-timers@8.1.1': {} '@types/sizzle@2.3.8': {} @@ -5481,6 +5602,8 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 + another-json@0.2.0: {} + ansi-colors@4.1.3: {} ansi-escapes@4.3.2: @@ -5606,6 +5729,8 @@ snapshots: balanced-match@2.0.0: {} + base-x@5.0.0: {} + base64-js@1.5.1: {} bcrypt-pbkdf@1.0.2: @@ -5644,6 +5769,10 @@ snapshots: node-releases: 2.0.14 update-browserslist-db: 1.1.0(browserslist@4.23.1) + bs58@6.0.0: + dependencies: + base-x: 5.0.0 + buffer-crc32@0.2.13: {} buffer-crc32@1.0.0: {} @@ -5773,6 +5902,8 @@ snapshots: concat-map@0.0.1: {} + content-type@1.0.5: {} + convert-source-map@2.0.0: {} cookie@0.6.0: {} @@ -6142,6 +6273,20 @@ snapshots: dependencies: once: 1.4.0 + engine.io-client@6.5.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.6 + engine.io-parser: 5.2.3 + ws: 8.17.1 + xmlhttprequest-ssl: 2.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 @@ -6277,6 +6422,8 @@ snapshots: eventemitter2@6.4.7: {} + events@3.3.0: {} + execa@4.1.0: dependencies: cross-spawn: 7.0.3 @@ -6841,6 +6988,8 @@ snapshots: json-schema: 0.4.0 verror: 1.10.0 + jwt-decode@4.0.0: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -6903,6 +7052,8 @@ snapshots: slice-ansi: 4.0.0 wrap-ansi: 6.2.0 + loglevel@1.9.2: {} + loupe@3.1.1: dependencies: get-func-name: 2.0.2 @@ -6923,6 +7074,31 @@ snapshots: mathml-tag-names@2.1.3: {} + matrix-events-sdk@0.0.1: {} + + matrix-js-sdk@34.4.0: + dependencies: + '@babel/runtime': 7.24.7 + '@matrix-org/matrix-sdk-crypto-wasm': 7.0.0 + '@matrix-org/olm': 3.2.15 + another-json: 0.2.0 + bs58: 6.0.0 + content-type: 1.0.5 + jwt-decode: 4.0.0 + loglevel: 1.9.2 + matrix-events-sdk: 0.0.1 + matrix-widget-api: 1.9.0 + oidc-client-ts: 3.0.1 + p-retry: 4.6.2 + sdp-transform: 2.14.2 + unhomoglyph: 1.0.6 + uuid: 10.0.0 + + matrix-widget-api@1.9.0: + dependencies: + '@types/events': 3.0.3 + events: 3.3.0 + mdn-data@2.0.30: {} memorystream@0.3.1: {} @@ -7030,6 +7206,10 @@ snapshots: has-symbols: 1.0.3 object-keys: 1.1.1 + oidc-client-ts@3.0.1: + dependencies: + jwt-decode: 4.0.0 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -7048,6 +7228,11 @@ snapshots: dependencies: aggregate-error: 3.1.0 + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + package-json-from-dist@1.0.0: {} pako@0.2.9: {} @@ -7273,6 +7458,8 @@ snapshots: restructure@3.0.2: {} + retry@0.13.1: {} + reusify@1.0.4: {} rfdc@1.4.1: {} @@ -7361,6 +7548,8 @@ snapshots: dependencies: xmlchars: 2.2.0 + sdp-transform@2.14.2: {} + semver@5.7.2: {} semver@6.3.1: {} @@ -7438,6 +7627,24 @@ snapshots: smob@1.5.0: {} + socket.io-client@4.7.5: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.6 + engine.io-client: 6.5.4 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + sorcery@0.11.1: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -7898,6 +8105,8 @@ snapshots: undici-types@5.26.5: optional: true + unhomoglyph@1.0.6: {} + unicode-canonical-property-names-ecmascript@2.0.0: {} unicode-match-property-ecmascript@2.0.0: @@ -7950,6 +8159,8 @@ snapshots: util-deprecate@1.0.2: {} + uuid@10.0.0: {} + uuid@8.3.2: {} validate-npm-package-license@3.0.4: @@ -8243,12 +8454,16 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 + ws@8.17.1: {} + ws@8.18.0: {} xml-name-validator@5.0.0: {} xmlchars@2.2.0: {} + xmlhttprequest-ssl@2.0.0: {} + yallist@3.1.1: {} yauzl@2.10.0: diff --git a/src/env.d.ts b/src/env.d.ts index c6623b88..6c3602f6 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -14,6 +14,7 @@ interface ImportMetaEnv { readonly VITE_LEARN_URL: string; readonly VITE_LATEST_FIRMWARE: string; readonly VITE_STORE_URL: string; + readonly VITE_MATRIX_URL: string; } interface ImportMeta { diff --git a/src/lib/charrecorder/CharRecorder.svelte b/src/lib/charrecorder/CharRecorder.svelte index b426e8bd..90784d54 100644 --- a/src/lib/charrecorder/CharRecorder.svelte +++ b/src/lib/charrecorder/CharRecorder.svelte @@ -11,11 +11,13 @@ cursor = false, keys = false, children, + ondone, }: { replay: ReplayPlayer | Replay; cursor?: boolean; keys?: boolean; children?: Snippet; + ondone?: () => void; } = $props(); let replayPlayer: ReplayPlayer | undefined = $state(); @@ -61,6 +63,7 @@ const unsubscribePlayer = player.subscribe(apply); textRenderer = renderer; + player.onDone = ondone; player.start(); apply(); setTimeout(() => { diff --git a/src/lib/charrecorder/core/player.ts b/src/lib/charrecorder/core/player.ts index 5d303b51..2332138c 100644 --- a/src/lib/charrecorder/core/player.ts +++ b/src/lib/charrecorder/core/player.ts @@ -18,6 +18,8 @@ export class ReplayPlayer { private subscribers = new Set<(value: TextToken | undefined) => void>(); + onDone?: () => void; + constructor( readonly replay: Replay, plugins: ReplayPlugin[] = [], @@ -37,8 +39,13 @@ export class ReplayPlayer { if ( this.replayCursor >= this.replay.keys.length && this.releaseAt.size === 0 - ) + ) { + if (this.onDone) { + this.onDone(); + } return; + } + const now = performance.now() - this.startTime; while ( @@ -118,7 +125,12 @@ export class ReplayPlayer { start(delay = 200): this { this.replayCursor = 0; this.stepper = new ReplayStepper([], this.replay.challenge); - if (this.replay.keys.length === 0) return this; + if (this.replay.keys.length === 0) { + if (this.onDone) { + this.onDone(); + } + return this; + } setTimeout(() => { this.startTime = performance.now(); this.animationFrameId = requestAnimationFrame(this.updateLoop.bind(this)); diff --git a/src/lib/charrecorder/core/recorder.ts b/src/lib/charrecorder/core/recorder.ts index a6e123ec..a9309ca7 100644 --- a/src/lib/charrecorder/core/recorder.ts +++ b/src/lib/charrecorder/core/recorder.ts @@ -1,6 +1,10 @@ import { ReplayPlayer } from "./player.js"; import type { Replay, ReplayEvent, TransmittableKeyEvent } from "./types.js"; +function maybeRound(value: T, round: boolean): T { + return typeof value === "number" && round ? (Math.round(value) as T) : value; +} + export class ReplayRecorder { private held = new Map(); @@ -39,7 +43,7 @@ export class ReplayRecorder { this.player.playLiveEvent(event.key, event.code), ); } else { - const [key, start] = this.held.get(event.code)!; + const [key, start] = this.held.get(event.code) ?? ["", 0]; const delta = event.timeStamp - start; this.held.delete(event.code); @@ -50,16 +54,24 @@ export class ReplayRecorder { } } - finish(trim = true) { + finish(trim = true, round = true) { return { - start: trim ? this.replay[0]?.[2] : this.start, - finish: trim - ? Math.max(...this.replay.map((it) => it[2] + it[3])) - : performance.now(), + start: maybeRound(trim ? this.replay[0]?.[2] : this.start, round), + finish: maybeRound( + trim + ? Math.max(...this.replay.map((it) => it[2] + it[3])) + : performance.now(), + round, + ), keys: this.replay .map( ([key, code, at, duration]) => - [key, code, Math.round(at), Math.round(duration)] as const, + [ + key, + code, + maybeRound(at, round), + maybeRound(duration, round), + ] as const, ) .sort((a, b) => a[2] - b[2]), }; diff --git a/src/lib/chat/MatrixRoomMembers.svelte b/src/lib/chat/MatrixRoomMembers.svelte new file mode 100644 index 00000000..900aa005 --- /dev/null +++ b/src/lib/chat/MatrixRoomMembers.svelte @@ -0,0 +1,71 @@ + + +
+ {#each members as member (member.userId)} + {@const avatar = member.getMxcAvatarUrl()} +
+ {#if avatar} + {member.name} + {:else} + {@const color = memberColor(member, $theme)} + {@const modeColor = $theme.mode === "dark" ? color.dark : color.light} +
+ person +
+ {/if} + {member.name} +
+ {/each} +
+ + diff --git a/src/lib/chat/MatrixRooms.svelte b/src/lib/chat/MatrixRooms.svelte new file mode 100644 index 00000000..71306d97 --- /dev/null +++ b/src/lib/chat/MatrixRooms.svelte @@ -0,0 +1,73 @@ + + +
+ {#each $matrixClient.getRooms() as room} + {@const avatar = room.getMxcAvatarUrl()} + + {/each} + + {#await $matrixClient.publicRooms()} +
Loading...
+ {:then rooms} + {#each rooms.chunk as room} + + {/each} + {:catch error} +
{error.message}
+ {/await} +
+ + diff --git a/src/lib/chat/MatrixTimeline.svelte b/src/lib/chat/MatrixTimeline.svelte new file mode 100644 index 00000000..38ce9553 --- /dev/null +++ b/src/lib/chat/MatrixTimeline.svelte @@ -0,0 +1,251 @@ + + +
+
+ {#each live.entries() as [userId, recorder] (userId)} + {@const roomId = timeline.getRoomId()} + {#if roomId} + {@const room = $matrixClient.getRoom(roomId)} + {@const member = room?.getMember(userId)} + {#if member} + + {/if} + {/if} + {/each} + {#each events as event, i (event.event["event_id"])} + {@const prev = events[i + 1]} + + {/each} +
+ +
+
+
+ +
(showCursor = true)} + onfocusout={() => (showCursor = false)} + > + +
+ +
+
+
+ + diff --git a/src/lib/chat/chat.ts b/src/lib/chat/chat.ts new file mode 100644 index 00000000..a2b07a6d --- /dev/null +++ b/src/lib/chat/chat.ts @@ -0,0 +1,35 @@ +import { writable, type Writable } from "svelte/store"; +import type { MatrixClient, RoomMember } from "matrix-js-sdk"; +import { persistentWritable } from "$lib/storage"; +import { + themeFromSourceColor, + argbFromHex, + type CustomColorGroup, +} from "@material/material-color-utilities"; +import type { UserTheme } from "$lib/preferences"; + +export const matrixClient: Writable = writable(); + +export const currentRoomId = persistentWritable( + "currentRoomId", + null, +); + +export function memberColor( + member: RoomMember, + theme: UserTheme, +): CustomColorGroup { + let hash = 0; + member.userId.split("").forEach((char) => { + hash = char.charCodeAt(0) + ((hash << 5) - hash); + }); + let color = "#"; + for (let i = 0; i < 3; i++) { + const value = (hash >> (i * 8)) & 0xff; + color += value.toString(16).padStart(2, "0"); + } + + return themeFromSourceColor(argbFromHex(theme.color), [ + { value: argbFromHex(color), name: "member", blend: true }, + ]).customColors.find((c) => c.color.name === "member")!; +} diff --git a/src/lib/chat/events/MatrixEvent.svelte b/src/lib/chat/events/MatrixEvent.svelte new file mode 100644 index 00000000..2c9d6424 --- /dev/null +++ b/src/lib/chat/events/MatrixEvent.svelte @@ -0,0 +1,357 @@ + + +
(mainHover = true)} + onfocus={() => (mainHover = true)} + onmouseout={() => (mainHover = false)} + onblur={() => (mainHover = false)} +> + {#if event && hover} +
+ {/if} + + {#if sender && !(prev && prev?.getType() === event?.getType() && prev.sender?.userId === event.sender?.userId)} + {@const color = memberColor(sender, $theme)} + {@const avatarMxc = sender.getMxcAvatarUrl()} + {#if avatarMxc} + {@const avatar = $matrixClient.mxcUrlToHttp(avatarMxc, 32, 32)} + {sender.name} + {:else} +
+ person +
+ {/if} + +
+ {sender.name} + {#if replay || replayPlayer} +
+ {#each new Array(3) as _, i} +
+ {/each} +
+ {/if} +
+ {/if} + +
+ {#if event} + {#if event.getType() === "m.room.message"} + + {:else} +
+ {event.getType()} +
{JSON.stringify(event.event, null, 2)}
+
+ {/if} + {/if} + {#if replayPlayer} + + {/if} +
+ + {#if event && hover} + + {/if} + + {#if $annotations && $annotations.length > 0} +
+ {#each $annotations as [reaction, events]} + + {/each} +
+ {/if} +
+ + diff --git a/src/lib/chat/events/MatrixMessageEvent.svelte b/src/lib/chat/events/MatrixMessageEvent.svelte new file mode 100644 index 00000000..465be181 --- /dev/null +++ b/src/lib/chat/events/MatrixMessageEvent.svelte @@ -0,0 +1,56 @@ + + +
+ {#if event.event.content?.msgtype === "m.image"} + {event.event.content["body"]} + {:else} + {event.event.content?.["body"]} + {/if} + {#if replay} +
+ (replay = undefined)} + /> +
+ {/if} +
+ + diff --git a/src/lib/learn/stats.ts b/src/lib/learn/stats.ts new file mode 100644 index 00000000..05df68a2 --- /dev/null +++ b/src/lib/learn/stats.ts @@ -0,0 +1,11 @@ +import { persistentWritable } from "$lib/storage"; + +interface ChordStats { + level: number; + lastUprank: number; +} + +export const chordStats = persistentWritable>( + "chord-stats", + {}, +); diff --git a/src/lib/preferences.ts b/src/lib/preferences.ts index 5f4c9bb1..05b8e5d4 100644 --- a/src/lib/preferences.ts +++ b/src/lib/preferences.ts @@ -6,9 +6,14 @@ export interface UserPreferences { autoConnect: boolean; } -export const theme = persistentWritable("user-theme", { +export interface UserTheme { + color: string; + mode: "light" | "dark" | "auto"; +} + +export const theme = persistentWritable("user-theme", { color: "#6D81C7", - mode: "dark" as "light" | "dark" | "auto", + mode: "dark", }); export const userPreferences = persistentWritable( diff --git a/src/routes/(app)/PageTransition.svelte b/src/routes/(app)/PageTransition.svelte index 46b39106..70bc372d 100644 --- a/src/routes/(app)/PageTransition.svelte +++ b/src/routes/(app)/PageTransition.svelte @@ -56,3 +56,9 @@ {@render children()} {/if} + + diff --git a/src/routes/(app)/chat/+page.svelte b/src/routes/(app)/chat/+page.svelte index be7b1331..5c3b5772 100644 --- a/src/routes/(app)/chat/+page.svelte +++ b/src/routes/(app)/chat/+page.svelte @@ -1 +1,184 @@ -

WIP

+ + +{#if $matrixClient && loggedIn} + {#if ready} +
+
+ + +
+ {#if $currentRoomId} + {@const room = $matrixClient.getRoom($currentRoomId)} + {#key room} + {#if room} +
+ +
+
+ +
+ {/if} + {/key} + {/if} +
+ {/if} +{:else if $matrixClient} + {#await $matrixClient.loginFlows() then flows} + {#each flows.flows as flow} + {#if flow.type === "m.login.sso"} + + {#each flow.identity_providers as idp} + {#if idp.icon} + {idp.name} + {:else} + {idp.name} + {/if} + {/each} + + {:else if flow.type === "m.login.password"} +
+ + + +
+ {/if} + {/each} + {/await} +{/if} + + diff --git a/src/routes/(app)/config/chords/+page.svelte b/src/routes/(app)/config/chords/+page.svelte index d93d1450..6582e5cb 100644 --- a/src/routes/(app)/config/chords/+page.svelte +++ b/src/routes/(app)/config/chords/+page.svelte @@ -262,9 +262,7 @@ {/if} {#each $items.slice(page * $pageSize - (page === 0 ? 0 : 1), (page + 1) * $pageSize - 1) as [chord] (JSON.stringify(chord?.id))} {#if chord} - - (page = 0)} /> - + (page = 0)} /> {/if} {/each} @@ -397,7 +395,7 @@ table { height: fit-content; - overflow: hidden; + overflow-y: hidden; transition: all 1s ease; } diff --git a/src/routes/(app)/config/chords/ChordEdit.svelte b/src/routes/(app)/config/chords/ChordEdit.svelte index b2432faa..e37bc7d3 100644 --- a/src/routes/(app)/config/chords/ChordEdit.svelte +++ b/src/routes/(app)/config/chords/ChordEdit.svelte @@ -89,33 +89,37 @@ } - - {}} /> - - - - - - {#if !chord.deleted} - - {:else} - - {/if} - - -
- - + + + {}} /> + + + + + +
+ {#if !chord.deleted} + + {:else} + + {/if} + + +
+ +
+ + diff --git a/src/routes/(app)/config/chords/ChordEditActions.svelte b/src/routes/(app)/config/chords/ChordEditActions.svelte new file mode 100644 index 00000000..4d59b0fb --- /dev/null +++ b/src/routes/(app)/config/chords/ChordEditActions.svelte @@ -0,0 +1,21 @@ +
+ {#if !chord.deleted} + + {:else} + + {/if} + + +
+ +
diff --git a/src/routes/(app)/learn/Pick.svelte b/src/routes/(app)/learn/Pick.svelte new file mode 100644 index 00000000..e69de29b diff --git a/src/routes/(app)/stats/+page.svelte b/src/routes/(app)/stats/+page.svelte new file mode 100644 index 00000000..e69de29b diff --git a/static/.htaccess b/static/.htaccess deleted file mode 100644 index b0febd0c..00000000 --- a/static/.htaccess +++ /dev/null @@ -1,12 +0,0 @@ -RewriteEngine On - -# force https -RewriteCond %{HTTPS} off -RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] - -# https://kit.svelte.dev/docs/single-page-apps#apache -# RewriteBase / -# RewriteRule ^index\.html$ - [L] -# RewriteCond %{REQUEST_FILENAME} !-f -# RewriteCond %{REQUEST_FILENAME} !-d -# RewriteRule . /index.html [QSA,L] diff --git a/vite.config.ts b/vite.config.ts index 6ee3917b..536bdc47 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -22,6 +22,7 @@ process.env["VITE_BUGS_URL"] = bugs.url; process.env["VITE_LEARN_URL"] = "https://www.iq-eq.io/"; process.env["VITE_LATEST_FIRMWARE"] = "1.1.4"; process.env["VITE_STORE_URL"] = "https://www.charachorder.com/"; +process.env["VITE_MATRIX_URL"] = "https://charachorder.io/"; export default defineConfig({ build: { @@ -32,6 +33,9 @@ export default defineConfig({ external: isTauri ? [/virtual:pwa.*/] : [], }, }, + define: { + global: "window", + }, envPrefix: ["TAURI_", "VITE_"], plugins: [ ViteYaml(), @@ -42,6 +46,7 @@ export default defineConfig({ SvelteKitPWA({ kit: { trailingSlash: "always", + adapterFallback: "404.html", }, scope: "/", base: "/", @@ -52,7 +57,7 @@ export default defineConfig({ "client/**/*.{js,map,css,ico,woff2,csv,png,webp,svg,webmanifest}", "prerendered/**/*.html", ], - ignoreURLParametersMatching: [/^import$/], + ignoreURLParametersMatching: [/^import|redirectUrl|loginToken$/], }, manifest: { name: "CharaChorder Device Manager",