This commit is contained in:
reya 2023-10-12 09:13:06 +07:00
parent 3b46e71525
commit 35650a40f2
36 changed files with 444 additions and 1447 deletions

View File

@ -47,13 +47,13 @@
"@tauri-apps/plugin-updater": "2.0.0-alpha.1", "@tauri-apps/plugin-updater": "2.0.0-alpha.1",
"@tauri-apps/plugin-upload": "2.0.0-alpha.1", "@tauri-apps/plugin-upload": "2.0.0-alpha.1",
"@tauri-apps/plugin-window": "2.0.0-alpha.1", "@tauri-apps/plugin-window": "2.0.0-alpha.1",
"@tiptap/extension-image": "^2.1.11", "@tiptap/extension-image": "^2.1.12",
"@tiptap/extension-mention": "^2.1.11", "@tiptap/extension-mention": "^2.1.12",
"@tiptap/extension-placeholder": "^2.1.11", "@tiptap/extension-placeholder": "^2.1.12",
"@tiptap/pm": "^2.1.11", "@tiptap/pm": "^2.1.12",
"@tiptap/react": "^2.1.11", "@tiptap/react": "^2.1.12",
"@tiptap/starter-kit": "^2.1.11", "@tiptap/starter-kit": "^2.1.12",
"@tiptap/suggestion": "^2.1.11", "@tiptap/suggestion": "^2.1.12",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"destr": "^2.0.1", "destr": "^2.0.1",
"html-to-text": "^9.0.5", "html-to-text": "^9.0.5",

View File

@ -93,26 +93,26 @@ dependencies:
specifier: 2.0.0-alpha.1 specifier: 2.0.0-alpha.1
version: 2.0.0-alpha.1 version: 2.0.0-alpha.1
'@tiptap/extension-image': '@tiptap/extension-image':
specifier: ^2.1.11 specifier: ^2.1.12
version: 2.1.11(@tiptap/core@2.1.11) version: 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-mention': '@tiptap/extension-mention':
specifier: ^2.1.11 specifier: ^2.1.12
version: 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11)(@tiptap/suggestion@2.1.11) version: 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)(@tiptap/suggestion@2.1.12)
'@tiptap/extension-placeholder': '@tiptap/extension-placeholder':
specifier: ^2.1.11 specifier: ^2.1.12
version: 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) version: 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/pm': '@tiptap/pm':
specifier: ^2.1.11 specifier: ^2.1.12
version: 2.1.11 version: 2.1.12
'@tiptap/react': '@tiptap/react':
specifier: ^2.1.11 specifier: ^2.1.12
version: 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11)(react-dom@18.2.0)(react@18.2.0) version: 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)(react-dom@18.2.0)(react@18.2.0)
'@tiptap/starter-kit': '@tiptap/starter-kit':
specifier: ^2.1.11 specifier: ^2.1.12
version: 2.1.11(@tiptap/pm@2.1.11) version: 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/suggestion': '@tiptap/suggestion':
specifier: ^2.1.11 specifier: ^2.1.12
version: 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) version: 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
dayjs: dayjs:
specifier: ^1.11.10 specifier: ^1.11.10
version: 1.11.10 version: 1.11.10
@ -313,24 +313,24 @@ packages:
'@babel/highlight': 7.22.20 '@babel/highlight': 7.22.20
chalk: 2.4.2 chalk: 2.4.2
/@babel/compat-data@7.22.20: /@babel/compat-data@7.23.2:
resolution: {integrity: sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==} resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dev: false dev: false
/@babel/core@7.23.0: /@babel/core@7.23.2:
resolution: {integrity: sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==} resolution: {integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@ampproject/remapping': 2.2.1 '@ampproject/remapping': 2.2.1
'@babel/code-frame': 7.22.13 '@babel/code-frame': 7.22.13
'@babel/generator': 7.23.0 '@babel/generator': 7.23.0
'@babel/helper-compilation-targets': 7.22.15 '@babel/helper-compilation-targets': 7.22.15
'@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2)
'@babel/helpers': 7.23.1 '@babel/helpers': 7.23.2
'@babel/parser': 7.23.0 '@babel/parser': 7.23.0
'@babel/template': 7.22.15 '@babel/template': 7.22.15
'@babel/traverse': 7.23.0 '@babel/traverse': 7.23.2
'@babel/types': 7.23.0 '@babel/types': 7.23.0
convert-source-map: 2.0.0 convert-source-map: 2.0.0
debug: 4.3.4 debug: 4.3.4
@ -364,7 +364,7 @@ packages:
resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/compat-data': 7.22.20 '@babel/compat-data': 7.23.2
'@babel/helper-validator-option': 7.22.15 '@babel/helper-validator-option': 7.22.15
browserslist: 4.22.1 browserslist: 4.22.1
lru-cache: 5.1.1 lru-cache: 5.1.1
@ -395,13 +395,13 @@ packages:
'@babel/types': 7.23.0 '@babel/types': 7.23.0
dev: false dev: false
/@babel/helper-module-transforms@7.23.0(@babel/core@7.23.0): /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.2):
resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
peerDependencies: peerDependencies:
'@babel/core': ^7.0.0 '@babel/core': ^7.0.0
dependencies: dependencies:
'@babel/core': 7.23.0 '@babel/core': 7.23.2
'@babel/helper-environment-visitor': 7.22.20 '@babel/helper-environment-visitor': 7.22.20
'@babel/helper-module-imports': 7.22.15 '@babel/helper-module-imports': 7.22.15
'@babel/helper-simple-access': 7.22.5 '@babel/helper-simple-access': 7.22.5
@ -440,12 +440,12 @@ packages:
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dev: false dev: false
/@babel/helpers@7.23.1: /@babel/helpers@7.23.2:
resolution: {integrity: sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==} resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/template': 7.22.15 '@babel/template': 7.22.15
'@babel/traverse': 7.23.0 '@babel/traverse': 7.23.2
'@babel/types': 7.23.0 '@babel/types': 7.23.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -466,28 +466,28 @@ packages:
dependencies: dependencies:
'@babel/types': 7.17.0 '@babel/types': 7.17.0
/@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.0): /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.2):
resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
peerDependencies: peerDependencies:
'@babel/core': ^7.0.0-0 '@babel/core': ^7.0.0-0
dependencies: dependencies:
'@babel/core': 7.23.0 '@babel/core': 7.23.2
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
dev: false dev: false
/@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.0): /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.2):
resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
peerDependencies: peerDependencies:
'@babel/core': ^7.0.0-0 '@babel/core': ^7.0.0-0
dependencies: dependencies:
'@babel/core': 7.23.0 '@babel/core': 7.23.2
'@babel/helper-plugin-utils': 7.22.5 '@babel/helper-plugin-utils': 7.22.5
dev: false dev: false
/@babel/runtime@7.23.1: /@babel/runtime@7.23.2:
resolution: {integrity: sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==} resolution: {integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
regenerator-runtime: 0.14.0 regenerator-runtime: 0.14.0
@ -518,8 +518,8 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@babel/traverse@7.23.0: /@babel/traverse@7.23.2:
resolution: {integrity: sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==} resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/code-frame': 7.22.13 '@babel/code-frame': 7.22.13
@ -984,7 +984,7 @@ packages:
/@radix-ui/primitive@1.0.1: /@radix-ui/primitive@1.0.1:
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
dev: false dev: false
/@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0):
@ -1000,7 +1000,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1026,7 +1026,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
'@types/react-dom': 18.2.13 '@types/react-dom': 18.2.13
@ -1047,7 +1047,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1071,7 +1071,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1099,7 +1099,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
@ -1119,7 +1119,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
dev: false dev: false
@ -1133,7 +1133,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
dev: false dev: false
@ -1151,7 +1151,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1181,7 +1181,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
dev: false dev: false
@ -1199,7 +1199,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
@ -1224,7 +1224,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1247,7 +1247,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
dev: false dev: false
@ -1265,7 +1265,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1288,7 +1288,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1313,7 +1313,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
@ -1332,7 +1332,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1370,7 +1370,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1405,7 +1405,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@floating-ui/react-dom': 2.0.2(react-dom@18.2.0)(react@18.2.0) '@floating-ui/react-dom': 2.0.2(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1435,7 +1435,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
'@types/react-dom': 18.2.13 '@types/react-dom': 18.2.13
@ -1456,7 +1456,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
@ -1478,7 +1478,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-slot': 1.0.2(@types/react@18.2.28)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
'@types/react-dom': 18.2.13 '@types/react-dom': 18.2.13
@ -1499,7 +1499,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1524,7 +1524,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
@ -1543,7 +1543,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/primitive': 1.0.1 '@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.28)(react@18.2.0)
@ -1571,7 +1571,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
dev: false dev: false
@ -1585,7 +1585,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
@ -1600,7 +1600,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
@ -1615,7 +1615,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
dev: false dev: false
@ -1629,7 +1629,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/rect': 1.0.1 '@radix-ui/rect': 1.0.1
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
@ -1644,7 +1644,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.28)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.28)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
react: 18.2.0 react: 18.2.0
@ -1663,7 +1663,7 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.13)(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.28 '@types/react': 18.2.28
'@types/react-dom': 18.2.13 '@types/react-dom': 18.2.13
@ -1674,7 +1674,7 @@ packages:
/@radix-ui/rect@1.0.1: /@radix-ui/rect@1.0.1:
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
dev: false dev: false
/@reactflow/background@11.3.3(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0): /@reactflow/background@11.3.3(@types/react@18.2.28)(react-dom@18.2.0)(react@18.2.0):
@ -2200,222 +2200,222 @@ packages:
'@tauri-apps/api': 2.0.0-alpha.6 '@tauri-apps/api': 2.0.0-alpha.6
dev: false dev: false
/@tiptap/core@2.1.11(@tiptap/pm@2.1.11): /@tiptap/core@2.1.12(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-1W2DdjpPwfphHgQ3Qm4s5wzCnEjiXm1TeZ+6/zBl89yKURXgv8Mw1JGdj/NcImQjtDcsNn97MscACK3GKbEJBA==} resolution: {integrity: sha512-ZGc3xrBJA9KY8kln5AYTj8y+GDrKxi7u95xIl2eccrqTY5CQeRu6HRNM1yT4mAjuSaG9jmazyjGRlQuhyxCKxQ==}
peerDependencies: peerDependencies:
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
dev: false dev: false
/@tiptap/extension-blockquote@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-blockquote@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-IEVe3goA0rgp1G8Wm733BSRJiy71Vh2fmTCyZKWmc2A6GREVSy1X3fCvAo6pMENRObhjIoaBQUCE3p4iJYOxqg==} resolution: {integrity: sha512-Qb3YRlCfugx9pw7VgLTb+jY37OY4aBJeZnqHzx4QThSm13edNYjasokbX0nTwL1Up4NPTcY19JUeHt6fVaVVGg==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-bold@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-bold@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-vhdkBtvd029ufOYt2ug49Gz+RLKSczO/CCqKYBqBmpIpsifyK7M6jkgamvAQg3c/vYk0LNcKiL2dp0Jp7L+5Gw==} resolution: {integrity: sha512-AZGxIxcGU1/y6V2YEbKsq6BAibL8yQrbRm6EdcBnby41vj1WziewEKswhLGmZx5IKM2r2ldxld03KlfSIlKQZg==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-bubble-menu@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11): /@tiptap/extension-bubble-menu@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-WFJJpZvl9DP94Y5RQZB/THDxvDbrTo8tuhjT7yWlhseJ6zyhWmRXdutt39wfSZNFxitv/As+s7cO9aYLML/TVg==} resolution: {integrity: sha512-gAGi21EQ4wvLmT7klgariAc2Hf+cIjaNU2NWze3ut6Ku9gUo5ZLqj1t9SKHmNf4d5JG63O8GxpErqpA7lHlRtw==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
tippy.js: 6.3.7 tippy.js: 6.3.7
dev: false dev: false
/@tiptap/extension-bullet-list@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-bullet-list@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-SOOVH2aSmdMtjWL7TTLbN72xbAFz2G5jifT4UCXb7Qx6LsyhNCyDCu0ukOW8rSosGoSdmBXxAsD9sBJ1jEOmZw==} resolution: {integrity: sha512-vtD8vWtNlmAZX8LYqt2yU9w3mU9rPCiHmbp4hDXJs2kBnI0Ju/qAyXFx6iJ3C3XyuMnMbJdDI9ee0spAvFz7cQ==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-code-block@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11): /@tiptap/extension-code-block@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-QhmhCCWqg/5qLXpZ3sl2A0rqJqV8zMOegcxUFaqcJMOqNbsuHcRgc9C+1hWSVLbCmstB7M6sgF02QpTBOkYHxg==} resolution: {integrity: sha512-RXtSYCVsnk8D+K80uNZShClfZjvv1EgO42JlXLVGWQdIgaNyuOv/6I/Jdf+ZzhnpsBnHufW+6TJjwP5vJPSPHA==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
dev: false dev: false
/@tiptap/extension-code@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-code@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-G0UEbMFunujy/F86yHN0/dumPLbwTis9C+6IQv1XRPNsV28U0MgxBhlPcJUgyO5lwuleePDxiBVcRv2XrysgKw==} resolution: {integrity: sha512-CRiRq5OTC1lFgSx6IMrECqmtb93a0ZZKujEnaRhzWliPBjLIi66va05f/P1vnV6/tHaC3yfXys6dxB5A4J8jxw==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-document@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-document@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-L/iLuqxvJep33ycCFNrnUhdR0VtcZyeNnqB+ZvVHzEwLoRud+LBy44lpEdBrAFsvRm3DG14m/FGYL+TfaD0vxA==} resolution: {integrity: sha512-0QNfAkCcFlB9O8cUNSwTSIQMV9TmoEhfEaLz/GvbjwEq4skXK3bU+OQX7Ih07waCDVXIGAZ7YAZogbvrn/WbOw==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-dropcursor@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11): /@tiptap/extension-dropcursor@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-MiJepRpHlu93aInOMW8NeRCvm9VE5rL0MA9TONY/IspJFGFIqonc/01J6t33JQa3Xh/x3xAfis4nKa/UazeVJw==} resolution: {integrity: sha512-0tT/q8nL4NBCYPxr9T0Brck+RQbWuczm9nV0bnxgt0IiQXoRHutfPWdS7GA65PTuVRBS/3LOco30fbjFhkfz/A==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
dev: false dev: false
/@tiptap/extension-floating-menu@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11): /@tiptap/extension-floating-menu@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-ExeoOQ6nT0CY0eWx6WjbG+osurXLXa7XrqIdhCAcTmzBAlGiKt8khX9qaZ+QF+BRK1r1lja2KX+5/fpLK7Dt1g==} resolution: {integrity: sha512-uo0ydCJNg6AWwLT6cMUJYVChfvw2PY9ZfvKRhh9YJlGfM02jS4RUG/bJBts6R37f+a5FsOvAVwg8EvqPlNND1A==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
tippy.js: 6.3.7 tippy.js: 6.3.7
dev: false dev: false
/@tiptap/extension-gapcursor@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11): /@tiptap/extension-gapcursor@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-P/xjyhSOVyop5XXbNtRPgrooQrSlpYblwR67ClI9FAC7uQliuOwi5VcndmEItjWWSe85kJa2IHjOS7mLYvJe8A==} resolution: {integrity: sha512-zFYdZCqPgpwoB7whyuwpc8EYLYjUE5QYKb8vICvc+FraBUDM51ujYhFSgJC3rhs8EjI+8GcK8ShLbSMIn49YOQ==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
dev: false dev: false
/@tiptap/extension-hard-break@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-hard-break@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-qhiPe6FA0b6PPb/ITlgSnY0l9tEVmXZ9e7eSjvks12ORfqL/dofSCLtChHWvhZxugzo92xejG2hXLi6lyOLbkg==} resolution: {integrity: sha512-nqKcAYGEOafg9D+2cy1E4gHNGuL12LerVa0eS2SQOb+PT8vSel9OTKU1RyZldsWSQJ5rq/w4uIjmLnrSR2w6Yw==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-heading@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-heading@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-QBtl0S1aDFB+F1wvTrS5iGdNUEeXp+WuTddj+L2f5EP4KqG2x7sj7e7ENMy20g/l8tbKwzd3AZZydvClH4Ybbw==} resolution: {integrity: sha512-MoANP3POAP68Ko9YXarfDKLM/kXtscgp6m+xRagPAghRNujVY88nK1qBMZ3JdvTVN6b/ATJhp8UdrZX96TLV2w==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-history@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11): /@tiptap/extension-history@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-88dovV2O9icmBn0IvaArFFeS6X5ts6BxZPu5VbGML8KBL8iAu+Og7RXEPdOy5e13K0K4V21fDpO3n7KdvNOAYQ==} resolution: {integrity: sha512-6b7UFVkvPjq3LVoCTrYZAczt5sQrQUaoDWAieVClVZoFLfjga2Fwjcfgcie8IjdPt8YO2hG/sar/c07i9vM0Sg==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
dev: false dev: false
/@tiptap/extension-horizontal-rule@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11): /@tiptap/extension-horizontal-rule@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-uvHPa2YCKnDhtSBSZB3lk5U4H3wRKP0DNvVx4Y2F7MdQianVzcyOd1pZYO9BQs+lUB1aZots6doE69Zqz3mU2Q==} resolution: {integrity: sha512-RRuoK4KxrXRrZNAjJW5rpaxjiP0FJIaqpi7nFbAua2oHXgsCsG8qbW2Y0WkbIoS8AJsvLZ3fNGsQ8gpdliuq3A==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
dev: false dev: false
/@tiptap/extension-image@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-image@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-dFFRvzl9F4fEcG95nyka72TeV127C1UVaMm816GHoFlVEFGV4yJ8NKgzT3UEDgFcs6OPwPlt8tuHuDeYm7EVOQ==} resolution: {integrity: sha512-VCgOTeNLuoR89WoCESLverpdZpPamOd7IprQbDIeG14sUySt7RHNgf2AEfyTYJEHij12rduvAwFzerPldVAIJg==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-italic@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-italic@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-QmDsHtnBBit/1KtQpBPxjSPjDC1mVKtoNTgsEwMWK6YAkCKOKPj7oPEqqjaNZIRMKPPzE5XCsfBoS3jtVmo+6A==} resolution: {integrity: sha512-/XYrW4ZEWyqDvnXVKbgTXItpJOp2ycswk+fJ3vuexyolO6NSs0UuYC6X4f+FbHYL5VuWqVBv7EavGa+tB6sl3A==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-list-item@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-list-item@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-YhwHaPGhffsFsg/zjCu1G24//j/BTRDRZbZXmMwp77m1yEqPULcWyoWrI+gUzetQxJRD/ruAucqjLtoLLfICmQ==} resolution: {integrity: sha512-Gk7hBFofAPmNQ8+uw8w5QSsZOMEGf7KQXJnx5B022YAUJTYYxO3jYVuzp34Drk9p+zNNIcXD4kc7ff5+nFOTrg==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-mention@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11)(@tiptap/suggestion@2.1.11): /@tiptap/extension-mention@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)(@tiptap/suggestion@2.1.12):
resolution: {integrity: sha512-QMHmAkhiDQEgAdUHdKRfVna0AINcbSbQCrpgwKLIHGWcpbi1zJbAPpm+xngbl0I9ZNxaMzbP4utTAzeQ92pJkw==} resolution: {integrity: sha512-Nc8wFlyPp+/48IpOFPk2O3hYsF465wizcM3aihMvZM96Ahic7dvv9yVptyOfoOwgpExl2FIn1QPjRDXF60VAUg==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
'@tiptap/suggestion': ^2.0.0 '@tiptap/suggestion': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
'@tiptap/suggestion': 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) '@tiptap/suggestion': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-ordered-list@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-ordered-list@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-/tghfEJ5U7WFbF8xyOqRJks8KxP/lRjnroMXMglaushSMx8PYPo1dZDB/dJZw7ksy47MAaKJfKlx3gyN2CPXBQ==} resolution: {integrity: sha512-tF6VGl+D2avCgn9U/2YLJ8qVmV6sPE/iEzVAFZuOSe6L0Pj7SQw4K6AO640QBob/d8VrqqJFHCb6l10amJOnXA==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-paragraph@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-paragraph@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-gXMgJ2CU3X4yh1wKnb8RdbDmhITB76pH6DX0uWprmEgvzNMN3Qw+h5uBD9lgxg1WVghbCmkG9mY9J4PPbPTLxw==} resolution: {integrity: sha512-hoH/uWPX+KKnNAZagudlsrr4Xu57nusGekkJWBcrb5MCDE91BS+DN2xifuhwXiTHxnwOMVFjluc0bPzQbkArsw==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-placeholder@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11): /@tiptap/extension-placeholder@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-laHYRFxJWj6m72Yf1v6Q5nF2nvwWpQlKUj6Yu/yluOOoVE92HpLqCAvA8RamqLtPiw5VxR3v3oCY0WNeQRvyIg==} resolution: {integrity: sha512-K52o7B1zkP4vaVy3z4ZwHn+tQy6KlXtedj1skLg+796ImwH2GYS5z6MFOTfKzBO2hLncUzLco/s0C5PLCD6SDw==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
dev: false dev: false
/@tiptap/extension-strike@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-strike@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-UnjeSVgu3bDuyjjUdWsUErRCoQKAHCzH/pAiqTEPEEdFYgZFQPBpcJICRVdlYjRmI2ZKh6d0TMUS55m7ckmwmQ==} resolution: {integrity: sha512-HlhrzIjYUT8oCH9nYzEL2QTTn8d1ECnVhKvzAe6x41xk31PjLMHTUy8aYjeQEkWZOWZ34tiTmslV1ce6R3Dt8g==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/extension-text@2.1.11(@tiptap/core@2.1.11): /@tiptap/extension-text@2.1.12(@tiptap/core@2.1.12):
resolution: {integrity: sha512-Iey0EXYv9079+lbHMvZtLc6XcYfKrq++msEXuFFNHxvL0i/XzndhGf+qlDhLROLgEtDiiTqzOBBwFCGlFjbDow==} resolution: {integrity: sha512-rCNUd505p/PXwU9Jgxo4ZJv4A3cIBAyAqlx/dtcY6cjztCQuXJhuQILPhjGhBTOLEEL4kW2wQtqzCmb7O8i2jg==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
dev: false dev: false
/@tiptap/pm@2.1.11: /@tiptap/pm@2.1.12:
resolution: {integrity: sha512-vBIAic+H8fjHfT8r2qJkAOxdx1Iiss9+qMyujAoIdPkiyjEc4+sXcM0qSYgIr6KL5icITyuK8J7x/V62VfB7Uw==} resolution: {integrity: sha512-Q3MXXQABG4CZBesSp82yV84uhJh/W0Gag6KPm2HRWPimSFELM09Z9/5WK9RItAYE0aLhe4Krnyiczn9AAa1tQQ==}
dependencies: dependencies:
prosemirror-changeset: 2.2.1 prosemirror-changeset: 2.2.1
prosemirror-collab: 1.3.1 prosemirror-collab: 1.3.1
@ -2437,56 +2437,56 @@ packages:
prosemirror-view: 1.32.0 prosemirror-view: 1.32.0
dev: false dev: false
/@tiptap/react@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11)(react-dom@18.2.0)(react@18.2.0): /@tiptap/react@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OLU4uqMeCE/LKz/GY2P1JRykUgHJDoPROHUa1IOnVpj/1FbbBHDyurT3eomwsVzScTULbrKGwrS3ada6QAmTTA==} resolution: {integrity: sha512-RMO4QmmpL7sPR7w8o1Wq0hrUe/ttHzsn5I/eWwqg1d3fGx5y9mOdfCoQ9XBtm49Xzdejy3QVzt4zYp9fX0X/xg==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
react: ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/extension-bubble-menu': 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) '@tiptap/extension-bubble-menu': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/extension-floating-menu': 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) '@tiptap/extension-floating-menu': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@tiptap/starter-kit@2.1.11(@tiptap/pm@2.1.11): /@tiptap/starter-kit@2.1.12(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-kZXwuo9yxrs1ASxluRKjXThjdcy90d7owJxnJWD7SyEwXaXYc4h+Ar1M9rP3jieCDBuRTtCgvAOKbVbhnRJ2jg==} resolution: {integrity: sha512-+RoP1rWV7rSCit2+3wl2bjvSRiePRJE/7YNKbvH8Faz/+AMO23AFegHoUFynR7U0ouGgYDljGkkj35e0asbSDA==}
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/extension-blockquote': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-blockquote': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-bold': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-bold': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-bullet-list': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-bullet-list': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-code': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-code': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-code-block': 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) '@tiptap/extension-code-block': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/extension-document': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-document': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-dropcursor': 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) '@tiptap/extension-dropcursor': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/extension-gapcursor': 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) '@tiptap/extension-gapcursor': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/extension-hard-break': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-hard-break': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-heading': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-heading': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-history': 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) '@tiptap/extension-history': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/extension-horizontal-rule': 2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11) '@tiptap/extension-horizontal-rule': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/extension-italic': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-italic': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-list-item': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-list-item': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-ordered-list': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-ordered-list': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-paragraph': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-paragraph': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-strike': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-strike': 2.1.12(@tiptap/core@2.1.12)
'@tiptap/extension-text': 2.1.11(@tiptap/core@2.1.11) '@tiptap/extension-text': 2.1.12(@tiptap/core@2.1.12)
transitivePeerDependencies: transitivePeerDependencies:
- '@tiptap/pm' - '@tiptap/pm'
dev: false dev: false
/@tiptap/suggestion@2.1.11(@tiptap/core@2.1.11)(@tiptap/pm@2.1.11): /@tiptap/suggestion@2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12):
resolution: {integrity: sha512-AVMB4x1X3eU7QCO1A8URQK0W7ps5dsVzveIP7+c//Z/GYe8lFSGIUnEbLJdr6bwgPkRL56m7c9+oZqVST5wfjQ==} resolution: {integrity: sha512-rhlLWwVkOodBGRMK0mAmE34l2a+BqM2Y7q1ViuQRBhs/6sZ8d83O4hARHKVwqT5stY4i1l7d7PoemV3uAGI6+g==}
peerDependencies: peerDependencies:
'@tiptap/core': ^2.0.0 '@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0 '@tiptap/pm': ^2.0.0
dependencies: dependencies:
'@tiptap/core': 2.1.11(@tiptap/pm@2.1.11) '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.11 '@tiptap/pm': 2.1.12
dev: false dev: false
/@trivago/prettier-plugin-sort-imports@4.2.0(prettier@3.0.3): /@trivago/prettier-plugin-sort-imports@4.2.0(prettier@3.0.3):
@ -3135,7 +3135,7 @@ packages:
hasBin: true hasBin: true
dependencies: dependencies:
caniuse-lite: 1.0.30001547 caniuse-lite: 1.0.30001547
electron-to-chromium: 1.4.549 electron-to-chromium: 1.4.551
node-releases: 2.0.13 node-releases: 2.0.13
update-browserslist-db: 1.0.13(browserslist@4.22.1) update-browserslist-db: 1.0.13(browserslist@4.22.1)
@ -3537,8 +3537,8 @@ packages:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true dev: true
/electron-to-chromium@1.4.549: /electron-to-chromium@1.4.551:
resolution: {integrity: sha512-gpXfJslSi4hYDkA0mTLEpYKRv9siAgSUgZ+UWyk+J5Cttpd1ThCVwdclzIwQSclz3hYn049+M2fgrP1WpvF8xg==} resolution: {integrity: sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==}
/emoji-regex@9.2.2: /emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
@ -3735,7 +3735,7 @@ packages:
peerDependencies: peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies: dependencies:
'@babel/runtime': 7.23.1 '@babel/runtime': 7.23.2
aria-query: 5.3.0 aria-query: 5.3.0
array-includes: 3.1.7 array-includes: 3.1.7
array.prototype.flatmap: 1.3.2 array.prototype.flatmap: 1.3.2
@ -5105,10 +5105,10 @@ packages:
resolution: {integrity: sha512-voUkdd/jHWrG+7NS+mX49Pat+POKdgGW78V7pYMSrTaOjUitR6ySEcAci8hn17Rsx1IMI3+5w41dkADM1J1ZEg==} resolution: {integrity: sha512-voUkdd/jHWrG+7NS+mX49Pat+POKdgGW78V7pYMSrTaOjUitR6ySEcAci8hn17Rsx1IMI3+5w41dkADM1J1ZEg==}
hasBin: true hasBin: true
dependencies: dependencies:
'@babel/core': 7.23.0 '@babel/core': 7.23.2
'@babel/generator': 7.23.0 '@babel/generator': 7.23.0
'@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.0) '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2)
'@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.0) '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.2)
'@babel/types': 7.23.0 '@babel/types': 7.23.0
kleur: 4.1.5 kleur: 4.1.5
rollup: 3.29.4 rollup: 3.29.4

91
src-tauri/Cargo.lock generated
View File

@ -170,18 +170,6 @@ dependencies = [
"x11rb", "x11rb",
] ]
[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]] [[package]]
name = "async-broadcast" name = "async-broadcast"
version = "0.5.1" version = "0.5.1"
@ -406,12 +394,6 @@ dependencies = [
"rustc-demangle", "rustc-demangle",
] ]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.4" version = "0.21.4"
@ -439,17 +421,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "blake2b_simd"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq 0.3.0",
]
[[package]] [[package]]
name = "block" name = "block"
version = "0.1.6" version = "0.1.6"
@ -860,12 +831,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "constant_time_eq"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
[[package]] [[package]]
name = "convert_case" name = "convert_case"
version = "0.4.0" version = "0.4.0"
@ -1051,9 +1016,9 @@ dependencies = [
[[package]] [[package]]
name = "curl-sys" name = "curl-sys"
version = "0.4.67+curl-8.3.0" version = "0.4.68+curl-8.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cc35d066510b197a0f72de863736641539957628c8a42e70e27c66849e77c34" checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -2554,7 +2519,6 @@ name = "lume"
version = "1.2.6" version = "1.2.6"
dependencies = [ dependencies = [
"keyring", "keyring",
"rust-argon2",
"serde", "serde",
"serde_json", "serde_json",
"sqlx-cli", "sqlx-cli",
@ -3444,7 +3408,7 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06"
dependencies = [ dependencies = [
"base64 0.21.4", "base64",
"indexmap 1.9.3", "indexmap 1.9.3",
"line-wrap", "line-wrap",
"quick-xml 0.29.0", "quick-xml 0.29.0",
@ -3724,7 +3688,7 @@ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata 0.4.1", "regex-automata 0.4.1",
"regex-syntax 0.8.0", "regex-syntax 0.8.1",
] ]
[[package]] [[package]]
@ -3744,7 +3708,7 @@ checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-syntax 0.8.0", "regex-syntax 0.8.1",
] ]
[[package]] [[package]]
@ -3755,9 +3719,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3cbb081b9784b07cceb8824c8583f86db4814d172ab043f3c23f7dc600bf83d" checksum = "56d84fdd47036b038fc80dd333d10b6aab10d5d31f4a366e20014def75328d33"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
@ -3765,7 +3729,7 @@ version = "0.11.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
dependencies = [ dependencies = [
"base64 0.21.4", "base64",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
@ -3861,18 +3825,6 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rust-argon2"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b50162d19404029c1ceca6f6980fe40d45c8b369f6f44446fa14bb39573b5bb9"
dependencies = [
"base64 0.13.1",
"blake2b_simd",
"constant_time_eq 0.1.5",
"crossbeam-utils",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -3932,7 +3884,7 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
dependencies = [ dependencies = [
"base64 0.21.4", "base64",
] ]
[[package]] [[package]]
@ -4167,7 +4119,7 @@ version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237"
dependencies = [ dependencies = [
"base64 0.21.4", "base64",
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
@ -4521,7 +4473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64 0.21.4", "base64",
"bitflags 2.4.0", "bitflags 2.4.0",
"byteorder", "byteorder",
"bytes", "bytes",
@ -4564,7 +4516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64 0.21.4", "base64",
"bitflags 2.4.0", "bitflags 2.4.0",
"byteorder", "byteorder",
"crc", "crc",
@ -4692,9 +4644,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.4.1" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]] [[package]]
name = "swift-rs" name = "swift-rs"
@ -4702,7 +4654,7 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bbdb58577b6301f8d17ae2561f32002a5bae056d444e0f69e611e504a276204" checksum = "1bbdb58577b6301f8d17ae2561f32002a5bae056d444e0f69e611e504a276204"
dependencies = [ dependencies = [
"base64 0.21.4", "base64",
"serde", "serde",
"serde_json", "serde_json",
] ]
@ -4924,7 +4876,7 @@ version = "2.0.0-alpha.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8acf40451edf9ccd16d110cb10e9959fec6ccca286c5cc66b859878f8aa9a7b" checksum = "d8acf40451edf9ccd16d110cb10e9959fec6ccca286c5cc66b859878f8aa9a7b"
dependencies = [ dependencies = [
"base64 0.21.4", "base64",
"brotli", "brotli",
"ico", "ico",
"json-patch", "json-patch",
@ -5159,7 +5111,7 @@ name = "tauri-plugin-updater"
version = "2.0.0-alpha.2" version = "2.0.0-alpha.2"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#8902fe9adf256c52e7e6a14370f56d0b4780a3a2" source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#8902fe9adf256c52e7e6a14370f56d0b4780a3a2"
dependencies = [ dependencies = [
"base64 0.21.4", "base64",
"dirs-next", "dirs-next",
"futures-util", "futures-util",
"http", "http",
@ -6340,7 +6292,7 @@ version = "0.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb3bc6ed7e3d905a5a963a3e4e9ee5ede76408e50de42d68e523ee75ab1c78a" checksum = "2cb3bc6ed7e3d905a5a963a3e4e9ee5ede76408e50de42d68e523ee75ab1c78a"
dependencies = [ dependencies = [
"base64 0.21.4", "base64",
"block", "block",
"cocoa 0.24.1", "cocoa 0.24.1",
"core-graphics 0.22.3", "core-graphics 0.22.3",
@ -6527,7 +6479,7 @@ dependencies = [
"aes 0.8.3", "aes 0.8.3",
"byteorder", "byteorder",
"bzip2", "bzip2",
"constant_time_eq 0.1.5", "constant_time_eq",
"crc32fast", "crc32fast",
"crossbeam-utils", "crossbeam-utils",
"flate2", "flate2",
@ -6559,12 +6511,11 @@ dependencies = [
[[package]] [[package]]
name = "zstd-sys" name = "zstd-sys"
version = "2.0.8+zstd.1.5.5" version = "2.0.9+zstd.1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
dependencies = [ dependencies = [
"cc", "cc",
"libc",
"pkg-config", "pkg-config",
] ]

View File

@ -40,7 +40,6 @@ tauri-plugin-sql = { git = "hhttps://github.com/tauri-apps/plugins-workspace", b
sqlx-cli = { version = "0.7.0", default-features = false, features = [ sqlx-cli = { version = "0.7.0", default-features = false, features = [
"sqlite", "sqlite",
] } ] }
rust-argon2 = "1.0"
webpage = { version = "1.6.0", features = ["serde"] } webpage = { version = "1.6.0", features = ["serde"] }
keyring = "2" keyring = "2"

View File

@ -99,10 +99,13 @@ fn secure_save(key: String, value: String) -> Result<(), ()> {
} }
#[tauri::command] #[tauri::command]
fn secure_load(key: String) -> Result<String, ()> { fn secure_load(key: String) -> Result<String, String> {
let entry = Entry::new("lume", &key).expect("Failed to create entry"); let entry = Entry::new("lume", &key).expect("Failed to create entry");
let password = entry.get_password().unwrap(); if let Ok(password) = entry.get_password() {
Ok(password) Ok(password)
} else {
Err("not found".to_string())
}
} }
fn main() { fn main() {

View File

@ -27,23 +27,14 @@ export default function App() {
try { try {
const totalAccount = await db.checkAccount(); const totalAccount = await db.checkAccount();
const stronghold = sessionStorage.getItem('stronghold');
const privkey = JSON.parse(stronghold).state.privkey || null;
const onboarding = localStorage.getItem('onboarding'); const onboarding = localStorage.getItem('onboarding');
const step = JSON.parse(onboarding).state.step || null; const step = JSON.parse(onboarding).state.step || null;
if (totalAccount === 0) { // redirect to welcome screen if none user exist
return redirect('/auth/welcome'); if (totalAccount === 0) return redirect('/auth/welcome');
} else {
if (step) {
return redirect(step);
}
if (!privkey) { // restart onboarding process
return redirect('/auth/unlock'); if (step) return redirect(step);
}
}
return null; return null;
} catch (e) { } catch (e) {
@ -204,13 +195,6 @@ export default function App() {
return { Component: ImportStep2Screen }; return { Component: ImportStep2Screen };
}, },
}, },
{
path: 'step-3',
async lazy() {
const { ImportStep3Screen } = await import('@app/auth/import/step-3');
return { Component: ImportStep3Screen };
},
},
], ],
}, },
{ {
@ -232,13 +216,6 @@ export default function App() {
return { Component: CreateStep2Screen }; return { Component: CreateStep2Screen };
}, },
}, },
{
path: 'step-3',
async lazy() {
const { CreateStep3Screen } = await import('@app/auth/create/step-3');
return { Component: CreateStep3Screen };
},
},
], ],
}, },
{ {
@ -273,34 +250,6 @@ export default function App() {
return { Component: CompleteScreen }; return { Component: CompleteScreen };
}, },
}, },
{
path: 'unlock',
async lazy() {
const { UnlockScreen } = await import('@app/auth/unlock');
return { Component: UnlockScreen };
},
},
{
path: 'lock',
async lazy() {
const { LockScreen } = await import('@app/auth/lock');
return { Component: LockScreen };
},
},
{
path: 'migrate',
async lazy() {
const { MigrateScreen } = await import('@app/auth/migrate');
return { Component: MigrateScreen };
},
},
{
path: 'reset',
async lazy() {
const { ResetScreen } = await import('@app/auth/reset');
return { Component: ResetScreen };
},
},
], ],
}, },
{ {

View File

@ -1,19 +1,6 @@
import { useEffect } from 'react';
import { Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
import { useOnboarding } from '@stores/onboarding';
import { useStronghold } from '@stores/stronghold';
export function AuthCreateScreen() { export function AuthCreateScreen() {
const [step, tmpPrivkey] = useOnboarding((state) => [state.step, state.tempPrivkey]);
const setPrivkey = useStronghold((state) => state.setPrivkey);
useEffect(() => {
if (step) {
setPrivkey(tmpPrivkey);
}
}, [tmpPrivkey]);
return ( return (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
<Outlet /> <Outlet />

View File

@ -11,13 +11,11 @@ import { useStorage } from '@libs/storage/provider';
import { CopyIcon } from '@shared/icons'; import { CopyIcon } from '@shared/icons';
import { useOnboarding } from '@stores/onboarding'; import { useOnboarding } from '@stores/onboarding';
import { useStronghold } from '@stores/stronghold';
export function CreateStep1Screen() { export function CreateStep1Screen() {
const { db } = useStorage(); const { db } = useStorage();
const navigate = useNavigate(); const navigate = useNavigate();
const setPrivkey = useStronghold((state) => state.setPrivkey);
const setTempPrivkey = useOnboarding((state) => state.setTempPrivkey); const setTempPrivkey = useOnboarding((state) => state.setTempPrivkey);
const setPubkey = useOnboarding((state) => state.setPubkey); const setPubkey = useOnboarding((state) => state.setPubkey);
const setStep = useOnboarding((state) => state.setStep); const setStep = useOnboarding((state) => state.setStep);
@ -67,7 +65,6 @@ export function CreateStep1Screen() {
setLoading(true); setLoading(true);
// update state // update state
setPrivkey(privkey);
setTempPrivkey(privkey); // only use if user close app and reopen it setTempPrivkey(privkey); // only use if user close app and reopen it
setPubkey(pubkey); setPubkey(pubkey);

View File

@ -1,122 +1,152 @@
import { NDKKind } from '@nostr-dev-kit/ndk';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Resolver, useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import { EyeOffIcon, EyeOnIcon, LoaderIcon } from '@shared/icons'; import { AvatarUploader } from '@shared/avatarUploader';
import { BannerUploader } from '@shared/bannerUploader';
import { LoaderIcon } from '@shared/icons';
import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle'; import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle';
import { Image } from '@shared/image';
import { useOnboarding } from '@stores/onboarding'; import { useOnboarding } from '@stores/onboarding';
import { useStronghold } from '@stores/stronghold'; import { WidgetKinds } from '@stores/widgets';
type FormValues = { import { useNostr } from '@utils/hooks/useNostr';
password: string;
};
const resolver: Resolver<FormValues> = async (values) => {
return {
values: values.password ? values : {},
errors: !values.password
? {
password: {
type: 'required',
message: 'This is required.',
},
}
: {},
};
};
export function CreateStep2Screen() { export function CreateStep2Screen() {
const navigate = useNavigate(); const navigate = useNavigate();
const setStep = useOnboarding((state) => state.setStep); const setStep = useOnboarding((state) => state.setStep);
const pubkey = useOnboarding((state) => state.pubkey);
const privkey = useStronghold((state) => state.privkey);
const [passwordInput, setPasswordInput] = useState('password');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [picture, setPicture] = useState('https://void.cat/d/5VKmKyuHyxrNMf9bWSVPih');
const [banner, setBanner] = useState('');
const { db } = useStorage(); const { db } = useStorage();
const { publish } = useNostr();
// toggle private key
const showPassword = () => {
if (passwordInput === 'password') {
setPasswordInput('text');
} else {
setPasswordInput('password');
}
};
const { const {
register, register,
setError,
handleSubmit, handleSubmit,
formState: { errors, isDirty, isValid }, formState: { isDirty, isValid },
} = useForm<FormValues>({ resolver }); } = useForm();
const onSubmit = async (data: { [x: string]: string }) => { const onSubmit = async (data: { name: string; about: string; website: string }) => {
setLoading(true); setLoading(true);
if (data.password.length > 3) { try {
// save privkey to secure storage const profile = {
await db.secureSave(pubkey, privkey); ...data,
name: data.name,
display_name: data.name,
bio: data.about,
website: data.website,
};
// redirect to next step const event = await publish({
navigate('/auth/create/step-3', { replace: true }); content: JSON.stringify(profile),
} else { kind: NDKKind.Metadata,
setLoading(false); tags: [],
setError('password', {
type: 'custom',
message: 'Password is required and must be greater than 3',
}); });
// create default widget
await db.createWidget(WidgetKinds.other.learnNostr, 'Learn Nostr', '');
if (event) {
navigate('/auth/onboarding', { replace: true });
}
} catch (e) {
console.log('error: ', e);
setLoading(false);
} }
}; };
useEffect(() => { useEffect(() => {
// save current step, if user close app and reopen it // save current step, if user close app and reopen it
setStep('/auth/create/step-2'); setStep('/auth/create/step-3');
}, []); }, []);
return ( return (
<div className="mx-auto w-full max-w-md"> <div className="mx-auto w-full max-w-md">
<div className="mb-4 border-b border-white/10 pb-4"> <div className="mb-4 border-b border-white/10 pb-4">
<h1 className="mb-2 text-center text-2xl font-semibold text-white"> <h1 className="mb-2 text-center text-2xl font-semibold text-white">
Set password to secure your key Personalize your Nostr profile
</h1> </h1>
<p className="text-white/70"> <p className="text-white/70">
Password is not related to your Nostr account. It is only used to secure your Nostr profile is synchronous across all Nostr clients. If you create a profile
keys stored on your local machine and to unlock the app (like unlocking your on Lume, it will also work well with other Nostr clients. If you update your
phone with a passcode). When you move to other Nostr clients, you just need to profile on another Nostr client, it will also sync to Lume.
copy your private key.
</p> </p>
</div> </div>
<div className="flex flex-col gap-4"> <div className="w-full overflow-hidden rounded-xl bg-white/10 backdrop-blur-xl">
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-3"> <form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col">
<div className="flex flex-col gap-1"> <input type={'hidden'} {...register('picture')} value={picture} />
<div className="relative"> <input type={'hidden'} {...register('banner')} value={banner} />
<input <div className="relative">
{...register('password', { required: true })} <div className="relative h-36 w-full bg-white/10 backdrop-blur-xl">
type={passwordInput} {banner ? (
placeholder="Enter password" <Image
className="relative h-12 w-full rounded-lg border-t border-white/10 bg-white/20 px-3.5 py-1 text-center tracking-widest text-white !outline-none backdrop-blur-xl placeholder:tracking-normal placeholder:text-white/70" src={banner}
/> alt="user's banner"
<button className="h-full w-full object-cover"
type="button" />
onClick={() => showPassword()} ) : (
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 backdrop-blur-xl hover:bg-white/20" <div className="h-full w-full bg-white/20" />
> )}
{passwordInput === 'password' ? ( <div className="absolute left-1/2 top-1/2 z-10 h-full w-full -translate-x-1/2 -translate-y-1/2 transform">
<EyeOffIcon className="h-4 w-4 text-white/50 group-hover:text-white" /> <BannerUploader setBanner={setBanner} />
) : ( </div>
<EyeOnIcon className="h-4 w-4 text-white/50 group-hover:text-white" /> </div>
)} <div className="mb-5 px-4">
</button> <div className="relative z-10 -mt-8 h-16 w-16">
<Image
src={picture}
alt="user's avatar"
className="h-16 w-16 rounded-lg object-cover ring-2 ring-white/20"
/>
<div className="absolute left-1/2 top-1/2 z-10 h-full w-full -translate-x-1/2 -translate-y-1/2 transform">
<AvatarUploader setPicture={setPicture} />
</div>
</div>
</div> </div>
<span className="text-sm text-red-400">
{errors.password && <p>{errors.password.message}</p>}
</span>
</div> </div>
<div className="flex items-center justify-center"> <div className="flex flex-col gap-4 px-4 pb-4">
<div className="flex flex-col gap-1">
<label htmlFor="name" className="font-medium text-white">
Name *
</label>
<input
type={'text'}
{...register('name', {
required: true,
minLength: 1,
})}
spellCheck={false}
className="relative h-12 w-full rounded-lg bg-white/20 px-3 py-1 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
/>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="about" className="font-medium text-white">
Bio
</label>
<textarea
{...register('about')}
spellCheck={false}
className="relative h-20 w-full resize-none rounded-lg bg-white/20 px-3 py-2 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
/>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="website" className="font-medium text-white">
Website
</label>
<input
{...register('website', {
required: false,
})}
spellCheck={false}
className="relative h-12 w-full rounded-lg bg-white/20 px-3 py-1 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
/>
</div>
<button <button
type="submit" type="submit"
disabled={!isDirty || !isValid} disabled={!isDirty || !isValid}
@ -125,7 +155,7 @@ export function CreateStep2Screen() {
{loading ? ( {loading ? (
<> <>
<span className="w-5" /> <span className="w-5" />
<span>Securing your account...</span> <span>Creating...</span>
<LoaderIcon className="h-5 w-5 animate-spin text-white" /> <LoaderIcon className="h-5 w-5 animate-spin text-white" />
</> </>
) : ( ) : (

View File

@ -1,174 +0,0 @@
import { NDKKind } from '@nostr-dev-kit/ndk';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
import { AvatarUploader } from '@shared/avatarUploader';
import { BannerUploader } from '@shared/bannerUploader';
import { LoaderIcon } from '@shared/icons';
import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle';
import { Image } from '@shared/image';
import { useOnboarding } from '@stores/onboarding';
import { WidgetKinds } from '@stores/widgets';
import { useNostr } from '@utils/hooks/useNostr';
export function CreateStep3Screen() {
const navigate = useNavigate();
const setStep = useOnboarding((state) => state.setStep);
const [loading, setLoading] = useState(false);
const [picture, setPicture] = useState('https://void.cat/d/5VKmKyuHyxrNMf9bWSVPih');
const [banner, setBanner] = useState('');
const { db } = useStorage();
const { publish } = useNostr();
const {
register,
handleSubmit,
formState: { isDirty, isValid },
} = useForm();
const onSubmit = async (data: { name: string; about: string; website: string }) => {
setLoading(true);
try {
const profile = {
...data,
name: data.name,
display_name: data.name,
bio: data.about,
website: data.website,
};
const event = await publish({
content: JSON.stringify(profile),
kind: NDKKind.Metadata,
tags: [],
});
// create default widget
await db.createWidget(WidgetKinds.other.learnNostr, 'Learn Nostr', '');
if (event) {
navigate('/auth/onboarding', { replace: true });
}
} catch (e) {
console.log('error: ', e);
setLoading(false);
}
};
useEffect(() => {
// save current step, if user close app and reopen it
setStep('/auth/create/step-3');
}, []);
return (
<div className="mx-auto w-full max-w-md">
<div className="mb-4 border-b border-white/10 pb-4">
<h1 className="mb-2 text-center text-2xl font-semibold text-white">
Personalize your Nostr profile
</h1>
<p className="text-white/70">
Nostr profile is synchronous across all Nostr clients. If you create a profile
on Lume, it will also work well with other Nostr clients. If you update your
profile on another Nostr client, it will also sync to Lume.
</p>
</div>
<div className="w-full overflow-hidden rounded-xl bg-white/10 backdrop-blur-xl">
<form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col">
<input type={'hidden'} {...register('picture')} value={picture} />
<input type={'hidden'} {...register('banner')} value={banner} />
<div className="relative">
<div className="relative h-36 w-full bg-white/10 backdrop-blur-xl">
{banner ? (
<Image
src={banner}
alt="user's banner"
className="h-full w-full object-cover"
/>
) : (
<div className="h-full w-full bg-white/20" />
)}
<div className="absolute left-1/2 top-1/2 z-10 h-full w-full -translate-x-1/2 -translate-y-1/2 transform">
<BannerUploader setBanner={setBanner} />
</div>
</div>
<div className="mb-5 px-4">
<div className="relative z-10 -mt-8 h-16 w-16">
<Image
src={picture}
alt="user's avatar"
className="h-16 w-16 rounded-lg object-cover ring-2 ring-white/20"
/>
<div className="absolute left-1/2 top-1/2 z-10 h-full w-full -translate-x-1/2 -translate-y-1/2 transform">
<AvatarUploader setPicture={setPicture} />
</div>
</div>
</div>
</div>
<div className="flex flex-col gap-4 px-4 pb-4">
<div className="flex flex-col gap-1">
<label htmlFor="name" className="font-medium text-white">
Name *
</label>
<input
type={'text'}
{...register('name', {
required: true,
minLength: 1,
})}
spellCheck={false}
className="relative h-12 w-full rounded-lg bg-white/20 px-3 py-1 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
/>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="about" className="font-medium text-white">
Bio
</label>
<textarea
{...register('about')}
spellCheck={false}
className="relative h-20 w-full resize-none rounded-lg bg-white/20 px-3 py-2 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
/>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="website" className="font-medium text-white">
Website
</label>
<input
{...register('website', {
required: false,
})}
spellCheck={false}
className="relative h-12 w-full rounded-lg bg-white/20 px-3 py-1 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
/>
</div>
<button
type="submit"
disabled={!isDirty || !isValid}
className="inline-flex h-12 w-full items-center justify-between gap-2 rounded-lg border-t border-white/10 bg-blue-500 px-6 font-medium leading-none text-white hover:bg-blue-600 focus:outline-none"
>
{loading ? (
<>
<span className="w-5" />
<span>Creating...</span>
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
</>
) : (
<>
<span className="w-5" />
<span>Continue</span>
<ArrowRightCircleIcon className="h-5 w-5" />
</>
)}
</button>
</div>
</form>
</div>
</div>
);
}

View File

@ -1,19 +1,6 @@
import { useEffect } from 'react';
import { Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
import { useOnboarding } from '@stores/onboarding';
import { useStronghold } from '@stores/stronghold';
export function AuthImportScreen() { export function AuthImportScreen() {
const [step, tmpPrivkey] = useOnboarding((state) => [state.step, state.tempPrivkey]);
const setPrivkey = useStronghold((state) => state.setPrivkey);
useEffect(() => {
if (step) {
setPrivkey(tmpPrivkey);
}
}, [tmpPrivkey]);
return ( return (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
<Outlet /> <Outlet />

View File

@ -9,7 +9,6 @@ import { EyeOffIcon, EyeOnIcon, LoaderIcon } from '@shared/icons';
import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle'; import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle';
import { useOnboarding } from '@stores/onboarding'; import { useOnboarding } from '@stores/onboarding';
import { useStronghold } from '@stores/stronghold';
type FormValues = { type FormValues = {
privkey: string; privkey: string;
@ -31,13 +30,14 @@ const resolver: Resolver<FormValues> = async (values) => {
export function ImportStep1Screen() { export function ImportStep1Screen() {
const navigate = useNavigate(); const navigate = useNavigate();
const setPrivkey = useStronghold((state) => state.setPrivkey);
const setTempPubkey = useOnboarding((state) => state.setTempPrivkey);
const setPubkey = useOnboarding((state) => state.setPubkey);
const setStep = useOnboarding((state) => state.setStep);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [passwordInput, setPasswordInput] = useState('password'); const [passwordInput, setPasswordInput] = useState('password');
const [setStep, setPubkey, setTempPrivkey] = useOnboarding((state) => [
state.setStep,
state.setPubkey,
state.setTempPrivkey,
]);
const { db } = useStorage(); const { db } = useStorage();
const { const {
@ -60,8 +60,7 @@ export function ImportStep1Screen() {
const pubkey = getPublicKey(privkey); const pubkey = getPublicKey(privkey);
const npub = nip19.npubEncode(pubkey); const npub = nip19.npubEncode(pubkey);
setPrivkey(privkey); setTempPrivkey(privkey);
setTempPubkey(privkey); // only use if user close app and reopen it
setPubkey(pubkey); setPubkey(pubkey);
// add account to local database // add account to local database

View File

@ -1,143 +1,89 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Resolver, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import { EyeOffIcon, EyeOnIcon, LoaderIcon } from '@shared/icons'; import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons';
import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle'; import { User } from '@shared/user';
import { useOnboarding } from '@stores/onboarding'; import { useOnboarding } from '@stores/onboarding';
import { useStronghold } from '@stores/stronghold'; import { WidgetKinds } from '@stores/widgets';
type FormValues = { import { useNostr } from '@utils/hooks/useNostr';
password: string;
};
const resolver: Resolver<FormValues> = async (values) => {
return {
values: values.password ? values : {},
errors: !values.password
? {
password: {
type: 'required',
message: 'This is required.',
},
}
: {},
};
};
export function ImportStep2Screen() { export function ImportStep2Screen() {
const navigate = useNavigate(); const navigate = useNavigate();
const setStep = useOnboarding((state) => state.setStep); const setStep = useOnboarding((state) => state.setStep);
const pubkey = useOnboarding((state) => state.pubkey);
const privkey = useStronghold((state) => state.privkey);
const [passwordInput, setPasswordInput] = useState('password');
const [loading, setLoading] = useState(false);
const { db } = useStorage(); const { db } = useStorage();
const { fetchUserData } = useNostr();
// toggle private key const [loading, setLoading] = useState(false);
const showPassword = () => {
if (passwordInput === 'password') {
setPasswordInput('text');
} else {
setPasswordInput('password');
}
};
const { const submit = async () => {
register, try {
setError, // show loading indicator
handleSubmit, setLoading(true);
formState: { errors, isDirty, isValid },
} = useForm<FormValues>({ resolver });
const onSubmit = async (data: { [x: string]: string }) => { // prefetch data
setLoading(true); const user = await fetchUserData();
if (data.password.length > 3) {
// save privkey to secure storage // create default widget
await db.secureSave(pubkey, privkey); await db.createWidget(WidgetKinds.other.learnNostr, 'Learn Nostr', '');
// redirect to next step // redirect to next step
navigate('/auth/import/step-3', { replace: true }); if (user.status === 'ok') {
} else { navigate('/auth/onboarding/step-2', { replace: true });
} else {
setLoading(false);
}
} catch (e) {
console.log('error: ', e);
setLoading(false); setLoading(false);
setError('password', {
type: 'custom',
message: 'Password is required and must be greater than 3, please check again',
});
} }
}; };
useEffect(() => { useEffect(() => {
// save current step, if user close app and reopen it // save current step, if user close app and reopen it
setStep('/auth/import/step-2'); setStep('/auth/import/step-3');
}, []); }, []);
return ( return (
<div className="mx-auto w-full max-w-md"> <div className="mx-auto w-full max-w-md">
<div className="mb-4 border-b border-white/10 pb-4"> <div className="mb-4 pb-4">
<h1 className="mb-2 text-center text-2xl font-semibold text-white"> <h1 className="text-center text-2xl font-semibold text-white">
Set password to secure your key {loading ? 'Downloading...' : 'Your Nostr profile'}
</h1> </h1>
<p className="text-white/70">
Password is not related to your Nostr account. It is only used to secure your
keys stored on your local machine and to unlock the app (like unlocking your
phone with a passcode). When you move to other Nostr clients, you only need to
copy your private key.
</p>
</div> </div>
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-3">
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-3"> <div className="rounded-lg border-t border-white/10 bg-white/20 px-3 py-3">
<div className="flex flex-col gap-1"> <User pubkey={db.account.pubkey} variant="simple" />
<div className="relative"> </div>
<input <div className="flex flex-col gap-2">
{...register('password', { required: true })} <button
type={passwordInput} type="button"
placeholder="Enter password" className="inline-flex h-12 w-full items-center justify-between gap-2 rounded-lg bg-blue-500 px-6 font-medium leading-none text-white hover:bg-blue-600 focus:outline-none"
className="relative h-12 w-full rounded-lg border-t border-white/10 bg-white/20 px-3.5 py-1 text-center tracking-widest text-white !outline-none backdrop-blur-xl placeholder:tracking-normal placeholder:text-white/70" onClick={() => submit()}
/> >
<button {loading ? (
type="button" <>
onClick={() => showPassword()} <span className="w-5" />
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 backdrop-blur-xl hover:bg-white/20" <span>It might take a bit, please patient...</span>
> <LoaderIcon className="h-5 w-5 animate-spin text-white" />
{passwordInput === 'password' ? ( </>
<EyeOffIcon className="h-4 w-4 text-white/50 group-hover:text-white" /> ) : (
) : ( <>
<EyeOnIcon className="h-4 w-4 text-white/50 group-hover:text-white" /> <span className="w-5" />
)} <span>Continue</span>
</button> <ArrowRightCircleIcon className="h-5 w-5" />
</div> </>
<span className="text-sm text-red-400"> )}
{errors.password && <p>{errors.password.message}</p>} </button>
</span> <span className="text-center text-sm text-white/50">
</div> By clicking &apos;Continue&apos;, Lume will download your old relay list and
<div className="flex items-center justify-center"> metadata. It may take a bit
<button </span>
type="submit" </div>
disabled={!isDirty || !isValid}
className="inline-flex h-12 w-full items-center justify-between gap-2 rounded-lg border-t border-white/10 bg-blue-500 px-6 font-medium leading-none text-white hover:bg-blue-600 focus:outline-none"
>
{loading ? (
<>
<span className="w-5" />
<span>Securing your account...</span>
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
</>
) : (
<>
<span className="w-5" />
<span>Continue</span>
<ArrowRightCircleIcon className="h-5 w-5" />
</>
)}
</button>
</div>
</form>
</div> </div>
</div> </div>
); );

View File

@ -1,90 +0,0 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons';
import { User } from '@shared/user';
import { useOnboarding } from '@stores/onboarding';
import { WidgetKinds } from '@stores/widgets';
import { useNostr } from '@utils/hooks/useNostr';
export function ImportStep3Screen() {
const navigate = useNavigate();
const setStep = useOnboarding((state) => state.setStep);
const { db } = useStorage();
const { fetchUserData } = useNostr();
const [loading, setLoading] = useState(false);
const submit = async () => {
try {
// show loading indicator
setLoading(true);
// prefetch data
const user = await fetchUserData();
// create default widget
await db.createWidget(WidgetKinds.other.learnNostr, 'Learn Nostr', '');
// redirect to next step
if (user.status === 'ok') {
navigate('/auth/onboarding/step-2', { replace: true });
} else {
setLoading(false);
}
} catch (e) {
console.log('error: ', e);
setLoading(false);
}
};
useEffect(() => {
// save current step, if user close app and reopen it
setStep('/auth/import/step-3');
}, []);
return (
<div className="mx-auto w-full max-w-md">
<div className="mb-4 pb-4">
<h1 className="text-center text-2xl font-semibold text-white">
{loading ? 'Downloading...' : 'Your Nostr profile'}
</h1>
</div>
<div className="flex flex-col gap-3">
<div className="rounded-lg border-t border-white/10 bg-white/20 px-3 py-3">
<User pubkey={db.account.pubkey} variant="simple" />
</div>
<div className="flex flex-col gap-2">
<button
type="button"
className="inline-flex h-12 w-full items-center justify-between gap-2 rounded-lg bg-blue-500 px-6 font-medium leading-none text-white hover:bg-blue-600 focus:outline-none"
onClick={() => submit()}
>
{loading ? (
<>
<span className="w-5" />
<span>It might take a bit, please patient...</span>
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
</>
) : (
<>
<span className="w-5" />
<span>Continue</span>
<ArrowRightCircleIcon className="h-5 w-5" />
</>
)}
</button>
<span className="text-center text-sm text-white/50">
By clicking &apos;Continue&apos;, Lume will download your old relay list and
metadata. It may take a bit
</span>
</div>
</div>
</div>
);
}

View File

@ -1,10 +0,0 @@
export function LockScreen() {
return (
<div
className="h-full w-full bg-cover bg-center"
style={{ backgroundImage: 'url(/wallpapers/1.png)' }}
>
<p>TODO</p>
</div>
);
}

View File

@ -1,175 +0,0 @@
import { useQueryClient } from '@tanstack/react-query';
import { appConfigDir } from '@tauri-apps/api/path';
import { Stronghold } from '@tauri-apps/plugin-stronghold';
import { useState } from 'react';
import { Resolver, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
import { EyeOffIcon, EyeOnIcon, LoaderIcon } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
type FormValues = {
password: string;
};
const resolver: Resolver<FormValues> = async (values) => {
return {
values: values.password ? values : {},
errors: !values.password
? {
password: {
type: 'required',
message: 'This is required.',
},
}
: {},
};
};
export function MigrateScreen() {
const queryClient = useQueryClient();
const navigate = useNavigate();
const setPrivkey = useStronghold((state) => state.setPrivkey);
const [passwordInput, setPasswordInput] = useState('password');
const [loading, setLoading] = useState(false);
const { db } = useStorage();
// toggle private key
const showPassword = () => {
if (passwordInput === 'password') {
setPasswordInput('text');
} else {
setPasswordInput('password');
}
};
const {
register,
setError,
handleSubmit,
formState: { errors, isDirty, isValid },
} = useForm<FormValues>({ resolver });
const onSubmit = async (data: { [x: string]: string }) => {
setLoading(true);
if (data.password.length > 3) {
// load private in secure storage
try {
// save privkey to secure storage
const dir = await appConfigDir();
const stronghold = await Stronghold.load(`${dir}/lume.stronghold`, data.password);
if (!db.secureDB) db.secureDB = stronghold;
await db.secureSave(db.account.pubkey, db.account.privkey);
// add privkey to state
setPrivkey(db.account.privkey);
// remove privkey in db
await db.removePrivkey();
// clear cache
await queryClient.invalidateQueries(['account']);
// redirect to home
navigate('/', { replace: true });
} catch {
setLoading(false);
setError('password', {
type: 'custom',
message: 'Wrong password',
});
}
} else {
setLoading(false);
setError('password', {
type: 'custom',
message: 'Password is required and must be greater than 3',
});
}
};
return (
<div className="flex h-full w-full items-center justify-center">
<div className="mx-auto w-full max-w-md">
<div className="mb-8 text-center">
<h1 className="text-xl font-semibold text-white">
Upgrade security for your account
</h1>
</div>
<div className="w-full rounded-xl bg-white/10 px-3 py-3 backdrop-blur-xl">
<div className="flex flex-col gap-4">
<div>
<div className="mt-1">
<p className="text-sm text-white/50">
You&apos;re using old Lume version which store your private key as
plaintext in database, this is huge security risk.
</p>
<p className="mt-2 text-sm text-white/50">
To secure your private key, please set a password and Lume will put your
private key in secure storage.
</p>
<p className="mt-2 text-sm text-white/50">
It is not possible to start the app without applying this step, it is
easy and fast!
</p>
</div>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="mb-0">
<div className="flex flex-col gap-1">
<span className="font-medium text-white">
Set a password to protect your key
</span>
<div className="relative">
<input
{...register('password', { required: true })}
type={passwordInput}
placeholder="min. 4 characters"
className="relative w-full rounded-lg bg-white/10 py-3 pl-3.5 pr-11 text-white !outline-none placeholder:text-white/50"
/>
<button
type="button"
onClick={() => showPassword()}
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-white/10"
>
{passwordInput === 'password' ? (
<EyeOffIcon
width={20}
height={20}
className="text-white/50 group-hover:text-white"
/>
) : (
<EyeOnIcon
width={20}
height={20}
className="text-white/50 group-hover:text-white"
/>
)}
</button>
</div>
<span className="text-sm text-red-400">
{errors.password && <p>{errors.password.message}</p>}
</span>
</div>
<div className="flex items-center justify-center">
<button
type="submit"
disabled={!isDirty || !isValid}
className="mt-3 inline-flex h-11 w-full items-center justify-center rounded-md bg-blue-500 font-medium text-white hover:bg-blue-600 disabled:pointer-events-none disabled:opacity-50"
>
{loading ? (
<LoaderIcon className="h-4 w-4 animate-spin text-black dark:text-white" />
) : (
'Continue →'
)}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,19 +1,6 @@
import { useEffect } from 'react';
import { Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
import { useOnboarding } from '@stores/onboarding';
import { useStronghold } from '@stores/stronghold';
export function OnboardingScreen() { export function OnboardingScreen() {
const [step, tmpPrivkey] = useOnboarding((state) => [state.step, state.tempPrivkey]);
const setPrivkey = useStronghold((state) => state.setPrivkey);
useEffect(() => {
if (step) {
setPrivkey(tmpPrivkey);
}
}, [tmpPrivkey]);
return ( return (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
<Outlet /> <Outlet />

View File

@ -1,181 +0,0 @@
import { appConfigDir } from '@tauri-apps/api/path';
import { Stronghold } from '@tauri-apps/plugin-stronghold';
import { getPublicKey, nip19 } from 'nostr-tools';
import { useState } from 'react';
import { Resolver, useForm } from 'react-hook-form';
import { Link, useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
import { EyeOffIcon, EyeOnIcon, LoaderIcon } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
type FormValues = {
password: string;
privkey: string;
};
const resolver: Resolver<FormValues> = async (values) => {
return {
values: values.password ? values : {},
errors: !values.password
? {
password: {
type: 'required',
message: 'This is required.',
},
}
: {},
};
};
export function ResetScreen() {
const navigate = useNavigate();
const setPrivkey = useStronghold((state) => state.setPrivkey);
const [passwordInput, setPasswordInput] = useState('password');
const [loading, setLoading] = useState(false);
const { db } = useStorage();
// toggle private key
const showPassword = () => {
if (passwordInput === 'password') {
setPasswordInput('text');
} else {
setPasswordInput('password');
}
};
const {
register,
setError,
handleSubmit,
formState: { errors, isDirty, isValid },
} = useForm<FormValues>({ resolver });
const onSubmit = async (data: { [x: string]: string }) => {
setLoading(true);
if (data.password.length > 3) {
try {
let privkey = data.privkey;
if (privkey.startsWith('nsec')) {
privkey = nip19.decode(privkey).data as string;
}
const tmpPubkey = getPublicKey(privkey);
if (tmpPubkey !== db.account.pubkey) {
setLoading(false);
setError('password', {
type: 'custom',
message:
"Private key don't match current account store in database, please check again",
});
} else {
// remove old stronghold
await db.secureReset();
// save privkey to secure storage
const dir = await appConfigDir();
const stronghold = await Stronghold.load(
`${dir}/lume.stronghold`,
data.password
);
if (!db.secureDB) db.secureDB = stronghold;
await db.secureSave(db.account.pubkey, db.account.privkey);
// add privkey to state
setPrivkey(db.account.privkey);
// redirect to home
navigate('/auth/unlock', { replace: true });
}
} catch {
setLoading(false);
setError('password', {
type: 'custom',
message: 'Invalid private key',
});
}
} else {
setLoading(false);
setError('password', {
type: 'custom',
message: 'Password is required and must be greater than 3',
});
}
};
return (
<div className="flex h-full w-full items-center justify-center">
<div className="mx-auto w-full max-w-md">
<div className="mb-6 text-center">
<h1 className="text-2xl font-semibold text-white">Reset unlock password</h1>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col gap-3">
<div className="flex flex-col gap-1">
<label htmlFor="privkey" className="font-medium text-white">
Private key
</label>
<div className="relative">
<input
{...register('privkey', { required: true })}
type="text"
placeholder="nsec1..."
className="relative h-12 w-full rounded-lg border-t border-white/10 bg-white/20 px-3.5 py-1 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
/>
</div>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="password" className="font-medium text-white">
Set a new password to protect your key
</label>
<div className="relative">
<input
{...register('password', { required: true })}
type={passwordInput}
placeholder="Min. 4 characters"
className="relative h-12 w-full rounded-lg border-t border-white/10 bg-white/20 px-3.5 py-1 text-white !outline-none backdrop-blur-xl placeholder:text-white/70"
/>
<button
type="button"
onClick={() => showPassword()}
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 backdrop-blur-xl hover:bg-white/10"
>
{passwordInput === 'password' ? (
<EyeOffIcon className="h-5 w-5 text-white/50 group-hover:text-white" />
) : (
<EyeOnIcon className="h-5 w-5 text-white/50 group-hover:text-white" />
)}
</button>
</div>
<span className="text-sm text-red-400">
{errors.password && <p>{errors.password.message}</p>}
</span>
</div>
<div className="flex flex-col items-center justify-center">
<button
type="submit"
disabled={!isDirty || !isValid}
className="inline-flex h-12 w-full items-center justify-center rounded-md bg-blue-500 font-medium text-white hover:bg-blue-600 disabled:pointer-events-none disabled:opacity-50"
>
{loading ? (
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
) : (
'Continue →'
)}
</button>
<Link
to="/auth/unlock"
className="mt-1 inline-flex h-12 w-full items-center justify-center rounded-lg text-center text-white/70 hover:bg-white/20"
>
Back
</Link>
</div>
</form>
</div>
</div>
);
}

View File

@ -1,127 +0,0 @@
import { appConfigDir } from '@tauri-apps/api/path';
import { Stronghold } from '@tauri-apps/plugin-stronghold';
import { useState } from 'react';
import { Resolver, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
import { ArrowRightCircleIcon, EyeOffIcon, EyeOnIcon, LoaderIcon } from '@shared/icons';
import { User } from '@shared/user';
import { useStronghold } from '@stores/stronghold';
type FormValues = {
password: string;
};
const resolver: Resolver<FormValues> = async (values) => {
return {
values: values.password ? values : {},
errors: !values.password
? {
password: {
type: 'required',
message: 'This is required.',
},
}
: {},
};
};
export function UnlockScreen() {
const navigate = useNavigate();
const setPrivkey = useStronghold((state) => state.setPrivkey);
const setWalletConnectURL = useStronghold((state) => state.setWalletConnectURL);
const [showPassword, setShowPassword] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
const { db } = useStorage();
const {
register,
setError,
handleSubmit,
formState: { isDirty, isValid },
} = useForm<FormValues>({ resolver });
const onSubmit = async (data: { [x: string]: string }) => {
try {
setLoading(true);
const dir = await appConfigDir();
const stronghold = await Stronghold.load(`${dir}/lume.stronghold`, data.password);
if (!db.secureDB) db.secureDB = stronghold;
const privkey = await db.secureLoad(db.account.pubkey);
const uri = await db.secureLoad('walletConnectURL', 'nwc');
if (privkey) setPrivkey(privkey);
if (uri) setWalletConnectURL(uri);
// redirect to home
navigate('/', { replace: true });
} catch (e) {
setLoading(false);
setError('password', {
type: 'custom',
message: e,
});
}
};
return (
<div className="flex h-full w-full items-center justify-center">
<div className="mx-auto w-full max-w-md">
<div className="mb-8 inline-flex w-full items-center justify-center">
<User pubkey={db.account.pubkey} variant="avatar" />
</div>
<form
onSubmit={handleSubmit(onSubmit)}
className="mx-auto mb-0 flex w-2/3 flex-col"
>
<div className="relative">
<input
{...register('password', { required: true, minLength: 4 })}
type={showPassword ? 'text' : 'password'}
placeholder="Enter password"
className="relative h-12 w-full rounded-lg bg-neutral-300 py-1 text-center tracking-widest text-neutral-900 !outline-none backdrop-blur-xl placeholder:tracking-normal placeholder:text-neutral-500 dark:bg-neutral-800 dark:text-neutral-100 dark:placeholder:text-neutral-500"
/>
<button
type="button"
onClick={() => setShowPassword((prev) => !prev)}
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded-lg p-1 backdrop-blur-xl hover:bg-black/10 dark:hover:bg-white/10"
>
{showPassword ? (
<EyeOffIcon className="group-hover:text-dark h-5 w-5 text-black/50 dark:text-white/50 dark:group-hover:text-white" />
) : (
<EyeOnIcon className="group-hover:text-dark h-5 w-5 text-black/50 dark:text-white/50 dark:group-hover:text-white" />
)}
</button>
</div>
<div className="mt-2 flex flex-col">
<button
type="submit"
disabled={!isDirty || !isValid}
className="inline-flex h-10 w-full items-center justify-between gap-2 rounded-lg bg-blue-500 px-6 text-white hover:bg-blue-600 focus:outline-none disabled:opacity-50"
>
{loading ? (
<>
<span className="w-5" />
<span>Unlocking...</span>
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
</>
) : (
<>
<span className="w-5" />
<span>Continue</span>
<ArrowRightCircleIcon className="h-5 w-5" />
</>
)}
</button>
</div>
</form>
</div>
</div>
);
}

View File

@ -13,13 +13,10 @@ import { useStorage } from '@libs/storage/provider';
import { LoaderIcon } from '@shared/icons'; import { LoaderIcon } from '@shared/icons';
import { User } from '@shared/user'; import { User } from '@shared/user';
import { useStronghold } from '@stores/stronghold';
import { useNostr } from '@utils/hooks/useNostr'; import { useNostr } from '@utils/hooks/useNostr';
export function ChatScreen() { export function ChatScreen() {
const listRef = useRef<VListHandle>(null); const listRef = useRef<VListHandle>(null);
const userPrivkey = useStronghold((state) => state.privkey);
const { db } = useStorage(); const { db } = useStorage();
const { ndk } = useNDK(); const { ndk } = useNDK();
@ -35,7 +32,7 @@ export function ChatScreen() {
<ChatMessage <ChatMessage
message={message} message={message}
userPubkey={db.account.pubkey} userPubkey={db.account.pubkey}
userPrivkey={userPrivkey} userPrivkey={''}
self={message.pubkey === db.account.pubkey} self={message.pubkey === db.account.pubkey}
/> />
); );
@ -108,7 +105,7 @@ export function ChatScreen() {
<ChatForm <ChatForm
receiverPubkey={pubkey} receiverPubkey={pubkey}
userPubkey={db.account.pubkey} userPubkey={db.account.pubkey}
userPrivkey={userPrivkey} userPrivkey={''}
/> />
</div> </div>
</div> </div>

View File

@ -9,8 +9,6 @@ import { useDecryptMessage } from '@app/chats/hooks/useDecryptMessage';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import { useStronghold } from '@stores/stronghold';
import { formatCreatedAt } from '@utils/createdAt'; import { formatCreatedAt } from '@utils/createdAt';
import { useProfile } from '@utils/hooks/useProfile'; import { useProfile } from '@utils/hooks/useProfile';
import { displayNpub } from '@utils/shortenKey'; import { displayNpub } from '@utils/shortenKey';
@ -19,8 +17,7 @@ export const ChatListItem = memo(function ChatListItem({ event }: { event: NDKEv
const { db } = useStorage(); const { db } = useStorage();
const { status, user } = useProfile(event.pubkey); const { status, user } = useProfile(event.pubkey);
const privkey = useStronghold((state) => state.privkey); const decryptedContent = useDecryptMessage(event, db.account.pubkey);
const decryptedContent = useDecryptMessage(event, db.account.pubkey, privkey);
const createdAt = formatCreatedAt(event.created_at, true); const createdAt = formatCreatedAt(event.created_at, true);
const svgURI = const svgURI =

View File

@ -1,7 +1,6 @@
import { webln } from '@getalby/sdk'; import { webln } from '@getalby/sdk';
import * as Dialog from '@radix-ui/react-dialog'; import * as Dialog from '@radix-ui/react-dialog';
import { message } from '@tauri-apps/plugin-dialog'; import { message } from '@tauri-apps/plugin-dialog';
import { WebviewWindow } from '@tauri-apps/plugin-window';
import { useState } from 'react'; import { useState } from 'react';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
@ -14,8 +13,6 @@ import {
LoaderIcon, LoaderIcon,
} from '@shared/icons'; } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
export function NWCAlby() { export function NWCAlby() {
const { db } = useStorage(); const { db } = useStorage();
@ -23,8 +20,6 @@ export function NWCAlby() {
const [isLoading, setIsloading] = useState(false); const [isLoading, setIsloading] = useState(false);
const [isConnected, setIsConnected] = useState(false); const [isConnected, setIsConnected] = useState(false);
const setWalletConnectURL = useStronghold((state) => state.setWalletConnectURL);
const initAlby = async () => { const initAlby = async () => {
try { try {
setIsloading(true); setIsloading(true);
@ -36,6 +31,7 @@ export function NWCAlby() {
const authURL = provider.getAuthorizationUrl({ name: 'Lume' }); const authURL = provider.getAuthorizationUrl({ name: 'Lume' });
// open auth window // open auth window
/*
const webview = new WebviewWindow('alby', { const webview = new WebviewWindow('alby', {
title: 'Connect Alby', title: 'Connect Alby',
url: authURL.href, url: authURL.href,
@ -45,11 +41,11 @@ export function NWCAlby() {
}); });
webview.listen('tauri://close-requested', async () => { webview.listen('tauri://close-requested', async () => {
await db.secureSave('walletConnectURL', walletConnectURL, 'nwc'); await db.secureSave('nwc', walletConnectURL);
setWalletConnectURL(walletConnectURL);
setIsConnected(true); setIsConnected(true);
setIsloading(false); setIsloading(false);
}); });
*/
} catch (e) { } catch (e) {
setIsloading(false); setIsloading(false);
await message(e.toString(), { title: 'Connect Alby', type: 'error' }); await message(e.toString(), { title: 'Connect Alby', type: 'error' });

View File

@ -6,8 +6,6 @@ import { useStorage } from '@libs/storage/provider';
import { ArrowRightCircleIcon, CancelIcon, LoaderIcon, WorldIcon } from '@shared/icons'; import { ArrowRightCircleIcon, CancelIcon, LoaderIcon, WorldIcon } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
type FormValues = { type FormValues = {
uri: string; uri: string;
}; };
@ -38,8 +36,6 @@ export function NWCOther() {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [isLoading, setIsloading] = useState(false); const [isLoading, setIsloading] = useState(false);
const setWalletConnectURL = useStronghold((state) => state.setWalletConnectURL);
const onSubmit = async (data: { [x: string]: string }) => { const onSubmit = async (data: { [x: string]: string }) => {
try { try {
if (!data.uri.startsWith('nostr+walletconnect:')) { if (!data.uri.startsWith('nostr+walletconnect:')) {
@ -57,8 +53,7 @@ export function NWCOther() {
const params = new URLSearchParams(uriObj.search); const params = new URLSearchParams(uriObj.search);
if (params.has('relay') && params.has('secret')) { if (params.has('relay') && params.has('secret')) {
await db.secureSave('walletConnectURL', data.uri, 'nwc'); await db.secureSave('nwc', data.uri);
setWalletConnectURL(data.uri);
setIsloading(false); setIsloading(false);
setIsOpen(false); setIsOpen(false);
} }
@ -95,7 +90,7 @@ export function NWCOther() {
</button> </button>
</Dialog.Trigger> </Dialog.Trigger>
</div> </div>
<Dialog.Portal className="relative z-10"> <Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" /> <Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center"> <Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<div className="relative h-min w-full max-w-xl rounded-xl bg-white/10 backdrop-blur-xl"> <div className="relative h-min w-full max-w-xl rounded-xl bg-white/10 backdrop-blur-xl">

View File

@ -1,23 +1,14 @@
import { NWCAlby } from '@app/nwc/components/alby'; import { NWCAlby } from '@app/nwc/components/alby';
import { NWCOther } from '@app/nwc/components/other'; import { NWCOther } from '@app/nwc/components/other';
import { useStorage } from '@libs/storage/provider';
import { CheckCircleIcon } from '@shared/icons'; import { CheckCircleIcon } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
export function NWCScreen() { export function NWCScreen() {
const { db } = useStorage(); const walletConnectURL = 'test';
const [walletConnectURL, setWalletConnectURL] = useStronghold((state) => [
state.walletConnectURL,
state.setWalletConnectURL,
]);
const remove = async () => { const remove = async () => {
setWalletConnectURL(''); // setWalletConnectURL('');
await db.secureSave('walletConnectURL', '', 'nwc'); // await db.secureSave('walletConnectURL', '', 'nwc');
}; };
return ( return (

View File

@ -5,15 +5,13 @@ import { useStorage } from '@libs/storage/provider';
import { EyeOffIcon, EyeOnIcon } from '@shared/icons'; import { EyeOffIcon, EyeOnIcon } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
export function AccountSettingsScreen() { export function AccountSettingsScreen() {
const { db } = useStorage(); const { db } = useStorage();
const [privType, setPrivType] = useState('password'); const [privType, setPrivType] = useState('password');
const [nsecType, setNsecType] = useState('password'); const [nsecType, setNsecType] = useState('password');
const privkey = useStronghold((state) => state.privkey); const privkey = 'todo';
const nsec = useMemo(() => nip19.nsecEncode(privkey), [privkey]); const nsec = useMemo(() => nip19.nsecEncode(privkey), [privkey]);
const showPrivkey = () => { const showPrivkey = () => {

View File

@ -57,7 +57,6 @@ export const NDKInstance = () => {
const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'lume_ndkcache' }); const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'lume_ndkcache' });
const instance = new NDK({ const instance = new NDK({
explicitRelayUrls, explicitRelayUrls,
// @ts-expect-error, wtf?
cacheAdapter: dexieAdapter, cacheAdapter: dexieAdapter,
}); });

View File

@ -19,11 +19,13 @@ export class LumeStorage {
} }
public async secureSave(value: string, key?: string) { public async secureSave(value: string, key?: string) {
await invoke('secure_save', { key: this.account.pubkey ?? key, value }); return await invoke('secure_save', { key: this.account.pubkey ?? key, value });
} }
public async secureLoad(key?: string) { public async secureLoad(key?: string) {
const value = invoke('secure_load', { key: this.account.pubkey ?? key }); const value: string = await invoke('secure_load', {
key: this.account.pubkey ?? key,
});
return value; return value;
} }

View File

@ -1,3 +1,4 @@
import { appConfigDir } from '@tauri-apps/api/path';
import { message } from '@tauri-apps/plugin-dialog'; import { message } from '@tauri-apps/plugin-dialog';
import { platform } from '@tauri-apps/plugin-os'; import { platform } from '@tauri-apps/plugin-os';
import Database from '@tauri-apps/plugin-sql'; import Database from '@tauri-apps/plugin-sql';
@ -20,10 +21,13 @@ const StorageProvider = ({ children }: PropsWithChildren<object>) => {
try { try {
const sqlite = await Database.load('sqlite:lume.db'); const sqlite = await Database.load('sqlite:lume.db');
const platformName = await platform(); const platformName = await platform();
const lumeStorage = new LumeStorage(sqlite, platformName); const dir = await appConfigDir();
const lumeStorage = new LumeStorage(sqlite, platformName);
if (!lumeStorage.account) await lumeStorage.getActiveAccount(); if (!lumeStorage.account) await lumeStorage.getActiveAccount();
setDB(lumeStorage); setDB(lumeStorage);
console.info(dir);
} catch (e) { } catch (e) {
await message(`Cannot initialize database: ${e}`, { await message(`Cannot initialize database: ${e}`, {
title: 'Lume', title: 'Lume',

View File

@ -5,19 +5,14 @@ import { useStorage } from '@libs/storage/provider';
import { LogoutIcon } from '@shared/icons'; import { LogoutIcon } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
export function Logout() { export function Logout() {
const { db } = useStorage(); const { db } = useStorage();
const navigate = useNavigate(); const navigate = useNavigate();
const resetStronghold = useStronghold((state) => state.reset);
const logout = async () => { const logout = async () => {
// remove account // remove account
db.accountLogout(); db.accountLogout();
// clear privkey in session storage
resetStronghold();
// redirect to welcome screen // redirect to welcome screen
navigate('/auth/welcome'); navigate('/auth/welcome');
}; };

View File

@ -8,8 +8,6 @@ import CurrencyInput from 'react-currency-input-field';
import { CancelIcon, ZapIcon } from '@shared/icons'; import { CancelIcon, ZapIcon } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
import { useEvent } from '@utils/hooks/useEvent'; import { useEvent } from '@utils/hooks/useEvent';
import { useNostr } from '@utils/hooks/useNostr'; import { useNostr } from '@utils/hooks/useNostr';
import { useProfile } from '@utils/hooks/useProfile'; import { useProfile } from '@utils/hooks/useProfile';
@ -21,6 +19,7 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
const { user } = useProfile(pubkey); const { user } = useProfile(pubkey);
const { data: event } = useEvent(id); const { data: event } = useEvent(id);
const [walletConnectURL, setWalletConnectURL] = useState<string>(null);
const [amount, setAmount] = useState<string>('21'); const [amount, setAmount] = useState<string>('21');
const [zapMessage, setZapMessage] = useState<string>(''); const [zapMessage, setZapMessage] = useState<string>('');
const [invoice, setInvoice] = useState<null | string>(null); const [invoice, setInvoice] = useState<null | string>(null);
@ -28,7 +27,6 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
const [isCompleted, setIsCompleted] = useState(false); const [isCompleted, setIsCompleted] = useState(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const walletConnectURL = useStronghold((state) => state.walletConnectURL);
const nwc = useRef(null); const nwc = useRef(null);
const createZapRequest = async () => { const createZapRequest = async () => {
@ -80,6 +78,12 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
}; };
useEffect(() => { useEffect(() => {
async function getWalletConnectURL() {
// const uri: string = await invoke('secure_load', { key: 'nwc' });
setWalletConnectURL('todo');
}
getWalletConnectURL();
return () => { return () => {
setAmount('21'); setAmount('21');
setZapMessage(''); setZapMessage('');

View File

@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import { useStronghold } from '@stores/stronghold'; import { useWidgets } from '@stores/widgets';
import { useNostr } from '@utils/hooks/useNostr'; import { useNostr } from '@utils/hooks/useNostr';
@ -11,11 +11,11 @@ export function EventLoader({ firstTime }: { firstTime: boolean }) {
const { db } = useStorage(); const { db } = useStorage();
const { getAllEventsSinceLastLogin } = useNostr(); const { getAllEventsSinceLastLogin } = useNostr();
const setIsFetched = useStronghold((state) => state.setIsFetched);
const queryClient = useQueryClient();
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
const queryClient = useQueryClient();
const setIsFetched = useWidgets((state) => state.setIsFetched);
useEffect(() => { useEffect(() => {
async function getEvents() { async function getEvents() {
const events = await getAllEventsSinceLastLogin(); const events = await getAllEventsSinceLastLogin();

View File

@ -18,7 +18,7 @@ import { NoteSkeleton } from '@shared/notes/skeleton';
import { TitleBar } from '@shared/titleBar'; import { TitleBar } from '@shared/titleBar';
import { EventLoader, WidgetWrapper } from '@shared/widgets'; import { EventLoader, WidgetWrapper } from '@shared/widgets';
import { useStronghold } from '@stores/stronghold'; import { useWidgets } from '@stores/widgets';
import { useNostr } from '@utils/hooks/useNostr'; import { useNostr } from '@utils/hooks/useNostr';
import { toRawEvent } from '@utils/rawEvent'; import { toRawEvent } from '@utils/rawEvent';
@ -36,7 +36,7 @@ export function LocalNetworkWidget() {
getNextPageParam: (lastPage) => lastPage.nextCursor, getNextPageParam: (lastPage) => lastPage.nextCursor,
}); });
const isFetched = useStronghold((state) => state.isFetched); const isFetched = useWidgets((state) => state.isFetched);
const dbEvents = useMemo( const dbEvents = useMemo(
() => (data ? data.pages.flatMap((d: { data: DBEvent[] }) => d.data) : []), () => (data ? data.pages.flatMap((d: { data: DBEvent[] }) => d.data) : []),
[data] [data]

View File

@ -1,22 +0,0 @@
import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';
interface BrowseState {
data: Array<{ title: string; data: string[] }>;
setData: ({ title, data }: { title: string; data: string[] }) => void;
}
export const useBrowse = create<BrowseState>()(
persist(
(set) => ({
data: [],
setData: (data) => {
set((state) => ({ data: [...state.data, data] }));
},
}),
{
name: 'browseUsers',
storage: createJSONStorage(() => localStorage),
}
)
);

View File

@ -1,42 +0,0 @@
import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';
interface StrongholdState {
privkey: null | string;
walletConnectURL: null | string;
isFetched: null | boolean;
setPrivkey: (privkey: string) => void;
setWalletConnectURL: (uri: string) => void;
clearPrivkey: () => void;
setIsFetched: () => void;
reset: () => void;
}
export const useStronghold = create<StrongholdState>()(
persist(
(set) => ({
privkey: null,
walletConnectURL: null,
isFetched: false,
setPrivkey: (privkey: string) => {
set({ privkey: privkey });
},
setWalletConnectURL: (uri: string) => {
set({ walletConnectURL: uri });
},
clearPrivkey: () => {
set({ privkey: null });
},
setIsFetched: () => {
set({ isFetched: true });
},
reset: () => {
set({ privkey: null, walletConnectURL: null, isFetched: false });
},
}),
{
name: 'stronghold',
storage: createJSONStorage(() => sessionStorage),
}
)
);

View File

@ -7,10 +7,12 @@ import { Widget, WidgetGroup } from '@utils/types';
interface WidgetState { interface WidgetState {
widgets: null | Array<Widget>; widgets: null | Array<Widget>;
isFetched: boolean;
fetchWidgets: (db: LumeStorage) => void; fetchWidgets: (db: LumeStorage) => void;
setWidget: (db: LumeStorage, { kind, title, content }: Widget) => void; setWidget: (db: LumeStorage, { kind, title, content }: Widget) => void;
removeWidget: (db: LumeStorage, id: string) => void; removeWidget: (db: LumeStorage, id: string) => void;
reorderWidget: (id: string, position: number) => void; reorderWidget: (id: string, position: number) => void;
setIsFetched: () => void;
} }
export const WidgetKinds = { export const WidgetKinds = {
@ -120,6 +122,7 @@ export const useWidgets = create<WidgetState>()(
persist( persist(
(set) => ({ (set) => ({
widgets: null, widgets: null,
isFetched: false,
fetchWidgets: async (db: LumeStorage) => { fetchWidgets: async (db: LumeStorage) => {
const dbWidgets = await db.getWidgets(); const dbWidgets = await db.getWidgets();
console.log('db widgets: ', dbWidgets); console.log('db widgets: ', dbWidgets);
@ -142,7 +145,7 @@ export const useWidgets = create<WidgetState>()(
await db.removeWidget(id); await db.removeWidget(id);
set((state) => ({ widgets: state.widgets.filter((widget) => widget.id !== id) })); set((state) => ({ widgets: state.widgets.filter((widget) => widget.id !== id) }));
}, },
reorderWidget: (id: string, position: number) => reorderWidget: (id: string, position: number) => {
set((state) => { set((state) => {
const widgets = [...state.widgets]; const widgets = [...state.widgets];
const widget = widgets.find((widget) => widget.id === id); const widget = widgets.find((widget) => widget.id === id);
@ -153,7 +156,11 @@ export const useWidgets = create<WidgetState>()(
widgets.splice(position, 0, widget); widgets.splice(position, 0, widget);
return { widgets }; return { widgets };
}), });
},
setIsFetched: () => {
set({ isFetched: true });
},
}), }),
{ {
name: 'widgets', name: 'widgets',

View File

@ -16,8 +16,6 @@ import { useMemo } from 'react';
import { useNDK } from '@libs/ndk/provider'; import { useNDK } from '@libs/ndk/provider';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import { useStronghold } from '@stores/stronghold';
import { nHoursAgo } from '@utils/date'; import { nHoursAgo } from '@utils/date';
import { getMultipleRandom } from '@utils/transform'; import { getMultipleRandom } from '@utils/transform';
import { NDKEventWithReplies, NostrBuildResponse } from '@utils/types'; import { NDKEventWithReplies, NostrBuildResponse } from '@utils/types';
@ -26,7 +24,6 @@ export function useNostr() {
const { db } = useStorage(); const { db } = useStorage();
const { ndk, relayUrls, fetcher } = useNDK(); const { ndk, relayUrls, fetcher } = useNDK();
const privkey = useStronghold((state) => state.privkey);
const subManager = useMemo( const subManager = useMemo(
() => () =>
new LRUCache<string, NDKSubscription, void>({ new LRUCache<string, NDKSubscription, void>({
@ -41,6 +38,7 @@ export function useNostr() {
callback: (event: NDKEvent) => void, callback: (event: NDKEvent) => void,
groupable?: boolean groupable?: boolean
) => { ) => {
console.info(ndk);
if (!ndk) throw new Error('NDK instance not found'); if (!ndk) throw new Error('NDK instance not found');
const subEvent = ndk.subscribe(filter, { const subEvent = ndk.subscribe(filter, {
@ -347,7 +345,7 @@ export function useNostr() {
kind: NDKKind | number; kind: NDKKind | number;
tags: string[][]; tags: string[][];
}): Promise<NDKEvent> => { }): Promise<NDKEvent> => {
if (!privkey) throw new Error('Private key not found'); const privkey: string = await db.secureLoad();
const event = new NDKEvent(ndk); const event = new NDKEvent(ndk);
const signer = new NDKPrivateKeySigner(privkey); const signer = new NDKPrivateKeySigner(privkey);
@ -365,7 +363,7 @@ export function useNostr() {
}; };
const createZap = async (event: NDKEvent, amount: number, message?: string) => { const createZap = async (event: NDKEvent, amount: number, message?: string) => {
if (!privkey) throw new Error('Private key not found'); const privkey: string = await db.secureLoad();
if (!ndk.signer) { if (!ndk.signer) {
const signer = new NDKPrivateKeySigner(privkey); const signer = new NDKPrivateKeySigner(privkey);