diff --git a/.vscode/settings.json b/.vscode/settings.json index 3ff535b..1c3aa0e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,6 @@ }, "files.associations": { "*.svelte": "svelte" - } + }, + "editor.tabSize": 2 } diff --git a/deno.lock b/deno.lock index 6e9e011..0a222bd 100644 --- a/deno.lock +++ b/deno.lock @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "specifiers": { "npm:@nostr-dev-kit/ndk-cache-dexie@2.5": "2.5.13_typescript@5.7.3", "npm:@nostr-dev-kit/ndk@2.11": "2.11.2_typescript@5.7.3", @@ -10,20 +10,22 @@ "npm:@sveltejs/adapter-static@3": "3.0.8_@sveltejs+kit@2.17.3__@sveltejs+vite-plugin-svelte@4.0.4___svelte@5.21.0____acorn@8.14.0___vite@5.4.14____@types+node@22.13.9___@types+node@22.13.9__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9", "npm:@sveltejs/kit@2": "2.17.3_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9", "npm:@sveltejs/kit@^2.16.0": "2.17.3_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9", + "npm:@sveltejs/kit@^2.22.2": "2.22.2_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.0.5__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_acorn@8.15.0_@types+node@22.13.9", "npm:@sveltejs/vite-plugin-svelte@4": "4.0.4_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9", "npm:@tailwindcss/forms@0.5": "0.5.10_tailwindcss@3.4.17__postcss@8.5.3", "npm:@tailwindcss/typography@0.5": "0.5.16_tailwindcss@3.4.17__postcss@8.5.3", "npm:@types/d3@^7.4.3": "7.4.3", "npm:@types/he@1.2": "1.2.3", "npm:@types/node@22": "22.13.9", + "npm:@types/qrcode@^1.5.5": "1.5.5", "npm:asciidoctor@3.0": "3.0.4_@asciidoctor+core@3.0.4", "npm:autoprefixer@10": "10.4.20_postcss@8.5.3", + "npm:bech32@2": "2.0.0", "npm:d3@7.9": "7.9.0_d3-selection@3.0.0", "npm:d3@^7.9.0": "7.9.0_d3-selection@3.0.0", - "npm:eslint-plugin-svelte@2": "2.46.1_eslint@9.21.0_svelte@5.21.0__acorn@8.14.0_postcss@8.5.3", + "npm:eslint-plugin-svelte@2": "2.46.1_eslint@9.21.0_svelte@5.21.0__acorn@8.14.0_postcss@8.5.3_svelte@5.0.5__acorn@8.14.0", "npm:flowbite-svelte-icons@2.1": "2.1.1_svelte@5.0.5__acorn@8.14.0_tailwind-merge@3.3.0", - "npm:flowbite-svelte@0": "0.48.4_svelte@5.21.0__acorn@8.14.0", - "npm:flowbite-svelte@0.44": "0.44.24_svelte@4.2.19", + "npm:flowbite-svelte@0.48": "0.48.6_svelte@5.0.5__acorn@8.14.0", "npm:flowbite@2": "2.5.2", "npm:flowbite@2.2": "2.2.1", "npm:he@1.2": "1.2.0", @@ -35,6 +37,7 @@ "npm:postcss@8": "8.5.3", "npm:prettier-plugin-svelte@3": "3.3.3_prettier@3.5.3_svelte@5.21.0__acorn@8.14.0", "npm:prettier@3": "3.5.3", + "npm:qrcode@^1.5.4": "1.5.4", "npm:svelte-check@4": "4.1.4_svelte@5.21.0__acorn@8.14.0_typescript@5.7.3", "npm:svelte@5": "5.21.0_acorn@8.14.0", "npm:svelte@5.0": "5.0.5_acorn@8.14.0", @@ -61,8 +64,9 @@ "integrity": "sha512-x2T9gW42921Zd90juEagtbViPZHNP2MWf0+6rJEkOzW7E9m3TGJtz+Guye9J0gwrpZsTMGCpfYMQy1We3X7osg==", "dependencies": [ "@asciidoctor/core", - "yargs" - ] + "yargs@17.3.1" + ], + "bin": true }, "@asciidoctor/core@3.0.4": { "integrity": "sha512-41SDMi7iRRBViPe0L6VWFTe55bv6HEOJeRqMj5+E5wB1YPdUPuTucL4UAESPZM6OWmn4t/5qM5LusXomFUVwVQ==", @@ -88,7 +92,8 @@ "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "dependencies": [ "@babel/types" - ] + ], + "bin": true }, "@babel/types@7.26.9": { "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", @@ -98,73 +103,119 @@ ] }, "@esbuild/aix-ppc64@0.21.5": { - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==" + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "os": ["aix"], + "cpu": ["ppc64"] }, "@esbuild/android-arm64@0.21.5": { - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==" + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "os": ["android"], + "cpu": ["arm64"] }, "@esbuild/android-arm@0.21.5": { - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==" + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "os": ["android"], + "cpu": ["arm"] }, "@esbuild/android-x64@0.21.5": { - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==" + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "os": ["android"], + "cpu": ["x64"] }, "@esbuild/darwin-arm64@0.21.5": { - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==" + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "os": ["darwin"], + "cpu": ["arm64"] }, "@esbuild/darwin-x64@0.21.5": { - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==" + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "os": ["darwin"], + "cpu": ["x64"] }, "@esbuild/freebsd-arm64@0.21.5": { - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==" + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "os": ["freebsd"], + "cpu": ["arm64"] }, "@esbuild/freebsd-x64@0.21.5": { - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==" + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "os": ["freebsd"], + "cpu": ["x64"] }, "@esbuild/linux-arm64@0.21.5": { - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==" + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "os": ["linux"], + "cpu": ["arm64"] }, "@esbuild/linux-arm@0.21.5": { - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==" + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "os": ["linux"], + "cpu": ["arm"] }, "@esbuild/linux-ia32@0.21.5": { - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==" + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "os": ["linux"], + "cpu": ["ia32"] }, "@esbuild/linux-loong64@0.21.5": { - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==" + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "os": ["linux"], + "cpu": ["loong64"] }, "@esbuild/linux-mips64el@0.21.5": { - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==" + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "os": ["linux"], + "cpu": ["mips64el"] }, "@esbuild/linux-ppc64@0.21.5": { - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==" + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "os": ["linux"], + "cpu": ["ppc64"] }, "@esbuild/linux-riscv64@0.21.5": { - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==" + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "os": ["linux"], + "cpu": ["riscv64"] }, "@esbuild/linux-s390x@0.21.5": { - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==" + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "os": ["linux"], + "cpu": ["s390x"] }, "@esbuild/linux-x64@0.21.5": { - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==" + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "os": ["linux"], + "cpu": ["x64"] }, "@esbuild/netbsd-x64@0.21.5": { - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==" + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "os": ["netbsd"], + "cpu": ["x64"] }, "@esbuild/openbsd-x64@0.21.5": { - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==" + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "os": ["openbsd"], + "cpu": ["x64"] }, "@esbuild/sunos-x64@0.21.5": { - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==" + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "os": ["sunos"], + "cpu": ["x64"] }, "@esbuild/win32-arm64@0.21.5": { - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==" + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "os": ["win32"], + "cpu": ["arm64"] }, "@esbuild/win32-ia32@0.21.5": { - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==" + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "os": ["win32"], + "cpu": ["ia32"] }, "@esbuild/win32-x64@0.21.5": { - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==" + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "os": ["win32"], + "cpu": ["x64"] }, "@eslint-community/eslint-utils@4.4.1_eslint@9.21.0": { "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", @@ -217,14 +268,14 @@ "levn" ] }, - "@floating-ui/core@1.6.9": { - "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "@floating-ui/core@1.7.1": { + "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", "dependencies": [ "@floating-ui/utils" ] }, - "@floating-ui/dom@1.6.13": { - "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "@floating-ui/dom@1.7.1": { + "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", "dependencies": [ "@floating-ui/core", "@floating-ui/utils" @@ -386,7 +437,8 @@ "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", "dependencies": [ "playwright" - ] + ], + "bin": true }, "@polka/url@1.0.0-next.28": { "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==" @@ -405,6 +457,9 @@ "magic-string", "picomatch@4.0.2", "rollup" + ], + "optionalPeers": [ + "rollup" ] }, "@rollup/plugin-json@6.1.0_rollup@4.34.9": { @@ -412,12 +467,15 @@ "dependencies": [ "@rollup/pluginutils@5.1.4_rollup@4.34.9", "rollup" + ], + "optionalPeers": [ + "rollup" ] }, "@rollup/plugin-node-resolve@15.3.1": { "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", "dependencies": [ - "@rollup/pluginutils@5.1.4", + "@rollup/pluginutils@5.1.4_rollup@4.34.9", "@types/resolve", "deepmerge", "is-module", @@ -433,6 +491,9 @@ "is-module", "resolve", "rollup" + ], + "optionalPeers": [ + "rollup" ] }, "@rollup/pluginutils@5.1.4": { @@ -441,6 +502,9 @@ "@types/estree", "estree-walker@2.0.2", "picomatch@4.0.2" + ], + "optionalPeers": [ + "rollup" ] }, "@rollup/pluginutils@5.1.4_rollup@4.34.9": { @@ -450,64 +514,105 @@ "estree-walker@2.0.2", "picomatch@4.0.2", "rollup" + ], + "optionalPeers": [ + "rollup" ] }, "@rollup/rollup-android-arm-eabi@4.34.9": { - "integrity": "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==" + "integrity": "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==", + "os": ["android"], + "cpu": ["arm"] }, "@rollup/rollup-android-arm64@4.34.9": { - "integrity": "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==" + "integrity": "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==", + "os": ["android"], + "cpu": ["arm64"] }, "@rollup/rollup-darwin-arm64@4.34.9": { - "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==" + "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", + "os": ["darwin"], + "cpu": ["arm64"] }, "@rollup/rollup-darwin-x64@4.34.9": { - "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==" + "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==", + "os": ["darwin"], + "cpu": ["x64"] }, "@rollup/rollup-freebsd-arm64@4.34.9": { - "integrity": "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==" + "integrity": "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==", + "os": ["freebsd"], + "cpu": ["arm64"] }, "@rollup/rollup-freebsd-x64@4.34.9": { - "integrity": "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==" + "integrity": "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==", + "os": ["freebsd"], + "cpu": ["x64"] }, "@rollup/rollup-linux-arm-gnueabihf@4.34.9": { - "integrity": "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==" + "integrity": "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==", + "os": ["linux"], + "cpu": ["arm"] }, "@rollup/rollup-linux-arm-musleabihf@4.34.9": { - "integrity": "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==" + "integrity": "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==", + "os": ["linux"], + "cpu": ["arm"] }, "@rollup/rollup-linux-arm64-gnu@4.34.9": { - "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==" + "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==", + "os": ["linux"], + "cpu": ["arm64"] }, "@rollup/rollup-linux-arm64-musl@4.34.9": { - "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==" + "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==", + "os": ["linux"], + "cpu": ["arm64"] }, "@rollup/rollup-linux-loongarch64-gnu@4.34.9": { - "integrity": "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==" + "integrity": "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==", + "os": ["linux"], + "cpu": ["loong64"] }, "@rollup/rollup-linux-powerpc64le-gnu@4.34.9": { - "integrity": "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==" + "integrity": "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==", + "os": ["linux"], + "cpu": ["ppc64"] }, "@rollup/rollup-linux-riscv64-gnu@4.34.9": { - "integrity": "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==" + "integrity": "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==", + "os": ["linux"], + "cpu": ["riscv64"] }, "@rollup/rollup-linux-s390x-gnu@4.34.9": { - "integrity": "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==" + "integrity": "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==", + "os": ["linux"], + "cpu": ["s390x"] }, "@rollup/rollup-linux-x64-gnu@4.34.9": { - "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==" + "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==", + "os": ["linux"], + "cpu": ["x64"] }, "@rollup/rollup-linux-x64-musl@4.34.9": { - "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==" + "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==", + "os": ["linux"], + "cpu": ["x64"] }, "@rollup/rollup-win32-arm64-msvc@4.34.9": { - "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==" + "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==", + "os": ["win32"], + "cpu": ["arm64"] }, "@rollup/rollup-win32-ia32-msvc@4.34.9": { - "integrity": "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==" + "integrity": "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==", + "os": ["win32"], + "cpu": ["ia32"] }, "@rollup/rollup-win32-x64-msvc@4.34.9": { - "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==" + "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", + "os": ["win32"], + "cpu": ["x64"] }, "@scure/base@1.1.1": { "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==" @@ -533,10 +638,16 @@ "@sindresorhus/is@4.6.0": { "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" }, + "@sveltejs/acorn-typescript@1.0.5_acorn@8.15.0": { + "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==", + "dependencies": [ + "acorn@8.15.0" + ] + }, "@sveltejs/adapter-auto@3.3.1_@sveltejs+kit@2.17.3__@sveltejs+vite-plugin-svelte@4.0.4___svelte@5.21.0____acorn@8.14.0___vite@5.4.14____@types+node@22.13.9___@types+node@22.13.9__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9": { "integrity": "sha512-5Sc7WAxYdL6q9j/+D0jJKjGREGlfIevDyHSQ2eNETHcB1TKlQWHcAo8AS8H1QdjNvSXpvOwNjykDUHPEAyGgdQ==", "dependencies": [ - "@sveltejs/kit", + "@sveltejs/kit@2.17.3_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9", "import-meta-resolve" ] }, @@ -546,20 +657,20 @@ "@rollup/plugin-commonjs", "@rollup/plugin-json", "@rollup/plugin-node-resolve@16.0.0_rollup@4.34.9", - "@sveltejs/kit", + "@sveltejs/kit@2.17.3_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9", "rollup" ] }, "@sveltejs/adapter-static@3.0.8_@sveltejs+kit@2.17.3__@sveltejs+vite-plugin-svelte@4.0.4___svelte@5.21.0____acorn@8.14.0___vite@5.4.14____@types+node@22.13.9___@types+node@22.13.9__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9": { "integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==", "dependencies": [ - "@sveltejs/kit" + "@sveltejs/kit@2.17.3_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9" ] }, "@sveltejs/kit@2.17.3_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9": { "integrity": "sha512-GcNaPDr0ti4O/TonPewkML2DG7UVXkSxPN3nPMlpmx0Rs4b2kVP4gymz98WEHlfzPXdd4uOOT1Js26DtieTNBQ==", "dependencies": [ - "@sveltejs/vite-plugin-svelte", + "@sveltejs/vite-plugin-svelte@4.0.4_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9", "@types/cookie", "cookie", "devalue", @@ -573,21 +684,53 @@ "sirv", "svelte@5.21.0_acorn@8.14.0", "vite" - ] + ], + "bin": true + }, + "@sveltejs/kit@2.22.2_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.0.5__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_acorn@8.15.0_@types+node@22.13.9": { + "integrity": "sha512-2MvEpSYabUrsJAoq5qCOBGAlkICjfjunrnLcx3YAk2XV7TvAIhomlKsAgR4H/4uns5rAfYmj7Wet5KRtc8dPIg==", + "dependencies": [ + "@sveltejs/acorn-typescript", + "@sveltejs/vite-plugin-svelte@4.0.4_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9_svelte@5.0.5__acorn@8.14.0", + "@types/cookie", + "acorn@8.15.0", + "cookie", + "devalue", + "esm-env", + "kleur", + "magic-string", + "mrmime", + "sade", + "set-cookie-parser", + "sirv", + "svelte@5.0.5_acorn@8.14.0", + "vite", + "vitefu" + ], + "bin": true }, "@sveltejs/vite-plugin-svelte-inspector@3.0.1_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9": { "integrity": "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==", "dependencies": [ - "@sveltejs/vite-plugin-svelte", + "@sveltejs/vite-plugin-svelte@4.0.4_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9_svelte@5.0.5__acorn@8.14.0", "debug@4.4.0", "svelte@5.21.0_acorn@8.14.0", "vite" ] }, + "@sveltejs/vite-plugin-svelte-inspector@3.0.1_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9_svelte@5.0.5__acorn@8.14.0": { + "integrity": "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==", + "dependencies": [ + "@sveltejs/vite-plugin-svelte@4.0.4_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9_svelte@5.0.5__acorn@8.14.0", + "debug@4.4.0", + "svelte@5.0.5_acorn@8.14.0", + "vite" + ] + }, "@sveltejs/vite-plugin-svelte@4.0.4_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9": { "integrity": "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==", "dependencies": [ - "@sveltejs/vite-plugin-svelte-inspector", + "@sveltejs/vite-plugin-svelte-inspector@3.0.1_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9", "debug@4.4.0", "deepmerge", "kleur", @@ -597,6 +740,19 @@ "vitefu" ] }, + "@sveltejs/vite-plugin-svelte@4.0.4_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9_svelte@5.0.5__acorn@8.14.0": { + "integrity": "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==", + "dependencies": [ + "@sveltejs/vite-plugin-svelte-inspector@3.0.1_@sveltejs+vite-plugin-svelte@4.0.4__svelte@5.21.0___acorn@8.14.0__vite@5.4.14___@types+node@22.13.9__@types+node@22.13.9_svelte@5.21.0__acorn@8.14.0_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9_svelte@5.0.5__acorn@8.14.0", + "debug@4.4.0", + "deepmerge", + "kleur", + "magic-string", + "svelte@5.0.5_acorn@8.14.0", + "vite", + "vitefu" + ] + }, "@tailwindcss/forms@0.5.10_tailwindcss@3.4.17__postcss@8.5.3": { "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", "dependencies": [ @@ -789,12 +945,24 @@ "@types/json-schema@7.0.15": { "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "@types/node@22.12.0": { + "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", + "dependencies": [ + "undici-types" + ] + }, "@types/node@22.13.9": { "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==", "dependencies": [ "undici-types" ] }, + "@types/qrcode@1.5.5": { + "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", + "dependencies": [ + "@types/node@22.12.0" + ] + }, "@types/resolve@1.20.2": { "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==" }, @@ -814,6 +982,9 @@ "estree-walker@3.0.3", "magic-string", "vite" + ], + "optionalPeers": [ + "vite" ] }, "@vitest/pretty-format@3.1.4": { @@ -870,10 +1041,16 @@ ] }, "acorn@7.4.1": { - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": true }, "acorn@8.14.0": { - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==" + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": true + }, + "acorn@8.15.0": { + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "bin": true }, "ajv@6.12.6": { "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", @@ -942,7 +1119,8 @@ "handlebars", "nunjucks", "pug" - ] + ], + "bin": true }, "assert-never@1.4.0": { "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==" @@ -963,7 +1141,8 @@ "picocolors", "postcss", "postcss-value-parser" - ] + ], + "bin": true }, "axobject-query@4.1.0": { "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==" @@ -977,6 +1156,9 @@ "balanced-match@1.0.2": { "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "bech32@2.0.0": { + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + }, "binary-extensions@2.3.0": { "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" }, @@ -1006,13 +1188,15 @@ "electron-to-chromium", "node-releases", "update-browserslist-db" - ] + ], + "bin": true }, "bufferutil@4.0.9": { "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==", "dependencies": [ "node-gyp-build" - ] + ], + "scripts": true }, "cac@6.7.14": { "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==" @@ -1037,6 +1221,9 @@ "camelcase-css@2.0.1": { "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" }, + "camelcase@5.3.1": { + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, "caniuse-lite@1.0.30001702": { "integrity": "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==" }, @@ -1074,12 +1261,14 @@ "dependencies": [ "anymatch", "braces", - "fsevents@2.3.3", "glob-parent@5.1.2", "is-binary-path", "is-glob", "normalize-path", "readdirp@3.6.0" + ], + "optionalDependencies": [ + "fsevents@2.3.3" ] }, "chokidar@4.0.3": { @@ -1088,6 +1277,14 @@ "readdirp@4.1.2" ] }, + "cliui@6.0.0": { + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dependencies": [ + "string-width@4.2.3", + "strip-ansi@6.0.1", + "wrap-ansi@6.2.0" + ] + }, "cliui@7.0.4": { "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dependencies": [ @@ -1099,16 +1296,6 @@ "clsx@2.1.1": { "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" }, - "code-red@1.0.4": { - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", - "dependencies": [ - "@jridgewell/sourcemap-codec", - "@types/estree", - "acorn@8.14.0", - "estree-walker@3.0.3", - "periscopic" - ] - }, "color-convert@2.0.1": { "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": [ @@ -1151,15 +1338,9 @@ "which" ] }, - "css-tree@2.3.1": { - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dependencies": [ - "mdn-data", - "source-map-js" - ] - }, "cssesc@3.0.0": { - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": true }, "d3-array@3.2.4": { "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", @@ -1217,7 +1398,8 @@ "commander@7.2.0", "iconv-lite", "rw" - ] + ], + "bin": true }, "d3-ease@3.0.1": { "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" @@ -1382,6 +1564,9 @@ "ms@2.1.3" ] }, + "decamelize@1.2.0": { + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + }, "deep-eql@5.0.2": { "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==" }, @@ -1406,6 +1591,9 @@ "didyoumean@1.2.2": { "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, + "dijkstrajs@1.0.3": { + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" + }, "dlv@1.1.3": { "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, @@ -1427,7 +1615,8 @@ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dependencies": [ "jake" - ] + ], + "bin": true }, "electron-to-chromium@1.5.111": { "integrity": "sha512-vJyJlO95wQRAw6K2ZGF/8nol7AcbCOnp8S6H91mwOOBbXoS9seDBYxCTPYAFsvXLxl3lc0jLXXe9GLxC4nXVog==" @@ -1463,7 +1652,8 @@ "es6-symbol", "esniff", "next-tick" - ] + ], + "scripts": true }, "es6-iterator@2.0.3": { "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", @@ -1482,7 +1672,7 @@ }, "esbuild@0.21.5": { "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dependencies": [ + "optionalDependencies": [ "@esbuild/aix-ppc64", "@esbuild/android-arm", "@esbuild/android-arm64", @@ -1506,7 +1696,9 @@ "@esbuild/win32-arm64", "@esbuild/win32-ia32", "@esbuild/win32-x64" - ] + ], + "scripts": true, + "bin": true }, "escalade@3.2.0": { "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" @@ -1536,7 +1728,31 @@ "postcss-selector-parser@6.1.2", "semver", "svelte@5.21.0_acorn@8.14.0", - "svelte-eslint-parser" + "svelte-eslint-parser@0.43.0_svelte@5.21.0__acorn@8.14.0_postcss@8.5.3" + ], + "optionalPeers": [ + "svelte@^3.37.0 || ^4.0.0 || ^5.0.0" + ] + }, + "eslint-plugin-svelte@2.46.1_eslint@9.21.0_svelte@5.21.0__acorn@8.14.0_postcss@8.5.3_svelte@5.0.5__acorn@8.14.0": { + "integrity": "sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==", + "dependencies": [ + "@eslint-community/eslint-utils", + "@jridgewell/sourcemap-codec", + "eslint", + "eslint-compat-utils", + "esutils", + "known-css-properties", + "postcss", + "postcss-load-config@3.1.4_postcss@8.5.3", + "postcss-safe-parser", + "postcss-selector-parser@6.1.2", + "semver", + "svelte@5.0.5_acorn@8.14.0", + "svelte-eslint-parser@0.43.0_svelte@5.21.0__acorn@8.14.0_postcss@8.5.3_svelte@5.0.5__acorn@8.14.0" + ], + "optionalPeers": [ + "svelte@5.0.5_acorn@8.14.0" ] }, "eslint-scope@7.2.2": { @@ -1586,7 +1802,7 @@ "esutils", "fast-deep-equal", "file-entry-cache", - "find-up", + "find-up@5.0.0", "glob-parent@6.0.2", "ignore", "imurmurhash", @@ -1596,7 +1812,8 @@ "minimatch@3.1.2", "natural-compare", "optionator" - ] + ], + "bin": true }, "esm-env@1.2.2": { "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==" @@ -1704,12 +1921,18 @@ "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", "dependencies": [ "picomatch@4.0.2" + ], + "optionalPeers": [ + "picomatch@4.0.2" ] }, "fdir@6.4.4_picomatch@4.0.2": { "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "dependencies": [ "picomatch@4.0.2" + ], + "optionalPeers": [ + "picomatch@4.0.2" ] }, "file-entry-cache@8.0.0": { @@ -1730,10 +1953,17 @@ "to-regex-range" ] }, + "find-up@4.1.0": { + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": [ + "locate-path@5.0.0", + "path-exists" + ] + }, "find-up@5.0.0": { "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dependencies": [ - "locate-path", + "locate-path@6.0.0", "path-exists" ] }, @@ -1768,24 +1998,14 @@ "tailwind-merge@3.3.0" ] }, - "flowbite-svelte@0.44.24_svelte@4.2.19": { - "integrity": "sha512-kXhJZHGpBVq5RFOoYnzRCEM8eFa81DVp4KjUbBsLJptKhizbSSBJuYApWIQb9pBCS8EBhX4PAX+RsgEDZfEqtA==", - "dependencies": [ - "@floating-ui/dom", - "apexcharts", - "flowbite@2.5.2", - "svelte@4.2.19", - "tailwind-merge@2.5.5" - ] - }, - "flowbite-svelte@0.48.4_svelte@5.21.0__acorn@8.14.0": { - "integrity": "sha512-ivlBxNi2u9+D/nFeHs+vLJU6nYjKq/ooAwdXPP3qIlEnUyIl/hVsH87JtVWwVEgF31NwwQcZeKFkWd8K5DWiGw==", + "flowbite-svelte@0.48.6_svelte@5.0.5__acorn@8.14.0": { + "integrity": "sha512-/PmeR3ipHHvda8vVY9MZlymaRoJsk8VddEeoLzIygfYwJV68ey8gHuQPC1dq9J6NDCTE5+xOPtBiYUtVjCfvZw==", "dependencies": [ "@floating-ui/dom", "apexcharts", "flowbite@3.1.2", - "svelte@5.21.0_acorn@8.14.0", - "tailwind-merge@3.0.2" + "svelte@5.0.5_acorn@8.14.0", + "tailwind-merge@3.3.0" ] }, "flowbite@2.2.1": { @@ -1826,10 +2046,14 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents@2.3.2": { - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "os": ["darwin"], + "scripts": true }, "fsevents@2.3.3": { - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==" + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "os": ["darwin"], + "scripts": true }, "function-bind@1.1.2": { "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" @@ -1880,7 +2104,8 @@ "minipass", "package-json-from-dist", "path-scurry" - ] + ], + "bin": true }, "glob@8.1.0": { "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", @@ -1890,7 +2115,8 @@ "inherits", "minimatch@5.1.6", "once" - ] + ], + "deprecated": true }, "globals@14.0.0": { "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==" @@ -1904,9 +2130,12 @@ "minimist", "neo-async", "source-map", - "uglify-js", "wordwrap" - ] + ], + "optionalDependencies": [ + "uglify-js" + ], + "bin": true }, "has-flag@4.0.0": { "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" @@ -1927,7 +2156,8 @@ ] }, "he@1.2.0": { - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": true }, "highlight.js@11.11.1": { "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==" @@ -1959,7 +2189,8 @@ "dependencies": [ "once", "wrappy" - ] + ], + "deprecated": true }, "inherits@2.0.4": { "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" @@ -2037,7 +2268,9 @@ "jackspeak@3.4.3": { "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dependencies": [ - "@isaacs/cliui", + "@isaacs/cliui" + ], + "optionalDependencies": [ "@pkgjs/parseargs" ] }, @@ -2048,10 +2281,12 @@ "chalk", "filelist", "minimatch@3.1.2" - ] + ], + "bin": true }, "jiti@1.21.7": { - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==" + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "bin": true }, "js-stringify@1.0.2": { "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==" @@ -2060,7 +2295,8 @@ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": [ "argparse" - ] + ], + "bin": true }, "json-buffer@3.0.1": { "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" @@ -2115,10 +2351,16 @@ "locate-character@3.0.0": { "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" }, + "locate-path@5.0.0": { + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": [ + "p-locate@4.1.0" + ] + }, "locate-path@6.0.0": { "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dependencies": [ - "p-locate" + "p-locate@5.0.0" ] }, "lodash.castarray@4.4.0": { @@ -2145,9 +2387,6 @@ "math-intrinsics@1.1.0": { "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" }, - "mdn-data@2.0.30": { - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" - }, "merge2@1.4.1": { "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, @@ -2159,7 +2398,8 @@ ] }, "mini-svg-data-uri@1.4.4": { - "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==" + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "bin": true }, "minimatch@3.1.2": { "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", @@ -2206,7 +2446,8 @@ ] }, "nanoid@3.3.8": { - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==" + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "bin": true }, "natural-compare@1.4.0": { "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" @@ -2227,7 +2468,8 @@ ] }, "node-gyp-build@4.8.4": { - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==" + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "bin": true }, "node-releases@2.0.19": { "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" @@ -2247,7 +2489,12 @@ "@scure/base@1.1.1", "@scure/bip32", "@scure/bip39", - "nostr-wasm", + "typescript" + ], + "optionalDependencies": [ + "nostr-wasm" + ], + "optionalPeers": [ "typescript" ] }, @@ -2260,7 +2507,8 @@ "a-sync-waterfall", "asap", "commander@5.1.0" - ] + ], + "bin": true }, "object-assign@4.1.1": { "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" @@ -2285,18 +2533,33 @@ "word-wrap" ] }, + "p-limit@2.3.0": { + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": [ + "p-try" + ] + }, "p-limit@3.1.0": { "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dependencies": [ "yocto-queue" ] }, + "p-locate@4.1.0": { + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": [ + "p-limit@2.3.0" + ] + }, "p-locate@5.0.0": { "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dependencies": [ - "p-limit" + "p-limit@3.1.0" ] }, + "p-try@2.2.0": { + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, "package-json-from-dist@1.0.1": { "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, @@ -2328,14 +2591,6 @@ "pathval@2.0.0": { "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==" }, - "periscopic@3.1.0": { - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "dependencies": [ - "@types/estree", - "estree-walker@3.0.3", - "is-reference@3.0.3" - ] - }, "picocolors@1.1.1": { "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, @@ -2352,14 +2607,21 @@ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==" }, "playwright-core@1.50.1": { - "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==" + "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", + "bin": true }, "playwright@1.50.1": { "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", "dependencies": [ - "fsevents@2.3.2", "playwright-core" - ] + ], + "optionalDependencies": [ + "fsevents@2.3.2" + ], + "bin": true + }, + "pngjs@5.0.0": { + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==" }, "postcss-import@15.1.0_postcss@8.5.3": { "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", @@ -2383,6 +2645,9 @@ "lilconfig@2.1.0", "postcss", "yaml@1.10.2" + ], + "optionalPeers": [ + "postcss" ] }, "postcss-load-config@4.0.2_postcss@8.5.3": { @@ -2391,6 +2656,9 @@ "lilconfig@3.1.3", "postcss", "yaml@2.7.0" + ], + "optionalPeers": [ + "postcss" ] }, "postcss-load-config@6.0.1_postcss@8.5.3": { @@ -2398,6 +2666,9 @@ "dependencies": [ "lilconfig@3.1.3", "postcss" + ], + "optionalPeers": [ + "postcss" ] }, "postcss-nested@6.2.0_postcss@8.5.3": { @@ -2455,7 +2726,8 @@ ] }, "prettier@3.5.3": { - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==" + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "bin": true }, "promise@7.3.1": { "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", @@ -2554,6 +2826,15 @@ "punycode@2.3.1": { "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, + "qrcode@1.5.4": { + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "dependencies": [ + "dijkstrajs", + "pngjs", + "yargs@15.4.1" + ], + "bin": true + }, "queue-microtask@1.2.3": { "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, @@ -2575,6 +2856,9 @@ "require-directory@2.1.1": { "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, + "require-main-filename@2.0.0": { + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, "resolve-from@4.0.0": { "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, @@ -2584,7 +2868,8 @@ "is-core-module", "path-parse", "supports-preserve-symlinks-flag" - ] + ], + "bin": true }, "reusify@1.1.0": { "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" @@ -2595,6 +2880,9 @@ "rollup@4.34.9": { "integrity": "sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==", "dependencies": [ + "@types/estree" + ], + "optionalDependencies": [ "@rollup/rollup-android-arm-eabi", "@rollup/rollup-android-arm64", "@rollup/rollup-darwin-arm64", @@ -2614,9 +2902,9 @@ "@rollup/rollup-win32-arm64-msvc", "@rollup/rollup-win32-ia32-msvc", "@rollup/rollup-win32-x64-msvc", - "@types/estree", "fsevents@2.3.3" - ] + ], + "bin": true }, "run-parallel@1.2.0": { "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", @@ -2637,7 +2925,11 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver@7.7.1": { - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "bin": true + }, + "set-blocking@2.0.0": { + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "set-cookie-parser@2.7.1": { "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" @@ -2724,7 +3016,8 @@ "mz", "pirates", "ts-interface-checker" - ] + ], + "bin": true }, "supports-color@7.2.0": { "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -2745,7 +3038,8 @@ "sade", "svelte@5.21.0_acorn@8.14.0", "typescript" - ] + ], + "bin": true }, "svelte-eslint-parser@0.43.0_svelte@5.21.0__acorn@8.14.0_postcss@8.5.3": { "integrity": "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==", @@ -2756,25 +3050,23 @@ "postcss", "postcss-scss", "svelte@5.21.0_acorn@8.14.0" + ], + "optionalPeers": [ + "svelte@^3.37.0 || ^4.0.0 || ^5.0.0" ] }, - "svelte@4.2.19": { - "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", + "svelte-eslint-parser@0.43.0_svelte@5.21.0__acorn@8.14.0_postcss@8.5.3_svelte@5.0.5__acorn@8.14.0": { + "integrity": "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==", "dependencies": [ - "@ampproject/remapping", - "@jridgewell/sourcemap-codec", - "@jridgewell/trace-mapping", - "@types/estree", - "acorn@8.14.0", - "aria-query", - "axobject-query", - "code-red", - "css-tree", - "estree-walker@3.0.3", - "is-reference@3.0.3", - "locate-character", - "magic-string", - "periscopic" + "eslint-scope@7.2.2", + "eslint-visitor-keys@3.4.3", + "espree@9.6.1_acorn@8.14.0", + "postcss", + "postcss-scss", + "svelte@5.0.5_acorn@8.14.0" + ], + "optionalPeers": [ + "svelte@5.0.5_acorn@8.14.0" ] }, "svelte@5.0.5_acorn@8.14.0": { @@ -2863,9 +3155,6 @@ "tailwind-merge@2.5.5": { "integrity": "sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==" }, - "tailwind-merge@3.0.2": { - "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==" - }, "tailwind-merge@3.3.0": { "integrity": "sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==" }, @@ -2894,7 +3183,8 @@ "postcss-selector-parser@6.1.2", "resolve", "sucrase" - ] + ], + "bin": true }, "thenify-all@1.6.0": { "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", @@ -2973,10 +3263,12 @@ "integrity": "sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA==" }, "typescript@5.7.3": { - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==" + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "bin": true }, "uglify-js@3.19.3": { - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==" + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "bin": true }, "undici-types@6.20.0": { "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" @@ -2993,7 +3285,8 @@ "browserslist", "escalade", "picocolors" - ] + ], + "bin": true }, "uri-js@4.4.1": { "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", @@ -3005,7 +3298,8 @@ "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", "dependencies": [ "node-gyp-build" - ] + ], + "scripts": true }, "utf8-buffer@1.0.0": { "integrity": "sha512-ueuhzvWnp5JU5CiGSY4WdKbiN/PO2AZ/lpeLiz2l38qwdLy/cW40XobgyuIWucNyum0B33bVB0owjFCeGBSLqg==" @@ -3021,28 +3315,38 @@ "es-module-lexer", "pathe", "vite" - ] + ], + "bin": true }, "vite@5.4.14_@types+node@22.13.9": { "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "dependencies": [ - "@types/node", + "@types/node@22.13.9", "esbuild", - "fsevents@2.3.3", "postcss", "rollup" - ] + ], + "optionalDependencies": [ + "fsevents@2.3.3" + ], + "optionalPeers": [ + "@types/node@22.13.9" + ], + "bin": true }, "vitefu@1.0.6_vite@5.4.14__@types+node@22.13.9_@types+node@22.13.9": { "integrity": "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==", "dependencies": [ "vite" + ], + "optionalPeers": [ + "vite" ] }, "vitest@3.1.4_@types+node@22.13.9_vite@5.4.14__@types+node@22.13.9": { "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", "dependencies": [ - "@types/node", + "@types/node@22.13.9", "@vitest/expect", "@vitest/mocker", "@vitest/pretty-format", @@ -3064,7 +3368,11 @@ "vite", "vite-node", "why-is-node-running" - ] + ], + "optionalPeers": [ + "@types/node@22.13.9" + ], + "bin": true }, "void-elements@3.1.0": { "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==" @@ -3087,18 +3395,23 @@ "yaeti" ] }, + "which-module@2.0.1": { + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, "which@2.0.2": { "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dependencies": [ "isexe" - ] + ], + "bin": true }, "why-is-node-running@2.3.0": { "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dependencies": [ "siginfo", "stackback" - ] + ], + "bin": true }, "with@7.0.2": { "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", @@ -3115,6 +3428,14 @@ "wordwrap@1.0.0": { "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, + "wrap-ansi@6.2.0": { + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": [ + "ansi-styles@4.3.0", + "string-width@4.2.3", + "strip-ansi@6.0.1" + ] + }, "wrap-ansi@7.0.0": { "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dependencies": [ @@ -3134,31 +3455,59 @@ "wrappy@1.0.2": { "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "y18n@4.0.3": { + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, "y18n@5.0.8": { "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, "yaeti@0.0.6": { - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==" + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "deprecated": true }, "yaml@1.10.2": { "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, "yaml@2.7.0": { - "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==" + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "bin": true + }, + "yargs-parser@18.1.3": { + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dependencies": [ + "camelcase", + "decamelize" + ] }, "yargs-parser@21.1.1": { "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" }, + "yargs@15.4.1": { + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dependencies": [ + "cliui@6.0.0", + "decamelize", + "find-up@4.1.0", + "get-caller-file", + "require-directory", + "require-main-filename", + "set-blocking", + "string-width@4.2.3", + "which-module", + "y18n@4.0.3", + "yargs-parser@18.1.3" + ] + }, "yargs@17.3.1": { "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", "dependencies": [ - "cliui", + "cliui@7.0.4", "escalade", "get-caller-file", "require-directory", "string-width@4.2.3", - "y18n", - "yargs-parser" + "y18n@5.0.8", + "yargs-parser@21.1.1" ] }, "yocto-queue@0.1.0": { @@ -3168,6 +3517,13 @@ "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==" } }, + "redirects": { + "https://esm.sh/bech32": "https://esm.sh/bech32@2.0.0" + }, + "remote": { + "https://esm.sh/bech32@2.0.0": "1b943d1583f3708812c3cfccc8236cf92d8f9b191881984e26e0b9decaed9a16", + "https://esm.sh/bech32@2.0.0/denonext/bech32.mjs": "80859514ec6ba04858364eba363d5ac73ac8d97086754e0baf14fa31d4f1b63a" + }, "workspace": { "dependencies": [ "npm:@nostr-dev-kit/ndk-cache-dexie@2.5", @@ -3178,7 +3534,7 @@ "npm:asciidoctor@3.0", "npm:d3@7.9", "npm:flowbite-svelte-icons@2.1", - "npm:flowbite-svelte@0.44", + "npm:flowbite-svelte@0.48", "npm:flowbite@2.2", "npm:he@1.2", "npm:nostr-tools@2.10", @@ -3194,7 +3550,7 @@ "npm:@sveltejs/adapter-auto@3", "npm:@sveltejs/adapter-node@^5.2.12", "npm:@sveltejs/adapter-static@3", - "npm:@sveltejs/kit@^2.16.0", + "npm:@sveltejs/kit@^2.22.2", "npm:@sveltejs/vite-plugin-svelte@4", "npm:@tailwindcss/forms@0.5", "npm:@tailwindcss/typography@0.5", @@ -3208,7 +3564,7 @@ "npm:d3@^7.9.0", "npm:eslint-plugin-svelte@2", "npm:flowbite-svelte-icons@2.1", - "npm:flowbite-svelte@0", + "npm:flowbite-svelte@0.48", "npm:flowbite@2", "npm:he@1.2", "npm:highlight.js@^11.11.1", diff --git a/import_map.json b/import_map.json index 811c3ed..0d971b8 100644 --- a/import_map.json +++ b/import_map.json @@ -12,7 +12,7 @@ "tailwind-merge": "npm:tailwind-merge@2.5.x", "svelte": "npm:svelte@5.0.x", "flowbite": "npm:flowbite@2.2.x", - "flowbite-svelte": "npm:flowbite-svelte@0.44.x", + "flowbite-svelte": "npm:flowbite-svelte@0.48.x", "flowbite-svelte-icons": "npm:flowbite-svelte-icons@2.1.x", "child_process": "node:child_process" } diff --git a/package.json b/package.json index 36d63f6..27d0dee 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "autoprefixer": "10.x", "eslint-plugin-svelte": "2.x", "flowbite": "2.x", - "flowbite-svelte": "0.x", + "flowbite-svelte": "0.48.x", "flowbite-svelte-icons": "2.1.x", "playwright": "^1.50.1", "postcss": "8.x", diff --git a/src/app.css b/src/app.css index 69a6e76..87577c2 100644 --- a/src/app.css +++ b/src/app.css @@ -155,20 +155,6 @@ @apply text-gray-900 hover:text-primary-600 dark:text-gray-100 dark:hover:text-primary-400; } - /* Sidebar */ - aside.sidebar-leather { - @apply fixed md:sticky top-[130px] sm:top-[146px] h-[calc(100vh-130px)] sm:h-[calc(100vh-146px)] z-10; - @apply bg-primary-0 dark:bg-primary-1000 px-5 w-full sm:w-auto sm:max-w-xl; - } - - aside.sidebar-leather > div { - @apply bg-primary-50 dark:bg-gray-800 h-full px-5 py-0; - } - - a.sidebar-item-leather { - @apply hover:bg-primary-100 dark:hover:bg-gray-800; - } - div.skeleton-leather div { @apply bg-primary-100 dark:bg-primary-800; } diff --git a/src/lib/components/PublicationHeader.svelte b/src/lib/components/PublicationHeader.svelte deleted file mode 100644 index ef55eb6..0000000 --- a/src/lib/components/PublicationHeader.svelte +++ /dev/null @@ -1,172 +0,0 @@ - - -{#if title != null && href != null} - -
- {#if image && !imageError} -
- -
- - Publication cover -
- {:else} - -
- {/if} -
-
-
- -
- - {#if hashtags.length > 0} -
- {#each hashtags as tag (tag)} - - {/each} -
- {/if} -
-
-{/if} diff --git a/src/lib/components/PublicationSection.svelte b/src/lib/components/PublicationSection.svelte deleted file mode 100644 index fdbd0a5..0000000 --- a/src/lib/components/PublicationSection.svelte +++ /dev/null @@ -1,198 +0,0 @@ - - -
- {#await Promise.all( [leafTitle, leafContent, leafHierarchy, publicationType, divergingBranches, leafEvent, leafHashtags], )} - - {:then [leafTitle, leafContent, leafHierarchy, publicationType, divergingBranches, resolvedLeafEvent, hashtags]} - {@const contentString = leafContent.toString()} - - - - {#each divergingBranches as [branch, depth]} - {@render sectionHeading( - getMatchingTags(branch, "title")[0]?.[1] ?? "", - depth, - )} - {/each} - {#if leafTitle} - {@const leafDepth = leafHierarchy.length - 1} - {@render sectionHeading(leafTitle, leafDepth)} - {/if} - {@render contentParagraph( - contentString, - publicationType ?? "article", - false, - )} - {#if hashtags.length > 0} -
- {#each hashtags as tag (tag)} - - {/each} -
- {/if} - {/await} -
diff --git a/src/lib/components/Toc.svelte b/src/lib/components/Toc.svelte deleted file mode 100644 index db49b82..0000000 --- a/src/lib/components/Toc.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - -
-

Table of contents

- -
- - diff --git a/src/lib/components/Publication.svelte b/src/lib/components/publications/Publication.svelte similarity index 72% rename from src/lib/components/Publication.svelte rename to src/lib/components/publications/Publication.svelte index 11d90e6..9a0dd70 100644 --- a/src/lib/components/Publication.svelte +++ b/src/lib/components/publications/Publication.svelte @@ -7,6 +7,7 @@ SidebarGroup, SidebarWrapper, Heading, + CloseButton, } from "flowbite-svelte"; import { getContext, onDestroy, onMount } from "svelte"; import { @@ -15,13 +16,13 @@ } from "flowbite-svelte-icons"; import type { NDKEvent } from "@nostr-dev-kit/ndk"; import PublicationSection from "./PublicationSection.svelte"; - import type { PublicationTree } from "$lib/data_structures/publication_tree"; import Details from "$components/util/Details.svelte"; import { publicationColumnVisibility } from "$lib/stores"; import BlogHeader from "$components/cards/BlogHeader.svelte"; import Interactions from "$components/util/Interactions.svelte"; - import TocToggle from "$components/util/TocToggle.svelte"; - import { pharosInstance } from "$lib/parser"; + import type { SveltePublicationTree } from "./svelte_publication_tree.svelte"; + import TableOfContents from "./TableOfContents.svelte"; + import type { TableOfContents as TocType } from "./table_of_contents.svelte"; let { rootAddress, publicationType, indexEvent } = $props<{ rootAddress: string; @@ -29,16 +30,16 @@ indexEvent: NDKEvent; }>(); - const publicationTree = getContext("publicationTree") as PublicationTree; + const publicationTree = getContext("publicationTree") as SveltePublicationTree; + const toc = getContext("toc") as TocType; // #region Loading - // TODO: Test load handling. - let leaves = $state>([]); let isLoading = $state(false); let isDone = $state(false); let lastElementRef = $state(null); + let activeAddress = $state(null); let observer: IntersectionObserver; @@ -82,7 +83,8 @@ // #endregion - // region Columns visibility + // #region Columns visibility + let currentBlog: null | string = $state(null); let currentBlogEvent: null | NDKEvent = $state(null); const isLeaf = $derived(indexEvent.kind === 30041); @@ -91,6 +93,10 @@ return currentBlog !== null && $publicationColumnVisibility.inner; } + function closeToc() { + publicationColumnVisibility.update((v) => ({ ...v, toc: false })); + } + function closeDiscussion() { publicationColumnVisibility.update((v) => ({ ...v, discussion: false })); } @@ -119,6 +125,33 @@ return currentBlog && currentBlogEvent && window.innerWidth < 1140; } + // #endregion + + /** + * Performs actions on the DOM element for a publication tree leaf when it is mounted. + * + * @param el The DOM element that was mounted. + * @param address The address of the event that was mounted. + */ + function onPublicationSectionMounted(el: HTMLElement, address: string) { + // Update last element ref for the intersection observer. + setLastElementRef(el, leaves.length); + + // Michael J - 08 July 2025 - NOTE: Updating the ToC from here somewhat breaks separation of + // concerns, since the TableOfContents component is primarily responsible for working with the + // ToC data structure. However, the Publication component has direct access to the needed DOM + // element already, and I want to avoid complicated callbacks between the two components. + // Update the ToC from the contents of the leaf section. + const entry = toc.getEntry(address); + if (!entry) { + console.warn(`[Publication] No parent found for ${address}`); + return; + } + toc.buildTocFromDocument(el, entry); + } + + // #region Lifecycle hooks + onDestroy(() => { // reset visibility publicationColumnVisibility.reset(); @@ -154,13 +187,27 @@ }; }); - // Whenever the publication changes, update rootId - let rootId = $derived($pharosInstance.getRootIndexId()); + // #endregion -{#if publicationType !== "blog" || !isLeaf} - +{#if publicationType !== 'blog' || !isLeaf} + {#if $publicationColumnVisibility.toc} + + + publicationTree.setBookmark(address)} + /> + + {/if} {/if} @@ -179,11 +226,12 @@ Error loading content. One or more events could not be loaded. {:else} + {@const address = leaf.tagAddress()} setLastElementRef(el, i)} + {address} + ref={(el) => onPublicationSectionMounted(el, address)} /> {/if} {/each} @@ -193,7 +241,7 @@ {:else if !isDone} {:else} -

+

You've reached the end of the publication.

{/if} @@ -204,9 +252,7 @@ {#if $publicationColumnVisibility.blog}
Unknown - 1.1.1970 + 1.1.1970
This is a very intelligent comment placeholder that applies to diff --git a/src/lib/components/PublicationFeed.svelte b/src/lib/components/publications/PublicationFeed.svelte similarity index 100% rename from src/lib/components/PublicationFeed.svelte rename to src/lib/components/publications/PublicationFeed.svelte diff --git a/src/lib/components/publications/PublicationHeader.svelte b/src/lib/components/publications/PublicationHeader.svelte new file mode 100644 index 0000000..25dfdc4 --- /dev/null +++ b/src/lib/components/publications/PublicationHeader.svelte @@ -0,0 +1,64 @@ + + +{#if title != null && href != null} + + {#if image} +
+ +
+ {/if} + +
+{/if} diff --git a/src/lib/components/publications/PublicationSection.svelte b/src/lib/components/publications/PublicationSection.svelte new file mode 100644 index 0000000..7b24fb3 --- /dev/null +++ b/src/lib/components/publications/PublicationSection.svelte @@ -0,0 +1,122 @@ + + +
+ {#await Promise.all([leafTitle, leafContent, leafHierarchy, publicationType, divergingBranches])} + + {:then [leafTitle, leafContent, leafHierarchy, publicationType, divergingBranches]} + {#each divergingBranches as [branch, depth]} + {@render sectionHeading(getMatchingTags(branch, 'title')[0]?.[1] ?? '', depth)} + {/each} + {#if leafTitle} + {@const leafDepth = leafHierarchy.length - 1} + {@render sectionHeading(leafTitle, leafDepth)} + {/if} + {@render contentParagraph(leafContent.toString(), publicationType ?? 'article', false)} + {/await} +
diff --git a/src/lib/components/publications/TableOfContents.svelte b/src/lib/components/publications/TableOfContents.svelte new file mode 100644 index 0000000..08097ed --- /dev/null +++ b/src/lib/components/publications/TableOfContents.svelte @@ -0,0 +1,77 @@ + + + + + + {#each entries as entry} + {@const address = entry.address} + {@const expanded = toc.expandedMap.get(address) ?? false} + {@const isLeaf = toc.leaves.has(address)} + {#if isLeaf} + onSectionFocused?.(address)} + /> + {:else} + {@const childDepth = depth + 1} + expanded, + (open) => setEntryExpanded(address, open) + } + > + + + {/if} + {/each} + diff --git a/src/lib/components/publications/svelte_publication_tree.svelte.ts b/src/lib/components/publications/svelte_publication_tree.svelte.ts new file mode 100644 index 0000000..0c1eefc --- /dev/null +++ b/src/lib/components/publications/svelte_publication_tree.svelte.ts @@ -0,0 +1,111 @@ +import { SvelteSet } from "svelte/reactivity"; +import { PublicationTree } from "../../data_structures/publication_tree.ts"; +import NDK, { NDKEvent } from "@nostr-dev-kit/ndk"; + +export class SveltePublicationTree { + resolvedAddresses: SvelteSet = new SvelteSet(); + + #publicationTree: PublicationTree; + #nodeResolvedObservers: Array<(address: string) => void> = []; + #bookmarkMovedObservers: Array<(address: string) => void> = []; + + constructor(rootEvent: NDKEvent, ndk: NDK) { + this.#publicationTree = new PublicationTree(rootEvent, ndk); + + this.#publicationTree.onNodeResolved(this.#handleNodeResolved); + this.#publicationTree.onBookmarkMoved(this.#handleBookmarkMoved); + } + + // #region Proxied Public Methods + + getChildAddresses(address: string): Promise> { + return this.#publicationTree.getChildAddresses(address); + } + + getEvent(address: string): Promise { + return this.#publicationTree.getEvent(address); + } + + getHierarchy(address: string): Promise { + return this.#publicationTree.getHierarchy(address); + } + + async getParent(address: string): Promise { + const hierarchy = await this.getHierarchy(address); + + // The last element in the hierarchy is the event with the given address, so the parent is the + // second to last element. + return hierarchy.at(-2) ?? null; + } + + setBookmark(address: string) { + this.#publicationTree.setBookmark(address); + } + + /** + * Registers an observer function that is invoked whenever a new node is resolved. + * @param observer The observer function. + */ + onNodeResolved(observer: (address: string) => void) { + this.#nodeResolvedObservers.push(observer); + } + + /** + * Registers an observer function that is invoked whenever the bookmark is moved. + * @param observer The observer function. + */ + onBookmarkMoved(observer: (address: string) => void) { + this.#bookmarkMovedObservers.push(observer); + } + + // #endregion + + // #region Proxied Async Iterator Methods + + [Symbol.asyncIterator](): AsyncIterator { + return this; + } + + next(): Promise> { + return this.#publicationTree.next(); + } + + previous(): Promise> { + return this.#publicationTree.previous(); + } + + // #endregion + + // #region Private Methods + + /** + * Observer function that is invoked whenever a new node is resolved on the publication tree. + * + * @param address The address of the resolved node. + * + * This member is declared as an arrow function to ensure that the correct `this` context is + * used when the function is invoked in this class's constructor. + */ + #handleNodeResolved = (address: string) => { + this.resolvedAddresses.add(address); + for (const observer of this.#nodeResolvedObservers) { + observer(address); + } + } + + /** + * Observer function that is invoked whenever the bookmark is moved on the publication tree. + * + * @param address The address of the new bookmark. + * + * This member is declared as an arrow function to ensure that the correct `this` context is + * used when the function is invoked in this class's constructor. + */ + #handleBookmarkMoved = (address: string) => { + for (const observer of this.#bookmarkMovedObservers) { + observer(address); + } + } + + // #endregion +} \ No newline at end of file diff --git a/src/lib/components/publications/table_of_contents.svelte.ts b/src/lib/components/publications/table_of_contents.svelte.ts new file mode 100644 index 0000000..0e4e310 --- /dev/null +++ b/src/lib/components/publications/table_of_contents.svelte.ts @@ -0,0 +1,292 @@ +import { SvelteMap, SvelteSet } from 'svelte/reactivity'; +import { SveltePublicationTree } from './svelte_publication_tree.svelte.ts'; +import type { NDKEvent } from '../../utils/nostrUtils.ts'; +import { indexKind } from '../../consts.ts'; + +export interface TocEntry { + address: string; + title: string; + href?: string; + children: TocEntry[]; + parent?: TocEntry; + depth: number; + childrenResolved: boolean; + resolveChildren: () => Promise; +} + +/** + * Maintains a table of contents (ToC) for a `SveltePublicationTree`. Since publication trees are + * conceptually infinite and lazy-loading, the ToC represents only the portion of the tree that has + * been "discovered". The ToC is updated as new nodes are resolved within the publication tree. + * + * @see SveltePublicationTree + */ +export class TableOfContents { + public addressMap: SvelteMap = new SvelteMap(); + public expandedMap: SvelteMap = new SvelteMap(); + public leaves: SvelteSet = new SvelteSet(); + + #root: TocEntry | null = null; + #publicationTree: SveltePublicationTree; + #pagePathname: string; + + /** + * Constructs a `TableOfContents` from a `SveltePublicationTree`. + * + * @param rootAddress The address of the root event. + * @param publicationTree The SveltePublicationTree instance. + * @param pagePathname The current page pathname for href generation. + */ + constructor(rootAddress: string, publicationTree: SveltePublicationTree, pagePathname: string) { + this.#publicationTree = publicationTree; + this.#pagePathname = pagePathname; + this.#init(rootAddress); + } + + // #region Public Methods + + /** + * Returns the root entry of the ToC. + * + * @returns The root entry of the ToC, or `null` if the ToC has not been initialized. + */ + getRootEntry(): TocEntry | null { + return this.#root; + } + + getEntry(address: string): TocEntry | undefined { + return this.addressMap.get(address); + } + + /** + * Builds a table of contents from the DOM subtree rooted at `parentElement`. + * + * @param parentElement The root of the DOM subtree containing the content to be added to the + * ToC. + * @param parentAddress The address of the event corresponding to the DOM subtree root indicated + * by `parentElement`. + * + * This function is intended for use on segments of HTML markup that are not directly derived + * from a structure publication of the kind supported by `PublicationTree`. It may be used to + * produce a table of contents from the contents of a kind `30041` event with AsciiDoc markup, or + * from a kind `30023` event with Markdown content. + */ + buildTocFromDocument( + parentElement: HTMLElement, + parentEntry: TocEntry, + ) { + parentElement + .querySelectorAll(`h${parentEntry.depth}`) + .forEach((header) => { + // TODO: Correctly update ToC state from DOM. + const title = header.textContent?.trim(); + const id = header.id; + + // Only create an entry if the header has an ID and a title. + if (id && title) { + const href = `${this.#pagePathname}#${id}`; + + // TODO: Check this logic. + const tocEntry: TocEntry = { + address: parentEntry.address, + title, + href, + depth: parentEntry.depth + 1, + children: [], + childrenResolved: true, + resolveChildren: () => Promise.resolve(), + }; + parentEntry.children.push(tocEntry); + this.expandedMap.set(tocEntry.address, false); + + this.buildTocFromDocument(header, tocEntry); + } + }); + } + + // #endregion + + // #region Iterator Methods + + /** + * Iterates over all ToC entries in depth-first order. + */ + *[Symbol.iterator](): IterableIterator { + function* traverse(entry: TocEntry | null): IterableIterator { + if (!entry) { + return; + } + + yield entry; + + if (entry.children) { + for (const child of entry.children) { + yield* traverse(child); + } + } + } + + yield* traverse(this.#root); + } + + // #endregion + + // #region Private Methods + + /** + * Initializes the ToC from the associated publication tree. + * + * @param rootAddress The address of the publication's root event. + * + * Michael J - 07 July 2025 - NOTE: Since the publication tree is conceptually infinite and + * lazy-loading, the ToC is not guaranteed to contain all the nodes at any layer until the + * publication has been fully resolved. + * + * Michael J - 07 July 2025 - TODO: If the relay provides event metadata, use the metadata to + * initialize the ToC with all of its first-level children. + */ + async #init(rootAddress: string) { + const rootEvent = await this.#publicationTree.getEvent(rootAddress); + if (!rootEvent) { + throw new Error(`[ToC] Root event ${rootAddress} not found.`); + } + + this.#root = await this.#buildTocEntry(rootAddress); + + this.addressMap.set(rootAddress, this.#root); + + // Handle any other nodes that have already been resolved in parallel. + await Promise.all( + Array.from(this.#publicationTree.resolvedAddresses).map((address) => + this.#buildTocEntryFromResolvedNode(address) + ) + ); + + // Set up an observer to handle progressive resolution of the publication tree. + this.#publicationTree.onNodeResolved((address: string) => { + this.#buildTocEntryFromResolvedNode(address); + }); + } + + #getTitle(event: NDKEvent | null): string { + if (!event) { + // TODO: What do we want to return in this case? + return '[untitled]'; + } + const titleTag = event.getMatchingTags?.('title')?.[0]?.[1]; + return titleTag || event.tagAddress() || '[untitled]'; + } + + async #buildTocEntry(address: string): Promise { + // Michael J - 07 July 2025 - NOTE: This arrow function is nested so as to use its containing + // scope in its operation. Do not move it to the top level without ensuring it still has access + // to the necessary variables. + const resolver = async () => { + if (entry.childrenResolved) { + return; + } + + const event = await this.#publicationTree.getEvent(entry.address); + if (event?.kind !== indexKind) { + // TODO: Build ToC entries from HTML markup in this case. + return; + } + + const childAddresses = await this.#publicationTree.getChildAddresses(entry.address); + for (const childAddress of childAddresses) { + if (!childAddress) { + continue; + } + + // Michael J - 16 June 2025 - This duplicates logic in the outer function, but is necessary + // here so that we can determine whether to render an entry as a leaf before it is fully + // resolved. + if (childAddress.split(':')[0] !== indexKind.toString()) { + this.leaves.add(childAddress); + } + + // Michael J - 05 June 2025 - The `getChildAddresses` method forces node resolution on the + // publication tree. This is acceptable here, because the tree is always resolved + // top-down. Therefore, by the time we handle a node's resolution, its parent and + // siblings have already been resolved. + const childEntry = await this.#buildTocEntry(childAddress); + childEntry.parent = entry; + childEntry.depth = entry.depth + 1; + entry.children.push(childEntry); + this.addressMap.set(childAddress, childEntry); + } + + await this.#matchChildrenToTagOrder(entry); + + entry.childrenResolved = true; + } + + const event = await this.#publicationTree.getEvent(address); + if (!event) { + throw new Error(`[ToC] Event ${address} not found.`); + } + + const depth = (await this.#publicationTree.getHierarchy(address)).length; + + const entry: TocEntry = { + address, + title: this.#getTitle(event), + href: `${this.#pagePathname}#${address}`, + children: [], + depth, + childrenResolved: false, + resolveChildren: resolver, + }; + this.expandedMap.set(address, false); + + // Michael J - 16 June 2025 - We determine whether to add a leaf both here and in the inner + // resolver function. The resolver function is called when entries are resolved by expanding + // a ToC entry, and we'll reach the block below when entries are resolved by the publication + // tree. + if (event.kind !== indexKind) { + this.leaves.add(address); + } + + return entry; + } + + /** + * Reorders the children of a ToC entry to match the order of 'a' tags in the corresponding + * Nostr index event. + * + * @param entry The ToC entry to reorder. + * + * This function has a time complexity of `O(n log n)`, where `n` is the number of children the + * parent event has. Average size of `n` is small enough to be negligible. + */ + async #matchChildrenToTagOrder(entry: TocEntry) { + const parentEvent = await this.#publicationTree.getEvent(entry.address); + if (parentEvent?.kind === indexKind) { + const tagOrder = parentEvent.getMatchingTags('a').map(tag => tag[1]); + const addressToOrdinal = new Map(); + + // Build map of addresses to their ordinals from tag order + tagOrder.forEach((address, index) => { + addressToOrdinal.set(address, index); + }); + + entry.children.sort((a, b) => { + const aOrdinal = addressToOrdinal.get(a.address) ?? Number.MAX_SAFE_INTEGER; + const bOrdinal = addressToOrdinal.get(b.address) ?? Number.MAX_SAFE_INTEGER; + return aOrdinal - bOrdinal; + }); + } + } + + #buildTocEntryFromResolvedNode(address: string) { + if (this.addressMap.has(address)) { + return; + } + + this.#buildTocEntry(address).then((entry) => { + this.addressMap.set(address, entry); + }); + } + + // #endregion +} diff --git a/src/lib/components/util/ArticleNav.svelte b/src/lib/components/util/ArticleNav.svelte index 1fcc001..24d2137 100644 --- a/src/lib/components/util/ArticleNav.svelte +++ b/src/lib/components/util/ArticleNav.svelte @@ -1,41 +1,35 @@ - \ No newline at end of file diff --git a/src/lib/components/util/TocToggle.svelte b/src/lib/components/util/TocToggle.svelte deleted file mode 100644 index 72083a3..0000000 --- a/src/lib/components/util/TocToggle.svelte +++ /dev/null @@ -1,150 +0,0 @@ - - - -{#if $publicationColumnVisibility.toc} - - - - Table of contents -

- (This ToC is only for demo purposes, and is not fully-functional.) -

- {#each tocItems as item} - - {/each} -
-
-
-{/if} diff --git a/src/lib/data_structures/publication_tree.ts b/src/lib/data_structures/publication_tree.ts index e0c56fa..4991b77 100644 --- a/src/lib/data_structures/publication_tree.ts +++ b/src/lib/data_structures/publication_tree.ts @@ -1,7 +1,6 @@ -import type NDK from "@nostr-dev-kit/ndk"; -import type { NDKEvent } from "@nostr-dev-kit/ndk"; -import { Lazy } from "./lazy.ts"; -import { findIndexAsync as _findIndexAsync } from "../utils.ts"; +import type NDK from '@nostr-dev-kit/ndk'; +import type { NDKEvent } from '@nostr-dev-kit/ndk'; +import { Lazy } from './lazy.ts'; enum PublicationTreeNodeType { Branch, @@ -13,6 +12,16 @@ enum PublicationTreeNodeStatus { Error, } +export enum TreeTraversalMode { + Leaves, + All, +} + +enum TreeTraversalDirection { + Forward, + Backward, +} + interface PublicationTreeNode { type: PublicationTreeNodeType; status: PublicationTreeNodeStatus; @@ -52,20 +61,23 @@ export class PublicationTree implements AsyncIterable { */ #ndk: NDK; + #nodeAddedObservers: Array<(address: string) => void> = []; + + #nodeResolvedObservers: Array<(address: string) => void> = []; + + #bookmarkMovedObservers: Array<(address: string) => void> = []; + constructor(rootEvent: NDKEvent, ndk: NDK) { const rootAddress = rootEvent.tagAddress(); this.#root = { - type: this.#getNodeType(rootEvent), + type: PublicationTreeNodeType.Branch, status: PublicationTreeNodeStatus.Resolved, address: rootAddress, children: [], }; this.#nodes = new Map>(); - this.#nodes.set( - rootAddress, - new Lazy(() => Promise.resolve(this.#root)), - ); + this.#nodes.set(rootAddress, new Lazy(() => Promise.resolve(this.#root))); this.#events = new Map(); this.#events.set(rootAddress, rootEvent); @@ -88,7 +100,7 @@ export class PublicationTree implements AsyncIterable { if (!parentNode) { throw new Error( - `PublicationTree: Parent node with address ${parentAddress} not found.`, + `PublicationTree: Parent node with address ${parentAddress} not found.` ); } @@ -119,7 +131,7 @@ export class PublicationTree implements AsyncIterable { if (!parentNode) { throw new Error( - `PublicationTree: Parent node with address ${parentAddress} not found.`, + `PublicationTree: Parent node with address ${parentAddress} not found.` ); } @@ -142,21 +154,22 @@ export class PublicationTree implements AsyncIterable { /** * Retrieves the addresses of the loaded children, if any, of the node with the given address. + * * @param address The address of the parent node. * @returns An array of addresses of any loaded child nodes. + * + * Note that this method resolves all children of the node. */ async getChildAddresses(address: string): Promise> { const node = await this.#nodes.get(address)?.value(); if (!node) { - throw new Error( - `PublicationTree: Node with address ${address} not found.`, - ); + throw new Error(`[PublicationTree] Node with address ${address} not found.`); } return Promise.all( - node.children?.map( - async (child) => (await child.value())?.address ?? null, - ) ?? [], + node.children?.map(async child => + (await child.value())?.address ?? null + ) ?? [] ); } /** @@ -168,13 +181,11 @@ export class PublicationTree implements AsyncIterable { async getHierarchy(address: string): Promise { let node = await this.#nodes.get(address)?.value(); if (!node) { - throw new Error( - `PublicationTree: Node with address ${address} not found.`, - ); + throw new Error(`[PublicationTree] Node with address ${address} not found.`); } const hierarchy: NDKEvent[] = [this.#events.get(address)!]; - + while (node.parent) { hierarchy.push(this.#events.get(node.parent.address)!); node = node.parent; @@ -189,12 +200,34 @@ export class PublicationTree implements AsyncIterable { */ setBookmark(address: string) { this.#bookmark = address; - this.#cursor.tryMoveTo(address); + this.#cursor.tryMoveTo(address).then(success => { + if (success) { + this.#bookmarkMovedObservers.forEach(observer => observer(address)); + } + }); + } + + onBookmarkMoved(observer: (address: string) => void) { + this.#bookmarkMovedObservers.push(observer); + } + + onNodeAdded(observer: (address: string) => void) { + this.#nodeAddedObservers.push(observer); + } + + /** + * Registers an observer function that is invoked whenever a new node is resolved. Nodes are + * added lazily. + * + * @param observer The observer function. + */ + onNodeResolved(observer: (address: string) => void) { + this.#nodeResolvedObservers.push(observer); } // #region Iteration Cursor - #cursor = new (class { + #cursor = new class { target: PublicationTreeNode | null | undefined; #tree: PublicationTree; @@ -206,9 +239,7 @@ export class PublicationTree implements AsyncIterable { async tryMoveTo(address?: string) { if (!address) { const startEvent = await this.#tree.#depthFirstRetrieve(); - this.target = await this.#tree.#nodes - .get(startEvent!.tagAddress()) - ?.value(); + this.target = await this.#tree.#nodes.get(startEvent!.tagAddress())?.value(); } else { this.target = await this.#tree.#nodes.get(address)?.value(); } @@ -222,7 +253,7 @@ export class PublicationTree implements AsyncIterable { async tryMoveToFirstChild(): Promise { if (!this.target) { - console.debug("Cursor: Target node is null or undefined."); + console.debug("[Publication Tree Cursor] Target node is null or undefined."); return false; } @@ -233,32 +264,32 @@ export class PublicationTree implements AsyncIterable { if (this.target.children == null || this.target.children.length === 0) { return false; } - + this.target = await this.target.children?.at(0)?.value(); return true; } async tryMoveToLastChild(): Promise { if (!this.target) { - console.debug("Cursor: Target node is null or undefined."); + console.debug("[Publication Tree Cursor] Target node is null or undefined."); return false; } - + if (this.target.type === PublicationTreeNodeType.Leaf) { return false; } - + if (this.target.children == null || this.target.children.length === 0) { return false; } - + this.target = await this.target.children?.at(-1)?.value(); return true; } async tryMoveToNextSibling(): Promise { if (!this.target) { - console.debug("Cursor: Target node is null or undefined."); + console.debug("[Publication Tree Cursor] Target node is null or undefined."); return false; } @@ -269,8 +300,7 @@ export class PublicationTree implements AsyncIterable { } const currentIndex = await siblings.findIndexAsync( - async (sibling: Lazy) => - (await sibling.value())?.address === this.target!.address, + async (sibling: Lazy) => (await sibling.value())?.address === this.target!.address ); if (currentIndex === -1) { @@ -287,36 +317,35 @@ export class PublicationTree implements AsyncIterable { async tryMoveToPreviousSibling(): Promise { if (!this.target) { - console.debug("Cursor: Target node is null or undefined."); + console.debug("[Publication Tree Cursor] Target node is null or undefined."); return false; } - + const parent = this.target.parent; const siblings = parent?.children; if (!siblings) { return false; } - + const currentIndex = await siblings.findIndexAsync( - async (sibling: Lazy) => - (await sibling.value())?.address === this.target!.address, + async (sibling: Lazy) => (await sibling.value())?.address === this.target!.address ); if (currentIndex === -1) { return false; } - + if (currentIndex <= 0) { return false; } - + this.target = await siblings.at(currentIndex - 1)?.value(); return true; } tryMoveToParent(): boolean { if (!this.target) { - console.debug("Cursor: Target node is null or undefined."); + console.debug("[Publication Tree Cursor] Target node is null or undefined."); return false; } @@ -328,7 +357,7 @@ export class PublicationTree implements AsyncIterable { this.target = parent; return true; } - })(this); + }(this); // #endregion @@ -338,9 +367,40 @@ export class PublicationTree implements AsyncIterable { return this; } - // TODO: Add `previous()` method. + /** + * Return the next event in the tree for the given traversal mode. + * + * @param mode The traversal mode. Can be {@link TreeTraversalMode.Leaves} or + * {@link TreeTraversalMode.All}. + * @returns The next event in the tree, or null if the tree is empty. + */ + async next( + mode: TreeTraversalMode = TreeTraversalMode.Leaves + ): Promise> { + if (!this.#cursor.target) { + if (await this.#cursor.tryMoveTo(this.#bookmark)) { + return this.#yieldEventAtCursor(false); + } + } + + switch (mode) { + case TreeTraversalMode.Leaves: + return this.#walkLeaves(TreeTraversalDirection.Forward); + case TreeTraversalMode.All: + return this.#preorderWalkAll(TreeTraversalDirection.Forward); + } + } - async next(): Promise> { + /** + * Return the previous event in the tree for the given traversal mode. + * + * @param mode The traversal mode. Can be {@link TreeTraversalMode.Leaves} or + * {@link TreeTraversalMode.All}. + * @returns The previous event in the tree, or null if the tree is empty. + */ + async previous( + mode: TreeTraversalMode = TreeTraversalMode.Leaves + ): Promise> { if (!this.#cursor.target) { if (await this.#cursor.tryMoveTo(this.#bookmark)) { const event = await this.getEvent(this.#cursor.target!.address); @@ -348,11 +408,41 @@ export class PublicationTree implements AsyncIterable { } } - // Based on Raymond Chen's tree traversal algorithm example. - // https://devblogs.microsoft.com/oldnewthing/20200106-00/?p=103300 + switch (mode) { + case TreeTraversalMode.Leaves: + return this.#walkLeaves(TreeTraversalDirection.Backward); + case TreeTraversalMode.All: + return this.#preorderWalkAll(TreeTraversalDirection.Backward); + } + } + + async #yieldEventAtCursor(done: boolean): Promise> { + const value = (await this.getEvent(this.#cursor.target!.address)) ?? null; + return { done, value }; + } + + /** + * Walks the tree in the given direction, yielding the event at each leaf. + * + * @param direction The direction to walk the tree. + * @returns The event at the leaf, or null if the tree is empty. + * + * Based on Raymond Chen's tree traversal algorithm example. + * https://devblogs.microsoft.com/oldnewthing/20200106-00/?p=103300 + */ + async #walkLeaves( + direction: TreeTraversalDirection = TreeTraversalDirection.Forward + ): Promise> { + const tryMoveToSibling: () => Promise = direction === TreeTraversalDirection.Forward + ? this.#cursor.tryMoveToNextSibling.bind(this.#cursor) + : this.#cursor.tryMoveToPreviousSibling.bind(this.#cursor); + const tryMoveToChild: () => Promise = direction === TreeTraversalDirection.Forward + ? this.#cursor.tryMoveToFirstChild.bind(this.#cursor) + : this.#cursor.tryMoveToLastChild.bind(this.#cursor); + do { - if (await this.#cursor.tryMoveToNextSibling()) { - while (await this.#cursor.tryMoveToFirstChild()) { + if (await tryMoveToSibling()) { + while (await tryMoveToChild()) { continue; } @@ -360,8 +450,7 @@ export class PublicationTree implements AsyncIterable { return { done: false, value: null }; } - const event = await this.getEvent(this.#cursor.target!.address); - return { done: false, value: event }; + return this.#yieldEventAtCursor(false); } } while (this.#cursor.tryMoveToParent()); @@ -373,28 +462,32 @@ export class PublicationTree implements AsyncIterable { return { done: true, value: null }; } - async previous(): Promise> { - if (!this.#cursor.target) { - if (await this.#cursor.tryMoveTo(this.#bookmark)) { - const event = await this.getEvent(this.#cursor.target!.address); - return { done: false, value: event }; - } + /** + * Walks the tree in the given direction, yielding the event at each node. + * + * @param direction The direction to walk the tree. + * @returns The event at the node, or null if the tree is empty. + * + * Based on Raymond Chen's preorder walk algorithm example. + * https://devblogs.microsoft.com/oldnewthing/20200107-00/?p=103304 + */ + async #preorderWalkAll( + direction: TreeTraversalDirection = TreeTraversalDirection.Forward + ): Promise> { + const tryMoveToSibling: () => Promise = direction === TreeTraversalDirection.Forward + ? this.#cursor.tryMoveToNextSibling.bind(this.#cursor) + : this.#cursor.tryMoveToPreviousSibling.bind(this.#cursor); + const tryMoveToChild: () => Promise = direction === TreeTraversalDirection.Forward + ? this.#cursor.tryMoveToFirstChild.bind(this.#cursor) + : this.#cursor.tryMoveToLastChild.bind(this.#cursor); + + if (await tryMoveToChild()) { + return this.#yieldEventAtCursor(false); } - // Based on Raymond Chen's tree traversal algorithm example. - // https://devblogs.microsoft.com/oldnewthing/20200106-00/?p=103300 do { - if (await this.#cursor.tryMoveToPreviousSibling()) { - while (await this.#cursor.tryMoveToLastChild()) { - continue; - } - - if (this.#cursor.target!.status === PublicationTreeNodeStatus.Error) { - return { done: false, value: null }; - } - - const event = await this.getEvent(this.#cursor.target!.address); - return { done: false, value: event }; + if (await tryMoveToSibling()) { + return this.#yieldEventAtCursor(false); } } while (this.#cursor.tryMoveToParent()); @@ -402,7 +495,8 @@ export class PublicationTree implements AsyncIterable { return { done: false, value: null }; } - return { done: true, value: null }; + // If we get to this point, we're at the root node (can't move up any more). + return this.#yieldEventAtCursor(true); } // #endregion @@ -423,23 +517,17 @@ export class PublicationTree implements AsyncIterable { const stack: string[] = [this.#root.address]; let currentNode: PublicationTreeNode | null | undefined = this.#root; - let currentEvent: NDKEvent | null | undefined = this.#events.get( - this.#root.address, - )!; + let currentEvent: NDKEvent | null | undefined = this.#events.get(this.#root.address)!; while (stack.length > 0) { const currentAddress = stack.pop(); currentNode = await this.#nodes.get(currentAddress!)?.value(); if (!currentNode) { - throw new Error( - `PublicationTree: Node with address ${currentAddress} not found.`, - ); + throw new Error(`[PublicationTree] Node with address ${currentAddress} not found.`); } currentEvent = this.#events.get(currentAddress!); if (!currentEvent) { - throw new Error( - `PublicationTree: Event with address ${currentAddress} not found.`, - ); + throw new Error(`[PublicationTree] Event with address ${currentAddress} not found.`); } // Stop immediately if the target of the search is found. @@ -448,8 +536,8 @@ export class PublicationTree implements AsyncIterable { } const currentChildAddresses = currentEvent.tags - .filter((tag) => tag[0] === "a") - .map((tag) => tag[1]); + .filter(tag => tag[0] === 'a') + .map(tag => tag[1]); // If the current event has no children, it is a leaf. if (currentChildAddresses.length === 0) { @@ -481,43 +569,36 @@ export class PublicationTree implements AsyncIterable { } #addNode(address: string, parentNode: PublicationTreeNode) { - if (this.#nodes.has(address)) { - console.debug( - `[PublicationTree] Node with address ${address} already exists.`, - ); - return; - } - - const lazyNode = new Lazy(() => - this.#resolveNode(address, parentNode), - ); + const lazyNode = new Lazy(() => this.#resolveNode(address, parentNode)); parentNode.children!.push(lazyNode); this.#nodes.set(address, lazyNode); + + this.#nodeAddedObservers.forEach(observer => observer(address)); } /** * Resolves a node address into an event, and creates new nodes for its children. - * + * * This method is intended for use as a {@link Lazy} resolver. - * + * * @param address The address of the node to resolve. * @param parentNode The parent node of the node to resolve. * @returns The resolved node. */ async #resolveNode( address: string, - parentNode: PublicationTreeNode, + parentNode: PublicationTreeNode ): Promise { - const [kind, pubkey, dTag] = address.split(":"); + const [kind, pubkey, dTag] = address.split(':'); const event = await this.#ndk.fetchEvent({ kinds: [parseInt(kind)], authors: [pubkey], - "#d": [dTag], + '#d': [dTag], }); if (!event) { console.debug( - `PublicationTree: Event with address ${address} not found.`, + `[PublicationTree] Event with address ${address} not found.` ); return { @@ -531,10 +612,8 @@ export class PublicationTree implements AsyncIterable { this.#events.set(address, event); - const childAddresses = event.tags - .filter((tag) => tag[0] === "a") - .map((tag) => tag[1]); - + const childAddresses = event.tags.filter(tag => tag[0] === 'a').map(tag => tag[1]); + const node: PublicationTreeNode = { type: this.#getNodeType(event), status: PublicationTreeNodeStatus.Resolved, @@ -547,11 +626,13 @@ export class PublicationTree implements AsyncIterable { this.addEventByAddress(address, event); } + this.#nodeResolvedObservers.forEach(observer => observer(address)); + return node; } #getNodeType(event: NDKEvent): PublicationTreeNodeType { - if (event.kind === 30040 && event.tags.some((tag) => tag[0] === "a")) { + if (event.kind === 30040 && event.tags.some(tag => tag[0] === 'a')) { return PublicationTreeNodeType.Branch; } @@ -559,4 +640,4 @@ export class PublicationTree implements AsyncIterable { } // #endregion -} +} \ No newline at end of file diff --git a/src/lib/stores.ts b/src/lib/stores.ts index 74219db..7962e7b 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -1,5 +1,5 @@ -import { readable, writable } from "svelte/store"; -import { FeedType } from "./consts"; +import { readable, writable } from 'svelte/store'; +import { FeedType } from './consts.ts'; export let idList = writable([]); @@ -7,23 +7,33 @@ export let alexandriaKinds = readable([30040, 30041, 30818]); export let feedType = writable(FeedType.StandardRelays); -const defaultVisibility = { +export interface PublicationLayoutVisibility { + toc: boolean; + blog: boolean; + main: boolean; + inner: boolean; + discussion: boolean; + editing: boolean; +} + +const defaultVisibility: PublicationLayoutVisibility = { toc: false, blog: true, main: true, inner: false, discussion: false, - editing: false, + editing: false }; function createVisibilityStore() { - const { subscribe, set, update } = writable({ ...defaultVisibility }); + const { subscribe, set, update } + = writable({ ...defaultVisibility }); return { subscribe, set, update, - reset: () => set({ ...defaultVisibility }), + reset: () => set({ ...defaultVisibility }) }; } diff --git a/src/routes/publication/+page.svelte b/src/routes/publication/+page.svelte index 5612115..5e92cbd 100644 --- a/src/routes/publication/+page.svelte +++ b/src/routes/publication/+page.svelte @@ -1,26 +1,31 @@ @@ -56,23 +109,16 @@ -{#key data} - + +
+ -{/key} - -
- {#await data.waitable} - - {:then} - {@const debugInfo = console.debug(`[Publication Page] Data loaded, rendering Publication component with publicationType: ${data.publicationType}, rootAddress: ${data.indexEvent.tagAddress()}`)} - - {/await}
diff --git a/src/routes/publication/+page.ts b/src/routes/publication/+page.ts index b3d0885..d154cd8 100644 --- a/src/routes/publication/+page.ts +++ b/src/routes/publication/+page.ts @@ -1,28 +1,28 @@ -import { error } from "@sveltejs/kit"; -import type { Load } from "@sveltejs/kit"; -import type { NDKEvent } from "@nostr-dev-kit/ndk"; -import { nip19 } from "nostr-tools"; -import { getActiveRelays } from "$lib/ndk"; -import { getMatchingTags } from "$lib/utils/nostrUtils"; +import { error } from '@sveltejs/kit'; +import type { Load } from '@sveltejs/kit'; +import type { NDKEvent } from '@nostr-dev-kit/ndk'; +import { nip19 } from 'nostr-tools'; +import { getActiveRelays } from '$lib/ndk'; +import { getMatchingTags } from '$lib/utils/nostrUtils'; /** * Decodes an naddr identifier and returns a filter object */ function decodeNaddr(id: string) { try { - if (!id.startsWith("naddr")) return {}; - + if (!id.startsWith('naddr')) return {}; + const decoded = nip19.decode(id); - if (decoded.type !== "naddr") return {}; - + if (decoded.type !== 'naddr') return {}; + const data = decoded.data; return { kinds: [data.kind], authors: [data.pubkey], - "#d": [data.identifier], + '#d': [data.identifier] }; } catch (e) { - console.error("Failed to decode naddr:", e); + console.error('Failed to decode naddr:', e); return null; } } @@ -32,7 +32,7 @@ function decodeNaddr(id: string) { */ async function fetchEventById(ndk: any, id: string): Promise { const filter = decodeNaddr(id); - + // Handle the case where filter is null (decoding error) if (filter === null) { // If we can't decode the naddr, try using the raw ID @@ -46,14 +46,14 @@ async function fetchEventById(ndk: any, id: string): Promise { throw error(404, `Failed to fetch publication root event.\n${err}`); } } - + const hasFilter = Object.keys(filter).length > 0; - + try { - const event = await (hasFilter - ? ndk.fetchEvent(filter) - : ndk.fetchEvent(id)); - + const event = await (hasFilter ? + ndk.fetchEvent(filter) : + ndk.fetchEvent(id)); + if (!event) { throw new Error(`Event not found for ID: ${id}`); } @@ -69,11 +69,11 @@ async function fetchEventById(ndk: any, id: string): Promise { async function fetchEventByDTag(ndk: any, dTag: string): Promise { try { const event = await ndk.fetchEvent( - { "#d": [dTag] }, - { closeOnEose: false }, - getActiveRelays(ndk), + { '#d': [dTag] }, + { closeOnEose: false }, + getActiveRelays(ndk) ); - + if (!event) { throw new Error(`Event not found for d tag: ${dTag}`); } @@ -83,33 +83,25 @@ async function fetchEventByDTag(ndk: any, dTag: string): Promise { } } -export const load: Load = async ({ - url, - parent, -}: { - url: URL; - parent: () => Promise; -}) => { - const id = url.searchParams.get("id"); - const dTag = url.searchParams.get("d"); - const { ndk, parser } = await parent(); - +// TODO: Use path params instead of query params. +export const load: Load = async ({ url, parent }: { url: URL; parent: () => Promise }) => { + const id = url.searchParams.get('id'); + const dTag = url.searchParams.get('d'); + const { ndk } = await parent(); + if (!id && !dTag) { - throw error(400, "No publication root event ID or d tag provided."); + throw error(400, 'No publication root event ID or d tag provided.'); } - + // Fetch the event based on available parameters - const indexEvent = id + const indexEvent = id ? await fetchEventById(ndk, id) : await fetchEventByDTag(ndk, dTag!); - - const publicationType = getMatchingTags(indexEvent, "type")[0]?.[1]; - const fetchPromise = parser.fetch(indexEvent); + + const publicationType = getMatchingTags(indexEvent, 'type')[0]?.[1]; return { - waitable: fetchPromise, publicationType, indexEvent, - url, }; };