[WIP] Update

This commit is contained in:
2024-01-25 14:32:12 +09:00
parent c97df03ca1
commit 3afcb40ae9
25 changed files with 600 additions and 260 deletions

View File

@ -28,19 +28,24 @@
"prepare": "husky install"
},
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@excalidraw/excalidraw": "^0.15.2",
"@fontsource/roboto": "^5.0.5",
"@mui/icons-material": "^5.14.1",
"@mui/material": "^5.14.1",
"@mui/styled-engine-sc": "^5.12.0",
"dexie": "^3.2.4",
"dexie-react-hooks": "^1.1.6",
"i18next": "^23.2.11",
"i18next-browser-languagedetector": "^7.1.0",
"i18next-http-backend": "^2.2.1",
"jotai": "^2.2.2",
"just-diff": "^6.0.2",
"just-diff-apply": "^5.5.0",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^13.0.2",
"react-router-dom": "^6.14.2",
"styled-components": "^6.0.4",
"ts-pattern": "^5.0.4",

292
pnpm-lock.yaml generated
View File

@ -5,6 +5,12 @@ settings:
excludeLinksFromLockfile: false
dependencies:
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.14)(react@18.2.0)
'@emotion/styled':
specifier: ^11.11.0
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.14)(react@18.2.0)
'@excalidraw/excalidraw':
specifier: ^0.15.2
version: 0.15.2(react-dom@18.2.0)(react@18.2.0)
@ -16,16 +22,22 @@ dependencies:
version: 5.14.1(@mui/material@5.14.1)(@types/react@18.2.14)(react@18.2.0)
'@mui/material':
specifier: ^5.14.1
version: 5.14.1(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@mui/styled-engine-sc':
specifier: ^5.12.0
version: 5.12.0(styled-components@6.0.4)
version: 5.14.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
dexie:
specifier: ^3.2.4
version: 3.2.4
dexie-react-hooks:
specifier: ^1.1.6
version: 1.1.6(@types/react@18.2.14)(dexie@3.2.4)(react@18.2.0)
i18next:
specifier: ^23.2.11
version: 23.2.11
i18next-browser-languagedetector:
specifier: ^7.1.0
version: 7.1.0
i18next-http-backend:
specifier: ^2.2.1
version: 2.2.1
jotai:
specifier: ^2.2.2
version: 2.2.2(react@18.2.0)
@ -44,6 +56,9 @@ dependencies:
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
react-i18next:
specifier: ^13.0.2
version: 13.0.2(i18next@23.2.11)(react-dom@18.2.0)(react@18.2.0)
react-router-dom:
specifier: ^6.14.2
version: 6.14.2(react-dom@18.2.0)(react@18.2.0)
@ -1456,6 +1471,22 @@ packages:
to-fast-properties: 2.0.0
dev: false
/@emotion/babel-plugin@11.11.0:
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
dependencies:
'@babel/helper-module-imports': 7.22.5
'@babel/runtime': 7.22.6
'@emotion/hash': 0.9.1
'@emotion/memoize': 0.8.1
'@emotion/serialize': 1.1.2
babel-plugin-macros: 3.1.0
convert-source-map: 1.9.0
escape-string-regexp: 4.0.0
find-root: 1.1.0
source-map: 0.5.7
stylis: 4.2.0
dev: false
/@emotion/cache@11.11.0:
resolution: {integrity: sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==}
dependencies:
@ -1466,6 +1497,10 @@ packages:
stylis: 4.2.0
dev: false
/@emotion/hash@0.9.1:
resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==}
dev: false
/@emotion/is-prop-valid@1.2.1:
resolution: {integrity: sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==}
dependencies:
@ -1476,14 +1511,74 @@ packages:
resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}
dev: false
/@emotion/react@11.11.1(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==}
peerDependencies:
'@types/react': '*'
react: '>=16.8.0'
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.6
'@emotion/babel-plugin': 11.11.0
'@emotion/cache': 11.11.0
'@emotion/serialize': 1.1.2
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
'@emotion/utils': 1.2.1
'@emotion/weak-memoize': 0.3.1
'@types/react': 18.2.14
hoist-non-react-statics: 3.3.2
react: 18.2.0
dev: false
/@emotion/serialize@1.1.2:
resolution: {integrity: sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==}
dependencies:
'@emotion/hash': 0.9.1
'@emotion/memoize': 0.8.1
'@emotion/unitless': 0.8.1
'@emotion/utils': 1.2.1
csstype: 3.1.2
dev: false
/@emotion/sheet@1.2.2:
resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==}
dev: false
/@emotion/styled@11.11.0(@emotion/react@11.11.1)(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==}
peerDependencies:
'@emotion/react': ^11.0.0-rc.0
'@types/react': '*'
react: '>=16.8.0'
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.6
'@emotion/babel-plugin': 11.11.0
'@emotion/is-prop-valid': 1.2.1
'@emotion/react': 11.11.1(@types/react@18.2.14)(react@18.2.0)
'@emotion/serialize': 1.1.2
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
'@emotion/utils': 1.2.1
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@emotion/unitless@0.8.1:
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
dev: false
/@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0):
resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==}
peerDependencies:
react: '>=16.8.0'
dependencies:
react: 18.2.0
dev: false
/@emotion/utils@1.2.1:
resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==}
dev: false
@ -1839,12 +1934,12 @@ packages:
optional: true
dependencies:
'@babel/runtime': 7.22.6
'@mui/material': 5.14.1(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@mui/material': 5.14.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@mui/material@5.14.1(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
/@mui/material@5.14.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-WtsgYuageTunLfxH3Ri+o1RuQTFImtRHxMcVNyD0Hhd2/znjW6KODNz0XfjvLRnNCAynBxZNiflcoIBW40h9PQ==}
engines: {node: '>=12.0.0'}
peerDependencies:
@ -1862,9 +1957,11 @@ packages:
optional: true
dependencies:
'@babel/runtime': 7.22.6
'@emotion/react': 11.11.1(@types/react@18.2.14)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.14)(react@18.2.0)
'@mui/base': 5.0.0-beta.8(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@mui/core-downloads-tracker': 5.14.1
'@mui/system': 5.14.1(@types/react@18.2.14)(react@18.2.0)
'@mui/system': 5.14.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.14)(react@18.2.0)
'@mui/types': 7.2.4(@types/react@18.2.14)
'@mui/utils': 5.14.1(react@18.2.0)
'@types/react': 18.2.14
@ -1895,22 +1992,7 @@ packages:
react: 18.2.0
dev: false
/@mui/styled-engine-sc@5.12.0(styled-components@6.0.4):
resolution: {integrity: sha512-3MgYoY2YG5tx0E5oKqvCv94oL0ABVBr+qpcyvciXW/v0wzPG6bXvuZV80GHYlJfasgnnRa1AbRWf5a9FcX8v6g==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/styled-components': ^5.1.14
styled-components: ^5.3.1
peerDependenciesMeta:
'@types/styled-components':
optional: true
dependencies:
'@babel/runtime': 7.22.6
prop-types: 15.8.1
styled-components: 6.0.4(react-dom@18.2.0)(react@18.2.0)
dev: false
/@mui/styled-engine@5.13.2(react@18.2.0):
/@mui/styled-engine@5.13.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0):
resolution: {integrity: sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw==}
engines: {node: '>=12.0.0'}
peerDependencies:
@ -1925,12 +2007,14 @@ packages:
dependencies:
'@babel/runtime': 7.22.6
'@emotion/cache': 11.11.0
'@emotion/react': 11.11.1(@types/react@18.2.14)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.14)(react@18.2.0)
csstype: 3.1.2
prop-types: 15.8.1
react: 18.2.0
dev: false
/@mui/system@5.14.1(@types/react@18.2.14)(react@18.2.0):
/@mui/system@5.14.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-u+xlsU34Jdkgx1CxmBnIC4Y08uPdVX5iEd3S/1dggDFtOGp+Lj8xmKRJAQ8PJOOJLOh8pDwaZx4AwXikL4l1QA==}
engines: {node: '>=12.0.0'}
peerDependencies:
@ -1947,8 +2031,10 @@ packages:
optional: true
dependencies:
'@babel/runtime': 7.22.6
'@emotion/react': 11.11.1(@types/react@18.2.14)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.14)(react@18.2.0)
'@mui/private-theming': 5.13.7(@types/react@18.2.14)(react@18.2.0)
'@mui/styled-engine': 5.13.2(react@18.2.0)
'@mui/styled-engine': 5.13.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0)
'@mui/types': 7.2.4(@types/react@18.2.14)
'@mui/utils': 5.14.1(react@18.2.0)
'@types/react': 18.2.14
@ -2156,6 +2242,10 @@ packages:
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
dev: true
/@types/parse-json@4.0.0:
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
dev: false
/@types/prop-types@15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
@ -2449,6 +2539,15 @@ packages:
engines: {node: '>= 0.4'}
dev: true
/babel-plugin-macros@3.1.0:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'}
dependencies:
'@babel/runtime': 7.22.6
cosmiconfig: 7.1.0
resolve: 1.22.2
dev: false
/babel-plugin-polyfill-corejs2@0.4.4(@babel/core@7.22.9):
resolution: {integrity: sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==}
peerDependencies:
@ -2545,7 +2644,6 @@ packages:
/callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
dev: true
/camelize@1.0.1:
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
@ -2672,6 +2770,25 @@ packages:
browserslist: 4.21.9
dev: false
/cosmiconfig@7.1.0:
resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==}
engines: {node: '>=10'}
dependencies:
'@types/parse-json': 4.0.0
import-fresh: 3.3.0
parse-json: 5.2.0
path-type: 4.0.0
yaml: 1.10.2
dev: false
/cross-fetch@3.1.6:
resolution: {integrity: sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==}
dependencies:
node-fetch: 2.6.12
transitivePeerDependencies:
- encoding
dev: false
/cross-spawn@6.0.5:
resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
engines: {node: '>=4.8'}
@ -2810,7 +2927,6 @@ packages:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
dependencies:
is-arrayish: 0.2.1
dev: true
/es-abstract@1.22.1:
resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==}
@ -2917,7 +3033,6 @@ packages:
/escape-string-regexp@4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
dev: true
/eslint-plugin-react-hooks@4.6.0(eslint@8.44.0):
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
@ -3099,6 +3214,10 @@ packages:
dependencies:
to-regex-range: 5.0.1
/find-root@1.1.0:
resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
dev: false
/find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
@ -3333,10 +3452,22 @@ packages:
dependencies:
function-bind: 1.1.1
/hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
dependencies:
react-is: 16.13.1
dev: false
/hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
dev: true
/html-parse-stringify@3.0.1:
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
dependencies:
void-elements: 3.1.0
dev: false
/http-cache-semantics@4.1.1:
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
dev: true
@ -3360,6 +3491,26 @@ packages:
hasBin: true
dev: true
/i18next-browser-languagedetector@7.1.0:
resolution: {integrity: sha512-cr2k7u1XJJ4HTOjM9GyOMtbOA47RtUoWRAtt52z43r3AoMs2StYKyjS3URPhzHaf+mn10hY9dZWamga5WPQjhA==}
dependencies:
'@babel/runtime': 7.22.6
dev: false
/i18next-http-backend@2.2.1:
resolution: {integrity: sha512-ZXIdn/8NJIBJ0X4hzXfc3STYxKrCKh1fYjji9HPyIpEJfvTvy8/ZlTl8RuTizzCPj2ZcWrfaecyOMKs6bQ7u5A==}
dependencies:
cross-fetch: 3.1.6
transitivePeerDependencies:
- encoding
dev: false
/i18next@23.2.11:
resolution: {integrity: sha512-MA4FsxOjyCaOZtRDB4yuwjCvqYEioD4G4LlXOn7SO3rnQUlxTufyLsOqfL9MKakeLRBkefe8bqcs0D6Z/xFk1w==}
dependencies:
'@babel/runtime': 7.22.6
dev: false
/ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
@ -3371,7 +3522,6 @@ packages:
dependencies:
parent-module: 1.0.1
resolve-from: 4.0.0
dev: true
/imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
@ -3415,7 +3565,6 @@ packages:
/is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
dev: true
/is-bigint@1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
@ -3593,6 +3742,10 @@ packages:
resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==}
dev: true
/json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
dev: false
/json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
dev: true
@ -3660,6 +3813,10 @@ packages:
engines: {node: '>=10'}
dev: true
/lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
dev: false
/lint-staged@13.2.3:
resolution: {integrity: sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg==}
engines: {node: ^14.13.1 || >=16.0.0}
@ -3854,6 +4011,18 @@ packages:
tslib: 2.6.0
dev: true
/node-fetch@2.6.12:
resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: false
/node-releases@2.0.13:
resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==}
dev: false
@ -3985,7 +4154,6 @@ packages:
engines: {node: '>=6'}
dependencies:
callsites: 3.1.0
dev: true
/parse-json@4.0.0:
resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==}
@ -3995,6 +4163,16 @@ packages:
json-parse-better-errors: 1.0.2
dev: true
/parse-json@5.2.0:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
dependencies:
'@babel/code-frame': 7.22.5
error-ex: 1.3.2
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
dev: false
/path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@ -4032,7 +4210,6 @@ packages:
/path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
dev: true
/picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
@ -4136,6 +4313,26 @@ packages:
scheduler: 0.23.0
dev: false
/react-i18next@13.0.2(i18next@23.2.11)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-NEVxC32v0oR4egwYM0QM0WE93AiJG5r0NTXTL8mhQfAhsMfDS2fSO6jpluyfsfypP988KzUQrAXncspcJ7+GHA==}
peerDependencies:
i18next: '>= 23.2.3'
react: '>= 16.8.0'
react-dom: '*'
react-native: '*'
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
dependencies:
'@babel/runtime': 7.22.6
html-parse-stringify: 3.0.1
i18next: 23.2.11
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: false
@ -4261,7 +4458,6 @@ packages:
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
dev: true
/resolve@1.22.2:
resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==}
@ -4455,6 +4651,11 @@ packages:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
/source-map@0.5.7:
resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
engines: {node: '>=0.10.0'}
dev: false
/spdx-correct@3.2.0:
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
dependencies:
@ -4664,6 +4865,10 @@ packages:
dependencies:
is-number: 7.0.0
/tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
dev: false
/ts-pattern@5.0.4:
resolution: {integrity: sha512-D5iVliqugv2C9541W2CNXFYNEZxr4TiHuLPuf49tKEdQFp/8y8fR0v1RExUvXkiWozKCwE7zv07C6EKxf0lKuQ==}
dev: false
@ -4882,6 +5087,22 @@ packages:
fsevents: 2.3.2
dev: true
/void-elements@3.1.0:
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
engines: {node: '>=0.10.0'}
dev: false
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: false
/whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: false
/which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies:
@ -4947,6 +5168,11 @@ packages:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: true
/yaml@1.10.2:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
engines: {node: '>= 6'}
dev: false
/yaml@2.3.1:
resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==}
engines: {node: '>= 14'}

View File

@ -3,7 +3,7 @@ import ExcalidrawContainer from './Excalidraw/Container';
import { useCallback, useState } from 'react';
import Sidebar, { SidebarVariant } from './Excalidraw/Sidebar';
import RightTopUI from './Excalidraw/RightTopUI';
import MainMenu from './Excalidraw/MainMenu';
import MainMenuBase from './Excalidraw/MainMenu';
export const ExcalidrawMain = () => {
const [excalidrawAPI, setExcalidrawAPI] = useState<ExcalidrawImperativeAPI>();
@ -32,7 +32,7 @@ export const ExcalidrawMain = () => {
return <Sidebar variant={sidebarVariant} variantProps={{ onClose }} />;
}}
>
<MainMenu />
<MainMenuBase />
</ExcalidrawContainer>
);
};

View File

@ -1,14 +1,13 @@
import { MainMenuLink, MainMenuProvider } from '@/Components/Utilities';
import { MainMenu as MainMenuBase } from '@excalidraw/excalidraw';
const MainMenu = () => {
return (
<MainMenuProvider>
<MainMenuBase>
<MainMenuBase.DefaultItems.ToggleTheme />
<MainMenuBase.DefaultItems.Export />
<MainMenuBase.DefaultItems.SaveAsImage />
<MainMenuLink href="/license">License</MainMenuLink>
</MainMenuProvider>
<MainMenuBase.ItemLink href="/license">License</MainMenuBase.ItemLink>
</MainMenuBase>
);
};

View File

@ -1,6 +1,6 @@
import { ExcalidrawImperativeAPI } from '@excalidraw/excalidraw/types/types';
import { FC, useCallback } from 'react';
import { ToggleButton, ToggleButtonGroup } from '@/Components/Utilities';
import { ToggleButton, ToggleButtonGroup } from '@mui/material';
import { SidebarVariant, SidebarVariantSchema } from './Sidebar';
import { z } from 'zod';
import { LiveCollaborationTrigger } from '@excalidraw/excalidraw';
@ -33,7 +33,7 @@ const RightTopUI: FC<RightTopUIProps> = (props) => {
<>
<LiveCollaborationTrigger
isCollaborating={false}
onSelect={() => console.log('nyya')}
onSelect={() => devlog.log('nyya')}
/>
<ToggleButtonGroup
exclusive

View File

@ -1,22 +1,23 @@
import { SidebarProvider } from '@/Components/Utilities';
import { Sidebar } from '@excalidraw/excalidraw';
import { FC } from 'react';
import { z } from 'zod';
import type { ReactNode } from 'react';
import { Box } from '@mui/material';
export const SidebarBasePropsSchema = z.object({
onClose: z.function().returns(z.void()),
onClose: z.function().args().returns(z.void()),
});
export type SidebarBaseProps = z.infer<typeof SidebarBasePropsSchema> & {
children?: ReactNode;
};
const SidebarProviderBase: FC<SidebarBaseProps> = (props) => {
const SidebarBase: FC<SidebarBaseProps> = (props) => {
return (
<SidebarProvider dockable={true} onClose={props.onClose}>
{props.children}
</SidebarProvider>
<Sidebar dockable={true} onClose={props.onClose}>
<Box sx={{ p: 2 }}>{props.children}</Box>
</Sidebar>
);
};
export default SidebarProviderBase;
export default SidebarBase;

View File

@ -1,16 +1,11 @@
import { FC, useCallback, useEffect, useState } from 'react';
import { Button, SidebarHeader } from '@/Components/Utilities';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { Sidebar } from '@excalidraw/excalidraw';
import { z } from 'zod';
import SidebarProviderBase, { SidebarBasePropsSchema } from './Base';
import { startCollaboration } from '@/webrtc/collaboration';
import {
SignalingData,
decodeSignalingData,
encodeSignalingData,
} from '@/webrtc/utilities';
import { Input, Link } from '@mui/material';
import { fromUrl, toUrl } from '@/utilities/url';
import SidebarBase, { SidebarBasePropsSchema } from './Base';
import { Button, Input, Link } from '@mui/material';
import { copyToClipboard } from '@/utilities/clipboard';
import { useAtom } from 'jotai';
import { PeerConfigurationAtom, PeerConnectionAtom } from '@/atoms/webrtc';
export type CollaborationSidebarProps = z.infer<
typeof CollaborationSidebarPropsSchema
@ -21,14 +16,18 @@ export const CollaborationSidebarPropsSchema = z
.merge(SidebarBasePropsSchema);
const CollaborationSidebar: FC<CollaborationSidebarProps> = (props) => {
const signalingDataCallback = useCallback((data: SignalingData) => {
const encodedData = encodeSignalingData(data);
const url = toUrl({ signalingData: encodedData });
const [peerConnection, setPeerConnection] = useAtom(PeerConnectionAtom);
setCollaborationUrl(url);
const [peerConfiguration] = useAtom(PeerConfigurationAtom);
console.log(data);
}, []);
const initializePeerConnection = useCallback(() => {
if (peerConnection) {
devlog.warn('Peer connection already initialized');
return;
}
setPeerConnection(new RTCPeerConnection(peerConfiguration));
}, [peerConfiguration, peerConnection, setPeerConnection]);
const [collaborationUrl, setCollaborationUrl] = useState<URL | null>(null);
const [isCollaborationStarted, setIsCollaborationStarted] = useState(false);
@ -36,58 +35,35 @@ const CollaborationSidebar: FC<CollaborationSidebarProps> = (props) => {
useState<URL | null>(null);
const onMessage = useCallback((event: MessageEvent) => {
console.log(event);
devlog.log(event);
}, []);
const startCollaborationWrapper = useCallback(async () => {
console.log('start collaboration');
const copyCollaborationUrl = useCallback(
() => copyToClipboard(collaborationUrl?.toString(), undefined),
[collaborationUrl]
);
const signalingData = providedCollaborationUrl
? decodeSignalingData(fromUrl(providedCollaborationUrl).signalingData!)
: undefined;
console.log(signalingData);
await startCollaboration(signalingData, signalingDataCallback, {
onMessage,
});
}, [signalingDataCallback, onMessage, providedCollaborationUrl]);
const copyCollaborationUrl = useCallback(async () => {
console.log('copy collaboration url');
console.log(collaborationUrl?.toString());
await copyToClipboard(collaborationUrl ? collaborationUrl?.toString() : '');
}, [collaborationUrl]);
useEffect(() => {
if (isCollaborationStarted || providedCollaborationUrl) {
startCollaborationWrapper().catch((error) => console.error(error));
// TODO: Check this !!
const offerWerapper = useCallback(async () => {
if (!peerConnection) {
devlog.error('Peer connection not initialized');
return;
}
}, [
isCollaborationStarted,
startCollaborationWrapper,
providedCollaborationUrl,
]);
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
devlog.log(offer);
}, []);
return (
<SidebarProviderBase onClose={props.onClose}>
<SidebarHeader>Collaboration</SidebarHeader>
<Button onClick={() => setIsCollaborationStarted(true)}>
Start Collaboration
<SidebarBase onClose={props.onClose}>
<Sidebar.Header>Collaboration</Sidebar.Header>
<Button onClick={initializePeerConnection}>
Initialize a local peer.
</Button>
<Link>{collaborationUrl?.toString()}</Link>
<Button
onClick={() => {
copyCollaborationUrl().catch((e) => console.error(e));
}}
>
Copy
</Button>
<Input
value={providedCollaborationUrl ?? ''}
onChange={(e) => setProvidedCollaborationUrl(new URL(e.target.value))}
></Input>
</SidebarProviderBase>
<Button> Create a new collaboration session. (offer)</Button>
</SidebarBase>
);
};

View File

@ -1,7 +1,11 @@
import { FC } from 'react';
import { Button, SidebarHeader } from '@/Components/Utilities';
import { Sidebar } from '@excalidraw/excalidraw';
import { z } from 'zod';
import SidebarProviderBase, { SidebarBasePropsSchema } from './Base';
import SidebarBase, { SidebarBasePropsSchema } from './Base';
import { Switch, FormControlLabel, FormGroup } from '@mui/material';
import { useAtom } from 'jotai';
import { LoggerStateAtom } from '@/atoms/debug';
import { useTranslation } from 'react-i18next';
export type GeneralSidebarProps = z.infer<typeof GeneralSidebarPropsSchema>;
@ -10,11 +14,24 @@ export const GeneralSidebarPropsSchema = z
.merge(SidebarBasePropsSchema);
const GeneralSidebar: FC<GeneralSidebarProps> = (props) => {
const { t } = useTranslation();
const [loggerState, setLoggerState] = useAtom(LoggerStateAtom);
return (
<SidebarProviderBase onClose={props.onClose}>
<SidebarHeader>General Settings</SidebarHeader>
<Button>Button</Button>
</SidebarProviderBase>
<SidebarBase onClose={props.onClose}>
<Sidebar.Header>{t('general-settings')}</Sidebar.Header>
<FormGroup>
<FormControlLabel
control={
<Switch
checked={loggerState}
onChange={(e) => setLoggerState?.(e.target.checked)}
/>
}
label={t('toggle-debug-mode')}
/>
</FormGroup>
</SidebarBase>
);
};

View File

@ -1,19 +0,0 @@
import styled from 'styled-components';
import {
Button as ButtonBase,
ToggleButtonGroup as ToggleButtonGroupBase,
ToggleButton as ToggleButtonBase,
} from '@mui/material';
import { MainMenu, Sidebar } from '@excalidraw/excalidraw';
export const SidebarProvider = styled(Sidebar)``;
export const SidebarHeader = styled(Sidebar.Header)``;
export const MainMenuProvider = styled(MainMenu)``;
export const MainMenuLink = styled(MainMenu.ItemLink)``;
export const Button = styled(ButtonBase)``;
export const ToggleButtonGroup = styled(ToggleButtonGroupBase)``;
export const ToggleButton = styled(ToggleButtonBase)``;

3
src/atoms/debug.ts Normal file
View File

@ -0,0 +1,3 @@
import { atom } from 'jotai';
export const LoggerStateAtom = atom(import.meta.env.DEV);

3
src/atoms/ui.ts Normal file
View File

@ -0,0 +1,3 @@
import { atom } from 'jotai';
export const LangCodeAtom = atom('en');

10
src/atoms/webrtc.ts Normal file
View File

@ -0,0 +1,10 @@
import { DefaultPeerConfiguration } from '@/webrtc/connection';
import { atom } from 'jotai';
export const PeerConnectionAtom = atom<RTCPeerConnection | undefined>(
undefined
);
export const PeerConfigurationAtom = atom<RTCConfiguration>(
DefaultPeerConfiguration
);

8
src/globals.ts Normal file
View File

@ -0,0 +1,8 @@
import type { DevLog } from '@/types/devlog';
declare global {
interface Window {
devlog: DevLog;
}
let devlog: DevLog;
}

36
src/i18n.ts Normal file
View File

@ -0,0 +1,36 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import en from '@/i18n/en/translation.json';
import ja_JP from '@/i18n/ja_JP/translation.json';
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
returnEmptyString: false,
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
resources: {
en: {
translation: en,
},
ja_JP: {
translation: ja_JP,
},
},
})
.catch((e) => {
devlog.error(e);
});
export default i18n;

View File

@ -0,0 +1,4 @@
{
"toggle-debug-mode": "Toggle Debug Mode",
"general-settings": "General Settings"
}

View File

@ -0,0 +1 @@
{}

View File

@ -1,11 +1,18 @@
import '@/globals';
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { RouterProvider } from 'react-router-dom';
import router from './routes';
import DevLogProvider from '@/utilities/DevLogProvider';
import '@/i18n';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RouterProvider router={router} />
<DevLogProvider>
<RouterProvider router={router} />
</DevLogProvider>
</React.StrictMode>
);

8
src/types/devlog.ts Normal file
View File

@ -0,0 +1,8 @@
export type Logger = <T>(...args: T[]) => void;
export type DevLog = {
info: Logger;
log: Logger;
error: Logger;
warn: Logger;
};

65
src/types/language.ts Normal file
View File

@ -0,0 +1,65 @@
import { languages } from '@excalidraw/excalidraw';
import { Language } from '@excalidraw/excalidraw/types/i18n';
// Reference:
// https://github.com/excalidraw/excalidraw/blob/e57dc405fa575a330110699ee762dce5918e9e04/src/i18n.ts#L20
const LangCodes = [
'en',
'ar-SA',
'bg-BG',
'ca-ES',
'cs-CZ',
'de-DE',
'el-GR',
'es-ES',
'eu-ES',
'fa-IR',
'fi-FI',
'fr-FR',
'gl-ES',
'he-IL',
'hi-IN',
'hu-HU',
'id-ID',
'it-IT',
'ja-JP',
'kab-KA',
'kk-KZ',
'ko-KR',
'ku-TR',
'lt-LT',
'lv-LV',
'my-MM',
'nb-NO',
'nl-NL',
'nn-NO',
'oc-FR',
'pa-IN',
'pl-PL',
'pt-BR',
'pt-PT',
'ro-RO',
'ru-RU',
'sk-SK',
'sv-SE',
'sl-SI',
'tr-TR',
'uk-UA',
'zh-CN',
'zh-TW',
'vi-VN',
'mr-IN',
] as const;
export type LangCode = (typeof LangCodes)[number];
export type Languages = {
[key in LangCode]: Language;
}[];
export const Languages = languages
.map((lang) => (LangCodes.includes(lang.code as LangCode) ? lang : undefined))
.filter((item) => item !== undefined)
.map((langCode) => ({
langCode: languages.find((lang) => lang.code === langCode?.code),
}));

30
src/types/utilities.ts Normal file
View File

@ -0,0 +1,30 @@
export type Result<T, E> = Ok<T> | Err<E>;
export type Ok<T> = {
v: T;
};
export type Err<E> = {
e: E;
};
export const ok = <T>(v: T): Ok<T> => ({ v });
export const err = <E>(e: E): Err<E> => ({ e });
export const isOk = <T, E>(r: Result<T, E>): r is Ok<T> => 'v' in r;
export const isErr = <T, E>(r: Result<T, E>): r is Err<E> => 'e' in r;
export const unwrap = <T, E>(r: Result<T, E>): T => {
if (isOk(r)) return r.v;
throw r.e;
};
export const re = <T, E>(r: Result<T, E>) => ({
ok: ok(r),
err: err(r),
isOk: isOk(r),
isErr: isErr(r),
unwrap: unwrap(r),
});

View File

@ -0,0 +1,55 @@
import { LoggerStateAtom } from '@/atoms/debug';
import { useAtom } from 'jotai';
import { FC, ReactNode, useCallback, useEffect } from 'react';
import type { Logger } from '@/types/devlog';
export type DevLogProviderProps = {
children: ReactNode;
};
const DevLogProvider: FC<DevLogProviderProps> = (props) => {
const [loggerState] = useAtom(LoggerStateAtom);
const info: Logger = useCallback(
(...args) => {
loggerState && console.info(args);
},
[loggerState]
);
const log: Logger = useCallback(
(...args) => {
loggerState && console.log(args);
},
[loggerState]
);
const error: Logger = useCallback(
(...args) => {
loggerState && console.error(args);
},
[loggerState]
);
const warn: Logger = useCallback(
(...args) => {
loggerState && console.warn(args);
},
[loggerState]
);
useEffect(() => {
window.devlog = {
info,
log,
error,
warn,
};
devlog = window.devlog;
}, [info, log, error, warn]);
return <>{props.children}</>;
};
export default DevLogProvider;

View File

@ -1,3 +1,23 @@
export const copyToClipboard = async (text: string) => {
await navigator.clipboard.writeText(text);
export const copyToClipboard = (
text: string | undefined | null,
callback: (() => void) | undefined
) => {
if (!text) {
devlog.log('no text to copy');
return;
}
devlog.log('copy collaboration url');
devlog.log(text);
navigator.clipboard
.writeText(text)
.then(() => {
if (callback) {
callback();
}
})
.catch((err: DOMException) => {
devlog.error(`Could not copy text why: ${err.toString()}`);
});
};

View File

@ -1,28 +0,0 @@
import establishConnection from './connection';
import type { Connection, DataChannelCallbacks } from './connection';
import type { SignalingData } from './utilities';
export type SignalingDataCallback = (signalingData: SignalingData) => void;
export type OnAnswerCallback = (answer: RTCSessionDescription) => void;
export type OnOfferCallback = (offer: RTCSessionDescription) => void;
export const startCollaboration = async (
signalingData: SignalingData | undefined,
onSignalingData: SignalingDataCallback,
dataChannelCallbacks: DataChannelCallbacks
): Promise<void> => {
let connection: Connection = {
connectionState: 'noConnection',
dataChannelCallbacks,
signalingData,
};
while (connection?.connectionState !== 'established') {
connection = await establishConnection(connection);
if (connection.signalingData) {
onSignalingData(connection.signalingData);
}
}
};

View File

@ -1,96 +1,10 @@
import { match } from 'ts-pattern';
import { SignalingDataType, SignalingData } from './utilities';
import { z } from 'zod';
export const PeerConfiguration: RTCConfiguration = {
export const DefaultPeerConfiguration: RTCConfiguration = {
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
};
export const EstablishingConnectionStateSchema = z.enum([
'noConnection',
'waiting',
'established',
]);
export type EstablishingConnectionState = z.infer<
typeof EstablishingConnectionStateSchema
>;
export type Connection = {
connectionState: EstablishingConnectionState;
signalingData?: SignalingData;
dataChannelCallbacks?: DataChannelCallbacks;
};
export const establishConnection = async ({
connectionState,
signalingData,
dataChannelCallbacks,
}: Connection): Promise<Connection> => {
const peerConnection = createPeerConnection();
return await match(connectionState)
.with('noConnection', async () => {
const _signalingData = await createOffer(peerConnection);
return {
connectionState: 'waiting',
signalingData: _signalingData,
dataChannelCallbacks,
} as Connection;
})
.with('waiting', async () => {
if (!dataChannelCallbacks) {
throw new Error('Data channel callbacks are not set');
}
if (!signalingData) {
throw new Error('Signaling data is not set');
}
await peerConnection.setRemoteDescription(signalingData.sdp);
const connection: Connection = await match(signalingData.type)
.with('offer', async () => {
const answer = await createAnswer(peerConnection);
return {
connectionState: 'waiting',
signalingData: answer,
dataChannelCallbacks,
} as Connection;
})
.with('answer', () => {
return {
connectionState: 'established',
signalingData: undefined,
dataChannelCallbacks,
} as Connection;
})
.exhaustive();
dataChannelHandler(
signalingData.type,
peerConnection,
dataChannelCallbacks
);
return connection;
})
.with('established', () => {
throw new Error(
"Connection can't be established because it is already established"
);
})
.exhaustive();
};
export default establishConnection;
export const createPeerConnection = () => {
const configuration = PeerConfiguration;
return new RTCPeerConnection(configuration);
};
export const createOffer = async (peerConnection: RTCPeerConnection) => {
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
@ -103,7 +17,7 @@ export const createOffer = async (peerConnection: RTCPeerConnection) => {
return signalingData;
} else {
throw new Error('Local description is not set');
devlog.error('Local description is not set');
}
};
@ -143,6 +57,6 @@ export const createAnswer = async (peerConnection: RTCPeerConnection) => {
return signalingData;
} else {
throw new Error('Local description is not set');
devlog.error('Local description is not set');
}
};

View File

@ -4,10 +4,9 @@ import tsconfigPaths from 'vite-tsconfig-paths';
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'@mui/styled-engine': '@mui/styled-engine-sc',
},
define: {
devlog: 'window.devlog',
'process.env': {},
},
optimizeDeps: {
include: [