Initial version
This commit is contained in:
commit
da9428f059
49 changed files with 5506 additions and 0 deletions
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
node_modules
|
||||
dist
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
1
.npmrc
Normal file
1
.npmrc
Normal file
|
@ -0,0 +1 @@
|
|||
@jsr:registry=https://npm.jsr.io
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["esbenp.prettier-vscode"]
|
||||
}
|
1
Readme.md
Normal file
1
Readme.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Eve
|
702
bun.lock
Normal file
702
bun.lock
Normal file
|
@ -0,0 +1,702 @@
|
|||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "eve",
|
||||
"dependencies": {
|
||||
"@lit-labs/motion": "^1.0.8",
|
||||
"@nostr-dev-kit/ndk": "^2.10.7",
|
||||
"@nostr-dev-kit/ndk-cache-dexie": "^2.5.8",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@std/http": "npm:@jsr/std__http@^1.0.13",
|
||||
"iconify-icon": "^2.2.0",
|
||||
"lit": "^3.2.1",
|
||||
"markdown-it": "^14.1.0",
|
||||
"nostr-tools": "^2.10.4",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@preact/preset-vite": "^2.9.3",
|
||||
"@tsconfig/node22": "^22.0.0",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@types/node": "^22.10.2",
|
||||
"jsdom": "^25.0.1",
|
||||
"lightningcss": "^1.28.2",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"sass-embedded": "^1.83.0",
|
||||
"typescript": "~5.6.3",
|
||||
"vite": "^6.0.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||
|
||||
"@asamuzakjp/css-color": ["@asamuzakjp/css-color@2.8.3", "", { "dependencies": { "@csstools/css-calc": "^2.1.1", "@csstools/css-color-parser": "^3.0.7", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw=="],
|
||||
|
||||
"@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
|
||||
|
||||
"@babel/compat-data": ["@babel/compat-data@7.26.8", "", {}, "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ=="],
|
||||
|
||||
"@babel/core": ["@babel/core@7.26.9", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.9", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", "@babel/helpers": "^7.26.9", "@babel/parser": "^7.26.9", "@babel/template": "^7.26.9", "@babel/traverse": "^7.26.9", "@babel/types": "^7.26.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw=="],
|
||||
|
||||
"@babel/generator": ["@babel/generator@7.26.9", "", { "dependencies": { "@babel/parser": "^7.26.9", "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg=="],
|
||||
|
||||
"@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="],
|
||||
|
||||
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.26.5", "", { "dependencies": { "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA=="],
|
||||
|
||||
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="],
|
||||
|
||||
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.26.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw=="],
|
||||
|
||||
"@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.26.5", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
|
||||
|
||||
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.25.9", "", {}, "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw=="],
|
||||
|
||||
"@babel/helpers": ["@babel/helpers@7.26.9", "", { "dependencies": { "@babel/template": "^7.26.9", "@babel/types": "^7.26.9" } }, "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA=="],
|
||||
|
||||
"@babel/parser": ["@babel/parser@7.26.9", "", { "dependencies": { "@babel/types": "^7.26.9" }, "bin": "./bin/babel-parser.js" }, "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A=="],
|
||||
|
||||
"@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/types": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.25.9", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw=="],
|
||||
|
||||
"@babel/template": ["@babel/template@7.26.9", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.26.9", "@babel/types": "^7.26.9" } }, "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA=="],
|
||||
|
||||
"@babel/traverse": ["@babel/traverse@7.26.9", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.9", "@babel/parser": "^7.26.9", "@babel/template": "^7.26.9", "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.26.9", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw=="],
|
||||
|
||||
"@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
|
||||
|
||||
"@bufbuild/protobuf": ["@bufbuild/protobuf@2.2.3", "", {}, "sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg=="],
|
||||
|
||||
"@csstools/color-helpers": ["@csstools/color-helpers@5.0.1", "", {}, "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA=="],
|
||||
|
||||
"@csstools/css-calc": ["@csstools/css-calc@2.1.1", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag=="],
|
||||
|
||||
"@csstools/css-color-parser": ["@csstools/css-color-parser@3.0.7", "", { "dependencies": { "@csstools/color-helpers": "^5.0.1", "@csstools/css-calc": "^2.1.1" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA=="],
|
||||
|
||||
"@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.4", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A=="],
|
||||
|
||||
"@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.3", "", {}, "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.24.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.24.2", "", { "os": "android", "cpu": "x64" }, "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.24.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.24.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.24.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.24.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.24.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.24.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.24.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.24.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.24.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.24.2", "", { "os": "linux", "cpu": "x64" }, "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.24.2", "", { "os": "none", "cpu": "arm64" }, "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.24.2", "", { "os": "none", "cpu": "x64" }, "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.24.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.24.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.24.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.24.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.24.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="],
|
||||
|
||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"@jsr/std__bytes": ["@jsr/std__bytes@1.0.5", "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.5.tgz", {}, "sha512-jcyXQSy5zAh2/6NLMWUkhJuQ0HihY5RwWF5RvpZyEmpmBaokkTwPkev0sqHzNJJvNusDUMznVBIclTT9augCkg=="],
|
||||
|
||||
"@jsr/std__cli": ["@jsr/std__cli@1.0.13", "https://npm.jsr.io/~/11/@jsr/std__cli/1.0.13.tgz", {}, "sha512-uLGrtZRqBdfKCVAbZC8BEMeBz/stU9aR+vGP2D7RslKhN8arOzVrcm2Uja6eN7mdOxPLWVPVrmC4Q1trq7Wdsg=="],
|
||||
|
||||
"@jsr/std__encoding": ["@jsr/std__encoding@1.0.7", "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.7.tgz", {}, "sha512-eySTZkCAHjiKGgOMvhUyTL4aT22svaCO0eO6CAbz48kRECMtRGEfe3KWXkVy4fhlZ/4OTw/RQLrTYyEqpGQp/Q=="],
|
||||
|
||||
"@jsr/std__fmt": ["@jsr/std__fmt@1.0.5", "https://npm.jsr.io/~/11/@jsr/std__fmt/1.0.5.tgz", {}, "sha512-twbjYrG/Pcm9eM1hAktHME3CNlkhXLtnQLkgPj7GCvAUNkkhO5Tlt9m81hPD2u6FmUZoyqTPQ3psXvhS7Pc3WQ=="],
|
||||
|
||||
"@jsr/std__html": ["@jsr/std__html@1.0.3", "https://npm.jsr.io/~/11/@jsr/std__html/1.0.3.tgz", {}, "sha512-yJrM4yMRZ3EQgzqR5oAP4xc4+QVo1GgimZ5NszA38quXCsFna0bEpl4heyosUtcDjuSYSdBGr5dKAQlfkQpZCQ=="],
|
||||
|
||||
"@jsr/std__media-types": ["@jsr/std__media-types@1.1.0", "https://npm.jsr.io/~/11/@jsr/std__media-types/1.1.0.tgz", {}, "sha512-dHvaxHL7ENWnltgL653uo3KnKFse3ZbopZop2gqsT7yrscx7irZEClu5Cba7gMPPRk4Lg1FbriNcaBViM2RSBw=="],
|
||||
|
||||
"@jsr/std__net": ["@jsr/std__net@1.0.4", "https://npm.jsr.io/~/11/@jsr/std__net/1.0.4.tgz", {}, "sha512-KJGU8ZpQ70sMW2Zk+wU3wFUkggS9lTLfRFBygnV9VaK8KI+1ggiqtB06rH4a14CNRGM9y46Mn/ZCbQUd4Q45Jg=="],
|
||||
|
||||
"@jsr/std__path": ["@jsr/std__path@1.0.8", "https://npm.jsr.io/~/11/@jsr/std__path/1.0.8.tgz", {}, "sha512-eNBGlh/8ZVkMxtFH4bwIzlAeKoHYk5in4wrBZhi20zMdOiuX4QozP4+19mIXBT2lzHDjhuVLyECbhFeR304iDg=="],
|
||||
|
||||
"@jsr/std__streams": ["@jsr/std__streams@1.0.9", "https://npm.jsr.io/~/11/@jsr/std__streams/1.0.9.tgz", { "dependencies": { "@jsr/std__bytes": "^1.0.5" } }, "sha512-nPkEijnOPwLQt6ZtvOajE6F2hCmRILaq0kR6I0JFVvxSsvhdeyFMGc8F4DqypCpfq/U+8umLcE0RNMcYdx4NlQ=="],
|
||||
|
||||
"@lit-labs/motion": ["@lit-labs/motion@1.0.8", "", { "dependencies": { "lit": "^3.1.2" } }, "sha512-sVwbJtkndmxre7RBKH+kWfHmYpqq2PJQ98RXgSxBAVUaKSLXvXTv9X5m4dhks2R50MUbpSygjEB14KO4tOn48A=="],
|
||||
|
||||
"@lit-labs/ssr-dom-shim": ["@lit-labs/ssr-dom-shim@1.3.0", "", {}, "sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ=="],
|
||||
|
||||
"@lit/reactive-element": ["@lit/reactive-element@2.0.4", "", { "dependencies": { "@lit-labs/ssr-dom-shim": "^1.2.0" } }, "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ=="],
|
||||
|
||||
"@noble/ciphers": ["@noble/ciphers@0.5.3", "", {}, "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w=="],
|
||||
|
||||
"@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="],
|
||||
|
||||
"@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="],
|
||||
|
||||
"@noble/secp256k1": ["@noble/secp256k1@2.2.3", "", {}, "sha512-l7r5oEQym9Us7EAigzg30/PQAvynhMt2uoYtT3t26eGDVm9Yii5mZ5jWSWmZ/oSIR2Et0xfc6DXrG0bZ787V3w=="],
|
||||
|
||||
"@nostr-dev-kit/ndk": ["@nostr-dev-kit/ndk@2.11.2", "", { "dependencies": { "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@noble/secp256k1": "^2.1.0", "@scure/base": "^1.1.9", "debug": "^4.3.6", "light-bolt11-decoder": "^3.2.0", "nostr-tools": "^2.7.1", "tseep": "^1.2.2", "typescript-lru-cache": "^2.0.0", "utf8-buffer": "^1.0.0", "websocket-polyfill": "^0.0.3" } }, "sha512-DNrodIBC0j2MqEUQ5Mqaa671iZiRiKluu0c/wLkX7PCva07KSPyvcuyGp5fhk+/EZBurwZccMaML0syH0Qu8kQ=="],
|
||||
|
||||
"@nostr-dev-kit/ndk-cache-dexie": ["@nostr-dev-kit/ndk-cache-dexie@2.5.11", "", { "dependencies": { "@nostr-dev-kit/ndk": "2.11.2", "debug": "^4.3.7", "dexie": "^4.0.8", "nostr-tools": "^2.4.0", "typescript-lru-cache": "^2.0.0" } }, "sha512-lhoKcjwxlNB2rrnZ2zDAGJeh5k7x1f51oAwUnlDAuPvNEe4q/2XynxnI3uTe7rBg9+pq085esOQK7pg75E+BgQ=="],
|
||||
|
||||
"@open-wc/lit-helpers": ["@open-wc/lit-helpers@0.7.0", "", { "peerDependencies": { "lit": "^2.0.0 || ^3.0.0" } }, "sha512-4NBlx5ve0EvZplCRJbESm0MdMbRCw16alP2y76KAAAwzmFFXXrUj5hFwhw55+sSg5qaRRx6sY+s7usKgnNo3TQ=="],
|
||||
|
||||
"@preact/preset-vite": ["@preact/preset-vite@2.10.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.22.15", "@babel/plugin-transform-react-jsx-development": "^7.22.5", "@prefresh/vite": "^2.4.1", "@rollup/pluginutils": "^4.1.1", "babel-plugin-transform-hook-names": "^1.0.2", "debug": "^4.3.4", "kolorist": "^1.8.0", "vite-prerender-plugin": "^0.5.3" }, "peerDependencies": { "@babel/core": "7.x", "vite": "2.x || 3.x || 4.x || 5.x || 6.x" } }, "sha512-59lyGBXNfZIr5OOuBUB4/IB8AqF/ULbvYnyItgK/2BJnsGJqaeaJobRVtMp1129obHQuj8oZ/dVxB9inmH8Xig=="],
|
||||
|
||||
"@prefresh/babel-plugin": ["@prefresh/babel-plugin@0.5.1", "", {}, "sha512-uG3jGEAysxWoyG3XkYfjYHgaySFrSsaEb4GagLzYaxlydbuREtaX+FTxuIidp241RaLl85XoHg9Ej6E4+V1pcg=="],
|
||||
|
||||
"@prefresh/core": ["@prefresh/core@1.5.3", "", { "peerDependencies": { "preact": "^10.0.0" } }, "sha512-nDzxj0tA1/M6APNAWqaxkZ+3sTdPHESa+gol4+Bw7rMc2btWdkLoNH7j9rGhUb8SThC0Vz0VoXtq+U+9azGLHg=="],
|
||||
|
||||
"@prefresh/utils": ["@prefresh/utils@1.2.0", "", {}, "sha512-KtC/fZw+oqtwOLUFM9UtiitB0JsVX0zLKNyRTA332sqREqSALIIQQxdUCS1P3xR/jT1e2e8/5rwH6gdcMLEmsQ=="],
|
||||
|
||||
"@prefresh/vite": ["@prefresh/vite@2.4.7", "", { "dependencies": { "@babel/core": "^7.22.1", "@prefresh/babel-plugin": "0.5.1", "@prefresh/core": "^1.5.1", "@prefresh/utils": "^1.2.0", "@rollup/pluginutils": "^4.2.1" }, "peerDependencies": { "preact": "^10.4.0", "vite": ">=2.0.0" } }, "sha512-zmCEDWSFHl5A7PciXa/fe+OUjoGi4iiCQclpWfpIg7LjxwWrtlUT4DfxDBcQwHfTyipS/XDm8x7WYrkiTW0q+w=="],
|
||||
|
||||
"@rollup/pluginutils": ["@rollup/pluginutils@4.2.1", "", { "dependencies": { "estree-walker": "^2.0.1", "picomatch": "^2.2.2" } }, "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.8", "", { "os": "android", "cpu": "arm" }, "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.8", "", { "os": "android", "cpu": "arm64" }, "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.34.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.34.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.34.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.34.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.34.8", "", { "os": "linux", "cpu": "arm" }, "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.34.8", "", { "os": "linux", "cpu": "arm" }, "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.34.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.34.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q=="],
|
||||
|
||||
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.34.8", "", { "os": "linux", "cpu": "none" }, "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ=="],
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.34.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.34.8", "", { "os": "linux", "cpu": "none" }, "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.34.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.34.8", "", { "os": "linux", "cpu": "x64" }, "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.34.8", "", { "os": "linux", "cpu": "x64" }, "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.34.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.34.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.8", "", { "os": "win32", "cpu": "x64" }, "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g=="],
|
||||
|
||||
"@scure/base": ["@scure/base@1.2.4", "", {}, "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ=="],
|
||||
|
||||
"@scure/bip32": ["@scure/bip32@1.3.1", "", { "dependencies": { "@noble/curves": "~1.1.0", "@noble/hashes": "~1.3.1", "@scure/base": "~1.1.0" } }, "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A=="],
|
||||
|
||||
"@scure/bip39": ["@scure/bip39@1.2.1", "", { "dependencies": { "@noble/hashes": "~1.3.0", "@scure/base": "~1.1.0" } }, "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg=="],
|
||||
|
||||
"@std/http": ["@jsr/std__http@1.0.13", "https://npm.jsr.io/~/11/@jsr/std__http/1.0.13.tgz", { "dependencies": { "@jsr/std__cli": "^1.0.12", "@jsr/std__encoding": "^1.0.7", "@jsr/std__fmt": "^1.0.5", "@jsr/std__html": "^1.0.3", "@jsr/std__media-types": "^1.1.0", "@jsr/std__net": "^1.0.4", "@jsr/std__path": "^1.0.8", "@jsr/std__streams": "^1.0.9" } }, "sha512-TFUNeEikNJ3iaD4KomIO6wkybDxl9CmgqdJdYeYfbWMvMvoYKGPTqvqicuqvFmOQhgbsSoB0g1OT1hWHJmN2/A=="],
|
||||
|
||||
"@tsconfig/node22": ["@tsconfig/node22@22.0.0", "", {}, "sha512-twLQ77zevtxobBOD4ToAtVmuYrpeYUh3qh+TEp+08IWhpsrIflVHqQ1F1CiPxQGL7doCdBIOOCF+1Tm833faNg=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
|
||||
|
||||
"@types/jsdom": ["@types/jsdom@21.1.7", "", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^7.0.0" } }, "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA=="],
|
||||
|
||||
"@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
|
||||
|
||||
"@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="],
|
||||
|
||||
"@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
|
||||
|
||||
"@types/node": ["@types/node@22.13.4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="],
|
||||
|
||||
"@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="],
|
||||
|
||||
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
||||
|
||||
"agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"babel-plugin-transform-hook-names": ["babel-plugin-transform-hook-names@1.0.2", "", { "peerDependencies": { "@babel/core": "^7.12.10" } }, "sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw=="],
|
||||
|
||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="],
|
||||
|
||||
"buffer-builder": ["buffer-builder@0.2.0", "", {}, "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg=="],
|
||||
|
||||
"bufferutil": ["bufferutil@4.0.9", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001700", "", {}, "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ=="],
|
||||
|
||||
"colorjs.io": ["colorjs.io@0.5.2", "", {}, "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw=="],
|
||||
|
||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"css-select": ["css-select@5.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg=="],
|
||||
|
||||
"css-what": ["css-what@6.1.0", "", {}, "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="],
|
||||
|
||||
"cssstyle": ["cssstyle@4.2.1", "", { "dependencies": { "@asamuzakjp/css-color": "^2.8.2", "rrweb-cssom": "^0.8.0" } }, "sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw=="],
|
||||
|
||||
"d": ["d@1.0.2", "", { "dependencies": { "es5-ext": "^0.10.64", "type": "^2.7.2" } }, "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw=="],
|
||||
|
||||
"data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="],
|
||||
|
||||
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||
|
||||
"decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="],
|
||||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
||||
|
||||
"dexie": ["dexie@4.0.11", "", {}, "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
|
||||
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||
|
||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.102", "", {}, "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q=="],
|
||||
|
||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
||||
|
||||
"es5-ext": ["es5-ext@0.10.64", "", { "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", "esniff": "^2.0.1", "next-tick": "^1.1.0" } }, "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg=="],
|
||||
|
||||
"es6-iterator": ["es6-iterator@2.0.3", "", { "dependencies": { "d": "1", "es5-ext": "^0.10.35", "es6-symbol": "^3.1.1" } }, "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g=="],
|
||||
|
||||
"es6-symbol": ["es6-symbol@3.1.4", "", { "dependencies": { "d": "^1.0.2", "ext": "^1.7.0" } }, "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg=="],
|
||||
|
||||
"esbuild": ["esbuild@0.24.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.2", "@esbuild/android-arm": "0.24.2", "@esbuild/android-arm64": "0.24.2", "@esbuild/android-x64": "0.24.2", "@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-x64": "0.24.2", "@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-x64": "0.24.2", "@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-x64": "0.24.2", "@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-x64": "0.24.2", "@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-x64": "0.24.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"esniff": ["esniff@2.0.1", "", { "dependencies": { "d": "^1.0.1", "es5-ext": "^0.10.62", "event-emitter": "^0.3.5", "type": "^2.7.2" } }, "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg=="],
|
||||
|
||||
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"event-emitter": ["event-emitter@0.3.5", "", { "dependencies": { "d": "1", "es5-ext": "~0.10.14" } }, "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA=="],
|
||||
|
||||
"ext": ["ext@1.7.0", "", { "dependencies": { "type": "^2.7.2" } }, "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw=="],
|
||||
|
||||
"form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.2.7", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
||||
|
||||
"html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="],
|
||||
|
||||
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
||||
|
||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"iconify-icon": ["iconify-icon@2.3.0", "", { "dependencies": { "@iconify/types": "^2.0.0" } }, "sha512-C0beI9oTDxQz6voI5CKl7MiJf0Lw4UU8K4G4t6pcUDClLmCvuMOpcvd8MAztQ2SfoH0iv7WHdxBFjekKPFKH2Q=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"immutable": ["immutable@5.0.3", "", {}, "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw=="],
|
||||
|
||||
"is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="],
|
||||
|
||||
"is-typedarray": ["is-typedarray@1.0.0", "", {}, "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="],
|
||||
|
||||
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"jsdom": ["jsdom@25.0.1", "", { "dependencies": { "cssstyle": "^4.1.0", "data-urls": "^5.0.0", "decimal.js": "^10.4.3", "form-data": "^4.0.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.5", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.12", "parse5": "^7.1.2", "rrweb-cssom": "^0.7.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^2.11.2" }, "optionalPeers": ["canvas"] }, "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
"json-parse-even-better-errors": ["json-parse-even-better-errors@4.0.0", "", {}, "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"kolorist": ["kolorist@1.8.0", "", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="],
|
||||
|
||||
"light-bolt11-decoder": ["light-bolt11-decoder@3.2.0", "", { "dependencies": { "@scure/base": "1.1.1" } }, "sha512-3QEofgiBOP4Ehs9BI+RkZdXZNtSys0nsJ6fyGeSiAGCBsMwHGUDS/JQlY/sTnWs91A2Nh0S9XXfA8Sy9g6QpuQ=="],
|
||||
|
||||
"lightningcss": ["lightningcss@1.29.1", "", { "dependencies": { "detect-libc": "^1.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.1", "lightningcss-darwin-x64": "1.29.1", "lightningcss-freebsd-x64": "1.29.1", "lightningcss-linux-arm-gnueabihf": "1.29.1", "lightningcss-linux-arm64-gnu": "1.29.1", "lightningcss-linux-arm64-musl": "1.29.1", "lightningcss-linux-x64-gnu": "1.29.1", "lightningcss-linux-x64-musl": "1.29.1", "lightningcss-win32-arm64-msvc": "1.29.1", "lightningcss-win32-x64-msvc": "1.29.1" } }, "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q=="],
|
||||
|
||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.29.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw=="],
|
||||
|
||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.29.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA=="],
|
||||
|
||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.29.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ=="],
|
||||
|
||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.29.1", "", { "os": "linux", "cpu": "arm" }, "sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg=="],
|
||||
|
||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.29.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ=="],
|
||||
|
||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.29.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw=="],
|
||||
|
||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.29.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw=="],
|
||||
|
||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.29.1", "", { "os": "linux", "cpu": "x64" }, "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw=="],
|
||||
|
||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.29.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog=="],
|
||||
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.1", "", { "os": "win32", "cpu": "x64" }, "sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q=="],
|
||||
|
||||
"linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="],
|
||||
|
||||
"lit": ["lit@3.2.1", "", { "dependencies": { "@lit/reactive-element": "^2.0.4", "lit-element": "^4.1.0", "lit-html": "^3.2.0" } }, "sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w=="],
|
||||
|
||||
"lit-element": ["lit-element@4.1.1", "", { "dependencies": { "@lit-labs/ssr-dom-shim": "^1.2.0", "@lit/reactive-element": "^2.0.4", "lit-html": "^3.2.0" } }, "sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew=="],
|
||||
|
||||
"lit-html": ["lit-html@3.2.1", "", { "dependencies": { "@types/trusted-types": "^2.0.2" } }, "sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
||||
|
||||
"markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="],
|
||||
|
||||
"memorystream": ["memorystream@0.3.1", "", {}, "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="],
|
||||
|
||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
|
||||
|
||||
"next-tick": ["next-tick@1.1.0", "", {}, "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="],
|
||||
|
||||
"node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
|
||||
|
||||
"node-html-parser": ["node-html-parser@6.1.13", "", { "dependencies": { "css-select": "^5.1.0", "he": "1.2.0" } }, "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg=="],
|
||||
|
||||
"node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
|
||||
|
||||
"nostr-tools": ["nostr-tools@2.10.4", "", { "dependencies": { "@noble/ciphers": "^0.5.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.1", "@scure/base": "1.1.1", "@scure/bip32": "1.3.1", "@scure/bip39": "1.2.1" }, "optionalDependencies": { "nostr-wasm": "0.1.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-biU7sk+jxHgVASfobg2T5ttxOGGSt69wEVBC51sHHOEaKAAdzHBLV/I2l9Rf61UzClhliZwNouYhqIso4a3HYg=="],
|
||||
|
||||
"nostr-wasm": ["nostr-wasm@0.1.0", "", {}, "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA=="],
|
||||
|
||||
"npm-normalize-package-bin": ["npm-normalize-package-bin@4.0.0", "", {}, "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w=="],
|
||||
|
||||
"npm-run-all2": ["npm-run-all2@7.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", "minimatch": "^9.0.0", "pidtree": "^0.6.0", "read-package-json-fast": "^4.0.0", "shell-quote": "^1.7.3", "which": "^5.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js", "npm-run-all2": "bin/npm-run-all/index.js" } }, "sha512-7tXR+r9hzRNOPNTvXegM+QzCuMjzUIIq66VDunL6j60O4RrExx32XUhlrS7UK4VcdGw5/Wxzb3kfNcFix9JKDA=="],
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
"nwsapi": ["nwsapi@2.2.16", "", {}, "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ=="],
|
||||
|
||||
"parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="],
|
||||
|
||||
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
|
||||
|
||||
"preact": ["preact@10.26.2", "", {}, "sha512-0gNmv4qpS9HaN3+40CLBAnKe0ZfyE4ZWo5xKlC1rVrr0ckkEvJvAQqKaHANdFKsGstoxrY4AItZ7kZSGVoVjgg=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="],
|
||||
|
||||
"read-package-json-fast": ["read-package-json-fast@4.0.0", "", { "dependencies": { "json-parse-even-better-errors": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" } }, "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg=="],
|
||||
|
||||
"rollup": ["rollup@4.34.8", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.34.8", "@rollup/rollup-android-arm64": "4.34.8", "@rollup/rollup-darwin-arm64": "4.34.8", "@rollup/rollup-darwin-x64": "4.34.8", "@rollup/rollup-freebsd-arm64": "4.34.8", "@rollup/rollup-freebsd-x64": "4.34.8", "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", "@rollup/rollup-linux-arm-musleabihf": "4.34.8", "@rollup/rollup-linux-arm64-gnu": "4.34.8", "@rollup/rollup-linux-arm64-musl": "4.34.8", "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", "@rollup/rollup-linux-riscv64-gnu": "4.34.8", "@rollup/rollup-linux-s390x-gnu": "4.34.8", "@rollup/rollup-linux-x64-gnu": "4.34.8", "@rollup/rollup-linux-x64-musl": "4.34.8", "@rollup/rollup-win32-arm64-msvc": "4.34.8", "@rollup/rollup-win32-ia32-msvc": "4.34.8", "@rollup/rollup-win32-x64-msvc": "4.34.8", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ=="],
|
||||
|
||||
"rrweb-cssom": ["rrweb-cssom@0.7.1", "", {}, "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg=="],
|
||||
|
||||
"rxjs": ["rxjs@7.8.1", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"sass-embedded": ["sass-embedded@1.85.0", "", { "dependencies": { "@bufbuild/protobuf": "^2.0.0", "buffer-builder": "^0.2.0", "colorjs.io": "^0.5.0", "immutable": "^5.0.2", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", "varint": "^6.0.0" }, "optionalDependencies": { "sass-embedded-android-arm": "1.85.0", "sass-embedded-android-arm64": "1.85.0", "sass-embedded-android-ia32": "1.85.0", "sass-embedded-android-riscv64": "1.85.0", "sass-embedded-android-x64": "1.85.0", "sass-embedded-darwin-arm64": "1.85.0", "sass-embedded-darwin-x64": "1.85.0", "sass-embedded-linux-arm": "1.85.0", "sass-embedded-linux-arm64": "1.85.0", "sass-embedded-linux-ia32": "1.85.0", "sass-embedded-linux-musl-arm": "1.85.0", "sass-embedded-linux-musl-arm64": "1.85.0", "sass-embedded-linux-musl-ia32": "1.85.0", "sass-embedded-linux-musl-riscv64": "1.85.0", "sass-embedded-linux-musl-x64": "1.85.0", "sass-embedded-linux-riscv64": "1.85.0", "sass-embedded-linux-x64": "1.85.0", "sass-embedded-win32-arm64": "1.85.0", "sass-embedded-win32-ia32": "1.85.0", "sass-embedded-win32-x64": "1.85.0" }, "bin": { "sass": "dist/bin/sass.js" } }, "sha512-x3Vv54g0jv1aPSW8OTA/0GzQCs/HMQOjIkLtZJ3Xsn/I4vnyjKbVTQmFTax9bQjldqLEEkdbvy6ES/cOOnYNwA=="],
|
||||
|
||||
"sass-embedded-android-arm": ["sass-embedded-android-arm@1.85.0", "", { "os": "android", "cpu": "arm" }, "sha512-pPBT7Ad6G8Mlao8ypVNXW2ya7I/Bhcny+RYZ/EmrunEXfhzCNp4PWV2VAweitPO9RnPIJwvUTkLc8Fu6K3nVmw=="],
|
||||
|
||||
"sass-embedded-android-arm64": ["sass-embedded-android-arm64@1.85.0", "", { "os": "android", "cpu": "arm64" }, "sha512-4itDzRwezwrW8+YzMLIwHtMeH+qrBNdBsRn9lTVI15K+cNLC8z5JWJi6UCZ8TNNZr9LDBfsh5jUdjSub0yF7jg=="],
|
||||
|
||||
"sass-embedded-android-ia32": ["sass-embedded-android-ia32@1.85.0", "", { "os": "android", "cpu": "ia32" }, "sha512-bwqKq95hzbGbMTeXCMQhH7yEdc2xJVwIXj7rGdD3McvyFWbED6362XRFFPI5YyjfD2wRJd9yWLh/hn+6VyjcYA=="],
|
||||
|
||||
"sass-embedded-android-riscv64": ["sass-embedded-android-riscv64@1.85.0", "", { "os": "android", "cpu": "none" }, "sha512-Fgkgay+5EePJXZFHR5Vlkutnsmox2V6nX4U3mfGbSN1xjLRm8F5ST72V2s5Z0mnIFpGvEu/v7hfptgViqMvaxg=="],
|
||||
|
||||
"sass-embedded-android-x64": ["sass-embedded-android-x64@1.85.0", "", { "os": "android", "cpu": "x64" }, "sha512-/bG3JgTn3eoIDHCiJNVkLeJgUesat4ghxqYmKMZUJx++4e6iKCDj8XwQTJAgm+QDrsPKXHBacHEANJ9LEAuTqg=="],
|
||||
|
||||
"sass-embedded-darwin-arm64": ["sass-embedded-darwin-arm64@1.85.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-plp8TyMz97YFBCB3ndftEvoW29vyfsSBJILM5U84cGzr06SvLh/Npjj8psfUeRw+upEk1zkFtw5u61sRCdgwIw=="],
|
||||
|
||||
"sass-embedded-darwin-x64": ["sass-embedded-darwin-x64@1.85.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-LP8Zv8DG57Gn6PmSwWzC0gEZUsGdg36Ps3m0i1fVTOelql7N3HZIrlPYRjJvidL8ZlB3ISxNANebTREUHn/wkQ=="],
|
||||
|
||||
"sass-embedded-linux-arm": ["sass-embedded-linux-arm@1.85.0", "", { "os": "linux", "cpu": "arm" }, "sha512-18xOAEfazJt1MMVS2TRHV94n81VyMnywOoJ7/S7I79qno/zx26OoqqP4XvH107xu8+mZ9Gg54LrUH6ZcgHk08g=="],
|
||||
|
||||
"sass-embedded-linux-arm64": ["sass-embedded-linux-arm64@1.85.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-JRIRKVOY5Y8M1zlUOv9AQGju4P6lj8i5vLJZsVYVN/uY8Cd2dDJZPC8EOhjntp+IpF8AOGIHqCeCkHBceIyIjA=="],
|
||||
|
||||
"sass-embedded-linux-ia32": ["sass-embedded-linux-ia32@1.85.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-4JH+h+gLt9So22nNPQtsKojEsLzjld9ol3zWcOtMGclv+HojZGbCuhJUrLUcK72F8adXYsULmWhJPKROLIwYMA=="],
|
||||
|
||||
"sass-embedded-linux-musl-arm": ["sass-embedded-linux-musl-arm@1.85.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Z1j4ageDVFihqNUBnm89fxY46pY0zD/Clp1D3ZdI7S+D280+AEpbm5vMoH8LLhBQfQLf2w7H++SZGpQwrisudQ=="],
|
||||
|
||||
"sass-embedded-linux-musl-arm64": ["sass-embedded-linux-musl-arm64@1.85.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-aoQjUjK28bvdw9XKTjQeayn8oWQ2QqvoTD11myklGd3IHH7Jj0nwXUstI4NxDueCKt3wghuZoIQkjOheReQxlg=="],
|
||||
|
||||
"sass-embedded-linux-musl-ia32": ["sass-embedded-linux-musl-ia32@1.85.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-/cJCSXOfXmQFH8deE+3U9x+BSz8i0d1Tt9gKV/Gat1Xm43Oumw8pmZgno+cDuGjYQInr9ryW5121pTMlj/PBXQ=="],
|
||||
|
||||
"sass-embedded-linux-musl-riscv64": ["sass-embedded-linux-musl-riscv64@1.85.0", "", { "os": "linux", "cpu": "none" }, "sha512-l+FJxMXkmg42RZq5RFKXg4InX0IA7yEiPHe4kVSdrczP7z3NLxk+W9wVkPnoRKYIMe1qZPPQ25y0TgI4HNWouA=="],
|
||||
|
||||
"sass-embedded-linux-musl-x64": ["sass-embedded-linux-musl-x64@1.85.0", "", { "os": "linux", "cpu": "x64" }, "sha512-M9ffjcYfFcRvkFA6V3DpOS955AyvmpvPAhL/xNK45d/ma1n1ehTWpd24tVeKiNK5CZkNjjMEfyw2fHa6MpqmEA=="],
|
||||
|
||||
"sass-embedded-linux-riscv64": ["sass-embedded-linux-riscv64@1.85.0", "", { "os": "linux", "cpu": "none" }, "sha512-yqPXQWfM+qiIPkfn++48GOlbmSvUZIyL9nwFstBk0k4x40UhbhilfknqeTUpxoHfQzylTGVhrm5JE7MjM+LNZA=="],
|
||||
|
||||
"sass-embedded-linux-x64": ["sass-embedded-linux-x64@1.85.0", "", { "os": "linux", "cpu": "x64" }, "sha512-NTDeQFZcuVR7COoaRy8pZD6/+QznwBR8kVFsj7NpmvX9aJ7TX/q+OQZHX7Bfb3tsfKXhf1YZozegPuYxRnMKAQ=="],
|
||||
|
||||
"sass-embedded-win32-arm64": ["sass-embedded-win32-arm64@1.85.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-gO0VAuxC4AdV+uZYJESRWVVHQWCGzNs0C3OKCAdH4r1vGRugooMi7J/5wbwUdXDA1MV9ICfhlKsph2n3GiPdqA=="],
|
||||
|
||||
"sass-embedded-win32-ia32": ["sass-embedded-win32-ia32@1.85.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-PCyn6xeFIBUgBceNypuf73/5DWF2VWPlPqPuBprPsTvpZOMUJeBtP+Lf4mnu3dNy1z76mYVnpaCnQmzZ0zHZaA=="],
|
||||
|
||||
"sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.85.0", "", { "os": "win32", "cpu": "x64" }, "sha512-AknE2jLp6OBwrR5hQ8pDsG94KhJCeSheFJ2xgbnk8RUjZX909JiNbgh2sNt9LG+RXf4xZa55dDL537gZoCx/iw=="],
|
||||
|
||||
"saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="],
|
||||
|
||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"shell-quote": ["shell-quote@1.8.2", "", {}, "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA=="],
|
||||
|
||||
"simple-code-frame": ["simple-code-frame@1.3.0", "", { "dependencies": { "kolorist": "^1.6.0" } }, "sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w=="],
|
||||
|
||||
"source-map": ["source-map@0.7.4", "", {}, "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"stack-trace": ["stack-trace@1.0.0-pre2", "", {}, "sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A=="],
|
||||
|
||||
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
||||
|
||||
"symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
|
||||
|
||||
"sync-child-process": ["sync-child-process@1.0.2", "", { "dependencies": { "sync-message-port": "^1.0.0" } }, "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA=="],
|
||||
|
||||
"sync-message-port": ["sync-message-port@1.1.3", "", {}, "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg=="],
|
||||
|
||||
"tldts": ["tldts@6.1.78", "", { "dependencies": { "tldts-core": "^6.1.78" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-fSgYrW0ITH0SR/CqKMXIruYIPpNu5aDgUp22UhYoSrnUQwc7SBqifEBFNce7AAcygUPBo6a/gbtcguWdmko4RQ=="],
|
||||
|
||||
"tldts-core": ["tldts-core@6.1.78", "", {}, "sha512-jS0svNsB99jR6AJBmfmEWuKIgz91Haya91Z43PATaeHJ24BkMoNRb/jlaD37VYjb0mYf6gRL/HOnvS1zEnYBiw=="],
|
||||
|
||||
"tough-cookie": ["tough-cookie@5.1.1", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA=="],
|
||||
|
||||
"tr46": ["tr46@5.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g=="],
|
||||
|
||||
"tseep": ["tseep@1.3.1", "", {}, "sha512-ZPtfk1tQnZVyr7BPtbJ93qaAh2lZuIOpTMjhrYa4XctT8xe7t4SAW9LIxrySDuYMsfNNayE51E/WNGrNVgVicQ=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"tstl": ["tstl@2.5.16", "", {}, "sha512-+O2ybLVLKcBwKm4HymCEwZIT0PpwS3gCYnxfSDEjJEKADvIFruaQjd3m7CAKNU1c7N3X3WjVz87re7TA2A5FUw=="],
|
||||
|
||||
"type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="],
|
||||
|
||||
"typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="],
|
||||
|
||||
"typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="],
|
||||
|
||||
"typescript-lru-cache": ["typescript-lru-cache@2.0.0", "", {}, "sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA=="],
|
||||
|
||||
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
|
||||
|
||||
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="],
|
||||
|
||||
"utf-8-validate": ["utf-8-validate@5.0.10", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ=="],
|
||||
|
||||
"utf8-buffer": ["utf8-buffer@1.0.0", "", {}, "sha512-ueuhzvWnp5JU5CiGSY4WdKbiN/PO2AZ/lpeLiz2l38qwdLy/cW40XobgyuIWucNyum0B33bVB0owjFCeGBSLqg=="],
|
||||
|
||||
"varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="],
|
||||
|
||||
"vite": ["vite@6.1.1", "", { "dependencies": { "esbuild": "^0.24.2", "postcss": "^8.5.2", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA=="],
|
||||
|
||||
"vite-prerender-plugin": ["vite-prerender-plugin@0.5.6", "", { "dependencies": { "magic-string": "^0.30.6", "node-html-parser": "^6.1.12", "simple-code-frame": "^1.3.0", "source-map": "^0.7.4", "stack-trace": "^1.0.0-pre2" } }, "sha512-ELG0pflVXWNVGaHme8g0rZB7xFnytf1U6fYLep3NUC4knGmOHtEc2R7DIlnCKeYGUGkzfMcvJOasK4C0dulKbQ=="],
|
||||
|
||||
"w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
|
||||
|
||||
"webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
|
||||
|
||||
"websocket": ["websocket@1.0.35", "", { "dependencies": { "bufferutil": "^4.0.1", "debug": "^2.2.0", "es5-ext": "^0.10.63", "typedarray-to-buffer": "^3.1.5", "utf-8-validate": "^5.0.2", "yaeti": "^0.0.6" } }, "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q=="],
|
||||
|
||||
"websocket-polyfill": ["websocket-polyfill@0.0.3", "", { "dependencies": { "tstl": "^2.0.7", "websocket": "^1.0.28" } }, "sha512-pF3kR8Uaoau78MpUmFfzbIRxXj9PeQrCuPepGE6JIsfsJ/o/iXr07Q2iQNzKSSblQJ0FiGWlS64N4pVSm+O3Dg=="],
|
||||
|
||||
"whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
|
||||
|
||||
"whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
|
||||
|
||||
"whatwg-url": ["whatwg-url@14.1.1", "", { "dependencies": { "tr46": "^5.0.0", "webidl-conversions": "^7.0.0" } }, "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ=="],
|
||||
|
||||
"which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="],
|
||||
|
||||
"ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
|
||||
|
||||
"xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="],
|
||||
|
||||
"xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="],
|
||||
|
||||
"yaeti": ["yaeti@0.0.6", "", {}, "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug=="],
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"@scure/bip32/@noble/curves": ["@noble/curves@1.1.0", "", { "dependencies": { "@noble/hashes": "1.3.1" } }, "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA=="],
|
||||
|
||||
"@scure/bip32/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="],
|
||||
|
||||
"@scure/bip32/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
||||
|
||||
"@scure/bip39/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="],
|
||||
|
||||
"@scure/bip39/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
||||
|
||||
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"cssstyle/rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="],
|
||||
|
||||
"light-bolt11-decoder/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
||||
|
||||
"nostr-tools/@noble/curves": ["@noble/curves@1.2.0", "", { "dependencies": { "@noble/hashes": "1.3.2" } }, "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw=="],
|
||||
|
||||
"nostr-tools/@noble/hashes": ["@noble/hashes@1.3.1", "", {}, "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="],
|
||||
|
||||
"nostr-tools/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
||||
|
||||
"websocket/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"@scure/bip32/@noble/curves/@noble/hashes": ["@noble/hashes@1.3.1", "", {}, "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="],
|
||||
|
||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"nostr-tools/@noble/curves/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="],
|
||||
|
||||
"websocket/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
}
|
||||
}
|
1
env.d.ts
vendored
Normal file
1
env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
12
index.html
Normal file
12
index.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/iconify-icon@2.3.0/dist/iconify-icon.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="/src/main.ts" type="module"></script>
|
||||
</body>
|
||||
</html>
|
37
package.json
Normal file
37
package.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "eve",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"browserslist": ["not dead"],
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@preact/preset-vite": "^2.9.3",
|
||||
"@tsconfig/node22": "^22.0.0",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@types/node": "^22.10.2",
|
||||
"jsdom": "^25.0.1",
|
||||
"lightningcss": "^1.28.2",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"sass-embedded": "^1.83.0",
|
||||
"typescript": "~5.6.3",
|
||||
"vite": "^6.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lit-labs/motion": "^1.0.8",
|
||||
"@nostr-dev-kit/ndk": "^2.10.7",
|
||||
"@nostr-dev-kit/ndk-cache-dexie": "^2.5.8",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@std/http": "npm:@jsr/std__http@^1.0.13",
|
||||
"iconify-icon": "^2.2.0",
|
||||
"lit": "^3.2.1",
|
||||
"markdown-it": "^14.1.0",
|
||||
"nostr-tools": "^2.10.4"
|
||||
}
|
||||
}
|
BIN
public/default-avatar.png
Normal file
BIN
public/default-avatar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
67
src/components/AppGrid.ts
Normal file
67
src/components/AppGrid.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import "@components/AppIcon";
|
||||
|
||||
@customElement("arx-app-grid")
|
||||
export class AppGrid extends LitElement {
|
||||
@property()
|
||||
apps: {
|
||||
icon: string | undefined;
|
||||
color: string;
|
||||
href: string;
|
||||
name: string;
|
||||
}[] = [];
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.app-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
|
||||
gap: 20px;
|
||||
padding: 30px;
|
||||
width: minmax(800px, 100cqw);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.app-grid {
|
||||
width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.app-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
|
||||
gap: 15px;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.app-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
|
||||
gap: 10px;
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="app-grid">
|
||||
${this.apps.map(
|
||||
(app) => html`
|
||||
<arx-app-icon
|
||||
.icon=${app.icon}
|
||||
.color=${app.color}
|
||||
.href=${app.href}
|
||||
.name=${app.name}
|
||||
></arx-app-icon>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
100
src/components/AppIcon.ts
Normal file
100
src/components/AppIcon.ts
Normal file
|
@ -0,0 +1,100 @@
|
|||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import "@components/EveLink";
|
||||
|
||||
@customElement("arx-app-icon")
|
||||
export class AppIcon extends LitElement {
|
||||
@property()
|
||||
icon: string | undefined;
|
||||
|
||||
@property()
|
||||
color = "#ff9900";
|
||||
|
||||
@property()
|
||||
href = "#";
|
||||
|
||||
@property()
|
||||
name = "App";
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.app-name {
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
text-shadow: 0px 0px 5px #ffffff;
|
||||
text-align: center;
|
||||
max-width: 70px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
.icon {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
border: 2px solid #000;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.app-name {
|
||||
color: white;
|
||||
text-shadow: 0px 0px 5px black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: clamp(48px, 8vw, 64px);
|
||||
height: clamp(48px, 8vw, 64px);
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid #aaa;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
.app-icon {
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.app-name {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<arx-eve-link href="${this.href}" class="app-icon">
|
||||
<div class="icon" style="background-color: ${this.color}">
|
||||
${this.icon
|
||||
? html`<iconify-icon
|
||||
icon="${this.icon}"
|
||||
class="app-icon"
|
||||
width="48"
|
||||
height="48"
|
||||
color="white"
|
||||
></iconify-icon>`
|
||||
: ""}
|
||||
</div>
|
||||
<span class="app-name">${this.name}</span>
|
||||
</arx-eve-link>
|
||||
`;
|
||||
}
|
||||
}
|
46
src/components/Breadcrumbs.ts
Normal file
46
src/components/Breadcrumbs.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { html, css, LitElement } from 'lit';
|
||||
import { property, customElement } from 'lit/decorators.js';
|
||||
import './BreadcrumbsItem';
|
||||
|
||||
@customElement('arx-breadcrumbs')
|
||||
export class Breadcrumbs extends LitElement {
|
||||
@property({ type: Array }) items: { text: string; href?: string }[] = [];
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
nav {
|
||||
max-width: 1200px;
|
||||
margin: 1rem auto;
|
||||
padding-inline: 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<nav aria-label="Breadcrumb">
|
||||
<ol>
|
||||
${this.items.map(
|
||||
(item, index) => html`
|
||||
<arx-breadcrumbs-item
|
||||
.text=${item.text}
|
||||
.href=${item.href}
|
||||
.index=${index}
|
||||
></arx-breadcrumbs-item>
|
||||
`,
|
||||
)}
|
||||
</ol>
|
||||
</nav>
|
||||
`;
|
||||
}
|
||||
}
|
51
src/components/BreadcrumbsItem.ts
Normal file
51
src/components/BreadcrumbsItem.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { property, customElement } from "lit/decorators.js";
|
||||
|
||||
import "@components/EveLink";
|
||||
|
||||
@customElement("arx-breadcrumbs-item")
|
||||
export class BreadcrumbsItem extends LitElement {
|
||||
@property() text = "";
|
||||
@property() href?: string;
|
||||
@property() index = 0;
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin-inline: 0.5rem;
|
||||
color: var(--secondary);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
transition: text-decoration 0.2s;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
color: var(--secondary);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<li>
|
||||
${this.index > 0
|
||||
? html`<span class="separator" aria-hidden="true">/</span>`
|
||||
: ""}
|
||||
${this.href
|
||||
? html`<arx-eve-link class="link" href=${this.href}
|
||||
>${this.text}</arx-eve-link
|
||||
>`
|
||||
: html`<span class="secondary">${this.text}</span>`}
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
}
|
23
src/components/ErrorView.ts
Normal file
23
src/components/ErrorView.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { html, LitElement, css } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
|
||||
@customElement('arx-error-view')
|
||||
export class ErrorView extends LitElement {
|
||||
@property()
|
||||
error!: string;
|
||||
|
||||
static override styles = css`
|
||||
.error {
|
||||
color: var(--error);
|
||||
}`;
|
||||
|
||||
override render() {
|
||||
return html`<span class="error">${this.error}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'arx-error-view': ErrorView;
|
||||
}
|
||||
}
|
37
src/components/EveLink.ts
Normal file
37
src/components/EveLink.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("arx-eve-link")
|
||||
export class EveLink extends LitElement {
|
||||
@property({ type: String }) href = "#";
|
||||
@property({ type: String }) target = "";
|
||||
@property({ type: String }) rel = "";
|
||||
|
||||
get hrefValue() {
|
||||
let href = this.href;
|
||||
if (href.startsWith("javascript:")) return href;
|
||||
if (href.startsWith("eve://")) href = href.replace("eve://", "#");
|
||||
if (href.startsWith("/")) href = href.replace("/", "#");
|
||||
if (!href.startsWith("#")) href = `#${href}`;
|
||||
return href;
|
||||
}
|
||||
|
||||
static override styles = css`
|
||||
a {
|
||||
part: link;
|
||||
}
|
||||
`;
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<a
|
||||
part="link"
|
||||
.href=${this.hrefValue}
|
||||
.target=${this.target}
|
||||
.rel=${this.rel}
|
||||
>
|
||||
<slot></slot>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
}
|
108
src/components/ForumPost.ts
Normal file
108
src/components/ForumPost.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { property, customElement } from "lit/decorators.js";
|
||||
import formatDateTime from "@utils/formatDateTime";
|
||||
|
||||
import "@components/MarkdownContent";
|
||||
|
||||
@customElement("arx-forum-post")
|
||||
export class ForumPost extends LitElement {
|
||||
@property({ type: String }) override id = "";
|
||||
@property({ type: String }) topicId = "";
|
||||
@property({ type: String }) npub = "";
|
||||
@property({ type: Date }) date = new Date();
|
||||
@property({ type: String }) content = "";
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.post {
|
||||
grid-column: span 2;
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
|
||||
gap: 1rem;
|
||||
padding: 1.5em;
|
||||
background: oklch(from var(--accent) l c h / 0.4);
|
||||
|
||||
& > :first-child {
|
||||
padding-right: 1.5rem;
|
||||
border-right: 2px solid var(--border);
|
||||
}
|
||||
}
|
||||
|
||||
.post-content {
|
||||
display: grid;
|
||||
gap: 1em;
|
||||
|
||||
& > :first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
border-top: 2px solid var(--border);
|
||||
border-bottom: 2px solid var(--border);
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
& > :nth-child(2) {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
& > :nth-child(3) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 1em;
|
||||
border-top: 2px solid var(--border);
|
||||
padding: 1em;
|
||||
|
||||
@media (max-width: 400px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
const permalink = `eve://phora/topics/${this.topicId}#post-${this.id}`;
|
||||
|
||||
return html`
|
||||
<div class="post" id="post-${this.id}">
|
||||
<arx-nostr-profile
|
||||
.npub=${this.npub}
|
||||
renderType="large"
|
||||
></arx-nostr-profile>
|
||||
<div class="post-content">
|
||||
<div>
|
||||
<iconify-icon icon="mdi:clock"></iconify-icon>
|
||||
${formatDateTime(this.date)}
|
||||
</div>
|
||||
<div>
|
||||
<arx-markdown-content
|
||||
.content=${this.content || "no content"}
|
||||
></arx-markdown-content>
|
||||
</div>
|
||||
<div>
|
||||
<arx-phora-button href="#">
|
||||
<iconify-icon size="32" icon="mdi:reply"></iconify-icon>
|
||||
Reply
|
||||
</arx-phora-button>
|
||||
<arx-phora-button href=${permalink}>
|
||||
<iconify-icon size="32" icon="mdi:link"></iconify-icon>
|
||||
Permalink
|
||||
</arx-phora-button>
|
||||
<arx-phora-button href="#">
|
||||
<iconify-icon size="32" icon="bxs:zap"></iconify-icon>
|
||||
Zap
|
||||
</arx-phora-button>
|
||||
<arx-phora-button href="#">
|
||||
<iconify-icon
|
||||
size="32"
|
||||
icon="bi:cloud-lightning-rain-fill"
|
||||
></iconify-icon>
|
||||
Downzap
|
||||
</arx-phora-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
133
src/components/Header.ts
Normal file
133
src/components/Header.ts
Normal file
|
@ -0,0 +1,133 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { property, customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("arx-header")
|
||||
export class Header extends LitElement {
|
||||
@property({ type: String }) override title = "Eve";
|
||||
@property({ type: String }) url = "eve://home";
|
||||
|
||||
@property({ type: Boolean }) canGoBack = false;
|
||||
@property({ type: Boolean }) canGoForward = false;
|
||||
|
||||
private searchQuery = "";
|
||||
private searchInput: HTMLInputElement | null = null;
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
header {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
z-index: 999999;
|
||||
background: var(--primary);
|
||||
height: var(--font-2xl);
|
||||
font-size: var(--header-height);
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 var(--space-md);
|
||||
}
|
||||
|
||||
.nav-buttons {
|
||||
display: flex;
|
||||
gap: var(--space-xs);
|
||||
padding-right: var(--space-xs);
|
||||
|
||||
button {
|
||||
text-decoration: none;
|
||||
color: var(--light);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
padding: var(--space-xs);
|
||||
border-radius: 100%;
|
||||
font-size: var(--font-md);
|
||||
backdrop-filter: blur(10px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
input {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
color: var(--light);
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
cursor: text;
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
padding: var(--space-xs);
|
||||
text-align: center;
|
||||
|
||||
&:not(:focus)::placeholder {
|
||||
color: var(--light);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<header>
|
||||
<div class="nav-buttons">
|
||||
<button
|
||||
class=${this.canGoBack ? "" : "disabled"}
|
||||
@click=${() => this.dispatchEvent(new CustomEvent("go-back"))}
|
||||
aria-label="Go back"
|
||||
>
|
||||
<iconify-icon icon="material-symbols:arrow-back"></iconify-icon>
|
||||
</button>
|
||||
<button
|
||||
class=${this.canGoForward ? "" : "disabled"}
|
||||
@click=${() => this.dispatchEvent(new CustomEvent("go-forward"))}
|
||||
aria-label="Go forward"
|
||||
>
|
||||
<iconify-icon icon="material-symbols:arrow-forward"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="search-container" @click=${this.focusSearch}>
|
||||
<input
|
||||
ref=${this.searchInput}
|
||||
type="text"
|
||||
.value=${this.searchQuery}
|
||||
@keyup=${this.handleSearch}
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
`;
|
||||
}
|
||||
|
||||
private focusSearch() {
|
||||
this.searchInput?.focus();
|
||||
}
|
||||
|
||||
private handleSearch(e: KeyboardEvent) {
|
||||
if (e.key !== "Enter") return;
|
||||
const hash = (e.target as HTMLInputElement).value.replace("eve://", "#");
|
||||
window.location.hash = hash;
|
||||
}
|
||||
}
|
687
src/components/InitialSetup.ts
Normal file
687
src/components/InitialSetup.ts
Normal file
|
@ -0,0 +1,687 @@
|
|||
import { LitElement, html, css } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { animate } from "@lit-labs/motion";
|
||||
import * as nostrTools from "nostr-tools/pure";
|
||||
import * as nip06 from "nostr-tools/nip06";
|
||||
import * as nip19 from "nostr-tools/nip19";
|
||||
import * as nip49 from "nostr-tools/nip49";
|
||||
import { ndk, setSigner } from "@/ndk";
|
||||
import { NDKEvent, NDKKind, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
||||
|
||||
@customElement("arx-initial-setup")
|
||||
export class InitialSetup extends LitElement {
|
||||
@state() private currentPage = 1;
|
||||
@state() private isAnimating = false;
|
||||
@state() private seedPhrase = "";
|
||||
@state() private userName = "";
|
||||
@state() private profileImage = "";
|
||||
@state() private lightningAddress = "";
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
--primary-color: var(--eve-primary-color, #4a90e2);
|
||||
--primary-hover: var(--eve-primary-hover, #357abd);
|
||||
--secondary-color: var(--eve-secondary-color, #6c757d);
|
||||
--secondary-hover: var(--eve-secondary-hover, #5a6268);
|
||||
--text-color: var(--eve-text-color, #2c3e50);
|
||||
--text-secondary: var(--eve-text-secondary, #64748b);
|
||||
--background-color: var(--eve-background-color, #ffffff);
|
||||
--error-color: var(--eve-error-color, #dc3545);
|
||||
--success-color: var(--eve-success-color, #28a745);
|
||||
--spacing-unit: 0.25rem;
|
||||
--border-radius: 8px;
|
||||
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-normal: 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
||||
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.welcome-container {
|
||||
max-width: min(800px, 90vw);
|
||||
margin: 0 auto;
|
||||
padding: calc(var(--spacing-unit) * 8);
|
||||
animation: fadeIn var(--transition-normal);
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: calc(var(--spacing-unit) * 12);
|
||||
}
|
||||
|
||||
.alpha-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
padding: 0.15em 0.5em;
|
||||
border-radius: 999px;
|
||||
font-size: 0.75em;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2rem, 5vw, 2.5rem);
|
||||
font-weight: 700;
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
var(--primary-color),
|
||||
var(--primary-hover)
|
||||
);
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
margin-bottom: calc(var(--spacing-unit) * 8);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(1.5rem, 3vw, 1.75rem);
|
||||
font-weight: 600;
|
||||
margin-bottom: calc(var(--spacing-unit) * 4);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: calc(var(--spacing-unit) * 4);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 calc(var(--spacing-unit) * 4);
|
||||
line-height: 1.6;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
gap: calc(var(--spacing-unit) * 4);
|
||||
margin-top: calc(var(--spacing-unit) * 6);
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 1;
|
||||
padding: calc(var(--spacing-unit) * 4);
|
||||
border: 2px solid var(--text-secondary);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 1rem;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2);
|
||||
}
|
||||
|
||||
.button {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: calc(var(--spacing-unit) * 2);
|
||||
padding: calc(var(--spacing-unit) * 3) calc(var(--spacing-unit) * 6);
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.button.primary {
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
var(--primary-color),
|
||||
var(--primary-hover)
|
||||
);
|
||||
color: white;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
var(--secondary-color),
|
||||
var(--secondary-hover)
|
||||
);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.navigation {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: calc(var(--spacing-unit) * 8);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.welcome-container {
|
||||
padding: calc(var(--spacing-unit) * 4);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: normal;
|
||||
}
|
||||
code {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.note {
|
||||
display: block;
|
||||
color: #666;
|
||||
font-size: 0.875rem;
|
||||
margin-top: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
border-left: 2px solid #ddd;
|
||||
font-style: italic;
|
||||
line-height: 1.4;
|
||||
max-width: 600px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
opacity: 1;
|
||||
border-left-color: #999;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 2px solid #3498db;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
background-color: #f8f9fa;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
legend {
|
||||
padding: 0 10px;
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
border-radius: 4px;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
fieldset:hover {
|
||||
border-color: #2980b9;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.external-link {
|
||||
color: #2970ff;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.external-link:hover {
|
||||
color: #1a56db;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.external-link:after {
|
||||
content: "↗";
|
||||
display: inline-block;
|
||||
margin-left: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
`;
|
||||
|
||||
private handleNavigation(page: number) {
|
||||
if (this.isAnimating) return;
|
||||
this.isAnimating = true;
|
||||
this.currentPage = page;
|
||||
setTimeout(() => (this.isAnimating = false), 300);
|
||||
}
|
||||
|
||||
private onSeedPhraseInput(event: Event) {
|
||||
this.seedPhrase = (event.target as HTMLInputElement).value;
|
||||
}
|
||||
|
||||
private generateSeedPhrase() {
|
||||
this.seedPhrase = nip06.generateSeedWords();
|
||||
}
|
||||
|
||||
private isValidSeedPhrase() {
|
||||
const words = this.seedPhrase.split(" ");
|
||||
if (words.length !== 12) return false;
|
||||
if (!nip06.validateWords(words.join(" "))) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private renderPageOne() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<section>
|
||||
<h1>Welcome to Eve</h1>
|
||||
<h2>Your Private Community Network</h2>
|
||||
<p>
|
||||
Connect, share, and engage with your community in a secure,
|
||||
members-only space designed just for you.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="alpha-badge">
|
||||
<iconify-icon icon="mdi:alpha"></iconify-icon> Alpha Preview
|
||||
</div>
|
||||
<p>
|
||||
We're actively developing Eve to create the best possible
|
||||
experience. Your feedback helps shape our platform.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="action-buttons">
|
||||
<a href="https://arx-ccn.com/eve-feedback" class="button primary">
|
||||
<iconify-icon icon="mdi:feedback"></iconify-icon>Share Feedback
|
||||
</a>
|
||||
<a
|
||||
href="https://arx-ccn.com/report-eve-bug"
|
||||
class="button secondary"
|
||||
>
|
||||
<iconify-icon icon="mdi:bug"></iconify-icon>Report a Bug
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="navigation">
|
||||
<span></span>
|
||||
<button
|
||||
@click=${() => this.handleNavigation(2)}
|
||||
class="button primary"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderPageTwo() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<section>
|
||||
<h1>Getting Started</h1>
|
||||
<h2>Creating a Community</h2>
|
||||
<p>
|
||||
Connect with others by joining an existing community or creating
|
||||
your own.
|
||||
</p>
|
||||
<p>
|
||||
During this alpha phase, community setup requires a few manual
|
||||
steps. We're actively working to streamline this process in future
|
||||
updates.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Seed Phrase</h3>
|
||||
<p>
|
||||
Enter your community's seed phrase below or generate a new one to
|
||||
create a community.
|
||||
</p>
|
||||
<p>
|
||||
<b>Important</b>: Keep your seed phrase secure. Anyone with access
|
||||
to it can join your community.
|
||||
</p>
|
||||
<p>
|
||||
In an upcoming release, we'll implement MLS (Messaging Layer
|
||||
Security) to enable simpler invitation-based community access, as
|
||||
well as improve your community's security.
|
||||
</p>
|
||||
<div class="input-group">
|
||||
<input
|
||||
@input=${this.onSeedPhraseInput}
|
||||
.value=${this.seedPhrase}
|
||||
id="seed-input"
|
||||
type="text"
|
||||
placeholder="Enter seed phrase..."
|
||||
/>
|
||||
<button
|
||||
@click=${() => this.generateSeedPhrase()}
|
||||
class="button secondary"
|
||||
>
|
||||
Generate
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="navigation">
|
||||
<button
|
||||
@click=${() => this.handleNavigation(1)}
|
||||
class="button secondary"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
@click=${() => this.handleNavigation(3)}
|
||||
?disabled=${!this.isValidSeedPhrase()}
|
||||
class="button primary"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
private getSetupCode() {
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
if (userAgent.includes("mac")) {
|
||||
return `
|
||||
mkdir -p ~/.config/arx/eve && cd ~/.config/arx/eve
|
||||
echo "${this.seedPhrase}" > ccn.seed
|
||||
launchctl load ~/Library/LaunchAgents/com.user.eve-relay.plist
|
||||
launchctl start com.user.eve-relay
|
||||
`
|
||||
.split("\n")
|
||||
.map((x) => x.trim())
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
if (userAgent.includes("linux")) {
|
||||
return `
|
||||
mkdir -p ~/.config/arx/eve && cd ~/.config/arx/eve
|
||||
echo "${this.seedPhrase}" > ccn.seed
|
||||
systemctl --user enable eve-relay.service
|
||||
systemctl --user start eve-relay.service
|
||||
`
|
||||
.split("\n")
|
||||
.map((x) => x.trim())
|
||||
.join("\n");
|
||||
}
|
||||
return "Unsupported OS";
|
||||
}
|
||||
|
||||
private renderPageThree() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<section>
|
||||
<h2>Configure Eve Relay</h2>
|
||||
<p>
|
||||
During this alpha phase, manual relay configuration is required.
|
||||
This process will be automated in future releases.
|
||||
</p>
|
||||
<p>Open your terminal and run following commands:</p>
|
||||
<pre>
|
||||
<code>${this.getSetupCode()}</code>
|
||||
</pre
|
||||
>
|
||||
<p>
|
||||
Having trouble? Our team is here to help if you encounter any
|
||||
issues.
|
||||
</p>
|
||||
<p>Click Continue once the relay is running.</p>
|
||||
</section>
|
||||
|
||||
<div class="navigation">
|
||||
<button
|
||||
@click=${() => this.handleNavigation(2)}
|
||||
class="button secondary"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
@click=${() => this.handleNavigation(4)}
|
||||
class="button primary"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
private onUserNameInput(e: Event) {
|
||||
this.userName = (e.target as HTMLInputElement).value;
|
||||
}
|
||||
|
||||
private onProfileImageInput(e: Event) {
|
||||
this.profileImage = (e.target as HTMLInputElement).value;
|
||||
}
|
||||
|
||||
private onLightningAddressInput(e: Event) {
|
||||
this.lightningAddress = (e.target as HTMLInputElement).value;
|
||||
}
|
||||
|
||||
private renderPageFour() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<section>
|
||||
<h2>Complete Your Profile</h2>
|
||||
<p>Great progress! Let's set up your community profile.</p>
|
||||
<p>
|
||||
Your profile information will be encrypted and visible only to
|
||||
community members.
|
||||
</p>
|
||||
<div class="profile-form">
|
||||
<fieldset>
|
||||
<legend>Display Name</legend>
|
||||
<input
|
||||
id="username"
|
||||
type="text"
|
||||
.value=${this.userName}
|
||||
@input=${this.onUserNameInput}
|
||||
placeholder="Enter your name"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Profile Picture</legend>
|
||||
<input
|
||||
id="profile-image"
|
||||
type="text"
|
||||
.value=${this.profileImage}
|
||||
@input=${this.onProfileImageInput}
|
||||
placeholder="Enter image URL"
|
||||
/>
|
||||
<small class="note">
|
||||
Direct file uploads will be supported in a future update. For
|
||||
now, please provide an image URL or leave blank.
|
||||
</small>
|
||||
</fieldset>
|
||||
</div>
|
||||
</section>
|
||||
<div class="navigation">
|
||||
<button
|
||||
@click=${() => this.handleNavigation(3)}
|
||||
class="button secondary"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
@click=${() => this.handleNavigation(5)}
|
||||
class="button primary"
|
||||
>
|
||||
Final Step
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderPageFive() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<section>
|
||||
<h2>Payment Setup</h2>
|
||||
<p>Almost done! Let's set up your payment options.</p>
|
||||
<p>
|
||||
Enter your existing Lightning address below for payments within the
|
||||
community. If you don't have one, leave this field blank and we'll
|
||||
automatically generate one for you through
|
||||
<a target="_blank" href="https://npub.cash" class="external-link"
|
||||
>npub.cash</a
|
||||
>.
|
||||
</p>
|
||||
<input
|
||||
id="lightning-address"
|
||||
type="text"
|
||||
.value=${this.lightningAddress}
|
||||
@input=${this.onLightningAddressInput}
|
||||
placeholder="your@lightning.address"
|
||||
/>
|
||||
<small class="note">
|
||||
Your Lightning address enables secure, instant payments within the
|
||||
community.
|
||||
</small>
|
||||
</section>
|
||||
|
||||
<div class="navigation">
|
||||
<button
|
||||
@click=${() => this.handleNavigation(4)}
|
||||
class="button secondary"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button @click=${() => this.goToFinalStep()} class="button primary">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
private async goToFinalStep() {
|
||||
let encryptionPassphrase = localStorage.getItem("encryption_key");
|
||||
if (!encryptionPassphrase) {
|
||||
encryptionPassphrase =
|
||||
Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
localStorage.setItem("encryption_key", encryptionPassphrase);
|
||||
}
|
||||
const randomPrivateKey = nostrTools.generateSecretKey();
|
||||
const encryptedNsec = nip49.encrypt(randomPrivateKey, encryptionPassphrase);
|
||||
const npub = nip19.npubEncode(nostrTools.getPublicKey(randomPrivateKey));
|
||||
|
||||
if (!this.lightningAddress) this.lightningAddress = `${npub}@npub.cash`;
|
||||
|
||||
localStorage.setItem("ncryptsec", encryptedNsec);
|
||||
|
||||
setSigner(new NDKPrivateKeySigner(randomPrivateKey));
|
||||
|
||||
const event = new NDKEvent(ndk);
|
||||
event.kind = NDKKind.Metadata;
|
||||
event.content = JSON.stringify({
|
||||
name: this.userName,
|
||||
image: this.profileImage || undefined,
|
||||
lud16: this.lightningAddress,
|
||||
});
|
||||
await event.sign();
|
||||
await event.publish();
|
||||
|
||||
this.handleNavigation(6);
|
||||
}
|
||||
|
||||
private renderPageSix() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<section>
|
||||
<h2>Done!</h2>
|
||||
<p>
|
||||
That's it! You're all set to start using the community. We hope you
|
||||
enjoy it!
|
||||
</p>
|
||||
<p>Your community's seed phrase is: <b>${this.seedPhrase}</b></p>
|
||||
<p>
|
||||
Please store this seed phrase somewhere safe and secure. It will be
|
||||
required to invite new members to the community.
|
||||
</p>
|
||||
<p>Your username is: <b>${this.userName}</b></p>
|
||||
${this.profileImage
|
||||
? html` <p>
|
||||
Your profile image is: <img .src=${this.profileImage} />
|
||||
</p>`
|
||||
: ""}
|
||||
<p>Your lightning address is: <b>${this.lightningAddress}</b></p>
|
||||
</section>
|
||||
|
||||
<div class="navigation">
|
||||
<button
|
||||
@click=${() => this.handleNavigation(5)}
|
||||
class="button secondary"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button @click=${this.finish} class="button primary">Finish</button>
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
finish() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("finish", {
|
||||
detail: {
|
||||
userName: this.userName,
|
||||
profileImage: this.profileImage,
|
||||
lightningAddress: this.lightningAddress,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override render() {
|
||||
switch (this.currentPage) {
|
||||
case 1:
|
||||
return this.renderPageOne();
|
||||
case 2:
|
||||
return this.renderPageTwo();
|
||||
case 3:
|
||||
return this.renderPageThree();
|
||||
case 4:
|
||||
return this.renderPageFour();
|
||||
case 5:
|
||||
return this.renderPageFive();
|
||||
case 6:
|
||||
return this.renderPageSix();
|
||||
default:
|
||||
return html`<div class="welcome-container">Loading...</div>`;
|
||||
}
|
||||
}
|
||||
}
|
17
src/components/LoadingView.ts
Normal file
17
src/components/LoadingView.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { html, LitElement } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
|
||||
@customElement('arx-loading-view')
|
||||
export class LoadingView extends LitElement {
|
||||
override render() {
|
||||
return html`
|
||||
<span>Loading...</span>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'arx-loading-view': LoadingView;
|
||||
}
|
||||
}
|
113
src/components/MarkdownContent.ts
Normal file
113
src/components/MarkdownContent.ts
Normal file
|
@ -0,0 +1,113 @@
|
|||
import { css, LitElement } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||
import MarkdownIt, { type StateCore, type Token } from 'markdown-it';
|
||||
|
||||
function nostrPlugin(md: MarkdownIt): void {
|
||||
const npubRegex = /(npub[0-9a-zA-Z]{59})/g;
|
||||
|
||||
md.core.ruler.after('inline', 'nostr_npub', (state: StateCore): boolean => {
|
||||
for (const token of state.tokens) {
|
||||
if (token.type === 'inline' && token.children) {
|
||||
for (let i = 0; i < token.children.length; i++) {
|
||||
const child = token.children[i];
|
||||
|
||||
if (child.type === 'text') {
|
||||
const matches = child.content.match(npubRegex);
|
||||
if (!matches) continue;
|
||||
|
||||
const newTokens: Token[] = [];
|
||||
let lastIndex = 0;
|
||||
|
||||
child.content.replace(
|
||||
npubRegex,
|
||||
(match: string, npub: string, offset: number) => {
|
||||
if (offset > lastIndex) {
|
||||
const textToken = new state.Token('text', '', 0);
|
||||
textToken.content = child.content.slice(lastIndex, offset);
|
||||
newTokens.push(textToken);
|
||||
}
|
||||
|
||||
const linkOpen = new state.Token('link_open', 'a', 1);
|
||||
linkOpen.attrs = [['href', `nostr:${npub}`]];
|
||||
|
||||
const text = new state.Token('text', '', 0);
|
||||
text.content = npub;
|
||||
|
||||
const linkClose = new state.Token('link_close', 'a', -1);
|
||||
|
||||
newTokens.push(linkOpen, text, linkClose);
|
||||
lastIndex = offset + match.length;
|
||||
},
|
||||
);
|
||||
|
||||
if (lastIndex < child.content.length) {
|
||||
const textToken = new state.Token('text', '', 0);
|
||||
textToken.content = child.content.slice(lastIndex);
|
||||
newTokens.push(textToken);
|
||||
}
|
||||
|
||||
token.children.splice(i, 1, ...newTokens);
|
||||
i += newTokens.length - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@customElement('arx-markdown-content')
|
||||
export class MarkdownContent extends LitElement {
|
||||
private md = new MarkdownIt({
|
||||
html: false,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
breaks: true,
|
||||
});
|
||||
|
||||
@property({ type: String })
|
||||
content = '';
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
align-items: flex-start;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
text-wrap: normal;
|
||||
}
|
||||
|
||||
* {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
text-wrap: normal;
|
||||
}
|
||||
|
||||
.text-content {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, code, ul, ol, blockquote {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.md.use(nostrPlugin);
|
||||
}
|
||||
|
||||
override render() {
|
||||
return unsafeHTML(this.md.render(this.content));
|
||||
}
|
||||
}
|
48
src/components/NostrAvatar.ts
Normal file
48
src/components/NostrAvatar.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import type { NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { css, html, LitElement } from 'lit-element';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
type AvatarSize = 'short' | 'medium' | 'large' | 'huge';
|
||||
|
||||
@customElement('arx-nostr-avatar')
|
||||
export class ArxNostrAvatar extends LitElement {
|
||||
@property({ type: Object }) profile!: NDKUserProfile;
|
||||
@property({ type: String }) size!: AvatarSize;
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
img {
|
||||
border-radius: 50%;
|
||||
width: var(--avatar-size);
|
||||
height: var(--avatar-size);
|
||||
}
|
||||
.short-avatar {
|
||||
--avatar-size: 2rem;
|
||||
}
|
||||
.medium-avatar {
|
||||
--avatar-size: 3rem;
|
||||
}
|
||||
.large-avatar {
|
||||
--avatar-size: 4rem;
|
||||
}
|
||||
.huge-avatar {
|
||||
--avatar-size: 5rem;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<img
|
||||
class=${this.size}-avatar
|
||||
src=${this.profile.image || '/default-avatar.png'}
|
||||
alt=${this.profile.name || this.profile.displayName || ''}
|
||||
@error=${this.handleError}
|
||||
/>
|
||||
`;
|
||||
}
|
||||
|
||||
handleError(event: Event) {
|
||||
event.preventDefault();
|
||||
(event.target as HTMLImageElement).src = '/default-avatar.png';
|
||||
}
|
||||
}
|
70
src/components/NostrProfile.ts
Normal file
70
src/components/NostrProfile.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { html, css, LitElement } from 'lit';
|
||||
import { property, customElement, state } from 'lit/decorators.js';
|
||||
import type { NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { getUserProfile } from '../ndk';
|
||||
import '@components/profiles/ShortProfile';
|
||||
import '@components/profiles/MediumProfile';
|
||||
import '@components/profiles/LargeProfile';
|
||||
import '@components/profiles/CardProfile';
|
||||
|
||||
@customElement('arx-nostr-profile')
|
||||
export class NostrProfile extends LitElement {
|
||||
@property() npub = '';
|
||||
|
||||
@property({ reflect: true }) renderType:
|
||||
| 'short'
|
||||
| 'medium'
|
||||
| 'large'
|
||||
| 'card' = 'short';
|
||||
|
||||
@state()
|
||||
private profile: NDKUserProfile | undefined = undefined;
|
||||
|
||||
@state()
|
||||
private error: string | null = null;
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.nostr-profile {
|
||||
display: inline-block;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
await this.loadProfile();
|
||||
}
|
||||
|
||||
async loadProfile() {
|
||||
try {
|
||||
this.profile = await getUserProfile(this.npub);
|
||||
} catch (error) {
|
||||
this.error = 'Failed to load profile';
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this.error) {
|
||||
return html`<arx-error-view .error=${this.error}></arx-error-view>`;
|
||||
}
|
||||
|
||||
if (!this.profile) {
|
||||
return html`<arx-loading-view></arx-loading-view>`;
|
||||
}
|
||||
|
||||
switch (this.renderType) {
|
||||
case 'short':
|
||||
return html`<arx-nostr-short-profile .profile=${this.profile} npub=${this.npub}></arx-nostr-short-profile>`;
|
||||
case 'medium':
|
||||
return html`<arx-nostr-medium-profile .profile=${this.profile} npub=${this.npub}></arx-nostr-medium-profile>`;
|
||||
case 'large':
|
||||
return html`<arx-nostr-large-profile .profile=${this.profile} npub=${this.npub}></arx-nostr-large-profile>`;
|
||||
case 'card':
|
||||
return html`<arx-nostr-card-profile .profile=${this.profile} npub=${this.npub}></arx-nostr-card-profile>`;
|
||||
default:
|
||||
return html`<p>Invalid render type</p>`;
|
||||
}
|
||||
}
|
||||
}
|
75
src/components/PhoraButton.ts
Normal file
75
src/components/PhoraButton.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { property, customElement } from "lit/decorators.js";
|
||||
|
||||
import "@components/EveLink";
|
||||
|
||||
@customElement("arx-phora-button")
|
||||
export class PhoraButton extends LitElement {
|
||||
@property({ type: String }) href = "";
|
||||
@property({ type: String }) target = "";
|
||||
@property({ type: String }) rel = "";
|
||||
@property({ type: Boolean }) disabled = false;
|
||||
|
||||
get hrefValue() {
|
||||
if (!this.href || this.disabled) return "javascript:void(0)";
|
||||
return this.href;
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
arx-eve-link::part(link) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
padding: 0.75rem 0.75rem;
|
||||
border-radius: 0.25rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
box-shadow: var(--shadow-sm);
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
arx-eve-link:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.15);
|
||||
background: color-mix(in oklch, var(--accent), black 15%);
|
||||
}
|
||||
|
||||
arx-eve-link:focus {
|
||||
outline: none;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
arx-eve-link:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
arx-eve-link.disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<arx-eve-link
|
||||
.href=${this.hrefValue}
|
||||
.target=${this.target}
|
||||
.rel=${this.rel}
|
||||
class="${this.disabled ? "disabled" : ""}"
|
||||
>
|
||||
<slot></slot>
|
||||
</arx-eve-link>
|
||||
`;
|
||||
}
|
||||
}
|
44
src/components/PhoraForumCategory.ts
Normal file
44
src/components/PhoraForumCategory.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { property, customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("arx-phora-forum-category")
|
||||
export class PhoraForumCategory extends LitElement {
|
||||
@property({ type: String }) override title = "";
|
||||
@property({ type: String }) override id = "";
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.forum-category {
|
||||
background: oklch(100% 0 0);
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: var(--shadow-xl);
|
||||
margin-block-end: 1.5rem;
|
||||
}
|
||||
|
||||
.category-header {
|
||||
background: var(--primary);
|
||||
color: oklch(100% 0 0);
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 0.5rem 0.5rem 0 0;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="forum-category">
|
||||
<div class="category-header">
|
||||
<span>${this.title}</span>
|
||||
<arx-phora-button href="/phora/new-topic/${this.id}">New Topic</phora-button>
|
||||
</div>
|
||||
<slot>
|
||||
<div style="padding: 1rem 1.5rem">No topics...</div>
|
||||
</slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
212
src/components/PhoraForumTopic.ts
Normal file
212
src/components/PhoraForumTopic.ts
Normal file
|
@ -0,0 +1,212 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { property, customElement } from "lit/decorators.js";
|
||||
|
||||
import "@components/EveLink";
|
||||
|
||||
@customElement("arx-phora-forum-topic")
|
||||
export class PhoraForumTopic extends LitElement {
|
||||
static override styles = [
|
||||
css`
|
||||
.topic {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
padding: 1.75rem;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 1rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.topic:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.topic-icon {
|
||||
inline-size: 40px;
|
||||
block-size: 40px;
|
||||
background: var(--accent);
|
||||
border-radius: 10px;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.topic-icon::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
oklch(from var(--accent) l c h / 0.2)
|
||||
);
|
||||
}
|
||||
|
||||
.topic-info {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.topic-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
arx-eve-link::part(link) {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.875rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
text-decoration: none;
|
||||
color: var(--primary);
|
||||
transition: all 0.2s ease;
|
||||
&:hover {
|
||||
color: var(--secondary);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
}
|
||||
|
||||
.topic-details p {
|
||||
margin: 0;
|
||||
font-size: 0.975rem;
|
||||
line-height: 1.6;
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
.new-status {
|
||||
position: relative;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.new-status::after {
|
||||
content: "New";
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -40px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
background: var(--accent);
|
||||
color: var(--light);
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.hot-status {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.hot-status::before {
|
||||
content: "🔥";
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.stats-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding-left: 1.5rem;
|
||||
border-left: 2px solid var(--border);
|
||||
}
|
||||
|
||||
.post-count {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
color: var(--secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.post-count :deep(iconify-icon) {
|
||||
font-size: 1.375rem;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.last-post-section {
|
||||
background: oklch(from var(--primary) 98% calc(c * 0.2) h);
|
||||
padding: 0.875rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.last-post-section > div:first-of-type {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--secondary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.last-post-info {
|
||||
color: var(--secondary);
|
||||
font-size: 0.875rem;
|
||||
margin-top: 0.625rem;
|
||||
padding-top: 0.625rem;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.topic {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.stats-container {
|
||||
padding-left: 0;
|
||||
border-left: none;
|
||||
padding-top: 1rem;
|
||||
border-top: 2px solid var(--border);
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@property({ type: String }) override id = "";
|
||||
@property({ type: String }) override title = "";
|
||||
@property({ type: String }) description = "";
|
||||
@property({ type: Number }) posts = 0;
|
||||
@property({ type: String }) lastPostBy = "";
|
||||
@property({ type: String }) lastPostTime = "";
|
||||
@property({ type: Boolean }) isNew = false;
|
||||
@property({ type: Boolean }) isHot = false;
|
||||
|
||||
get status() {
|
||||
if (this.isNew) return "new-status";
|
||||
if (this.isHot) return "hot-status";
|
||||
return "";
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="topic">
|
||||
<div class="topic-info">
|
||||
<div class="topic-icon"></div>
|
||||
<div class="topic-details">
|
||||
<arx-eve-link
|
||||
class="${this.status}"
|
||||
href="/phora/topics/${this.id}"
|
||||
>
|
||||
${this.title}
|
||||
</arx-eve-link>
|
||||
<p>${this.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats-container">
|
||||
<div class="post-count">
|
||||
<iconify-icon icon="material-symbols:forum-rounded"></iconify-icon>
|
||||
<span>${this.posts.toLocaleString()} posts</span>
|
||||
</div>
|
||||
<div class="last-post-section">
|
||||
<div>Latest activity</div>
|
||||
<arx-nostr-profile npub="${this.lastPostBy}"></arx-nostr-profile>
|
||||
<div class="last-post-info">${this.lastPostTime}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
101
src/components/Widgets/BitcoinBlockWidget.ts
Normal file
101
src/components/Widgets/BitcoinBlockWidget.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { getLastBlockHeight } from "@utils/lastBlockHeight";
|
||||
|
||||
@customElement("arx-bitcoin-block-widget")
|
||||
export class BitcoinBlockWidget extends LitElement {
|
||||
@state()
|
||||
private lastBlock: number | null = null;
|
||||
|
||||
@state()
|
||||
private isLoading = true;
|
||||
|
||||
@state()
|
||||
private error: string | null = null;
|
||||
|
||||
private REFRESH_INTERVAL = 5000;
|
||||
|
||||
@state()
|
||||
private timer: number | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.loadBlockHeight();
|
||||
this.timer = window.setInterval(
|
||||
this.loadBlockHeight,
|
||||
this.REFRESH_INTERVAL
|
||||
);
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this.timer) clearInterval(this.timer);
|
||||
}
|
||||
|
||||
async loadBlockHeight() {
|
||||
try {
|
||||
const response = await getLastBlockHeight();
|
||||
this.lastBlock = response.height;
|
||||
this.error = null;
|
||||
} catch (error) {
|
||||
this.error = "Failed to load block height";
|
||||
console.error(error);
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.error {
|
||||
color: #dc3545;
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
background: #f8d7da;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.block-height {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
if (this.error) {
|
||||
return html`<div class="error">${this.error}</div>`;
|
||||
}
|
||||
|
||||
if (this.isLoading) {
|
||||
return html`
|
||||
<div class="loading">
|
||||
<span class="loader"></span>
|
||||
Loading latest block...
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="block-height">
|
||||
<span class="label">Last Block:</span>
|
||||
<span class="value">${this.lastBlock?.toLocaleString()}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
121
src/components/profiles/CardProfile.ts
Normal file
121
src/components/profiles/CardProfile.ts
Normal file
|
@ -0,0 +1,121 @@
|
|||
import { html, css, LitElement } from 'lit';
|
||||
import { property, customElement, state } from 'lit/decorators.js';
|
||||
import type { NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { getProfile } from '@utils/profileUtils';
|
||||
|
||||
@customElement('arx-nostr-card-profile')
|
||||
export class CardProfile extends LitElement {
|
||||
@property() profile!: NDKUserProfile;
|
||||
@property() npub = '';
|
||||
|
||||
@state()
|
||||
private displayName = '';
|
||||
|
||||
@state()
|
||||
private profileUrl = '';
|
||||
|
||||
@state()
|
||||
private website = '';
|
||||
|
||||
@state()
|
||||
private about = '';
|
||||
|
||||
@state()
|
||||
private firstLineOfAbout = '';
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.card {
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.25rem 0;
|
||||
color: var(--secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.bio {
|
||||
white-space: pre-line;
|
||||
font-size: 0.9rem;
|
||||
color: var(--primary);
|
||||
margin: 0.5rem 0;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.website-link {
|
||||
display: block;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.website-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override firstUpdated() {
|
||||
const { displayName, profileUrl } = getProfile(this);
|
||||
this.displayName = displayName;
|
||||
this.profileUrl = profileUrl;
|
||||
this.website = this.profile.website || '';
|
||||
const lines = (this.profile.about || '').split('\n');
|
||||
let firstLine = lines[0].trim();
|
||||
|
||||
let remainingContent = lines
|
||||
.slice(1)
|
||||
.join('\n')
|
||||
.trim()
|
||||
.split(/-+\n/, 2)
|
||||
.filter(section => section.trim() !== '')
|
||||
.join('\n')
|
||||
.trim();
|
||||
|
||||
if (firstLine.length > 20) {
|
||||
remainingContent = `${firstLine}\n${remainingContent}`;
|
||||
firstLine = '';
|
||||
}
|
||||
|
||||
this.about = remainingContent;
|
||||
this.firstLineOfAbout = firstLine;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="card">
|
||||
<router-link to=${this.profileUrl}>
|
||||
<arx-nostr-avatar .profile=${this.profile} size="large"></arx-nostr-avatar>
|
||||
<div>
|
||||
<h3>${this.displayName}</h3>
|
||||
<p v-if=${this.firstLineOfAbout}>${this.firstLineOfAbout}</p>
|
||||
</div>
|
||||
</router-link>
|
||||
|
||||
<div v-if=${this.about} class="bio">${this.about}</div>
|
||||
|
||||
<a v-if=${this.website} href=${this.website} target="_blank" rel="noreferrer noopener" class="website-link">
|
||||
${this.website}
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
61
src/components/profiles/LargeProfile.ts
Normal file
61
src/components/profiles/LargeProfile.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
import { html, css, LitElement } from 'lit';
|
||||
import { property, customElement, state } from 'lit/decorators.js';
|
||||
import type { NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { getProfile } from '@utils/profileUtils';
|
||||
import firstLine from '@utils/firstLine';
|
||||
|
||||
@customElement('arx-nostr-large-profile')
|
||||
export class LargeProfile extends LitElement {
|
||||
@property() profile!: NDKUserProfile;
|
||||
@property() npub = '';
|
||||
|
||||
@state()
|
||||
private displayName = '';
|
||||
|
||||
@state()
|
||||
private profileUrl = '';
|
||||
|
||||
@state()
|
||||
private about = '';
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
a {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.bio {
|
||||
white-space: pre-line;
|
||||
font-size: 0.9rem;
|
||||
color: var(--primary);
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override firstUpdated() {
|
||||
const { displayName, profileUrl } = getProfile({
|
||||
profile: this.profile,
|
||||
npub: this.npub,
|
||||
});
|
||||
this.displayName = displayName;
|
||||
this.profileUrl = profileUrl;
|
||||
this.about = firstLine(this.profile.about);
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<router-link to=${this.profileUrl}>
|
||||
<arx-nostr-avatar .profile=${this.profile} size="large"></arx-nostr-avatar>
|
||||
<div>
|
||||
<h3>${this.displayName}</h3>
|
||||
<div class="bio">${this.about}</div>
|
||||
</div>
|
||||
</router-link>
|
||||
`;
|
||||
}
|
||||
}
|
64
src/components/profiles/MediumProfile.ts
Normal file
64
src/components/profiles/MediumProfile.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { property, customElement, state } from "lit/decorators.js";
|
||||
import type { NDKUserProfile } from "@nostr-dev-kit/ndk";
|
||||
import { getProfile } from "@utils/profileUtils";
|
||||
import firstLine from "@utils/firstLine";
|
||||
|
||||
import "@components/EveLink";
|
||||
|
||||
@customElement("arx-nostr-medium-profile")
|
||||
export class MediumProfile extends LitElement {
|
||||
@property() profile!: NDKUserProfile;
|
||||
@property() npub = "";
|
||||
|
||||
@state()
|
||||
private displayName = "";
|
||||
|
||||
@state()
|
||||
private profileUrl = "";
|
||||
|
||||
@state()
|
||||
private truncatedAbout = "";
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
arx-eve-link::part(link) {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override firstUpdated() {
|
||||
const { displayName, profileUrl } = getProfile({
|
||||
profile: this.profile,
|
||||
npub: this.npub,
|
||||
});
|
||||
this.displayName = displayName;
|
||||
this.profileUrl = profileUrl;
|
||||
this.truncatedAbout = this.getTruncatedAbout();
|
||||
}
|
||||
|
||||
getTruncatedAbout() {
|
||||
const about = firstLine(this.profile.about);
|
||||
return about?.length > 80 ? `${about.substring(0, 80)}...` : about;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<arx-eve-link href=${this.profileUrl}>
|
||||
<arx-nostr-avatar
|
||||
.profile=${this.profile}
|
||||
size="medium"
|
||||
></arx-nostr-avatar>
|
||||
<div>
|
||||
<div>${this.displayName}</div>
|
||||
<div class="bio">${this.truncatedAbout}</div>
|
||||
</div>
|
||||
</arx-eve-link>
|
||||
`;
|
||||
}
|
||||
}
|
51
src/components/profiles/ShortProfile.ts
Normal file
51
src/components/profiles/ShortProfile.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { property, customElement, state } from "lit/decorators.js";
|
||||
import type { NDKUserProfile } from "@nostr-dev-kit/ndk";
|
||||
import { getProfile } from "@utils/profileUtils";
|
||||
|
||||
import "@components/EveLink";
|
||||
|
||||
@customElement("arx-nostr-short-profile")
|
||||
export class ShortProfile extends LitElement {
|
||||
@property() profile!: NDKUserProfile;
|
||||
@property() npub = "";
|
||||
|
||||
@state()
|
||||
private displayName = "";
|
||||
|
||||
@state()
|
||||
private profileUrl = "";
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
arx-eve-link::part(link) {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
protected override firstUpdated(): void {
|
||||
const { displayName, profileUrl } = getProfile({
|
||||
profile: this.profile,
|
||||
npub: this.npub,
|
||||
});
|
||||
this.displayName = displayName;
|
||||
this.profileUrl = profileUrl;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<arx-eve-link href=${this.profileUrl}>
|
||||
<arx-nostr-avatar
|
||||
.profile=${this.profile}
|
||||
size="short"
|
||||
></arx-nostr-avatar>
|
||||
<span>${this.displayName}</span>
|
||||
</arx-eve-link>
|
||||
`;
|
||||
}
|
||||
}
|
14
src/main.ts
Normal file
14
src/main.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import "./style.scss";
|
||||
|
||||
import "@components/ErrorView";
|
||||
import "@components/NostrAvatar";
|
||||
import "@components/LoadingView";
|
||||
import "@components/NostrProfile";
|
||||
import "@components/Breadcrumbs";
|
||||
import "@components/Header";
|
||||
import "@routes/router";
|
||||
import type EveRouter from "@routes/router";
|
||||
|
||||
const router = document.createElement("arx-eve-router") as EveRouter;
|
||||
router.ccnSetup = localStorage.getItem("ncryptsec");
|
||||
document.body.appendChild(router);
|
39
src/ndk.ts
Normal file
39
src/ndk.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import NDK, { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
||||
import * as nip49 from "nostr-tools/nip49";
|
||||
|
||||
export const ndk = new NDK({
|
||||
explicitRelayUrls: ["ws://localhost:6942"],
|
||||
enableOutboxModel: false,
|
||||
autoConnectUserRelays: false,
|
||||
clientName: "Arx",
|
||||
clientNip89: "arx",
|
||||
});
|
||||
|
||||
export async function getSigner() {
|
||||
await ndk.connect();
|
||||
if (ndk.signer) return;
|
||||
const encryptionPassphrase = localStorage.getItem("encryption_key");
|
||||
const signer = new NDKPrivateKeySigner(
|
||||
nip49.decrypt(localStorage.getItem("ncryptsec"), encryptionPassphrase)
|
||||
);
|
||||
setSigner(signer);
|
||||
}
|
||||
|
||||
export async function getNpub() {
|
||||
await getSigner();
|
||||
const user = await ndk.signer?.user();
|
||||
if (user) return user.npub;
|
||||
throw new Error("Could not get npub");
|
||||
}
|
||||
|
||||
export function setSigner(signer: NDKPrivateKeySigner) {
|
||||
ndk.signer = signer;
|
||||
}
|
||||
|
||||
export async function getUserProfile(npub: string) {
|
||||
await ndk.connect();
|
||||
const query = npub.startsWith("npub") ? { npub } : { pubkey: npub };
|
||||
const user = ndk.getUser(query);
|
||||
await user.fetchProfile();
|
||||
return user.profile;
|
||||
}
|
199
src/routes/404Page.ts
Normal file
199
src/routes/404Page.ts
Normal file
|
@ -0,0 +1,199 @@
|
|||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import type { RouteParams } from "./router";
|
||||
|
||||
import "@components/EveLink";
|
||||
|
||||
@customElement("arx-404-page")
|
||||
export class FourOhFourPage extends LitElement {
|
||||
@property({ type: Object })
|
||||
params: RouteParams = {};
|
||||
|
||||
@property({ type: String })
|
||||
path = "";
|
||||
|
||||
@property({ type: Boolean })
|
||||
canGoBack = false;
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.not-found {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Inter", sans-serif;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-container h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.error-container h1 * {
|
||||
position: relative;
|
||||
margin: 0 -20px;
|
||||
}
|
||||
|
||||
.spinning-gear {
|
||||
animation: spin 5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.path-container {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
margin-bottom: 2rem;
|
||||
padding: 1rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.path-text {
|
||||
color: var(--secondary);
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.path {
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
color: var(--primary);
|
||||
word-break: break-all;
|
||||
font-size: 1.1rem;
|
||||
padding: 0.5rem;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 8rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.message-text .inline-icon {
|
||||
font-size: 1.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 1.25rem;
|
||||
color: #94a3b8;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.sub-text {
|
||||
color: #64748b;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.inline-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.primary-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: var(--secondary);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
arx-eve-link::part(link) {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="not-found">
|
||||
<div class="content">
|
||||
<div class="error-container">
|
||||
<h1>
|
||||
<span class="four">4</span>
|
||||
<iconify-icon
|
||||
icon="fluent-emoji:gear"
|
||||
class="spinning-gear"
|
||||
></iconify-icon>
|
||||
<span class="four">4</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="path-container">
|
||||
<div class="path-text">Path:</div>
|
||||
<div class="path">${this.path}</div>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
<div class="status">Page not found.</div>
|
||||
<div class="sub-text">
|
||||
The page you are looking for does not exist.
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a
|
||||
href="javascript:void(0)"
|
||||
@click="${() =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("go-back", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
)}"
|
||||
class="primary-button"
|
||||
>
|
||||
<iconify-icon icon="material-symbols:arrow-back"></iconify-icon>
|
||||
Go back
|
||||
</a>
|
||||
<arx-eve-link href="home" class="primary-button">
|
||||
<iconify-icon icon="material-symbols:home"></iconify-icon>
|
||||
Home
|
||||
</arx-eve-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
252
src/routes/Home.ts
Normal file
252
src/routes/Home.ts
Normal file
|
@ -0,0 +1,252 @@
|
|||
import { getNpub, getUserProfile } from "@/ndk";
|
||||
import type { NDKUserProfile } from "@nostr-dev-kit/ndk";
|
||||
import { css, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { html, literal } from "lit/static-html.js";
|
||||
import "@widgets/BitcoinBlockWidget";
|
||||
import "@components/AppGrid";
|
||||
|
||||
@customElement("arx-eve-home")
|
||||
export class Home extends LitElement {
|
||||
@state()
|
||||
private npub: string | undefined;
|
||||
@state()
|
||||
private profile: NDKUserProfile | undefined;
|
||||
@state()
|
||||
private username: string | undefined;
|
||||
|
||||
apps = [
|
||||
{
|
||||
id: 0,
|
||||
href: "letters",
|
||||
name: "Letters",
|
||||
color: "#FF33BB",
|
||||
icon: "bxs:envelope",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
href: "messages",
|
||||
name: "Messages",
|
||||
color: "#34C759",
|
||||
icon: "bxs:chat",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
href: "calendar",
|
||||
name: "Calendar",
|
||||
color: "#FF9500",
|
||||
icon: "bxs:calendar",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
href: "phora",
|
||||
name: "Phora",
|
||||
color: "#FF3B30",
|
||||
icon: "bxs:conversation",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
href: "agora",
|
||||
name: "Agora",
|
||||
color: "#5856D6",
|
||||
icon: "bxs:store",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
href: "wallet",
|
||||
name: "Wallet",
|
||||
color: "#007AFF",
|
||||
icon: "bxs:wallet",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
href: "consortium",
|
||||
name: "Consortium",
|
||||
color: "#FFCC00",
|
||||
icon: "bxs:landmark",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
href: "settings",
|
||||
name: "Settings",
|
||||
color: "#deadbeef",
|
||||
icon: "bxs:wrench",
|
||||
},
|
||||
];
|
||||
|
||||
widgets = [
|
||||
{
|
||||
title: "Bitcoin Block",
|
||||
content: literal`arx-bitcoin-block-widget`,
|
||||
},
|
||||
];
|
||||
|
||||
async loadProperties() {
|
||||
const npub = await getNpub();
|
||||
if (!npub) return alert("No npub?");
|
||||
this.npub = npub;
|
||||
this.profile = (await getUserProfile(this.npub)) as NDKUserProfile;
|
||||
this.username = this.profile?.name || this.npub.substring(0, 8);
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.home {
|
||||
min-height: calc(100vh - var(--font-2xl));
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.home-container {
|
||||
flex: 1;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.widgets-container {
|
||||
width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.widget {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
h3 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 18px;
|
||||
color: #1c1c1e;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.content-wrapper {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.widgets-container {
|
||||
width: calc(100vw - 40px);
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.widget {
|
||||
min-width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.widgets-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.widget {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.welcome-text h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: #1c1c1e;
|
||||
}
|
||||
|
||||
.welcome-text p {
|
||||
margin: 5px 0 0;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.loadProperties();
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="home">
|
||||
<div class="content-wrapper">
|
||||
<div class="home-container">
|
||||
<div class="welcome-section">
|
||||
<div class="avatar">
|
||||
<arx-nostr-profile
|
||||
.npub=${this.npub}
|
||||
render-type="avatar"
|
||||
></arx-nostr-profile>
|
||||
</div>
|
||||
<div class="welcome-text">
|
||||
<h1>Welcome, ${this.username}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<arx-app-grid .apps=${this.apps}></arx-app-grid>
|
||||
</div>
|
||||
<div class="widgets-container">
|
||||
${this.widgets.map(
|
||||
(widget) => html`
|
||||
<div class="widget">
|
||||
<h3>${widget.title}</h3>
|
||||
<${widget.content}></${widget.content}>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
126
src/routes/Phora/Home.ts
Normal file
126
src/routes/Phora/Home.ts
Normal file
|
@ -0,0 +1,126 @@
|
|||
import { LitElement, html, css } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { getSigner, ndk } from "@/ndk";
|
||||
import formatDateTime from "@utils/formatDateTime";
|
||||
import type { NDKSubscription } from "@nostr-dev-kit/ndk";
|
||||
|
||||
import "@components/Breadcrumbs";
|
||||
import "@components/PhoraForumCategory";
|
||||
import "@components/PhoraForumTopic";
|
||||
import "@components/PhoraButton";
|
||||
|
||||
interface ForumTopic {
|
||||
id: string;
|
||||
title: string;
|
||||
author: string;
|
||||
description: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface ForumCategory {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
topics: ForumTopic[];
|
||||
}
|
||||
|
||||
@customElement("arx-phora-home")
|
||||
export class PhoraForum extends LitElement {
|
||||
@state()
|
||||
private categories: ForumCategory[] = [];
|
||||
|
||||
private categoriesQuery: NDKSubscription | undefined;
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
|
||||
override async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
await this.loadCategories();
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this.categoriesQuery) this.categoriesQuery.stop();
|
||||
}
|
||||
|
||||
private async loadCategories() {
|
||||
await getSigner();
|
||||
this.categoriesQuery = ndk
|
||||
.subscribe({
|
||||
kinds: [11],
|
||||
})
|
||||
.on("event", (event) => {
|
||||
const subject = event.tags.find(
|
||||
(tag: string[]) => tag[0] === "subject"
|
||||
);
|
||||
const parent = event.tags.find((tag: string[]) => tag[0] === "e");
|
||||
|
||||
if (!subject) return;
|
||||
|
||||
if (parent) {
|
||||
const categoryIndex = this.categories.findIndex(
|
||||
(category) => category.id === parent[1]
|
||||
);
|
||||
if (categoryIndex === -1) return;
|
||||
|
||||
const updatedCategories = [...this.categories];
|
||||
updatedCategories[categoryIndex].topics.push({
|
||||
id: event.id,
|
||||
title: subject[1],
|
||||
author: event.pubkey,
|
||||
created_at: formatDateTime((event.created_at || 0) * 1000),
|
||||
description: event.content,
|
||||
});
|
||||
|
||||
this.categories = updatedCategories;
|
||||
return;
|
||||
}
|
||||
|
||||
this.categories = [
|
||||
...this.categories,
|
||||
{
|
||||
id: event.id,
|
||||
name: subject[1],
|
||||
description: event.content.substring(0, 100),
|
||||
topics: [],
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<arx-breadcrumbs
|
||||
.items=${[{ text: "Home", href: "/" }, { text: "Phora" }]}
|
||||
>
|
||||
</arx-breadcrumbs>
|
||||
|
||||
<arx-phora-button href="/phora/new-category">
|
||||
New Category
|
||||
</arx-phora-button>
|
||||
|
||||
${this.categories.map(
|
||||
(category) => html`
|
||||
<arx-phora-forum-category id=${category.id} title=${category.name}>
|
||||
${category.topics.map(
|
||||
(topic) => html`
|
||||
<arx-phora-forum-topic
|
||||
id=${topic.id}
|
||||
title=${topic.title}
|
||||
description=${topic.description}
|
||||
lastPostBy=${topic.author}
|
||||
lastPostTime=${topic.created_at}
|
||||
>
|
||||
</arx-phora-forum-topic>
|
||||
`
|
||||
)}
|
||||
</arx-phora-forum-category>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
}
|
97
src/routes/Phora/NewCategory.ts
Normal file
97
src/routes/Phora/NewCategory.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
import { LitElement, html, css } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { getSigner, ndk } from "@/ndk";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
|
||||
@customElement("arx-phora-category-creator")
|
||||
export class PhoraCategoryCreator extends LitElement {
|
||||
@state()
|
||||
private newCategory = "";
|
||||
|
||||
@state()
|
||||
private categoryDescription = "";
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
`;
|
||||
|
||||
private async doCreateCategory() {
|
||||
if (this.newCategory.length < 3) {
|
||||
alert("Category name must be at least 3 characters long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.categoryDescription.length < 10) {
|
||||
alert("Category description must be at least 10 characters long");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await getSigner();
|
||||
const event = new NDKEvent(ndk);
|
||||
event.kind = 11;
|
||||
event.tags = [["subject", this.newCategory]];
|
||||
event.content = this.categoryDescription;
|
||||
await event.sign();
|
||||
await event.publish();
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("category-created", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
|
||||
this.newCategory = "";
|
||||
this.categoryDescription = "";
|
||||
} catch (error) {
|
||||
console.error("Failed to create category:", error);
|
||||
alert("Failed to create category");
|
||||
}
|
||||
}
|
||||
|
||||
private handleCategoryInput(e: InputEvent) {
|
||||
this.newCategory = (e.target as HTMLInputElement).value;
|
||||
}
|
||||
|
||||
private handleDescriptionInput(e: InputEvent) {
|
||||
this.categoryDescription = (e.target as HTMLTextAreaElement).value;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<input
|
||||
type="text"
|
||||
placeholder="New Category"
|
||||
.value=${this.newCategory}
|
||||
@input=${this.handleCategoryInput}
|
||||
/>
|
||||
|
||||
<textarea
|
||||
placeholder="Category Description"
|
||||
.value=${this.categoryDescription}
|
||||
@input=${this.handleDescriptionInput}
|
||||
></textarea>
|
||||
|
||||
<arx-phora-button @click=${this.doCreateCategory}>
|
||||
Create
|
||||
</arx-phora-button>
|
||||
`;
|
||||
}
|
||||
}
|
150
src/routes/Phora/NewPost.ts
Normal file
150
src/routes/Phora/NewPost.ts
Normal file
|
@ -0,0 +1,150 @@
|
|||
import { LitElement, html, css } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { getSigner, ndk } from "@/ndk";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
|
||||
@customElement("arx-phora-post-creator")
|
||||
export class PhoraPostCreator extends LitElement {
|
||||
@property({ type: String })
|
||||
topicId = "";
|
||||
|
||||
@state()
|
||||
private postContent = "";
|
||||
|
||||
@state()
|
||||
private isCreating = false;
|
||||
|
||||
@state()
|
||||
private error: string | null = null;
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.topic-id {
|
||||
color: #666;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 0.5rem;
|
||||
resize: vertical;
|
||||
font-family: inherit;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color, #3b82f6);
|
||||
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #dc2626;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
`;
|
||||
|
||||
private async doCreatePost() {
|
||||
if (this.isCreating) return;
|
||||
|
||||
if (this.postContent.length < 10) {
|
||||
this.error = "Post content must be at least 10 characters long";
|
||||
return;
|
||||
}
|
||||
|
||||
this.error = null;
|
||||
this.isCreating = true;
|
||||
|
||||
try {
|
||||
await getSigner();
|
||||
const event = new NDKEvent(ndk);
|
||||
event.kind = 1111;
|
||||
event.tags = [["e", this.topicId]];
|
||||
event.content = this.postContent;
|
||||
await event.sign();
|
||||
await event.publish();
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("post-created", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
postId: event.id,
|
||||
topicId: this.topicId,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// Reset form
|
||||
this.postContent = "";
|
||||
} catch (error) {
|
||||
console.error("Failed to create post:", error);
|
||||
this.error = "Failed to create post. Please try again.";
|
||||
} finally {
|
||||
this.isCreating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private handleContentInput(e: InputEvent) {
|
||||
this.postContent = (e.target as HTMLTextAreaElement).value;
|
||||
if (this.error && this.postContent.length >= 10) {
|
||||
this.error = null;
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="container">
|
||||
<div class="topic-id">Topic ID: ${this.topicId}</div>
|
||||
|
||||
<textarea
|
||||
placeholder="Post. You can use Markdown here."
|
||||
.value=${this.postContent}
|
||||
@input=${this.handleContentInput}
|
||||
?disabled=${this.isCreating}
|
||||
></textarea>
|
||||
|
||||
${this.error ? html` <div class="error">${this.error}</div> ` : null}
|
||||
|
||||
<div class="actions">
|
||||
<arx-phora-button
|
||||
@click=${() => this.dispatchEvent(new CustomEvent("cancel"))}
|
||||
?disabled=${this.isCreating}
|
||||
>
|
||||
Cancel
|
||||
</arx-phora-button>
|
||||
|
||||
<arx-phora-button
|
||||
@click=${this.doCreatePost}
|
||||
?disabled=${this.isCreating}
|
||||
>
|
||||
${this.isCreating ? "Creating..." : "Create"}
|
||||
</arx-phora-button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
143
src/routes/Phora/NewTopic.ts
Normal file
143
src/routes/Phora/NewTopic.ts
Normal file
|
@ -0,0 +1,143 @@
|
|||
import { LitElement, html, css } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { getSigner, ndk } from "@/ndk";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
|
||||
@customElement("arx-phora-topic-creator")
|
||||
export class PhoraTopicCreator extends LitElement {
|
||||
@property({ type: String })
|
||||
categoryId = "";
|
||||
|
||||
@state()
|
||||
private newTopic = "";
|
||||
|
||||
@state()
|
||||
private topicContent = "";
|
||||
|
||||
@state()
|
||||
private isCreating = false;
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 200px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.category-id {
|
||||
color: #666;
|
||||
margin-bottom: 1rem;
|
||||
font-family: monospace;
|
||||
}
|
||||
`;
|
||||
|
||||
private async doCreateTopic() {
|
||||
if (this.isCreating) return;
|
||||
|
||||
if (this.newTopic.length < 3) {
|
||||
alert("Topic title must be at least 3 characters long");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.topicContent.length < 10) {
|
||||
alert("Topic content must be at least 10 characters long");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isCreating = true;
|
||||
|
||||
try {
|
||||
await getSigner();
|
||||
const event = new NDKEvent(ndk);
|
||||
event.kind = 11;
|
||||
event.tags = [
|
||||
["subject", this.newTopic],
|
||||
["e", this.categoryId],
|
||||
];
|
||||
event.content = this.topicContent;
|
||||
await event.sign();
|
||||
await event.publish();
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("topic-created", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
topicId: event.id,
|
||||
categoryId: this.categoryId,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
this.newTopic = "";
|
||||
this.topicContent = "";
|
||||
} catch (error) {
|
||||
console.error("Failed to create topic:", error);
|
||||
alert("Failed to create topic");
|
||||
} finally {
|
||||
this.isCreating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private handleTopicInput(e: InputEvent) {
|
||||
this.newTopic = (e.target as HTMLInputElement).value;
|
||||
}
|
||||
|
||||
private handleContentInput(e: InputEvent) {
|
||||
this.topicContent = (e.target as HTMLTextAreaElement).value;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="category-id">Category ID: ${this.categoryId}</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="New Topic"
|
||||
.value=${this.newTopic}
|
||||
@input=${this.handleTopicInput}
|
||||
?disabled=${this.isCreating}
|
||||
/>
|
||||
|
||||
<textarea
|
||||
placeholder="Topic. You can use Markdown here."
|
||||
.value=${this.topicContent}
|
||||
@input=${this.handleContentInput}
|
||||
?disabled=${this.isCreating}
|
||||
></textarea>
|
||||
|
||||
<div class="button-group">
|
||||
<arx-phora-button
|
||||
@click=${() => this.dispatchEvent(new CustomEvent("cancel"))}
|
||||
?disabled=${this.isCreating}
|
||||
>
|
||||
Cancel
|
||||
</arx-phora-button>
|
||||
|
||||
<arx-phora-button
|
||||
@click=${this.doCreateTopic}
|
||||
?disabled=${this.isCreating}
|
||||
>
|
||||
${this.isCreating ? "Creating..." : "Create"}
|
||||
</arx-phora-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
158
src/routes/Phora/TopicView.ts
Normal file
158
src/routes/Phora/TopicView.ts
Normal file
|
@ -0,0 +1,158 @@
|
|||
import { LitElement, html, css } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { getSigner, ndk } from "@/ndk";
|
||||
import type { NDKSubscription } from "@nostr-dev-kit/ndk";
|
||||
|
||||
import "@components/Breadcrumbs";
|
||||
import "@components/ForumPost";
|
||||
import "@components/PhoraButton";
|
||||
|
||||
interface ForumPost {
|
||||
id: string;
|
||||
npub: string;
|
||||
date: Date;
|
||||
content: string;
|
||||
}
|
||||
|
||||
@customElement("arx-phora-topic-view")
|
||||
export class PhoraTopicView extends LitElement {
|
||||
@property({ type: String })
|
||||
topicId = "";
|
||||
|
||||
@state()
|
||||
override title = "";
|
||||
|
||||
@state()
|
||||
private posts: ForumPost[] = [];
|
||||
|
||||
private subscription: NDKSubscription | undefined;
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.topic {
|
||||
max-width: 1200px;
|
||||
padding: 1em;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 0.5rem;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: [column-1] auto [column-2] 1fr;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: var(--primary);
|
||||
color: oklch(100% 0 0);
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 0.5rem 0.5rem 0 0;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.posts-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
override async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
await this.loadTopic();
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this.subscription) {
|
||||
this.subscription.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private async loadTopic() {
|
||||
try {
|
||||
await getSigner();
|
||||
const event = await ndk.fetchEvent(this.topicId);
|
||||
|
||||
if (!event) {
|
||||
throw new Error("Could not load topic");
|
||||
}
|
||||
|
||||
this.title = event.tags.find((tag) => tag[0] === "subject")?.[1] || "";
|
||||
|
||||
this.posts = [
|
||||
{
|
||||
id: event.id,
|
||||
npub: event.pubkey,
|
||||
date: new Date((event.created_at || 0) * 1000),
|
||||
content: event.content,
|
||||
},
|
||||
];
|
||||
|
||||
// Subscribe to new posts
|
||||
this.subscription = ndk
|
||||
.subscribe({
|
||||
kinds: [1111],
|
||||
"#e": [this.topicId],
|
||||
})
|
||||
.on("event", (event) => {
|
||||
this.posts = [
|
||||
...this.posts,
|
||||
{
|
||||
id: event.id,
|
||||
npub: event.pubkey,
|
||||
date: new Date((event.created_at || 0) * 1000),
|
||||
content: event.content,
|
||||
},
|
||||
];
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to load topic:", error);
|
||||
alert("Could not load topic");
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
const breadcrumbItems = [
|
||||
{ text: "Home", href: "/" },
|
||||
{ text: "Phora", href: "/phora" },
|
||||
{ text: this.title },
|
||||
];
|
||||
|
||||
return html`
|
||||
<arx-breadcrumbs .items=${breadcrumbItems}></arx-breadcrumbs>
|
||||
|
||||
<div class="header">
|
||||
<span>${this.title}</span>
|
||||
</div>
|
||||
|
||||
<div class="posts-container">
|
||||
${this.posts.map(
|
||||
(post) => html`
|
||||
<arx-forum-post
|
||||
class="topic"
|
||||
id=${post.id}
|
||||
.topicId=${this.topicId}
|
||||
.npub=${post.npub}
|
||||
.date=${post.date}
|
||||
.content=${post.content}
|
||||
></arx-forum-post>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<arx-phora-button href="/phora/new-post/${this.topicId}">
|
||||
New Post
|
||||
</arx-phora-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
411
src/routes/Profile.ts
Normal file
411
src/routes/Profile.ts
Normal file
|
@ -0,0 +1,411 @@
|
|||
import { LitElement, html, css } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { when } from "lit/directives/when.js";
|
||||
import { styleMap } from "lit/directives/style-map.js";
|
||||
import type { NDKUserProfile } from "@nostr-dev-kit/ndk";
|
||||
import { getUserProfile } from "../ndk";
|
||||
|
||||
@customElement("arx-profile-route")
|
||||
export class NostrProfile extends LitElement {
|
||||
@property({ type: String })
|
||||
npub = "";
|
||||
|
||||
@state()
|
||||
private profile: NDKUserProfile | undefined;
|
||||
|
||||
@state()
|
||||
private error: string | null = null;
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.banner-container {
|
||||
position: relative;
|
||||
height: 20rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transform: scale(1);
|
||||
transition: transform 700ms;
|
||||
}
|
||||
|
||||
.banner-image:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.banner-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.banner-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgba(0, 0, 0, 0.2),
|
||||
rgba(0, 0, 0, 0.4)
|
||||
);
|
||||
}
|
||||
|
||||
.profile-container {
|
||||
max-width: 64rem;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.profile-container.with-banner {
|
||||
margin-top: -8rem;
|
||||
}
|
||||
|
||||
.profile-container.no-banner {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5rem;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.profile-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.profile-content {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-image-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.profile-image {
|
||||
width: 10rem;
|
||||
height: 10rem;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 4px solid white;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 300ms;
|
||||
}
|
||||
|
||||
.profile-image:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.profile-image-placeholder {
|
||||
width: 10rem;
|
||||
height: 10rem;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(to bottom right, #e5e7eb, #d1d5db);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.profile-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.profile-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.profile-header {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.display-name {
|
||||
font-size: 1.875rem;
|
||||
font-weight: bold;
|
||||
color: #111827;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.verified-icon {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.nip05 {
|
||||
color: #4b5563;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.action-buttons {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.follow-button {
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-radius: 9999px;
|
||||
font-weight: 500;
|
||||
transition: all 300ms;
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.follow-button:hover {
|
||||
background-color: #2563eb;
|
||||
}
|
||||
|
||||
.follow-button.following {
|
||||
background-color: #e5e7eb;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.follow-button.following:hover {
|
||||
background-color: #d1d5db;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
padding: 0.5rem;
|
||||
border-radius: 9999px;
|
||||
background-color: #f3f4f6;
|
||||
transition: background-color 300ms;
|
||||
}
|
||||
|
||||
.copy-button:hover {
|
||||
background-color: #e5e7eb;
|
||||
}
|
||||
|
||||
.copy-icon {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.links-section {
|
||||
display: grid;
|
||||
grid-auto-columns: minmax(300px, 1fr);
|
||||
gap: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.link-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: #f9fafb;
|
||||
transition: background-color 300ms;
|
||||
}
|
||||
|
||||
.link-item:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
.link-icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.link-icon.website {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.link-icon.lightning {
|
||||
color: #eab308;
|
||||
}
|
||||
|
||||
.bio {
|
||||
white-space: pre-line;
|
||||
font-size: 0.9rem;
|
||||
color: #4b5563;
|
||||
margin: 1rem 0;
|
||||
padding-top: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.animate-gradient {
|
||||
background-size: 200% 200%;
|
||||
animation: gradient 15s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
protected override async firstUpdated() {
|
||||
try {
|
||||
this.profile = await getUserProfile(this.npub);
|
||||
} catch (err) {
|
||||
this.error = "Failed to load profile";
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
private get displayName() {
|
||||
if (!this.profile) return this.npub;
|
||||
return (
|
||||
this.profile.displayName || this.profile.name || this.npub.substring(0, 8)
|
||||
);
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this.error) {
|
||||
return html`<arx-error-view .error=${this.error}></arx-error-view>`;
|
||||
}
|
||||
|
||||
if (!this.profile) {
|
||||
return html`<arx-loading-view></arx-loading-view>`;
|
||||
}
|
||||
|
||||
return html`
|
||||
${when(
|
||||
this.profile.banner,
|
||||
() => html`
|
||||
<div class="banner-container">
|
||||
<div class="banner-image">
|
||||
<img src=${this.profile!.banner} alt="Banner" />
|
||||
</div>
|
||||
<div class="banner-overlay"></div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
|
||||
<div
|
||||
class=${this.profile.banner
|
||||
? "profile-container with-banner"
|
||||
: "profile-container no-banner"}
|
||||
>
|
||||
<div class="profile-card">
|
||||
<div class="profile-content">
|
||||
<div class="profile-image-container">
|
||||
${when(
|
||||
this.profile.image,
|
||||
() =>
|
||||
html`<img
|
||||
src=${this.profile!.image}
|
||||
alt="Profile"
|
||||
class="profile-image"
|
||||
/>`,
|
||||
() => html`
|
||||
<div class="profile-image-placeholder">
|
||||
<svg-icon
|
||||
icon="mdi:account"
|
||||
class="placeholder-icon"
|
||||
></svg-icon>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="profile-info">
|
||||
<div class="profile-header">
|
||||
<div>
|
||||
<h1 class="display-name">
|
||||
${this.displayName}
|
||||
${when(
|
||||
this.profile.verified,
|
||||
() => html`
|
||||
<span class="verified-icon">
|
||||
<svg-icon icon="mdi:check-decagram"></svg-icon>
|
||||
</span>
|
||||
`
|
||||
)}
|
||||
</h1>
|
||||
${when(
|
||||
this.profile.nip05,
|
||||
() => html`
|
||||
<p class="nip05">
|
||||
<svg-icon icon="mdi:at"></svg-icon>
|
||||
${this.profile!.nip05}
|
||||
</p>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${when(
|
||||
this.profile.about,
|
||||
() => html` <p class="bio">${this.profile!.about}</p> `
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="links-section">
|
||||
${when(
|
||||
this.profile.website,
|
||||
() => html`
|
||||
<a
|
||||
href=${this.profile!.website}
|
||||
target="_blank"
|
||||
class="link-item"
|
||||
>
|
||||
<svg-icon icon="mdi:web" class="link-icon website"></svg-icon>
|
||||
<span>${this.profile!.website}</span>
|
||||
</a>
|
||||
`
|
||||
)}
|
||||
${when(
|
||||
this.profile.lud16,
|
||||
() => html`
|
||||
<a href="lightning:${this.profile!.lud16}" class="link-item">
|
||||
<svg-icon
|
||||
icon="mdi:lightning-bolt"
|
||||
class="link-icon lightning"
|
||||
></svg-icon>
|
||||
<span>${this.profile!.lud16}</span>
|
||||
</a>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
297
src/routes/router.ts
Normal file
297
src/routes/router.ts
Normal file
|
@ -0,0 +1,297 @@
|
|||
import "@routes/404Page";
|
||||
import "@routes/Home";
|
||||
import "@routes/Profile";
|
||||
import "@routes/Phora/Home";
|
||||
import "@routes/Phora/NewCategory";
|
||||
import "@routes/Phora/NewTopic";
|
||||
import "@routes/Phora/TopicView";
|
||||
import "@routes/Phora/NewPost";
|
||||
import "@components/InitialSetup";
|
||||
|
||||
import { css, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { html, literal, type StaticValue } from "lit/static-html.js";
|
||||
import { spread } from "@open-wc/lit-helpers";
|
||||
|
||||
export interface RouteParams {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface Route {
|
||||
pattern: string;
|
||||
params: RouteParams;
|
||||
component: StaticValue;
|
||||
// component: typeof LitElement | ((params: RouteParams) => typeof LitElement);
|
||||
title?: string;
|
||||
meta?: Record<string, string>;
|
||||
}
|
||||
|
||||
@customElement("arx-eve-router")
|
||||
export default class EveRouter extends LitElement {
|
||||
private static routes: Route[] = [
|
||||
{
|
||||
pattern: "home",
|
||||
params: {},
|
||||
component: literal`arx-eve-home`,
|
||||
},
|
||||
{
|
||||
pattern: "profile/:npub",
|
||||
params: {},
|
||||
component: literal`arx-profile-route`,
|
||||
},
|
||||
{
|
||||
pattern: "phora",
|
||||
params: {},
|
||||
component: literal`arx-phora-home`,
|
||||
},
|
||||
{
|
||||
pattern: "phora/new-category",
|
||||
params: {},
|
||||
component: literal`arx-phora-category-creator`,
|
||||
},
|
||||
{
|
||||
pattern: "phora/new-topic/:categoryId",
|
||||
params: {},
|
||||
component: literal`arx-phora-topic-creator`,
|
||||
},
|
||||
{
|
||||
pattern: "phora/topics/:topicId",
|
||||
params: {},
|
||||
component: literal`arx-phora-topic-view`,
|
||||
},
|
||||
{
|
||||
pattern: "phora/new-post/:topicId",
|
||||
params: {},
|
||||
component: literal`arx-phora-post-creator`,
|
||||
},
|
||||
{
|
||||
pattern: "404",
|
||||
params: {},
|
||||
component: literal`arx-404-page`,
|
||||
},
|
||||
];
|
||||
|
||||
@state()
|
||||
private history: string[] = [];
|
||||
|
||||
@state()
|
||||
private currentIndex = -1;
|
||||
|
||||
@property()
|
||||
private ccnSetup = false;
|
||||
|
||||
private beforeEachGuards: ((to: Route, from: Route | null) => boolean)[] = [];
|
||||
private afterEachHooks: ((to: Route, from: Route | null) => void)[] = [];
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: 1px solid var(--border);
|
||||
margin: 1rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.window {
|
||||
max-width: 1200px;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
`;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.initializeRouter();
|
||||
}
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
override disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener("hashchange", this.handleHashChange);
|
||||
window.removeEventListener("popstate", this.handlePopState);
|
||||
}
|
||||
|
||||
private initializeRouter(): void {
|
||||
const initialPath = this.currentPath;
|
||||
this.history = [initialPath];
|
||||
this.currentIndex = 0;
|
||||
this.navigate(initialPath);
|
||||
}
|
||||
|
||||
private setupEventListeners(): void {
|
||||
window.addEventListener("hashchange", this.handleHashChange.bind(this));
|
||||
window.addEventListener("popstate", this.handlePopState.bind(this));
|
||||
}
|
||||
|
||||
private handleHashChange(): void {
|
||||
const newPath = this.currentPath;
|
||||
if (newPath !== this.history[this.currentIndex]) {
|
||||
this.updateHistory(newPath);
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private handlePopState(): void {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private updateHistory(newPath: string): void {
|
||||
this.history = this.history.slice(0, this.currentIndex + 1);
|
||||
this.history.push(newPath);
|
||||
this.currentIndex = this.history.length - 1;
|
||||
}
|
||||
|
||||
private matchRoute(
|
||||
pattern: string,
|
||||
path: string
|
||||
): { isMatch: boolean; params: RouteParams } {
|
||||
const patternParts = pattern.split("/").filter(Boolean);
|
||||
const pathParts = path.split("/").filter(Boolean);
|
||||
const params: RouteParams = {};
|
||||
|
||||
if (patternParts.length !== pathParts.length) {
|
||||
return { isMatch: false, params };
|
||||
}
|
||||
|
||||
const isMatch = patternParts.every((patternPart, index) => {
|
||||
const pathPart = pathParts[index];
|
||||
if (patternPart.startsWith(":")) {
|
||||
const paramName = patternPart.slice(1);
|
||||
params[paramName] = decodeURIComponent(pathPart);
|
||||
return true;
|
||||
}
|
||||
return patternPart === pathPart;
|
||||
});
|
||||
|
||||
return { isMatch, params };
|
||||
}
|
||||
|
||||
get currentPath(): string {
|
||||
const hash = window.location.hash?.slice(1);
|
||||
return hash === "" ? "home" : hash;
|
||||
}
|
||||
|
||||
get currentRoute(): Route {
|
||||
const route = EveRouter.routes.find((route) => {
|
||||
const { isMatch, params } = this.matchRoute(
|
||||
route.pattern,
|
||||
this.currentPath
|
||||
);
|
||||
if (isMatch) {
|
||||
route.params = params;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return route || this.getNotFoundRoute();
|
||||
}
|
||||
|
||||
private getNotFoundRoute(): Route {
|
||||
return {
|
||||
pattern: "404",
|
||||
params: {},
|
||||
component: literal`arx-404-page`,
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(guard: (to: Route, from: Route | null) => boolean): void {
|
||||
this.beforeEachGuards.push(guard);
|
||||
}
|
||||
|
||||
afterEach(hook: (to: Route, from: Route | null) => void): void {
|
||||
this.afterEachHooks.push(hook);
|
||||
}
|
||||
|
||||
async navigate(path: string): Promise<void> {
|
||||
const from = this.currentRoute;
|
||||
window.location.hash = path;
|
||||
|
||||
const to = this.currentRoute;
|
||||
const canProceed = this.beforeEachGuards.every((guard) => guard(to, from));
|
||||
|
||||
if (canProceed) {
|
||||
this.requestUpdate();
|
||||
for (const hook of this.afterEachHooks) {
|
||||
hook(to, from);
|
||||
}
|
||||
|
||||
if (to.title) {
|
||||
document.title = to.title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goBack(): void {
|
||||
if (this.currentIndex > 0) {
|
||||
this.currentIndex--;
|
||||
this.navigate(this.history[this.currentIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
goForward(): void {
|
||||
if (this.currentIndex < this.history.length - 1) {
|
||||
this.currentIndex++;
|
||||
this.navigate(this.history[this.currentIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
finishSetup() {
|
||||
this.ccnSetup = true;
|
||||
}
|
||||
|
||||
renderSetup() {
|
||||
return html`
|
||||
<div class="window">
|
||||
<div class="window-content">
|
||||
<arx-initial-setup
|
||||
@finish=${() => this.finishSetup()}
|
||||
></arx-initial-setup>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (!this.ccnSetup) return this.renderSetup();
|
||||
return html`
|
||||
<arx-header
|
||||
?canGoBack=${this.currentIndex > 0}
|
||||
?canGoForward=${this.currentIndex < this.history.length - 1}
|
||||
url="eve://${this.currentPath}"
|
||||
@navigate=${(e) => this.navigate(e.detail)}
|
||||
@go-back=${this.goBack}
|
||||
@go-forward=${this.goForward}
|
||||
title="Eve"
|
||||
></arx-header>
|
||||
<div class="window">
|
||||
<div class="window-content">
|
||||
<${this.currentRoute.component}
|
||||
${spread(this.currentRoute.params)}
|
||||
path=${this.currentPath}
|
||||
@navigate=${this.navigate}
|
||||
@go-back=${this.goBack}
|
||||
@go-forward=${this.goForward}
|
||||
></${this.currentRoute.component}>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
443
src/style.scss
Normal file
443
src/style.scss
Normal file
|
@ -0,0 +1,443 @@
|
|||
:root {
|
||||
--primary: oklch(25% 0.09 210);
|
||||
--secondary: oklch(from var(--primary) calc(l + 0.05) calc(c * 1.1) h);
|
||||
--accent: oklch(from var(--primary) calc(l + 0.35) calc(c + 0.15) h);
|
||||
--light: oklch(from var(--primary) 96% calc(c * 0.3) h);
|
||||
--dark: oklch(from var(--primary) 20% calc(c * 0.3) h);
|
||||
--border: oklch(from var(--primary) 80% calc(c * 0.5) h);
|
||||
--success: oklch(from var(--primary) 70% calc(c + 0.2) 142);
|
||||
--warning: oklch(from var(--primary) 85% calc(c + 0.2) 85);
|
||||
--error: oklch(from var(--primary) 65% calc(c + 0.2) 25);
|
||||
|
||||
--shadow-color: oklch(from var(--primary) 20% calc(c * 0.3) h);
|
||||
--shadow-sm: 0 1px 2px rgba(var(--shadow-color), 0.1);
|
||||
--shadow-md: 0 2px 4px oklch(from var(--shadow-color) l c h / 0.1),
|
||||
0 1px 2px oklch(from var(--shadow-color) l c h / 0.05);
|
||||
--shadow-lg: 0 4px 6px oklch(from var(--shadow-color) l c h / 0.1),
|
||||
0 2px 4px oklch(from var(--shadow-color) l c h / 0.06);
|
||||
--shadow-xl: 0 10px 15px oklch(from var(--shadow-color) l c h / 0.1),
|
||||
0 4px 6px oklch(from var(--shadow-color) l c h / 0.08);
|
||||
|
||||
--font-xs: clamp(0.65rem, 0.7vw, 0.8rem);
|
||||
--font-sm: clamp(0.8rem, 0.9vw, 0.9rem);
|
||||
--font-base: clamp(1rem, 1.1vw, 1.125rem);
|
||||
--font-md: clamp(1.333rem, 1.5vw, 1.5rem);
|
||||
--font-lg: clamp(1.777rem, 2vw, 2rem);
|
||||
--font-xl: clamp(2.369rem, 2.7vw, 2.666rem);
|
||||
--font-2xl: clamp(3.157rem, 3.6vw, 3.555rem);
|
||||
--font-3xl: clamp(4.209rem, 4.8vw, 4.74rem);
|
||||
|
||||
--space-xs: clamp(0.5rem, 0.75vw, 0.75rem);
|
||||
--space-sm: clamp(1rem, 1.5vw, 1.5rem);
|
||||
--space-md: clamp(2rem, 3vw, 3rem);
|
||||
--space-lg: clamp(3rem, 4.5vw, 4.5rem);
|
||||
|
||||
--header-height: var(--font-2xl);
|
||||
}
|
||||
|
||||
.window {
|
||||
display: grid;
|
||||
gap: 0;
|
||||
grid-template-rows: var(--header-height) 1fr;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
header {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||
border-bottom-left-radius: 16px;
|
||||
border-bottom-right-radius: 16px;
|
||||
z-index: 999999;
|
||||
margin-bottom: var(--font-2xl);
|
||||
background: var(--primary);
|
||||
height: var(--font-2xl);
|
||||
font-size: var(--header-height);
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 var(--space-md);
|
||||
|
||||
.search-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
input {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||
border-radius: 16px;
|
||||
color: var(--light);
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
cursor: text;
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
padding: var(--space-xs);
|
||||
text-align: center;
|
||||
|
||||
&:not(:focus)::placeholder {
|
||||
color: var(--light);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-buttons {
|
||||
display: flex;
|
||||
gap: var(--space-xs);
|
||||
padding-right: var(--space-xs);
|
||||
|
||||
button {
|
||||
text-decoration: none;
|
||||
color: var(--light);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
padding: var(--space-xs);
|
||||
border-radius: 100%;
|
||||
font-size: var(--font-md);
|
||||
backdrop-filter: blur(10px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: oklch(from var(--accent) l c h / 0.2);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Inter var", system-ui, -apple-system, sans-serif;
|
||||
font-size: var(--font-base);
|
||||
line-height: 1.7;
|
||||
font-feature-settings: "liga" 1, "kern" 1, "calt" 1;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
margin: 0;
|
||||
background: oklch(from var(--primary) 97% calc(c * 0.25) h);
|
||||
color: oklch(from var(--primary) 20% calc(c * 0.1) h);
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: "Inter var", system-ui, sans-serif;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.03em;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: var(--font-2xl);
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--font-xl);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: var(--font-lg);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
section {
|
||||
max-width: 1200px;
|
||||
margin-inline: auto;
|
||||
padding-inline: 1rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
place-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin: var(--space-sm) 0;
|
||||
padding-left: var(--space-md);
|
||||
|
||||
& li {
|
||||
margin-bottom: var(--space-xs);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
& li::marker {
|
||||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "JetBrains Mono", "SF Mono", monospace;
|
||||
font-size: 0.9em;
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 4px;
|
||||
background: oklch(from var(--primary) 97% calc(c * 0.15) h);
|
||||
border: 1px solid oklch(from var(--primary) 90% calc(c * 0.2) h);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
position: relative;
|
||||
padding: var(--space-md);
|
||||
border-radius: 1rem;
|
||||
background: linear-gradient(135deg,
|
||||
oklch(from var(--primary) calc(l + 0.02) calc(c * 0.8) h),
|
||||
oklch(from var(--secondary) calc(l + 0.02) calc(c * 0.8) h));
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: var(--shadow-lg), inset 0 2px 4px oklch(from var(--primary) 100% 0 h / 0.1);
|
||||
color: var(--light);
|
||||
|
||||
& p {
|
||||
font-size: var(--font-md);
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
line-height: 1.6;
|
||||
margin-bottom: var(--space-sm);
|
||||
color: var(--light);
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
font-family: Georgia, serif;
|
||||
opacity: 0.15;
|
||||
content: "❞";
|
||||
bottom: -1rem;
|
||||
right: 1rem;
|
||||
font-size: 5rem;
|
||||
color: var(--light);
|
||||
}
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: var(--font-xs);
|
||||
line-height: var(--leading-normal);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 auto;
|
||||
margin-bottom: var(--space-md);
|
||||
max-width: 80ch;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
background-image: linear-gradient(transparent calc(100% - 2px),
|
||||
var(--accent) 2px);
|
||||
background-size: 0% 100%;
|
||||
background-repeat: no-repeat;
|
||||
transition: background-size 0.3s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 5px;
|
||||
border: 0;
|
||||
margin: var(--space-md);
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
var(--primary) 20%,
|
||||
var(--accent) 50%,
|
||||
var(--primary) 80%,
|
||||
transparent);
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: inherit;
|
||||
top: 2px;
|
||||
filter: blur(2px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.blurfade-enter-active {
|
||||
transition: all 0.5s ease-out;
|
||||
}
|
||||
|
||||
.blurfade-leave-active {
|
||||
transition: all 0.25s cubic-bezier(1, 0.5, 0.8, 1);
|
||||
}
|
||||
|
||||
.blurfade-enter-from,
|
||||
.blurfade-leave-to {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
transform: translate(0, 0) !important;
|
||||
transform-style: preserve-3d;
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
backdrop-filter: blur(1000px);
|
||||
filter: blur(1000px);
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: "Inter var", system-ui, -apple-system, sans-serif;
|
||||
font-size: var(--font-base);
|
||||
line-height: 1.7;
|
||||
color: oklch(from var(--primary) 20% calc(c * 0.1) h);
|
||||
background: oklch(from var(--primary) 97% calc(c * 0.25) h);
|
||||
border: 1px solid oklch(from var(--primary) 90% calc(c * 0.2) h);
|
||||
border-radius: 6px;
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
outline: none;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px oklch(from var(--accent) l c h / 0.2);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: oklch(from var(--primary) 80% calc(c * 0.3) h);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: oklch(from var(--primary) 60% calc(c * 0.2) h);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&[type="submit"],
|
||||
&[type="button"] {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
color: var(--light);
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(135deg, var(--secondary) 0%, var(--accent) 100%);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
&[type="checkbox"],
|
||||
&[type="radio"] {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
accent-color: var(--accent);
|
||||
margin-right: var(--space-xs);
|
||||
}
|
||||
|
||||
&[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
height: 6px;
|
||||
background: oklch(from var(--primary) 90% calc(c * 0.2) h);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--accent);
|
||||
border-radius: 50%;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
height: 6px;
|
||||
background: oklch(from var(--primary) 90% calc(c * 0.2) h);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--accent);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: "Inter var", system-ui, -apple-system, sans-serif;
|
||||
font-size: var(--font-base);
|
||||
line-height: 1.7;
|
||||
color: oklch(from var(--primary) 20% calc(c * 0.1) h);
|
||||
background: oklch(from var(--primary) 97% calc(c * 0.25) h);
|
||||
border: 1px solid oklch(from var(--primary) 90% calc(c * 0.2) h);
|
||||
border-radius: 6px;
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
outline: none;
|
||||
transition: all 0.2s ease-in-out;
|
||||
resize: vertical;
|
||||
min-height: 120px;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px oklch(from var(--accent) l c h / 0.2);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: oklch(from var(--primary) 80% calc(c * 0.3) h);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: oklch(from var(--primary) 60% calc(c * 0.2) h);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
4
src/utils/firstLine.ts
Normal file
4
src/utils/firstLine.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default function firstLine(str: string | undefined): string {
|
||||
if (!str) return '';
|
||||
return str.split('\n')[0];
|
||||
}
|
12
src/utils/formatDateTime.ts
Normal file
12
src/utils/formatDateTime.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
export default function formatDateTime(date: Date | string | number): string {
|
||||
const properDate = new Date(date);
|
||||
return properDate.toLocaleString('en-US', {
|
||||
weekday: 'short',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
}
|
26
src/utils/lastBlockHeight.ts
Normal file
26
src/utils/lastBlockHeight.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
const MEMPOOL_API = 'https://mempool.space/api/blocks/tip/height';
|
||||
|
||||
export interface LastBlockHeight {
|
||||
height: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export async function getLastBlockHeight(): Promise<LastBlockHeight> {
|
||||
try {
|
||||
const response = await fetch(MEMPOOL_API);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
|
||||
const data = await response.text();
|
||||
return {
|
||||
height: Number.parseInt(data, 10),
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
height: 0,
|
||||
error: e instanceof Error ? e.message : 'Failed to fetch block height',
|
||||
};
|
||||
}
|
||||
}
|
11
src/utils/profileUtils.ts
Normal file
11
src/utils/profileUtils.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import type { NDKUserProfile } from "@nostr-dev-kit/ndk";
|
||||
|
||||
interface ProfileProps {
|
||||
profile: NDKUserProfile;
|
||||
npub: string;
|
||||
}
|
||||
|
||||
export const getProfile = ({ profile, npub }: ProfileProps) => ({
|
||||
displayName: profile.displayName || profile.name || npub.substring(0, 8),
|
||||
profileUrl: `/profile/${npub}`,
|
||||
});
|
47
src/utils/satsComma.ts
Normal file
47
src/utils/satsComma.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
export enum SatcommaFormat {
|
||||
BTC = 'BTC',
|
||||
SATS = 'SATS',
|
||||
}
|
||||
|
||||
const SATS_PER_BTC = 100_000_000;
|
||||
|
||||
function formatSatsGroup(sats: number): string {
|
||||
return (
|
||||
sats
|
||||
.toString()
|
||||
.split('')
|
||||
.reverse()
|
||||
.join('')
|
||||
.match(/.{1,3}/g)
|
||||
?.reverse()
|
||||
.map(group => group.split('').reverse().join(''))
|
||||
.join(' ') || '0'
|
||||
);
|
||||
}
|
||||
|
||||
function formatBtcGroup(sats: number): string {
|
||||
const btc = sats / SATS_PER_BTC;
|
||||
const [whole, decimal = ''] = btc.toFixed(8).split('.');
|
||||
const paddedDecimal = decimal.padEnd(8, '0');
|
||||
return `${whole}.${paddedDecimal.slice(0, 2)} ${paddedDecimal
|
||||
.slice(2)
|
||||
.match(/.{1,3}/g)
|
||||
?.join(' ')} BTC`.trim();
|
||||
}
|
||||
|
||||
export default function satsComma(
|
||||
sats: number,
|
||||
format: SatcommaFormat = SatcommaFormat.SATS,
|
||||
): string {
|
||||
if (!Number.isFinite(sats)) {
|
||||
throw new Error('Invalid input: sats must be a finite number');
|
||||
}
|
||||
|
||||
if (sats < 0) {
|
||||
return `-${satsComma(Math.abs(sats), format)}`;
|
||||
}
|
||||
|
||||
return format === SatcommaFormat.SATS
|
||||
? formatSatsGroup(sats)
|
||||
: formatBtcGroup(sats);
|
||||
}
|
35
tsconfig.json
Normal file
35
tsconfig.json
Normal file
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2021",
|
||||
"module": "ES2022",
|
||||
"lib": ["ES2021", "DOM", "DOM.Iterable"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitOverride": true,
|
||||
"useDefineForClassFields": false,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@utils/*": ["./src/utils/*"],
|
||||
"@routes/*": ["./src/routes/*"],
|
||||
"@styles/*": ["./src/styles/*"],
|
||||
"@components/*": ["./src/components/*"],
|
||||
"@widgets/*": ["./src/components/Widgets/*"]
|
||||
}
|
||||
}
|
||||
}
|
39
vite.config.ts
Normal file
39
vite.config.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { fileURLToPath, URL } from "node:url";
|
||||
import { defineConfig } from "vite";
|
||||
import { resolve } from "path";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [],
|
||||
build: {
|
||||
target: "es2024",
|
||||
outDir: "dist",
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: resolve(__dirname, "index.html"),
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@utils": fileURLToPath(new URL("./src/utils", import.meta.url)),
|
||||
"@routes": fileURLToPath(new URL("./src/routes", import.meta.url)),
|
||||
"@styles": fileURLToPath(new URL("./src/styles", import.meta.url)),
|
||||
"@widgets": fileURLToPath(
|
||||
new URL("./src/components/Widgets", import.meta.url)
|
||||
),
|
||||
"@components": fileURLToPath(
|
||||
new URL("./src/components", import.meta.url)
|
||||
),
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
open: true,
|
||||
},
|
||||
optimizeDeps: {
|
||||
esbuildOptions: {
|
||||
target: "es2024",
|
||||
},
|
||||
},
|
||||
});
|
Loading…
Add table
Reference in a new issue