Write on the margins of the internet. Powered by the AT Protocol. margin.at
extension web atproto comments

have fun eva

+7406 -4924
+24
web/.gitignore
··· 1 + # build output 2 + dist/ 3 + # generated types 4 + .astro/ 5 + 6 + # dependencies 7 + node_modules/ 8 + 9 + # logs 10 + npm-debug.log* 11 + yarn-debug.log* 12 + yarn-error.log* 13 + pnpm-debug.log* 14 + 15 + 16 + # environment variables 17 + .env 18 + .env.production 19 + 20 + # macOS-specific files 21 + .DS_Store 22 + 23 + # jetbrains setting folder 24 + .idea/
+23
web/astro.config.mjs
··· 1 + // @ts-check 2 + import { defineConfig } from 'astro/config'; 3 + import react from '@astrojs/react'; 4 + import tailwind from '@astrojs/tailwind'; 5 + 6 + // https://astro.build/config 7 + export default defineConfig({ 8 + integrations: [react(), tailwind()], 9 + vite: { 10 + server: { 11 + proxy: { 12 + '/api': { 13 + target: 'http://localhost:8080', 14 + changeOrigin: true, 15 + }, 16 + '/auth': { 17 + target: 'http://localhost:8080', 18 + changeOrigin: true, 19 + }, 20 + } 21 + } 22 + } 23 + });
+1086
web/bun.lock
··· 1 + { 2 + "lockfileVersion": 1, 3 + "workspaces": { 4 + "": { 5 + "name": "web", 6 + "dependencies": { 7 + "@astrojs/node": "^9.5.2", 8 + "@astrojs/react": "^4.4.2", 9 + "@astrojs/tailwind": "^6.0.2", 10 + "@nanostores/react": "^1.0.0", 11 + "@tailwindcss/vite": "^4.1.18", 12 + "astro": "^5.17.1", 13 + "autoprefixer": "^10.4.24", 14 + "clsx": "^2.1.1", 15 + "date-fns": "^4.1.0", 16 + "lucide-react": "^0.563.0", 17 + "nanostores": "^1.1.0", 18 + "postcss": "^8.5.6", 19 + "react": "^19.2.4", 20 + "react-dom": "^19.2.4", 21 + "react-router-dom": "^7.13.0", 22 + "tailwind-merge": "^3.4.0", 23 + "tailwindcss": "^3.4.19", 24 + }, 25 + "devDependencies": { 26 + "@types/react": "^19.2.11", 27 + "@types/react-dom": "^19.2.3", 28 + "react-icons": "^5.5.0", 29 + "typescript": "^5.9.3", 30 + }, 31 + }, 32 + }, 33 + "packages": { 34 + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], 35 + 36 + "@astrojs/compiler": ["@astrojs/compiler@2.13.0", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="], 37 + 38 + "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.5", "", {}, "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA=="], 39 + 40 + "@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.10", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.19.0", "smol-toml": "^1.5.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A=="], 41 + 42 + "@astrojs/node": ["@astrojs/node@9.5.2", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "send": "^1.2.1", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^5.14.3" } }, "sha512-85/x+FRwbNGDip1TzSGMiak31/6LvBhA8auqd9lLoHaM5XElk+uIfIr3KjJqucDojE0PtiLk1lMSwD9gd3YlGg=="], 43 + 44 + "@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], 45 + 46 + "@astrojs/react": ["@astrojs/react@4.4.2", "", { "dependencies": { "@vitejs/plugin-react": "^4.7.0", "ultrahtml": "^1.6.0", "vite": "^6.4.1" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" } }, "sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ=="], 47 + 48 + "@astrojs/tailwind": ["@astrojs/tailwind@6.0.2", "", { "dependencies": { "autoprefixer": "^10.4.21", "postcss": "^8.5.3", "postcss-load-config": "^4.0.2" }, "peerDependencies": { "astro": "^3.0.0 || ^4.0.0 || ^5.0.0", "tailwindcss": "^3.0.24" } }, "sha512-j3mhLNeugZq6A8dMNXVarUa8K6X9AW+QHU9u3lKNrPLMHhOQ0S7VeWhHwEeJFpEK1BTKEUY1U78VQv2gN6hNGg=="], 49 + 50 + "@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="], 51 + 52 + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], 53 + 54 + "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], 55 + 56 + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "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-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], 57 + 58 + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], 59 + 60 + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], 61 + 62 + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], 63 + 64 + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], 65 + 66 + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], 67 + 68 + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], 69 + 70 + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], 71 + 72 + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], 73 + 74 + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], 75 + 76 + "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], 77 + 78 + "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], 79 + 80 + "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], 81 + 82 + "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], 83 + 84 + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], 85 + 86 + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], 87 + 88 + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], 89 + 90 + "@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="], 91 + 92 + "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], 93 + 94 + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], 95 + 96 + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], 97 + 98 + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], 99 + 100 + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], 101 + 102 + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], 103 + 104 + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], 105 + 106 + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], 107 + 108 + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], 109 + 110 + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], 111 + 112 + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], 113 + 114 + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], 115 + 116 + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], 117 + 118 + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], 119 + 120 + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], 121 + 122 + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], 123 + 124 + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], 125 + 126 + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], 127 + 128 + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], 129 + 130 + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], 131 + 132 + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], 133 + 134 + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], 135 + 136 + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], 137 + 138 + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], 139 + 140 + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], 141 + 142 + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], 143 + 144 + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], 145 + 146 + "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], 147 + 148 + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], 149 + 150 + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], 151 + 152 + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], 153 + 154 + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], 155 + 156 + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], 157 + 158 + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], 159 + 160 + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], 161 + 162 + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], 163 + 164 + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], 165 + 166 + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], 167 + 168 + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], 169 + 170 + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], 171 + 172 + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], 173 + 174 + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], 175 + 176 + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], 177 + 178 + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], 179 + 180 + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], 181 + 182 + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], 183 + 184 + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], 185 + 186 + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], 187 + 188 + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], 189 + 190 + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], 191 + 192 + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], 193 + 194 + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], 195 + 196 + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 197 + 198 + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], 199 + 200 + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 201 + 202 + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 203 + 204 + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 205 + 206 + "@nanostores/react": ["@nanostores/react@1.0.0", "", { "peerDependencies": { "nanostores": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^1.0.0", "react": ">=18.0.0" } }, "sha512-eDduyNy+lbQJMg6XxZ/YssQqF6b4OXMFEZMYKPJCCmBevp1lg0g+4ZRi94qGHirMtsNfAWKNwsjOhC+q1gvC+A=="], 207 + 208 + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 209 + 210 + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], 211 + 212 + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], 213 + 214 + "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], 215 + 216 + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], 217 + 218 + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], 219 + 220 + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], 221 + 222 + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], 223 + 224 + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="], 225 + 226 + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="], 227 + 228 + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="], 229 + 230 + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="], 231 + 232 + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="], 233 + 234 + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="], 235 + 236 + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="], 237 + 238 + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="], 239 + 240 + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="], 241 + 242 + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="], 243 + 244 + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="], 245 + 246 + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="], 247 + 248 + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="], 249 + 250 + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="], 251 + 252 + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="], 253 + 254 + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="], 255 + 256 + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="], 257 + 258 + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="], 259 + 260 + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="], 261 + 262 + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="], 263 + 264 + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="], 265 + 266 + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="], 267 + 268 + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], 269 + 270 + "@shikijs/core": ["@shikijs/core@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA=="], 271 + 272 + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw=="], 273 + 274 + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA=="], 275 + 276 + "@shikijs/langs": ["@shikijs/langs@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA=="], 277 + 278 + "@shikijs/themes": ["@shikijs/themes@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g=="], 279 + 280 + "@shikijs/types": ["@shikijs/types@3.22.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg=="], 281 + 282 + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], 283 + 284 + "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], 285 + 286 + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], 287 + 288 + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], 289 + 290 + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], 291 + 292 + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], 293 + 294 + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], 295 + 296 + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], 297 + 298 + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], 299 + 300 + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], 301 + 302 + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], 303 + 304 + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], 305 + 306 + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], 307 + 308 + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], 309 + 310 + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], 311 + 312 + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.18", "", { "dependencies": { "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "tailwindcss": "4.1.18" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA=="], 313 + 314 + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], 315 + 316 + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], 317 + 318 + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], 319 + 320 + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], 321 + 322 + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], 323 + 324 + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 325 + 326 + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], 327 + 328 + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], 329 + 330 + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], 331 + 332 + "@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="], 333 + 334 + "@types/react": ["@types/react@19.2.11", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-tORuanb01iEzWvMGVGv2ZDhYZVeRMrw453DCSAIn/5yvcSVnMoUMTyf33nQJLahYEnv9xqrTNbgz4qY5EfSh0g=="], 335 + 336 + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], 337 + 338 + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], 339 + 340 + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], 341 + 342 + "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], 343 + 344 + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], 345 + 346 + "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], 347 + 348 + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], 349 + 350 + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], 351 + 352 + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], 353 + 354 + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], 355 + 356 + "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], 357 + 358 + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], 359 + 360 + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], 361 + 362 + "array-iterate": ["array-iterate@2.0.1", "", {}, "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg=="], 363 + 364 + "astro": ["astro@5.17.1", "", { "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", "@astrojs/markdown-remark": "6.3.10", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.1.1", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", "devalue": "^5.6.2", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.3", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-oD3tlxTaVWGq/Wfbqk6gxzVRz98xa/rYlpe+gU2jXJMSD01k6sEDL01ZlT8mVSYB/rMgnvIOfiQQ3BbLdN237A=="], 365 + 366 + "autoprefixer": ["autoprefixer@10.4.24", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001766", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw=="], 367 + 368 + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], 369 + 370 + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], 371 + 372 + "base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="], 373 + 374 + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], 375 + 376 + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], 377 + 378 + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], 379 + 380 + "boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="], 381 + 382 + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], 383 + 384 + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], 385 + 386 + "camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], 387 + 388 + "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], 389 + 390 + "caniuse-lite": ["caniuse-lite@1.0.30001768", "", {}, "sha512-qY3aDRZC5nWPgHUgIB84WL+nySuo19wk0VJpp/XI9T34lrvkyhRvNVOFJOp2kxClQhiFBu+TaUSudf6oa3vkSA=="], 391 + 392 + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], 393 + 394 + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], 395 + 396 + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], 397 + 398 + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], 399 + 400 + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], 401 + 402 + "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], 403 + 404 + "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], 405 + 406 + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], 407 + 408 + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 409 + 410 + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], 411 + 412 + "commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], 413 + 414 + "common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], 415 + 416 + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], 417 + 418 + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], 419 + 420 + "cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], 421 + 422 + "crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], 423 + 424 + "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], 425 + 426 + "css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="], 427 + 428 + "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], 429 + 430 + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], 431 + 432 + "csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="], 433 + 434 + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], 435 + 436 + "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], 437 + 438 + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], 439 + 440 + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], 441 + 442 + "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], 443 + 444 + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], 445 + 446 + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], 447 + 448 + "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], 449 + 450 + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 451 + 452 + "deterministic-object-hash": ["deterministic-object-hash@2.0.2", "", { "dependencies": { "base-64": "^1.0.0" } }, "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ=="], 453 + 454 + "devalue": ["devalue@5.6.2", "", {}, "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg=="], 455 + 456 + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], 457 + 458 + "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], 459 + 460 + "diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], 461 + 462 + "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], 463 + 464 + "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=="], 465 + 466 + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], 467 + 468 + "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], 469 + 470 + "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=="], 471 + 472 + "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], 473 + 474 + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], 475 + 476 + "electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="], 477 + 478 + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], 479 + 480 + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], 481 + 482 + "enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="], 483 + 484 + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], 485 + 486 + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], 487 + 488 + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], 489 + 490 + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], 491 + 492 + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], 493 + 494 + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], 495 + 496 + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], 497 + 498 + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], 499 + 500 + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], 501 + 502 + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], 503 + 504 + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], 505 + 506 + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], 507 + 508 + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 509 + 510 + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], 511 + 512 + "flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], 513 + 514 + "fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], 515 + 516 + "fontkitten": ["fontkitten@1.0.2", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-piJxbLnkD9Xcyi7dWJRnqszEURixe7CrF/efBfbffe2DPyabmuIuqraruY8cXTs19QoM8VJzx47BDRVNXETM7Q=="], 517 + 518 + "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], 519 + 520 + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], 521 + 522 + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 523 + 524 + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], 525 + 526 + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], 527 + 528 + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], 529 + 530 + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], 531 + 532 + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], 533 + 534 + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 535 + 536 + "h3": ["h3@1.15.5", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg=="], 537 + 538 + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 539 + 540 + "hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="], 541 + 542 + "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], 543 + 544 + "hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="], 545 + 546 + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], 547 + 548 + "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="], 549 + 550 + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], 551 + 552 + "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="], 553 + 554 + "hast-util-to-text": ["hast-util-to-text@4.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" } }, "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A=="], 555 + 556 + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], 557 + 558 + "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], 559 + 560 + "html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], 561 + 562 + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], 563 + 564 + "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], 565 + 566 + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], 567 + 568 + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], 569 + 570 + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], 571 + 572 + "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], 573 + 574 + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], 575 + 576 + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], 577 + 578 + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], 579 + 580 + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 581 + 582 + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], 583 + 584 + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 585 + 586 + "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], 587 + 588 + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], 589 + 590 + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], 591 + 592 + "is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], 593 + 594 + "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], 595 + 596 + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], 597 + 598 + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], 599 + 600 + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], 601 + 602 + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], 603 + 604 + "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], 605 + 606 + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], 607 + 608 + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], 609 + 610 + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], 611 + 612 + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], 613 + 614 + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], 615 + 616 + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], 617 + 618 + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], 619 + 620 + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], 621 + 622 + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], 623 + 624 + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], 625 + 626 + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], 627 + 628 + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], 629 + 630 + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], 631 + 632 + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], 633 + 634 + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], 635 + 636 + "lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="], 637 + 638 + "lucide-react": ["lucide-react@0.563.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA=="], 639 + 640 + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 641 + 642 + "magicast": ["magicast@0.5.2", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ=="], 643 + 644 + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], 645 + 646 + "mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="], 647 + 648 + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], 649 + 650 + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="], 651 + 652 + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], 653 + 654 + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], 655 + 656 + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], 657 + 658 + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], 659 + 660 + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], 661 + 662 + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], 663 + 664 + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], 665 + 666 + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], 667 + 668 + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], 669 + 670 + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], 671 + 672 + "mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="], 673 + 674 + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], 675 + 676 + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], 677 + 678 + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], 679 + 680 + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], 681 + 682 + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], 683 + 684 + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], 685 + 686 + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], 687 + 688 + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], 689 + 690 + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], 691 + 692 + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], 693 + 694 + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], 695 + 696 + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], 697 + 698 + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], 699 + 700 + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], 701 + 702 + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], 703 + 704 + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], 705 + 706 + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], 707 + 708 + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], 709 + 710 + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], 711 + 712 + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], 713 + 714 + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], 715 + 716 + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], 717 + 718 + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], 719 + 720 + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], 721 + 722 + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], 723 + 724 + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], 725 + 726 + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], 727 + 728 + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], 729 + 730 + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], 731 + 732 + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], 733 + 734 + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], 735 + 736 + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], 737 + 738 + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], 739 + 740 + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 741 + 742 + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], 743 + 744 + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 745 + 746 + "nanostores": ["nanostores@1.1.0", "", {}, "sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA=="], 747 + 748 + "neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], 749 + 750 + "nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="], 751 + 752 + "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], 753 + 754 + "node-mock-http": ["node-mock-http@1.0.4", "", {}, "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ=="], 755 + 756 + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], 757 + 758 + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], 759 + 760 + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], 761 + 762 + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], 763 + 764 + "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], 765 + 766 + "ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], 767 + 768 + "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], 769 + 770 + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], 771 + 772 + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], 773 + 774 + "oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="], 775 + 776 + "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], 777 + 778 + "p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], 779 + 780 + "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], 781 + 782 + "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], 783 + 784 + "parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="], 785 + 786 + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], 787 + 788 + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], 789 + 790 + "piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="], 791 + 792 + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 793 + 794 + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 795 + 796 + "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], 797 + 798 + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], 799 + 800 + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], 801 + 802 + "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], 803 + 804 + "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="], 805 + 806 + "postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="], 807 + 808 + "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], 809 + 810 + "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], 811 + 812 + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], 813 + 814 + "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], 815 + 816 + "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], 817 + 818 + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], 819 + 820 + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], 821 + 822 + "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="], 823 + 824 + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], 825 + 826 + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], 827 + 828 + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], 829 + 830 + "react-icons": ["react-icons@5.5.0", "", { "peerDependencies": { "react": "*" } }, "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw=="], 831 + 832 + "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], 833 + 834 + "react-router": ["react-router@7.13.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw=="], 835 + 836 + "react-router-dom": ["react-router-dom@7.13.0", "", { "dependencies": { "react-router": "7.13.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g=="], 837 + 838 + "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], 839 + 840 + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], 841 + 842 + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], 843 + 844 + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], 845 + 846 + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], 847 + 848 + "rehype": ["rehype@13.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" } }, "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A=="], 849 + 850 + "rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="], 851 + 852 + "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], 853 + 854 + "rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="], 855 + 856 + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], 857 + 858 + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], 859 + 860 + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], 861 + 862 + "remark-smartypants": ["remark-smartypants@3.0.2", "", { "dependencies": { "retext": "^9.0.0", "retext-smartypants": "^6.0.0", "unified": "^11.0.4", "unist-util-visit": "^5.0.0" } }, "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA=="], 863 + 864 + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], 865 + 866 + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], 867 + 868 + "retext": ["retext@9.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "retext-latin": "^4.0.0", "retext-stringify": "^4.0.0", "unified": "^11.0.0" } }, "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA=="], 869 + 870 + "retext-latin": ["retext-latin@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "parse-latin": "^7.0.0", "unified": "^11.0.0" } }, "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA=="], 871 + 872 + "retext-smartypants": ["retext-smartypants@6.2.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ=="], 873 + 874 + "retext-stringify": ["retext-stringify@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" } }, "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA=="], 875 + 876 + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], 877 + 878 + "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], 879 + 880 + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], 881 + 882 + "sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="], 883 + 884 + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], 885 + 886 + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], 887 + 888 + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], 889 + 890 + "server-destroy": ["server-destroy@1.0.1", "", {}, "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="], 891 + 892 + "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], 893 + 894 + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], 895 + 896 + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], 897 + 898 + "shiki": ["shiki@3.22.0", "", { "dependencies": { "@shikijs/core": "3.22.0", "@shikijs/engine-javascript": "3.22.0", "@shikijs/engine-oniguruma": "3.22.0", "@shikijs/langs": "3.22.0", "@shikijs/themes": "3.22.0", "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g=="], 899 + 900 + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], 901 + 902 + "smol-toml": ["smol-toml@1.6.0", "", {}, "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw=="], 903 + 904 + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 905 + 906 + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], 907 + 908 + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], 909 + 910 + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], 911 + 912 + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], 913 + 914 + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], 915 + 916 + "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], 917 + 918 + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], 919 + 920 + "svgo": ["svgo@4.0.0", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.4.1" }, "bin": "./bin/svgo.js" }, "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw=="], 921 + 922 + "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], 923 + 924 + "tailwindcss": ["tailwindcss@3.4.19", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="], 925 + 926 + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], 927 + 928 + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], 929 + 930 + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], 931 + 932 + "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], 933 + 934 + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], 935 + 936 + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 937 + 938 + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 939 + 940 + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], 941 + 942 + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], 943 + 944 + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], 945 + 946 + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], 947 + 948 + "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], 949 + 950 + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 951 + 952 + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], 953 + 954 + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 955 + 956 + "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], 957 + 958 + "ultrahtml": ["ultrahtml@1.6.0", "", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="], 959 + 960 + "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], 961 + 962 + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], 963 + 964 + "unifont": ["unifont@0.7.3", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA=="], 965 + 966 + "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], 967 + 968 + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], 969 + 970 + "unist-util-modify-children": ["unist-util-modify-children@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "array-iterate": "^2.0.0" } }, "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw=="], 971 + 972 + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], 973 + 974 + "unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], 975 + 976 + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], 977 + 978 + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], 979 + 980 + "unist-util-visit-children": ["unist-util-visit-children@3.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA=="], 981 + 982 + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], 983 + 984 + "unstorage": ["unstorage@1.17.4", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.5", "lru-cache": "^11.2.0", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw=="], 985 + 986 + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], 987 + 988 + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], 989 + 990 + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], 991 + 992 + "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="], 993 + 994 + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], 995 + 996 + "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "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-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], 997 + 998 + "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], 999 + 1000 + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], 1001 + 1002 + "which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="], 1003 + 1004 + "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], 1005 + 1006 + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], 1007 + 1008 + "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], 1009 + 1010 + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], 1011 + 1012 + "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], 1013 + 1014 + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], 1015 + 1016 + "yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], 1017 + 1018 + "yocto-spinner": ["yocto-spinner@0.2.3", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ=="], 1019 + 1020 + "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], 1021 + 1022 + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], 1023 + 1024 + "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], 1025 + 1026 + "zod-to-ts": ["zod-to-ts@1.2.0", "", { "peerDependencies": { "typescript": "^4.9.4 || ^5.0.2", "zod": "^3" } }, "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA=="], 1027 + 1028 + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], 1029 + 1030 + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], 1031 + 1032 + "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], 1033 + 1034 + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], 1035 + 1036 + "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], 1037 + 1038 + "@tailwindcss/node/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 1039 + 1040 + "@tailwindcss/node/tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], 1041 + 1042 + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], 1043 + 1044 + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], 1045 + 1046 + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], 1047 + 1048 + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], 1049 + 1050 + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], 1051 + 1052 + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 1053 + 1054 + "@tailwindcss/vite/tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], 1055 + 1056 + "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 1057 + 1058 + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1059 + 1060 + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 1061 + 1062 + "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], 1063 + 1064 + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], 1065 + 1066 + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 1067 + 1068 + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1069 + 1070 + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1071 + 1072 + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], 1073 + 1074 + "unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], 1075 + 1076 + "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 1077 + 1078 + "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 1079 + 1080 + "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], 1081 + 1082 + "unstorage/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], 1083 + 1084 + "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 1085 + } 1086 + }
-4902
web/package-lock.json
··· 1 - { 2 - "name": "margin-web", 3 - "version": "0.0.1", 4 - "lockfileVersion": 3, 5 - "requires": true, 6 - "packages": { 7 - "": { 8 - "name": "margin-web", 9 - "version": "0.0.1", 10 - "dependencies": { 11 - "date-fns": "^4.1.0", 12 - "lucide-react": "^0.562.0", 13 - "react": "^18.3.1", 14 - "react-dom": "^18.3.1", 15 - "react-icons": "^5.5.0", 16 - "react-router-dom": "^6.28.0" 17 - }, 18 - "devDependencies": { 19 - "@eslint/js": "^9.39.2", 20 - "@types/react": "^18.3.12", 21 - "@types/react-dom": "^18.3.1", 22 - "@vitejs/plugin-react": "^4.3.3", 23 - "eslint": "^9.39.2", 24 - "eslint-plugin-react": "^7.37.5", 25 - "eslint-plugin-react-hooks": "^7.0.1", 26 - "eslint-plugin-react-refresh": "^0.4.26", 27 - "globals": "^17.0.0", 28 - "vite": "^6.0.3" 29 - } 30 - }, 31 - "node_modules/@babel/code-frame": { 32 - "version": "7.27.1", 33 - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", 34 - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", 35 - "dev": true, 36 - "license": "MIT", 37 - "dependencies": { 38 - "@babel/helper-validator-identifier": "^7.27.1", 39 - "js-tokens": "^4.0.0", 40 - "picocolors": "^1.1.1" 41 - }, 42 - "engines": { 43 - "node": ">=6.9.0" 44 - } 45 - }, 46 - "node_modules/@babel/compat-data": { 47 - "version": "7.28.5", 48 - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", 49 - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", 50 - "dev": true, 51 - "license": "MIT", 52 - "engines": { 53 - "node": ">=6.9.0" 54 - } 55 - }, 56 - "node_modules/@babel/core": { 57 - "version": "7.28.5", 58 - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", 59 - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", 60 - "dev": true, 61 - "license": "MIT", 62 - "peer": true, 63 - "dependencies": { 64 - "@babel/code-frame": "^7.27.1", 65 - "@babel/generator": "^7.28.5", 66 - "@babel/helper-compilation-targets": "^7.27.2", 67 - "@babel/helper-module-transforms": "^7.28.3", 68 - "@babel/helpers": "^7.28.4", 69 - "@babel/parser": "^7.28.5", 70 - "@babel/template": "^7.27.2", 71 - "@babel/traverse": "^7.28.5", 72 - "@babel/types": "^7.28.5", 73 - "@jridgewell/remapping": "^2.3.5", 74 - "convert-source-map": "^2.0.0", 75 - "debug": "^4.1.0", 76 - "gensync": "^1.0.0-beta.2", 77 - "json5": "^2.2.3", 78 - "semver": "^6.3.1" 79 - }, 80 - "engines": { 81 - "node": ">=6.9.0" 82 - }, 83 - "funding": { 84 - "type": "opencollective", 85 - "url": "https://opencollective.com/babel" 86 - } 87 - }, 88 - "node_modules/@babel/generator": { 89 - "version": "7.28.5", 90 - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", 91 - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", 92 - "dev": true, 93 - "license": "MIT", 94 - "dependencies": { 95 - "@babel/parser": "^7.28.5", 96 - "@babel/types": "^7.28.5", 97 - "@jridgewell/gen-mapping": "^0.3.12", 98 - "@jridgewell/trace-mapping": "^0.3.28", 99 - "jsesc": "^3.0.2" 100 - }, 101 - "engines": { 102 - "node": ">=6.9.0" 103 - } 104 - }, 105 - "node_modules/@babel/helper-compilation-targets": { 106 - "version": "7.27.2", 107 - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", 108 - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", 109 - "dev": true, 110 - "license": "MIT", 111 - "dependencies": { 112 - "@babel/compat-data": "^7.27.2", 113 - "@babel/helper-validator-option": "^7.27.1", 114 - "browserslist": "^4.24.0", 115 - "lru-cache": "^5.1.1", 116 - "semver": "^6.3.1" 117 - }, 118 - "engines": { 119 - "node": ">=6.9.0" 120 - } 121 - }, 122 - "node_modules/@babel/helper-globals": { 123 - "version": "7.28.0", 124 - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", 125 - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", 126 - "dev": true, 127 - "license": "MIT", 128 - "engines": { 129 - "node": ">=6.9.0" 130 - } 131 - }, 132 - "node_modules/@babel/helper-module-imports": { 133 - "version": "7.27.1", 134 - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", 135 - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", 136 - "dev": true, 137 - "license": "MIT", 138 - "dependencies": { 139 - "@babel/traverse": "^7.27.1", 140 - "@babel/types": "^7.27.1" 141 - }, 142 - "engines": { 143 - "node": ">=6.9.0" 144 - } 145 - }, 146 - "node_modules/@babel/helper-module-transforms": { 147 - "version": "7.28.3", 148 - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", 149 - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", 150 - "dev": true, 151 - "license": "MIT", 152 - "dependencies": { 153 - "@babel/helper-module-imports": "^7.27.1", 154 - "@babel/helper-validator-identifier": "^7.27.1", 155 - "@babel/traverse": "^7.28.3" 156 - }, 157 - "engines": { 158 - "node": ">=6.9.0" 159 - }, 160 - "peerDependencies": { 161 - "@babel/core": "^7.0.0" 162 - } 163 - }, 164 - "node_modules/@babel/helper-plugin-utils": { 165 - "version": "7.27.1", 166 - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", 167 - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", 168 - "dev": true, 169 - "license": "MIT", 170 - "engines": { 171 - "node": ">=6.9.0" 172 - } 173 - }, 174 - "node_modules/@babel/helper-string-parser": { 175 - "version": "7.27.1", 176 - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", 177 - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", 178 - "dev": true, 179 - "license": "MIT", 180 - "engines": { 181 - "node": ">=6.9.0" 182 - } 183 - }, 184 - "node_modules/@babel/helper-validator-identifier": { 185 - "version": "7.28.5", 186 - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", 187 - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", 188 - "dev": true, 189 - "license": "MIT", 190 - "engines": { 191 - "node": ">=6.9.0" 192 - } 193 - }, 194 - "node_modules/@babel/helper-validator-option": { 195 - "version": "7.27.1", 196 - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", 197 - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", 198 - "dev": true, 199 - "license": "MIT", 200 - "engines": { 201 - "node": ">=6.9.0" 202 - } 203 - }, 204 - "node_modules/@babel/helpers": { 205 - "version": "7.28.4", 206 - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", 207 - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", 208 - "dev": true, 209 - "license": "MIT", 210 - "dependencies": { 211 - "@babel/template": "^7.27.2", 212 - "@babel/types": "^7.28.4" 213 - }, 214 - "engines": { 215 - "node": ">=6.9.0" 216 - } 217 - }, 218 - "node_modules/@babel/parser": { 219 - "version": "7.28.5", 220 - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", 221 - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", 222 - "dev": true, 223 - "license": "MIT", 224 - "dependencies": { 225 - "@babel/types": "^7.28.5" 226 - }, 227 - "bin": { 228 - "parser": "bin/babel-parser.js" 229 - }, 230 - "engines": { 231 - "node": ">=6.0.0" 232 - } 233 - }, 234 - "node_modules/@babel/plugin-transform-react-jsx-self": { 235 - "version": "7.27.1", 236 - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", 237 - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", 238 - "dev": true, 239 - "license": "MIT", 240 - "dependencies": { 241 - "@babel/helper-plugin-utils": "^7.27.1" 242 - }, 243 - "engines": { 244 - "node": ">=6.9.0" 245 - }, 246 - "peerDependencies": { 247 - "@babel/core": "^7.0.0-0" 248 - } 249 - }, 250 - "node_modules/@babel/plugin-transform-react-jsx-source": { 251 - "version": "7.27.1", 252 - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", 253 - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", 254 - "dev": true, 255 - "license": "MIT", 256 - "dependencies": { 257 - "@babel/helper-plugin-utils": "^7.27.1" 258 - }, 259 - "engines": { 260 - "node": ">=6.9.0" 261 - }, 262 - "peerDependencies": { 263 - "@babel/core": "^7.0.0-0" 264 - } 265 - }, 266 - "node_modules/@babel/template": { 267 - "version": "7.27.2", 268 - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", 269 - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", 270 - "dev": true, 271 - "license": "MIT", 272 - "dependencies": { 273 - "@babel/code-frame": "^7.27.1", 274 - "@babel/parser": "^7.27.2", 275 - "@babel/types": "^7.27.1" 276 - }, 277 - "engines": { 278 - "node": ">=6.9.0" 279 - } 280 - }, 281 - "node_modules/@babel/traverse": { 282 - "version": "7.28.5", 283 - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", 284 - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", 285 - "dev": true, 286 - "license": "MIT", 287 - "dependencies": { 288 - "@babel/code-frame": "^7.27.1", 289 - "@babel/generator": "^7.28.5", 290 - "@babel/helper-globals": "^7.28.0", 291 - "@babel/parser": "^7.28.5", 292 - "@babel/template": "^7.27.2", 293 - "@babel/types": "^7.28.5", 294 - "debug": "^4.3.1" 295 - }, 296 - "engines": { 297 - "node": ">=6.9.0" 298 - } 299 - }, 300 - "node_modules/@babel/types": { 301 - "version": "7.28.5", 302 - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", 303 - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", 304 - "dev": true, 305 - "license": "MIT", 306 - "dependencies": { 307 - "@babel/helper-string-parser": "^7.27.1", 308 - "@babel/helper-validator-identifier": "^7.28.5" 309 - }, 310 - "engines": { 311 - "node": ">=6.9.0" 312 - } 313 - }, 314 - "node_modules/@esbuild/aix-ppc64": { 315 - "version": "0.25.12", 316 - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", 317 - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", 318 - "cpu": [ 319 - "ppc64" 320 - ], 321 - "dev": true, 322 - "license": "MIT", 323 - "optional": true, 324 - "os": [ 325 - "aix" 326 - ], 327 - "engines": { 328 - "node": ">=18" 329 - } 330 - }, 331 - "node_modules/@esbuild/android-arm": { 332 - "version": "0.25.12", 333 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", 334 - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", 335 - "cpu": [ 336 - "arm" 337 - ], 338 - "dev": true, 339 - "license": "MIT", 340 - "optional": true, 341 - "os": [ 342 - "android" 343 - ], 344 - "engines": { 345 - "node": ">=18" 346 - } 347 - }, 348 - "node_modules/@esbuild/android-arm64": { 349 - "version": "0.25.12", 350 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", 351 - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", 352 - "cpu": [ 353 - "arm64" 354 - ], 355 - "dev": true, 356 - "license": "MIT", 357 - "optional": true, 358 - "os": [ 359 - "android" 360 - ], 361 - "engines": { 362 - "node": ">=18" 363 - } 364 - }, 365 - "node_modules/@esbuild/android-x64": { 366 - "version": "0.25.12", 367 - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", 368 - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", 369 - "cpu": [ 370 - "x64" 371 - ], 372 - "dev": true, 373 - "license": "MIT", 374 - "optional": true, 375 - "os": [ 376 - "android" 377 - ], 378 - "engines": { 379 - "node": ">=18" 380 - } 381 - }, 382 - "node_modules/@esbuild/darwin-arm64": { 383 - "version": "0.25.12", 384 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", 385 - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", 386 - "cpu": [ 387 - "arm64" 388 - ], 389 - "dev": true, 390 - "license": "MIT", 391 - "optional": true, 392 - "os": [ 393 - "darwin" 394 - ], 395 - "engines": { 396 - "node": ">=18" 397 - } 398 - }, 399 - "node_modules/@esbuild/darwin-x64": { 400 - "version": "0.25.12", 401 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", 402 - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", 403 - "cpu": [ 404 - "x64" 405 - ], 406 - "dev": true, 407 - "license": "MIT", 408 - "optional": true, 409 - "os": [ 410 - "darwin" 411 - ], 412 - "engines": { 413 - "node": ">=18" 414 - } 415 - }, 416 - "node_modules/@esbuild/freebsd-arm64": { 417 - "version": "0.25.12", 418 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", 419 - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", 420 - "cpu": [ 421 - "arm64" 422 - ], 423 - "dev": true, 424 - "license": "MIT", 425 - "optional": true, 426 - "os": [ 427 - "freebsd" 428 - ], 429 - "engines": { 430 - "node": ">=18" 431 - } 432 - }, 433 - "node_modules/@esbuild/freebsd-x64": { 434 - "version": "0.25.12", 435 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", 436 - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", 437 - "cpu": [ 438 - "x64" 439 - ], 440 - "dev": true, 441 - "license": "MIT", 442 - "optional": true, 443 - "os": [ 444 - "freebsd" 445 - ], 446 - "engines": { 447 - "node": ">=18" 448 - } 449 - }, 450 - "node_modules/@esbuild/linux-arm": { 451 - "version": "0.25.12", 452 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", 453 - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", 454 - "cpu": [ 455 - "arm" 456 - ], 457 - "dev": true, 458 - "license": "MIT", 459 - "optional": true, 460 - "os": [ 461 - "linux" 462 - ], 463 - "engines": { 464 - "node": ">=18" 465 - } 466 - }, 467 - "node_modules/@esbuild/linux-arm64": { 468 - "version": "0.25.12", 469 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", 470 - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", 471 - "cpu": [ 472 - "arm64" 473 - ], 474 - "dev": true, 475 - "license": "MIT", 476 - "optional": true, 477 - "os": [ 478 - "linux" 479 - ], 480 - "engines": { 481 - "node": ">=18" 482 - } 483 - }, 484 - "node_modules/@esbuild/linux-ia32": { 485 - "version": "0.25.12", 486 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", 487 - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", 488 - "cpu": [ 489 - "ia32" 490 - ], 491 - "dev": true, 492 - "license": "MIT", 493 - "optional": true, 494 - "os": [ 495 - "linux" 496 - ], 497 - "engines": { 498 - "node": ">=18" 499 - } 500 - }, 501 - "node_modules/@esbuild/linux-loong64": { 502 - "version": "0.25.12", 503 - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", 504 - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", 505 - "cpu": [ 506 - "loong64" 507 - ], 508 - "dev": true, 509 - "license": "MIT", 510 - "optional": true, 511 - "os": [ 512 - "linux" 513 - ], 514 - "engines": { 515 - "node": ">=18" 516 - } 517 - }, 518 - "node_modules/@esbuild/linux-mips64el": { 519 - "version": "0.25.12", 520 - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", 521 - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", 522 - "cpu": [ 523 - "mips64el" 524 - ], 525 - "dev": true, 526 - "license": "MIT", 527 - "optional": true, 528 - "os": [ 529 - "linux" 530 - ], 531 - "engines": { 532 - "node": ">=18" 533 - } 534 - }, 535 - "node_modules/@esbuild/linux-ppc64": { 536 - "version": "0.25.12", 537 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", 538 - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", 539 - "cpu": [ 540 - "ppc64" 541 - ], 542 - "dev": true, 543 - "license": "MIT", 544 - "optional": true, 545 - "os": [ 546 - "linux" 547 - ], 548 - "engines": { 549 - "node": ">=18" 550 - } 551 - }, 552 - "node_modules/@esbuild/linux-riscv64": { 553 - "version": "0.25.12", 554 - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", 555 - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", 556 - "cpu": [ 557 - "riscv64" 558 - ], 559 - "dev": true, 560 - "license": "MIT", 561 - "optional": true, 562 - "os": [ 563 - "linux" 564 - ], 565 - "engines": { 566 - "node": ">=18" 567 - } 568 - }, 569 - "node_modules/@esbuild/linux-s390x": { 570 - "version": "0.25.12", 571 - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", 572 - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", 573 - "cpu": [ 574 - "s390x" 575 - ], 576 - "dev": true, 577 - "license": "MIT", 578 - "optional": true, 579 - "os": [ 580 - "linux" 581 - ], 582 - "engines": { 583 - "node": ">=18" 584 - } 585 - }, 586 - "node_modules/@esbuild/linux-x64": { 587 - "version": "0.25.12", 588 - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", 589 - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", 590 - "cpu": [ 591 - "x64" 592 - ], 593 - "dev": true, 594 - "license": "MIT", 595 - "optional": true, 596 - "os": [ 597 - "linux" 598 - ], 599 - "engines": { 600 - "node": ">=18" 601 - } 602 - }, 603 - "node_modules/@esbuild/netbsd-arm64": { 604 - "version": "0.25.12", 605 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", 606 - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", 607 - "cpu": [ 608 - "arm64" 609 - ], 610 - "dev": true, 611 - "license": "MIT", 612 - "optional": true, 613 - "os": [ 614 - "netbsd" 615 - ], 616 - "engines": { 617 - "node": ">=18" 618 - } 619 - }, 620 - "node_modules/@esbuild/netbsd-x64": { 621 - "version": "0.25.12", 622 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", 623 - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", 624 - "cpu": [ 625 - "x64" 626 - ], 627 - "dev": true, 628 - "license": "MIT", 629 - "optional": true, 630 - "os": [ 631 - "netbsd" 632 - ], 633 - "engines": { 634 - "node": ">=18" 635 - } 636 - }, 637 - "node_modules/@esbuild/openbsd-arm64": { 638 - "version": "0.25.12", 639 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", 640 - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", 641 - "cpu": [ 642 - "arm64" 643 - ], 644 - "dev": true, 645 - "license": "MIT", 646 - "optional": true, 647 - "os": [ 648 - "openbsd" 649 - ], 650 - "engines": { 651 - "node": ">=18" 652 - } 653 - }, 654 - "node_modules/@esbuild/openbsd-x64": { 655 - "version": "0.25.12", 656 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", 657 - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", 658 - "cpu": [ 659 - "x64" 660 - ], 661 - "dev": true, 662 - "license": "MIT", 663 - "optional": true, 664 - "os": [ 665 - "openbsd" 666 - ], 667 - "engines": { 668 - "node": ">=18" 669 - } 670 - }, 671 - "node_modules/@esbuild/openharmony-arm64": { 672 - "version": "0.25.12", 673 - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", 674 - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", 675 - "cpu": [ 676 - "arm64" 677 - ], 678 - "dev": true, 679 - "license": "MIT", 680 - "optional": true, 681 - "os": [ 682 - "openharmony" 683 - ], 684 - "engines": { 685 - "node": ">=18" 686 - } 687 - }, 688 - "node_modules/@esbuild/sunos-x64": { 689 - "version": "0.25.12", 690 - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", 691 - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", 692 - "cpu": [ 693 - "x64" 694 - ], 695 - "dev": true, 696 - "license": "MIT", 697 - "optional": true, 698 - "os": [ 699 - "sunos" 700 - ], 701 - "engines": { 702 - "node": ">=18" 703 - } 704 - }, 705 - "node_modules/@esbuild/win32-arm64": { 706 - "version": "0.25.12", 707 - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", 708 - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", 709 - "cpu": [ 710 - "arm64" 711 - ], 712 - "dev": true, 713 - "license": "MIT", 714 - "optional": true, 715 - "os": [ 716 - "win32" 717 - ], 718 - "engines": { 719 - "node": ">=18" 720 - } 721 - }, 722 - "node_modules/@esbuild/win32-ia32": { 723 - "version": "0.25.12", 724 - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", 725 - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", 726 - "cpu": [ 727 - "ia32" 728 - ], 729 - "dev": true, 730 - "license": "MIT", 731 - "optional": true, 732 - "os": [ 733 - "win32" 734 - ], 735 - "engines": { 736 - "node": ">=18" 737 - } 738 - }, 739 - "node_modules/@esbuild/win32-x64": { 740 - "version": "0.25.12", 741 - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", 742 - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", 743 - "cpu": [ 744 - "x64" 745 - ], 746 - "dev": true, 747 - "license": "MIT", 748 - "optional": true, 749 - "os": [ 750 - "win32" 751 - ], 752 - "engines": { 753 - "node": ">=18" 754 - } 755 - }, 756 - "node_modules/@eslint-community/eslint-utils": { 757 - "version": "4.9.1", 758 - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", 759 - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", 760 - "dev": true, 761 - "license": "MIT", 762 - "dependencies": { 763 - "eslint-visitor-keys": "^3.4.3" 764 - }, 765 - "engines": { 766 - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 767 - }, 768 - "funding": { 769 - "url": "https://opencollective.com/eslint" 770 - }, 771 - "peerDependencies": { 772 - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 773 - } 774 - }, 775 - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { 776 - "version": "3.4.3", 777 - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 778 - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 779 - "dev": true, 780 - "license": "Apache-2.0", 781 - "engines": { 782 - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 783 - }, 784 - "funding": { 785 - "url": "https://opencollective.com/eslint" 786 - } 787 - }, 788 - "node_modules/@eslint-community/regexpp": { 789 - "version": "4.12.2", 790 - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", 791 - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", 792 - "dev": true, 793 - "license": "MIT", 794 - "engines": { 795 - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 796 - } 797 - }, 798 - "node_modules/@eslint/config-array": { 799 - "version": "0.21.1", 800 - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", 801 - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", 802 - "dev": true, 803 - "license": "Apache-2.0", 804 - "dependencies": { 805 - "@eslint/object-schema": "^2.1.7", 806 - "debug": "^4.3.1", 807 - "minimatch": "^3.1.2" 808 - }, 809 - "engines": { 810 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 811 - } 812 - }, 813 - "node_modules/@eslint/config-helpers": { 814 - "version": "0.4.2", 815 - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", 816 - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", 817 - "dev": true, 818 - "license": "Apache-2.0", 819 - "dependencies": { 820 - "@eslint/core": "^0.17.0" 821 - }, 822 - "engines": { 823 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 824 - } 825 - }, 826 - "node_modules/@eslint/core": { 827 - "version": "0.17.0", 828 - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", 829 - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", 830 - "dev": true, 831 - "license": "Apache-2.0", 832 - "dependencies": { 833 - "@types/json-schema": "^7.0.15" 834 - }, 835 - "engines": { 836 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 837 - } 838 - }, 839 - "node_modules/@eslint/eslintrc": { 840 - "version": "3.3.3", 841 - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", 842 - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", 843 - "dev": true, 844 - "license": "MIT", 845 - "dependencies": { 846 - "ajv": "^6.12.4", 847 - "debug": "^4.3.2", 848 - "espree": "^10.0.1", 849 - "globals": "^14.0.0", 850 - "ignore": "^5.2.0", 851 - "import-fresh": "^3.2.1", 852 - "js-yaml": "^4.1.1", 853 - "minimatch": "^3.1.2", 854 - "strip-json-comments": "^3.1.1" 855 - }, 856 - "engines": { 857 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 858 - }, 859 - "funding": { 860 - "url": "https://opencollective.com/eslint" 861 - } 862 - }, 863 - "node_modules/@eslint/eslintrc/node_modules/globals": { 864 - "version": "14.0.0", 865 - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", 866 - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", 867 - "dev": true, 868 - "license": "MIT", 869 - "engines": { 870 - "node": ">=18" 871 - }, 872 - "funding": { 873 - "url": "https://github.com/sponsors/sindresorhus" 874 - } 875 - }, 876 - "node_modules/@eslint/js": { 877 - "version": "9.39.2", 878 - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", 879 - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", 880 - "dev": true, 881 - "license": "MIT", 882 - "engines": { 883 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 884 - }, 885 - "funding": { 886 - "url": "https://eslint.org/donate" 887 - } 888 - }, 889 - "node_modules/@eslint/object-schema": { 890 - "version": "2.1.7", 891 - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", 892 - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", 893 - "dev": true, 894 - "license": "Apache-2.0", 895 - "engines": { 896 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 897 - } 898 - }, 899 - "node_modules/@eslint/plugin-kit": { 900 - "version": "0.4.1", 901 - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", 902 - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", 903 - "dev": true, 904 - "license": "Apache-2.0", 905 - "dependencies": { 906 - "@eslint/core": "^0.17.0", 907 - "levn": "^0.4.1" 908 - }, 909 - "engines": { 910 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 911 - } 912 - }, 913 - "node_modules/@humanfs/core": { 914 - "version": "0.19.1", 915 - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", 916 - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", 917 - "dev": true, 918 - "license": "Apache-2.0", 919 - "engines": { 920 - "node": ">=18.18.0" 921 - } 922 - }, 923 - "node_modules/@humanfs/node": { 924 - "version": "0.16.7", 925 - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", 926 - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", 927 - "dev": true, 928 - "license": "Apache-2.0", 929 - "dependencies": { 930 - "@humanfs/core": "^0.19.1", 931 - "@humanwhocodes/retry": "^0.4.0" 932 - }, 933 - "engines": { 934 - "node": ">=18.18.0" 935 - } 936 - }, 937 - "node_modules/@humanwhocodes/module-importer": { 938 - "version": "1.0.1", 939 - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 940 - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 941 - "dev": true, 942 - "license": "Apache-2.0", 943 - "engines": { 944 - "node": ">=12.22" 945 - }, 946 - "funding": { 947 - "type": "github", 948 - "url": "https://github.com/sponsors/nzakas" 949 - } 950 - }, 951 - "node_modules/@humanwhocodes/retry": { 952 - "version": "0.4.3", 953 - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", 954 - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", 955 - "dev": true, 956 - "license": "Apache-2.0", 957 - "engines": { 958 - "node": ">=18.18" 959 - }, 960 - "funding": { 961 - "type": "github", 962 - "url": "https://github.com/sponsors/nzakas" 963 - } 964 - }, 965 - "node_modules/@jridgewell/gen-mapping": { 966 - "version": "0.3.13", 967 - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", 968 - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", 969 - "dev": true, 970 - "license": "MIT", 971 - "dependencies": { 972 - "@jridgewell/sourcemap-codec": "^1.5.0", 973 - "@jridgewell/trace-mapping": "^0.3.24" 974 - } 975 - }, 976 - "node_modules/@jridgewell/remapping": { 977 - "version": "2.3.5", 978 - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", 979 - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", 980 - "dev": true, 981 - "license": "MIT", 982 - "dependencies": { 983 - "@jridgewell/gen-mapping": "^0.3.5", 984 - "@jridgewell/trace-mapping": "^0.3.24" 985 - } 986 - }, 987 - "node_modules/@jridgewell/resolve-uri": { 988 - "version": "3.1.2", 989 - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 990 - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 991 - "dev": true, 992 - "license": "MIT", 993 - "engines": { 994 - "node": ">=6.0.0" 995 - } 996 - }, 997 - "node_modules/@jridgewell/sourcemap-codec": { 998 - "version": "1.5.5", 999 - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", 1000 - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", 1001 - "dev": true, 1002 - "license": "MIT" 1003 - }, 1004 - "node_modules/@jridgewell/trace-mapping": { 1005 - "version": "0.3.31", 1006 - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", 1007 - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", 1008 - "dev": true, 1009 - "license": "MIT", 1010 - "dependencies": { 1011 - "@jridgewell/resolve-uri": "^3.1.0", 1012 - "@jridgewell/sourcemap-codec": "^1.4.14" 1013 - } 1014 - }, 1015 - "node_modules/@remix-run/router": { 1016 - "version": "1.23.2", 1017 - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", 1018 - "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", 1019 - "license": "MIT", 1020 - "engines": { 1021 - "node": ">=14.0.0" 1022 - } 1023 - }, 1024 - "node_modules/@rolldown/pluginutils": { 1025 - "version": "1.0.0-beta.27", 1026 - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", 1027 - "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", 1028 - "dev": true, 1029 - "license": "MIT" 1030 - }, 1031 - "node_modules/@rollup/rollup-android-arm-eabi": { 1032 - "version": "4.54.0", 1033 - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz", 1034 - "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==", 1035 - "cpu": [ 1036 - "arm" 1037 - ], 1038 - "dev": true, 1039 - "license": "MIT", 1040 - "optional": true, 1041 - "os": [ 1042 - "android" 1043 - ] 1044 - }, 1045 - "node_modules/@rollup/rollup-android-arm64": { 1046 - "version": "4.54.0", 1047 - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz", 1048 - "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==", 1049 - "cpu": [ 1050 - "arm64" 1051 - ], 1052 - "dev": true, 1053 - "license": "MIT", 1054 - "optional": true, 1055 - "os": [ 1056 - "android" 1057 - ] 1058 - }, 1059 - "node_modules/@rollup/rollup-darwin-arm64": { 1060 - "version": "4.54.0", 1061 - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", 1062 - "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", 1063 - "cpu": [ 1064 - "arm64" 1065 - ], 1066 - "dev": true, 1067 - "license": "MIT", 1068 - "optional": true, 1069 - "os": [ 1070 - "darwin" 1071 - ] 1072 - }, 1073 - "node_modules/@rollup/rollup-darwin-x64": { 1074 - "version": "4.54.0", 1075 - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz", 1076 - "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==", 1077 - "cpu": [ 1078 - "x64" 1079 - ], 1080 - "dev": true, 1081 - "license": "MIT", 1082 - "optional": true, 1083 - "os": [ 1084 - "darwin" 1085 - ] 1086 - }, 1087 - "node_modules/@rollup/rollup-freebsd-arm64": { 1088 - "version": "4.54.0", 1089 - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz", 1090 - "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==", 1091 - "cpu": [ 1092 - "arm64" 1093 - ], 1094 - "dev": true, 1095 - "license": "MIT", 1096 - "optional": true, 1097 - "os": [ 1098 - "freebsd" 1099 - ] 1100 - }, 1101 - "node_modules/@rollup/rollup-freebsd-x64": { 1102 - "version": "4.54.0", 1103 - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz", 1104 - "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==", 1105 - "cpu": [ 1106 - "x64" 1107 - ], 1108 - "dev": true, 1109 - "license": "MIT", 1110 - "optional": true, 1111 - "os": [ 1112 - "freebsd" 1113 - ] 1114 - }, 1115 - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 1116 - "version": "4.54.0", 1117 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz", 1118 - "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==", 1119 - "cpu": [ 1120 - "arm" 1121 - ], 1122 - "dev": true, 1123 - "license": "MIT", 1124 - "optional": true, 1125 - "os": [ 1126 - "linux" 1127 - ] 1128 - }, 1129 - "node_modules/@rollup/rollup-linux-arm-musleabihf": { 1130 - "version": "4.54.0", 1131 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz", 1132 - "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==", 1133 - "cpu": [ 1134 - "arm" 1135 - ], 1136 - "dev": true, 1137 - "license": "MIT", 1138 - "optional": true, 1139 - "os": [ 1140 - "linux" 1141 - ] 1142 - }, 1143 - "node_modules/@rollup/rollup-linux-arm64-gnu": { 1144 - "version": "4.54.0", 1145 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz", 1146 - "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==", 1147 - "cpu": [ 1148 - "arm64" 1149 - ], 1150 - "dev": true, 1151 - "license": "MIT", 1152 - "optional": true, 1153 - "os": [ 1154 - "linux" 1155 - ] 1156 - }, 1157 - "node_modules/@rollup/rollup-linux-arm64-musl": { 1158 - "version": "4.54.0", 1159 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz", 1160 - "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==", 1161 - "cpu": [ 1162 - "arm64" 1163 - ], 1164 - "dev": true, 1165 - "license": "MIT", 1166 - "optional": true, 1167 - "os": [ 1168 - "linux" 1169 - ] 1170 - }, 1171 - "node_modules/@rollup/rollup-linux-loong64-gnu": { 1172 - "version": "4.54.0", 1173 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz", 1174 - "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==", 1175 - "cpu": [ 1176 - "loong64" 1177 - ], 1178 - "dev": true, 1179 - "license": "MIT", 1180 - "optional": true, 1181 - "os": [ 1182 - "linux" 1183 - ] 1184 - }, 1185 - "node_modules/@rollup/rollup-linux-ppc64-gnu": { 1186 - "version": "4.54.0", 1187 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz", 1188 - "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==", 1189 - "cpu": [ 1190 - "ppc64" 1191 - ], 1192 - "dev": true, 1193 - "license": "MIT", 1194 - "optional": true, 1195 - "os": [ 1196 - "linux" 1197 - ] 1198 - }, 1199 - "node_modules/@rollup/rollup-linux-riscv64-gnu": { 1200 - "version": "4.54.0", 1201 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz", 1202 - "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==", 1203 - "cpu": [ 1204 - "riscv64" 1205 - ], 1206 - "dev": true, 1207 - "license": "MIT", 1208 - "optional": true, 1209 - "os": [ 1210 - "linux" 1211 - ] 1212 - }, 1213 - "node_modules/@rollup/rollup-linux-riscv64-musl": { 1214 - "version": "4.54.0", 1215 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz", 1216 - "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==", 1217 - "cpu": [ 1218 - "riscv64" 1219 - ], 1220 - "dev": true, 1221 - "license": "MIT", 1222 - "optional": true, 1223 - "os": [ 1224 - "linux" 1225 - ] 1226 - }, 1227 - "node_modules/@rollup/rollup-linux-s390x-gnu": { 1228 - "version": "4.54.0", 1229 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz", 1230 - "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==", 1231 - "cpu": [ 1232 - "s390x" 1233 - ], 1234 - "dev": true, 1235 - "license": "MIT", 1236 - "optional": true, 1237 - "os": [ 1238 - "linux" 1239 - ] 1240 - }, 1241 - "node_modules/@rollup/rollup-linux-x64-gnu": { 1242 - "version": "4.54.0", 1243 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz", 1244 - "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==", 1245 - "cpu": [ 1246 - "x64" 1247 - ], 1248 - "dev": true, 1249 - "license": "MIT", 1250 - "optional": true, 1251 - "os": [ 1252 - "linux" 1253 - ] 1254 - }, 1255 - "node_modules/@rollup/rollup-linux-x64-musl": { 1256 - "version": "4.54.0", 1257 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz", 1258 - "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==", 1259 - "cpu": [ 1260 - "x64" 1261 - ], 1262 - "dev": true, 1263 - "license": "MIT", 1264 - "optional": true, 1265 - "os": [ 1266 - "linux" 1267 - ] 1268 - }, 1269 - "node_modules/@rollup/rollup-openharmony-arm64": { 1270 - "version": "4.54.0", 1271 - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz", 1272 - "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==", 1273 - "cpu": [ 1274 - "arm64" 1275 - ], 1276 - "dev": true, 1277 - "license": "MIT", 1278 - "optional": true, 1279 - "os": [ 1280 - "openharmony" 1281 - ] 1282 - }, 1283 - "node_modules/@rollup/rollup-win32-arm64-msvc": { 1284 - "version": "4.54.0", 1285 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz", 1286 - "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==", 1287 - "cpu": [ 1288 - "arm64" 1289 - ], 1290 - "dev": true, 1291 - "license": "MIT", 1292 - "optional": true, 1293 - "os": [ 1294 - "win32" 1295 - ] 1296 - }, 1297 - "node_modules/@rollup/rollup-win32-ia32-msvc": { 1298 - "version": "4.54.0", 1299 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz", 1300 - "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==", 1301 - "cpu": [ 1302 - "ia32" 1303 - ], 1304 - "dev": true, 1305 - "license": "MIT", 1306 - "optional": true, 1307 - "os": [ 1308 - "win32" 1309 - ] 1310 - }, 1311 - "node_modules/@rollup/rollup-win32-x64-gnu": { 1312 - "version": "4.54.0", 1313 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz", 1314 - "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==", 1315 - "cpu": [ 1316 - "x64" 1317 - ], 1318 - "dev": true, 1319 - "license": "MIT", 1320 - "optional": true, 1321 - "os": [ 1322 - "win32" 1323 - ] 1324 - }, 1325 - "node_modules/@rollup/rollup-win32-x64-msvc": { 1326 - "version": "4.54.0", 1327 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz", 1328 - "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==", 1329 - "cpu": [ 1330 - "x64" 1331 - ], 1332 - "dev": true, 1333 - "license": "MIT", 1334 - "optional": true, 1335 - "os": [ 1336 - "win32" 1337 - ] 1338 - }, 1339 - "node_modules/@types/babel__core": { 1340 - "version": "7.20.5", 1341 - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", 1342 - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", 1343 - "dev": true, 1344 - "license": "MIT", 1345 - "dependencies": { 1346 - "@babel/parser": "^7.20.7", 1347 - "@babel/types": "^7.20.7", 1348 - "@types/babel__generator": "*", 1349 - "@types/babel__template": "*", 1350 - "@types/babel__traverse": "*" 1351 - } 1352 - }, 1353 - "node_modules/@types/babel__generator": { 1354 - "version": "7.27.0", 1355 - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", 1356 - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", 1357 - "dev": true, 1358 - "license": "MIT", 1359 - "dependencies": { 1360 - "@babel/types": "^7.0.0" 1361 - } 1362 - }, 1363 - "node_modules/@types/babel__template": { 1364 - "version": "7.4.4", 1365 - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", 1366 - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", 1367 - "dev": true, 1368 - "license": "MIT", 1369 - "dependencies": { 1370 - "@babel/parser": "^7.1.0", 1371 - "@babel/types": "^7.0.0" 1372 - } 1373 - }, 1374 - "node_modules/@types/babel__traverse": { 1375 - "version": "7.28.0", 1376 - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", 1377 - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", 1378 - "dev": true, 1379 - "license": "MIT", 1380 - "dependencies": { 1381 - "@babel/types": "^7.28.2" 1382 - } 1383 - }, 1384 - "node_modules/@types/estree": { 1385 - "version": "1.0.8", 1386 - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", 1387 - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", 1388 - "dev": true, 1389 - "license": "MIT" 1390 - }, 1391 - "node_modules/@types/json-schema": { 1392 - "version": "7.0.15", 1393 - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 1394 - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 1395 - "dev": true, 1396 - "license": "MIT" 1397 - }, 1398 - "node_modules/@types/prop-types": { 1399 - "version": "15.7.15", 1400 - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", 1401 - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", 1402 - "dev": true, 1403 - "license": "MIT" 1404 - }, 1405 - "node_modules/@types/react": { 1406 - "version": "18.3.27", 1407 - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", 1408 - "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", 1409 - "dev": true, 1410 - "license": "MIT", 1411 - "peer": true, 1412 - "dependencies": { 1413 - "@types/prop-types": "*", 1414 - "csstype": "^3.2.2" 1415 - } 1416 - }, 1417 - "node_modules/@types/react-dom": { 1418 - "version": "18.3.7", 1419 - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", 1420 - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", 1421 - "dev": true, 1422 - "license": "MIT", 1423 - "peerDependencies": { 1424 - "@types/react": "^18.0.0" 1425 - } 1426 - }, 1427 - "node_modules/@vitejs/plugin-react": { 1428 - "version": "4.7.0", 1429 - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", 1430 - "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", 1431 - "dev": true, 1432 - "license": "MIT", 1433 - "dependencies": { 1434 - "@babel/core": "^7.28.0", 1435 - "@babel/plugin-transform-react-jsx-self": "^7.27.1", 1436 - "@babel/plugin-transform-react-jsx-source": "^7.27.1", 1437 - "@rolldown/pluginutils": "1.0.0-beta.27", 1438 - "@types/babel__core": "^7.20.5", 1439 - "react-refresh": "^0.17.0" 1440 - }, 1441 - "engines": { 1442 - "node": "^14.18.0 || >=16.0.0" 1443 - }, 1444 - "peerDependencies": { 1445 - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" 1446 - } 1447 - }, 1448 - "node_modules/acorn": { 1449 - "version": "8.15.0", 1450 - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", 1451 - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 1452 - "dev": true, 1453 - "license": "MIT", 1454 - "peer": true, 1455 - "bin": { 1456 - "acorn": "bin/acorn" 1457 - }, 1458 - "engines": { 1459 - "node": ">=0.4.0" 1460 - } 1461 - }, 1462 - "node_modules/acorn-jsx": { 1463 - "version": "5.3.2", 1464 - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 1465 - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 1466 - "dev": true, 1467 - "license": "MIT", 1468 - "peerDependencies": { 1469 - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 1470 - } 1471 - }, 1472 - "node_modules/ajv": { 1473 - "version": "6.12.6", 1474 - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 1475 - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 1476 - "dev": true, 1477 - "license": "MIT", 1478 - "dependencies": { 1479 - "fast-deep-equal": "^3.1.1", 1480 - "fast-json-stable-stringify": "^2.0.0", 1481 - "json-schema-traverse": "^0.4.1", 1482 - "uri-js": "^4.2.2" 1483 - }, 1484 - "funding": { 1485 - "type": "github", 1486 - "url": "https://github.com/sponsors/epoberezkin" 1487 - } 1488 - }, 1489 - "node_modules/ansi-styles": { 1490 - "version": "4.3.0", 1491 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1492 - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1493 - "dev": true, 1494 - "license": "MIT", 1495 - "dependencies": { 1496 - "color-convert": "^2.0.1" 1497 - }, 1498 - "engines": { 1499 - "node": ">=8" 1500 - }, 1501 - "funding": { 1502 - "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1503 - } 1504 - }, 1505 - "node_modules/argparse": { 1506 - "version": "2.0.1", 1507 - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1508 - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1509 - "dev": true, 1510 - "license": "Python-2.0" 1511 - }, 1512 - "node_modules/array-buffer-byte-length": { 1513 - "version": "1.0.2", 1514 - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", 1515 - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", 1516 - "dev": true, 1517 - "license": "MIT", 1518 - "dependencies": { 1519 - "call-bound": "^1.0.3", 1520 - "is-array-buffer": "^3.0.5" 1521 - }, 1522 - "engines": { 1523 - "node": ">= 0.4" 1524 - }, 1525 - "funding": { 1526 - "url": "https://github.com/sponsors/ljharb" 1527 - } 1528 - }, 1529 - "node_modules/array-includes": { 1530 - "version": "3.1.9", 1531 - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", 1532 - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", 1533 - "dev": true, 1534 - "license": "MIT", 1535 - "dependencies": { 1536 - "call-bind": "^1.0.8", 1537 - "call-bound": "^1.0.4", 1538 - "define-properties": "^1.2.1", 1539 - "es-abstract": "^1.24.0", 1540 - "es-object-atoms": "^1.1.1", 1541 - "get-intrinsic": "^1.3.0", 1542 - "is-string": "^1.1.1", 1543 - "math-intrinsics": "^1.1.0" 1544 - }, 1545 - "engines": { 1546 - "node": ">= 0.4" 1547 - }, 1548 - "funding": { 1549 - "url": "https://github.com/sponsors/ljharb" 1550 - } 1551 - }, 1552 - "node_modules/array.prototype.findlast": { 1553 - "version": "1.2.5", 1554 - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", 1555 - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", 1556 - "dev": true, 1557 - "license": "MIT", 1558 - "dependencies": { 1559 - "call-bind": "^1.0.7", 1560 - "define-properties": "^1.2.1", 1561 - "es-abstract": "^1.23.2", 1562 - "es-errors": "^1.3.0", 1563 - "es-object-atoms": "^1.0.0", 1564 - "es-shim-unscopables": "^1.0.2" 1565 - }, 1566 - "engines": { 1567 - "node": ">= 0.4" 1568 - }, 1569 - "funding": { 1570 - "url": "https://github.com/sponsors/ljharb" 1571 - } 1572 - }, 1573 - "node_modules/array.prototype.flat": { 1574 - "version": "1.3.3", 1575 - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", 1576 - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", 1577 - "dev": true, 1578 - "license": "MIT", 1579 - "dependencies": { 1580 - "call-bind": "^1.0.8", 1581 - "define-properties": "^1.2.1", 1582 - "es-abstract": "^1.23.5", 1583 - "es-shim-unscopables": "^1.0.2" 1584 - }, 1585 - "engines": { 1586 - "node": ">= 0.4" 1587 - }, 1588 - "funding": { 1589 - "url": "https://github.com/sponsors/ljharb" 1590 - } 1591 - }, 1592 - "node_modules/array.prototype.flatmap": { 1593 - "version": "1.3.3", 1594 - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", 1595 - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", 1596 - "dev": true, 1597 - "license": "MIT", 1598 - "dependencies": { 1599 - "call-bind": "^1.0.8", 1600 - "define-properties": "^1.2.1", 1601 - "es-abstract": "^1.23.5", 1602 - "es-shim-unscopables": "^1.0.2" 1603 - }, 1604 - "engines": { 1605 - "node": ">= 0.4" 1606 - }, 1607 - "funding": { 1608 - "url": "https://github.com/sponsors/ljharb" 1609 - } 1610 - }, 1611 - "node_modules/array.prototype.tosorted": { 1612 - "version": "1.1.4", 1613 - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", 1614 - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", 1615 - "dev": true, 1616 - "license": "MIT", 1617 - "dependencies": { 1618 - "call-bind": "^1.0.7", 1619 - "define-properties": "^1.2.1", 1620 - "es-abstract": "^1.23.3", 1621 - "es-errors": "^1.3.0", 1622 - "es-shim-unscopables": "^1.0.2" 1623 - }, 1624 - "engines": { 1625 - "node": ">= 0.4" 1626 - } 1627 - }, 1628 - "node_modules/arraybuffer.prototype.slice": { 1629 - "version": "1.0.4", 1630 - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", 1631 - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", 1632 - "dev": true, 1633 - "license": "MIT", 1634 - "dependencies": { 1635 - "array-buffer-byte-length": "^1.0.1", 1636 - "call-bind": "^1.0.8", 1637 - "define-properties": "^1.2.1", 1638 - "es-abstract": "^1.23.5", 1639 - "es-errors": "^1.3.0", 1640 - "get-intrinsic": "^1.2.6", 1641 - "is-array-buffer": "^3.0.4" 1642 - }, 1643 - "engines": { 1644 - "node": ">= 0.4" 1645 - }, 1646 - "funding": { 1647 - "url": "https://github.com/sponsors/ljharb" 1648 - } 1649 - }, 1650 - "node_modules/async-function": { 1651 - "version": "1.0.0", 1652 - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", 1653 - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", 1654 - "dev": true, 1655 - "license": "MIT", 1656 - "engines": { 1657 - "node": ">= 0.4" 1658 - } 1659 - }, 1660 - "node_modules/available-typed-arrays": { 1661 - "version": "1.0.7", 1662 - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", 1663 - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", 1664 - "dev": true, 1665 - "license": "MIT", 1666 - "dependencies": { 1667 - "possible-typed-array-names": "^1.0.0" 1668 - }, 1669 - "engines": { 1670 - "node": ">= 0.4" 1671 - }, 1672 - "funding": { 1673 - "url": "https://github.com/sponsors/ljharb" 1674 - } 1675 - }, 1676 - "node_modules/balanced-match": { 1677 - "version": "1.0.2", 1678 - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1679 - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1680 - "dev": true, 1681 - "license": "MIT" 1682 - }, 1683 - "node_modules/baseline-browser-mapping": { 1684 - "version": "2.9.11", 1685 - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", 1686 - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", 1687 - "dev": true, 1688 - "license": "Apache-2.0", 1689 - "bin": { 1690 - "baseline-browser-mapping": "dist/cli.js" 1691 - } 1692 - }, 1693 - "node_modules/brace-expansion": { 1694 - "version": "1.1.12", 1695 - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", 1696 - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", 1697 - "dev": true, 1698 - "license": "MIT", 1699 - "dependencies": { 1700 - "balanced-match": "^1.0.0", 1701 - "concat-map": "0.0.1" 1702 - } 1703 - }, 1704 - "node_modules/browserslist": { 1705 - "version": "4.28.1", 1706 - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", 1707 - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", 1708 - "dev": true, 1709 - "funding": [ 1710 - { 1711 - "type": "opencollective", 1712 - "url": "https://opencollective.com/browserslist" 1713 - }, 1714 - { 1715 - "type": "tidelift", 1716 - "url": "https://tidelift.com/funding/github/npm/browserslist" 1717 - }, 1718 - { 1719 - "type": "github", 1720 - "url": "https://github.com/sponsors/ai" 1721 - } 1722 - ], 1723 - "license": "MIT", 1724 - "peer": true, 1725 - "dependencies": { 1726 - "baseline-browser-mapping": "^2.9.0", 1727 - "caniuse-lite": "^1.0.30001759", 1728 - "electron-to-chromium": "^1.5.263", 1729 - "node-releases": "^2.0.27", 1730 - "update-browserslist-db": "^1.2.0" 1731 - }, 1732 - "bin": { 1733 - "browserslist": "cli.js" 1734 - }, 1735 - "engines": { 1736 - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 1737 - } 1738 - }, 1739 - "node_modules/call-bind": { 1740 - "version": "1.0.8", 1741 - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", 1742 - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", 1743 - "dev": true, 1744 - "license": "MIT", 1745 - "dependencies": { 1746 - "call-bind-apply-helpers": "^1.0.0", 1747 - "es-define-property": "^1.0.0", 1748 - "get-intrinsic": "^1.2.4", 1749 - "set-function-length": "^1.2.2" 1750 - }, 1751 - "engines": { 1752 - "node": ">= 0.4" 1753 - }, 1754 - "funding": { 1755 - "url": "https://github.com/sponsors/ljharb" 1756 - } 1757 - }, 1758 - "node_modules/call-bind-apply-helpers": { 1759 - "version": "1.0.2", 1760 - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 1761 - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 1762 - "dev": true, 1763 - "license": "MIT", 1764 - "dependencies": { 1765 - "es-errors": "^1.3.0", 1766 - "function-bind": "^1.1.2" 1767 - }, 1768 - "engines": { 1769 - "node": ">= 0.4" 1770 - } 1771 - }, 1772 - "node_modules/call-bound": { 1773 - "version": "1.0.4", 1774 - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", 1775 - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", 1776 - "dev": true, 1777 - "license": "MIT", 1778 - "dependencies": { 1779 - "call-bind-apply-helpers": "^1.0.2", 1780 - "get-intrinsic": "^1.3.0" 1781 - }, 1782 - "engines": { 1783 - "node": ">= 0.4" 1784 - }, 1785 - "funding": { 1786 - "url": "https://github.com/sponsors/ljharb" 1787 - } 1788 - }, 1789 - "node_modules/callsites": { 1790 - "version": "3.1.0", 1791 - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 1792 - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 1793 - "dev": true, 1794 - "license": "MIT", 1795 - "engines": { 1796 - "node": ">=6" 1797 - } 1798 - }, 1799 - "node_modules/caniuse-lite": { 1800 - "version": "1.0.30001762", 1801 - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", 1802 - "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", 1803 - "dev": true, 1804 - "funding": [ 1805 - { 1806 - "type": "opencollective", 1807 - "url": "https://opencollective.com/browserslist" 1808 - }, 1809 - { 1810 - "type": "tidelift", 1811 - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 1812 - }, 1813 - { 1814 - "type": "github", 1815 - "url": "https://github.com/sponsors/ai" 1816 - } 1817 - ], 1818 - "license": "CC-BY-4.0" 1819 - }, 1820 - "node_modules/chalk": { 1821 - "version": "4.1.2", 1822 - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1823 - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1824 - "dev": true, 1825 - "license": "MIT", 1826 - "dependencies": { 1827 - "ansi-styles": "^4.1.0", 1828 - "supports-color": "^7.1.0" 1829 - }, 1830 - "engines": { 1831 - "node": ">=10" 1832 - }, 1833 - "funding": { 1834 - "url": "https://github.com/chalk/chalk?sponsor=1" 1835 - } 1836 - }, 1837 - "node_modules/color-convert": { 1838 - "version": "2.0.1", 1839 - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1840 - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1841 - "dev": true, 1842 - "license": "MIT", 1843 - "dependencies": { 1844 - "color-name": "~1.1.4" 1845 - }, 1846 - "engines": { 1847 - "node": ">=7.0.0" 1848 - } 1849 - }, 1850 - "node_modules/color-name": { 1851 - "version": "1.1.4", 1852 - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1853 - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1854 - "dev": true, 1855 - "license": "MIT" 1856 - }, 1857 - "node_modules/concat-map": { 1858 - "version": "0.0.1", 1859 - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1860 - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1861 - "dev": true, 1862 - "license": "MIT" 1863 - }, 1864 - "node_modules/convert-source-map": { 1865 - "version": "2.0.0", 1866 - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", 1867 - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", 1868 - "dev": true, 1869 - "license": "MIT" 1870 - }, 1871 - "node_modules/cross-spawn": { 1872 - "version": "7.0.6", 1873 - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 1874 - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 1875 - "dev": true, 1876 - "license": "MIT", 1877 - "dependencies": { 1878 - "path-key": "^3.1.0", 1879 - "shebang-command": "^2.0.0", 1880 - "which": "^2.0.1" 1881 - }, 1882 - "engines": { 1883 - "node": ">= 8" 1884 - } 1885 - }, 1886 - "node_modules/csstype": { 1887 - "version": "3.2.3", 1888 - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", 1889 - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", 1890 - "dev": true, 1891 - "license": "MIT" 1892 - }, 1893 - "node_modules/data-view-buffer": { 1894 - "version": "1.0.2", 1895 - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", 1896 - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", 1897 - "dev": true, 1898 - "license": "MIT", 1899 - "dependencies": { 1900 - "call-bound": "^1.0.3", 1901 - "es-errors": "^1.3.0", 1902 - "is-data-view": "^1.0.2" 1903 - }, 1904 - "engines": { 1905 - "node": ">= 0.4" 1906 - }, 1907 - "funding": { 1908 - "url": "https://github.com/sponsors/ljharb" 1909 - } 1910 - }, 1911 - "node_modules/data-view-byte-length": { 1912 - "version": "1.0.2", 1913 - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", 1914 - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", 1915 - "dev": true, 1916 - "license": "MIT", 1917 - "dependencies": { 1918 - "call-bound": "^1.0.3", 1919 - "es-errors": "^1.3.0", 1920 - "is-data-view": "^1.0.2" 1921 - }, 1922 - "engines": { 1923 - "node": ">= 0.4" 1924 - }, 1925 - "funding": { 1926 - "url": "https://github.com/sponsors/inspect-js" 1927 - } 1928 - }, 1929 - "node_modules/data-view-byte-offset": { 1930 - "version": "1.0.1", 1931 - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", 1932 - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", 1933 - "dev": true, 1934 - "license": "MIT", 1935 - "dependencies": { 1936 - "call-bound": "^1.0.2", 1937 - "es-errors": "^1.3.0", 1938 - "is-data-view": "^1.0.1" 1939 - }, 1940 - "engines": { 1941 - "node": ">= 0.4" 1942 - }, 1943 - "funding": { 1944 - "url": "https://github.com/sponsors/ljharb" 1945 - } 1946 - }, 1947 - "node_modules/date-fns": { 1948 - "version": "4.1.0", 1949 - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", 1950 - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", 1951 - "license": "MIT", 1952 - "funding": { 1953 - "type": "github", 1954 - "url": "https://github.com/sponsors/kossnocorp" 1955 - } 1956 - }, 1957 - "node_modules/debug": { 1958 - "version": "4.4.3", 1959 - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", 1960 - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", 1961 - "dev": true, 1962 - "license": "MIT", 1963 - "dependencies": { 1964 - "ms": "^2.1.3" 1965 - }, 1966 - "engines": { 1967 - "node": ">=6.0" 1968 - }, 1969 - "peerDependenciesMeta": { 1970 - "supports-color": { 1971 - "optional": true 1972 - } 1973 - } 1974 - }, 1975 - "node_modules/deep-is": { 1976 - "version": "0.1.4", 1977 - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 1978 - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 1979 - "dev": true, 1980 - "license": "MIT" 1981 - }, 1982 - "node_modules/define-data-property": { 1983 - "version": "1.1.4", 1984 - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 1985 - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 1986 - "dev": true, 1987 - "license": "MIT", 1988 - "dependencies": { 1989 - "es-define-property": "^1.0.0", 1990 - "es-errors": "^1.3.0", 1991 - "gopd": "^1.0.1" 1992 - }, 1993 - "engines": { 1994 - "node": ">= 0.4" 1995 - }, 1996 - "funding": { 1997 - "url": "https://github.com/sponsors/ljharb" 1998 - } 1999 - }, 2000 - "node_modules/define-properties": { 2001 - "version": "1.2.1", 2002 - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", 2003 - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", 2004 - "dev": true, 2005 - "license": "MIT", 2006 - "dependencies": { 2007 - "define-data-property": "^1.0.1", 2008 - "has-property-descriptors": "^1.0.0", 2009 - "object-keys": "^1.1.1" 2010 - }, 2011 - "engines": { 2012 - "node": ">= 0.4" 2013 - }, 2014 - "funding": { 2015 - "url": "https://github.com/sponsors/ljharb" 2016 - } 2017 - }, 2018 - "node_modules/doctrine": { 2019 - "version": "2.1.0", 2020 - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", 2021 - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", 2022 - "dev": true, 2023 - "license": "Apache-2.0", 2024 - "dependencies": { 2025 - "esutils": "^2.0.2" 2026 - }, 2027 - "engines": { 2028 - "node": ">=0.10.0" 2029 - } 2030 - }, 2031 - "node_modules/dunder-proto": { 2032 - "version": "1.0.1", 2033 - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 2034 - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 2035 - "dev": true, 2036 - "license": "MIT", 2037 - "dependencies": { 2038 - "call-bind-apply-helpers": "^1.0.1", 2039 - "es-errors": "^1.3.0", 2040 - "gopd": "^1.2.0" 2041 - }, 2042 - "engines": { 2043 - "node": ">= 0.4" 2044 - } 2045 - }, 2046 - "node_modules/electron-to-chromium": { 2047 - "version": "1.5.267", 2048 - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", 2049 - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", 2050 - "dev": true, 2051 - "license": "ISC" 2052 - }, 2053 - "node_modules/es-abstract": { 2054 - "version": "1.24.1", 2055 - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", 2056 - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", 2057 - "dev": true, 2058 - "license": "MIT", 2059 - "dependencies": { 2060 - "array-buffer-byte-length": "^1.0.2", 2061 - "arraybuffer.prototype.slice": "^1.0.4", 2062 - "available-typed-arrays": "^1.0.7", 2063 - "call-bind": "^1.0.8", 2064 - "call-bound": "^1.0.4", 2065 - "data-view-buffer": "^1.0.2", 2066 - "data-view-byte-length": "^1.0.2", 2067 - "data-view-byte-offset": "^1.0.1", 2068 - "es-define-property": "^1.0.1", 2069 - "es-errors": "^1.3.0", 2070 - "es-object-atoms": "^1.1.1", 2071 - "es-set-tostringtag": "^2.1.0", 2072 - "es-to-primitive": "^1.3.0", 2073 - "function.prototype.name": "^1.1.8", 2074 - "get-intrinsic": "^1.3.0", 2075 - "get-proto": "^1.0.1", 2076 - "get-symbol-description": "^1.1.0", 2077 - "globalthis": "^1.0.4", 2078 - "gopd": "^1.2.0", 2079 - "has-property-descriptors": "^1.0.2", 2080 - "has-proto": "^1.2.0", 2081 - "has-symbols": "^1.1.0", 2082 - "hasown": "^2.0.2", 2083 - "internal-slot": "^1.1.0", 2084 - "is-array-buffer": "^3.0.5", 2085 - "is-callable": "^1.2.7", 2086 - "is-data-view": "^1.0.2", 2087 - "is-negative-zero": "^2.0.3", 2088 - "is-regex": "^1.2.1", 2089 - "is-set": "^2.0.3", 2090 - "is-shared-array-buffer": "^1.0.4", 2091 - "is-string": "^1.1.1", 2092 - "is-typed-array": "^1.1.15", 2093 - "is-weakref": "^1.1.1", 2094 - "math-intrinsics": "^1.1.0", 2095 - "object-inspect": "^1.13.4", 2096 - "object-keys": "^1.1.1", 2097 - "object.assign": "^4.1.7", 2098 - "own-keys": "^1.0.1", 2099 - "regexp.prototype.flags": "^1.5.4", 2100 - "safe-array-concat": "^1.1.3", 2101 - "safe-push-apply": "^1.0.0", 2102 - "safe-regex-test": "^1.1.0", 2103 - "set-proto": "^1.0.0", 2104 - "stop-iteration-iterator": "^1.1.0", 2105 - "string.prototype.trim": "^1.2.10", 2106 - "string.prototype.trimend": "^1.0.9", 2107 - "string.prototype.trimstart": "^1.0.8", 2108 - "typed-array-buffer": "^1.0.3", 2109 - "typed-array-byte-length": "^1.0.3", 2110 - "typed-array-byte-offset": "^1.0.4", 2111 - "typed-array-length": "^1.0.7", 2112 - "unbox-primitive": "^1.1.0", 2113 - "which-typed-array": "^1.1.19" 2114 - }, 2115 - "engines": { 2116 - "node": ">= 0.4" 2117 - }, 2118 - "funding": { 2119 - "url": "https://github.com/sponsors/ljharb" 2120 - } 2121 - }, 2122 - "node_modules/es-define-property": { 2123 - "version": "1.0.1", 2124 - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 2125 - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 2126 - "dev": true, 2127 - "license": "MIT", 2128 - "engines": { 2129 - "node": ">= 0.4" 2130 - } 2131 - }, 2132 - "node_modules/es-errors": { 2133 - "version": "1.3.0", 2134 - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 2135 - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 2136 - "dev": true, 2137 - "license": "MIT", 2138 - "engines": { 2139 - "node": ">= 0.4" 2140 - } 2141 - }, 2142 - "node_modules/es-iterator-helpers": { 2143 - "version": "1.2.2", 2144 - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", 2145 - "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", 2146 - "dev": true, 2147 - "license": "MIT", 2148 - "dependencies": { 2149 - "call-bind": "^1.0.8", 2150 - "call-bound": "^1.0.4", 2151 - "define-properties": "^1.2.1", 2152 - "es-abstract": "^1.24.1", 2153 - "es-errors": "^1.3.0", 2154 - "es-set-tostringtag": "^2.1.0", 2155 - "function-bind": "^1.1.2", 2156 - "get-intrinsic": "^1.3.0", 2157 - "globalthis": "^1.0.4", 2158 - "gopd": "^1.2.0", 2159 - "has-property-descriptors": "^1.0.2", 2160 - "has-proto": "^1.2.0", 2161 - "has-symbols": "^1.1.0", 2162 - "internal-slot": "^1.1.0", 2163 - "iterator.prototype": "^1.1.5", 2164 - "safe-array-concat": "^1.1.3" 2165 - }, 2166 - "engines": { 2167 - "node": ">= 0.4" 2168 - } 2169 - }, 2170 - "node_modules/es-object-atoms": { 2171 - "version": "1.1.1", 2172 - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 2173 - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 2174 - "dev": true, 2175 - "license": "MIT", 2176 - "dependencies": { 2177 - "es-errors": "^1.3.0" 2178 - }, 2179 - "engines": { 2180 - "node": ">= 0.4" 2181 - } 2182 - }, 2183 - "node_modules/es-set-tostringtag": { 2184 - "version": "2.1.0", 2185 - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", 2186 - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", 2187 - "dev": true, 2188 - "license": "MIT", 2189 - "dependencies": { 2190 - "es-errors": "^1.3.0", 2191 - "get-intrinsic": "^1.2.6", 2192 - "has-tostringtag": "^1.0.2", 2193 - "hasown": "^2.0.2" 2194 - }, 2195 - "engines": { 2196 - "node": ">= 0.4" 2197 - } 2198 - }, 2199 - "node_modules/es-shim-unscopables": { 2200 - "version": "1.1.0", 2201 - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", 2202 - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", 2203 - "dev": true, 2204 - "license": "MIT", 2205 - "dependencies": { 2206 - "hasown": "^2.0.2" 2207 - }, 2208 - "engines": { 2209 - "node": ">= 0.4" 2210 - } 2211 - }, 2212 - "node_modules/es-to-primitive": { 2213 - "version": "1.3.0", 2214 - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", 2215 - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", 2216 - "dev": true, 2217 - "license": "MIT", 2218 - "dependencies": { 2219 - "is-callable": "^1.2.7", 2220 - "is-date-object": "^1.0.5", 2221 - "is-symbol": "^1.0.4" 2222 - }, 2223 - "engines": { 2224 - "node": ">= 0.4" 2225 - }, 2226 - "funding": { 2227 - "url": "https://github.com/sponsors/ljharb" 2228 - } 2229 - }, 2230 - "node_modules/esbuild": { 2231 - "version": "0.25.12", 2232 - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", 2233 - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", 2234 - "dev": true, 2235 - "hasInstallScript": true, 2236 - "license": "MIT", 2237 - "bin": { 2238 - "esbuild": "bin/esbuild" 2239 - }, 2240 - "engines": { 2241 - "node": ">=18" 2242 - }, 2243 - "optionalDependencies": { 2244 - "@esbuild/aix-ppc64": "0.25.12", 2245 - "@esbuild/android-arm": "0.25.12", 2246 - "@esbuild/android-arm64": "0.25.12", 2247 - "@esbuild/android-x64": "0.25.12", 2248 - "@esbuild/darwin-arm64": "0.25.12", 2249 - "@esbuild/darwin-x64": "0.25.12", 2250 - "@esbuild/freebsd-arm64": "0.25.12", 2251 - "@esbuild/freebsd-x64": "0.25.12", 2252 - "@esbuild/linux-arm": "0.25.12", 2253 - "@esbuild/linux-arm64": "0.25.12", 2254 - "@esbuild/linux-ia32": "0.25.12", 2255 - "@esbuild/linux-loong64": "0.25.12", 2256 - "@esbuild/linux-mips64el": "0.25.12", 2257 - "@esbuild/linux-ppc64": "0.25.12", 2258 - "@esbuild/linux-riscv64": "0.25.12", 2259 - "@esbuild/linux-s390x": "0.25.12", 2260 - "@esbuild/linux-x64": "0.25.12", 2261 - "@esbuild/netbsd-arm64": "0.25.12", 2262 - "@esbuild/netbsd-x64": "0.25.12", 2263 - "@esbuild/openbsd-arm64": "0.25.12", 2264 - "@esbuild/openbsd-x64": "0.25.12", 2265 - "@esbuild/openharmony-arm64": "0.25.12", 2266 - "@esbuild/sunos-x64": "0.25.12", 2267 - "@esbuild/win32-arm64": "0.25.12", 2268 - "@esbuild/win32-ia32": "0.25.12", 2269 - "@esbuild/win32-x64": "0.25.12" 2270 - } 2271 - }, 2272 - "node_modules/escalade": { 2273 - "version": "3.2.0", 2274 - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 2275 - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 2276 - "dev": true, 2277 - "license": "MIT", 2278 - "engines": { 2279 - "node": ">=6" 2280 - } 2281 - }, 2282 - "node_modules/escape-string-regexp": { 2283 - "version": "4.0.0", 2284 - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 2285 - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 2286 - "dev": true, 2287 - "license": "MIT", 2288 - "engines": { 2289 - "node": ">=10" 2290 - }, 2291 - "funding": { 2292 - "url": "https://github.com/sponsors/sindresorhus" 2293 - } 2294 - }, 2295 - "node_modules/eslint": { 2296 - "version": "9.39.2", 2297 - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", 2298 - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", 2299 - "dev": true, 2300 - "license": "MIT", 2301 - "peer": true, 2302 - "dependencies": { 2303 - "@eslint-community/eslint-utils": "^4.8.0", 2304 - "@eslint-community/regexpp": "^4.12.1", 2305 - "@eslint/config-array": "^0.21.1", 2306 - "@eslint/config-helpers": "^0.4.2", 2307 - "@eslint/core": "^0.17.0", 2308 - "@eslint/eslintrc": "^3.3.1", 2309 - "@eslint/js": "9.39.2", 2310 - "@eslint/plugin-kit": "^0.4.1", 2311 - "@humanfs/node": "^0.16.6", 2312 - "@humanwhocodes/module-importer": "^1.0.1", 2313 - "@humanwhocodes/retry": "^0.4.2", 2314 - "@types/estree": "^1.0.6", 2315 - "ajv": "^6.12.4", 2316 - "chalk": "^4.0.0", 2317 - "cross-spawn": "^7.0.6", 2318 - "debug": "^4.3.2", 2319 - "escape-string-regexp": "^4.0.0", 2320 - "eslint-scope": "^8.4.0", 2321 - "eslint-visitor-keys": "^4.2.1", 2322 - "espree": "^10.4.0", 2323 - "esquery": "^1.5.0", 2324 - "esutils": "^2.0.2", 2325 - "fast-deep-equal": "^3.1.3", 2326 - "file-entry-cache": "^8.0.0", 2327 - "find-up": "^5.0.0", 2328 - "glob-parent": "^6.0.2", 2329 - "ignore": "^5.2.0", 2330 - "imurmurhash": "^0.1.4", 2331 - "is-glob": "^4.0.0", 2332 - "json-stable-stringify-without-jsonify": "^1.0.1", 2333 - "lodash.merge": "^4.6.2", 2334 - "minimatch": "^3.1.2", 2335 - "natural-compare": "^1.4.0", 2336 - "optionator": "^0.9.3" 2337 - }, 2338 - "bin": { 2339 - "eslint": "bin/eslint.js" 2340 - }, 2341 - "engines": { 2342 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2343 - }, 2344 - "funding": { 2345 - "url": "https://eslint.org/donate" 2346 - }, 2347 - "peerDependencies": { 2348 - "jiti": "*" 2349 - }, 2350 - "peerDependenciesMeta": { 2351 - "jiti": { 2352 - "optional": true 2353 - } 2354 - } 2355 - }, 2356 - "node_modules/eslint-plugin-react": { 2357 - "version": "7.37.5", 2358 - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", 2359 - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", 2360 - "dev": true, 2361 - "license": "MIT", 2362 - "dependencies": { 2363 - "array-includes": "^3.1.8", 2364 - "array.prototype.findlast": "^1.2.5", 2365 - "array.prototype.flatmap": "^1.3.3", 2366 - "array.prototype.tosorted": "^1.1.4", 2367 - "doctrine": "^2.1.0", 2368 - "es-iterator-helpers": "^1.2.1", 2369 - "estraverse": "^5.3.0", 2370 - "hasown": "^2.0.2", 2371 - "jsx-ast-utils": "^2.4.1 || ^3.0.0", 2372 - "minimatch": "^3.1.2", 2373 - "object.entries": "^1.1.9", 2374 - "object.fromentries": "^2.0.8", 2375 - "object.values": "^1.2.1", 2376 - "prop-types": "^15.8.1", 2377 - "resolve": "^2.0.0-next.5", 2378 - "semver": "^6.3.1", 2379 - "string.prototype.matchall": "^4.0.12", 2380 - "string.prototype.repeat": "^1.0.0" 2381 - }, 2382 - "engines": { 2383 - "node": ">=4" 2384 - }, 2385 - "peerDependencies": { 2386 - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" 2387 - } 2388 - }, 2389 - "node_modules/eslint-plugin-react-hooks": { 2390 - "version": "7.0.1", 2391 - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", 2392 - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", 2393 - "dev": true, 2394 - "license": "MIT", 2395 - "dependencies": { 2396 - "@babel/core": "^7.24.4", 2397 - "@babel/parser": "^7.24.4", 2398 - "hermes-parser": "^0.25.1", 2399 - "zod": "^3.25.0 || ^4.0.0", 2400 - "zod-validation-error": "^3.5.0 || ^4.0.0" 2401 - }, 2402 - "engines": { 2403 - "node": ">=18" 2404 - }, 2405 - "peerDependencies": { 2406 - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" 2407 - } 2408 - }, 2409 - "node_modules/eslint-plugin-react-refresh": { 2410 - "version": "0.4.26", 2411 - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", 2412 - "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", 2413 - "dev": true, 2414 - "license": "MIT", 2415 - "peerDependencies": { 2416 - "eslint": ">=8.40" 2417 - } 2418 - }, 2419 - "node_modules/eslint-scope": { 2420 - "version": "8.4.0", 2421 - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", 2422 - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", 2423 - "dev": true, 2424 - "license": "BSD-2-Clause", 2425 - "dependencies": { 2426 - "esrecurse": "^4.3.0", 2427 - "estraverse": "^5.2.0" 2428 - }, 2429 - "engines": { 2430 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2431 - }, 2432 - "funding": { 2433 - "url": "https://opencollective.com/eslint" 2434 - } 2435 - }, 2436 - "node_modules/eslint-visitor-keys": { 2437 - "version": "4.2.1", 2438 - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", 2439 - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", 2440 - "dev": true, 2441 - "license": "Apache-2.0", 2442 - "engines": { 2443 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2444 - }, 2445 - "funding": { 2446 - "url": "https://opencollective.com/eslint" 2447 - } 2448 - }, 2449 - "node_modules/espree": { 2450 - "version": "10.4.0", 2451 - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", 2452 - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", 2453 - "dev": true, 2454 - "license": "BSD-2-Clause", 2455 - "dependencies": { 2456 - "acorn": "^8.15.0", 2457 - "acorn-jsx": "^5.3.2", 2458 - "eslint-visitor-keys": "^4.2.1" 2459 - }, 2460 - "engines": { 2461 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2462 - }, 2463 - "funding": { 2464 - "url": "https://opencollective.com/eslint" 2465 - } 2466 - }, 2467 - "node_modules/esquery": { 2468 - "version": "1.7.0", 2469 - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", 2470 - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", 2471 - "dev": true, 2472 - "license": "BSD-3-Clause", 2473 - "dependencies": { 2474 - "estraverse": "^5.1.0" 2475 - }, 2476 - "engines": { 2477 - "node": ">=0.10" 2478 - } 2479 - }, 2480 - "node_modules/esrecurse": { 2481 - "version": "4.3.0", 2482 - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 2483 - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 2484 - "dev": true, 2485 - "license": "BSD-2-Clause", 2486 - "dependencies": { 2487 - "estraverse": "^5.2.0" 2488 - }, 2489 - "engines": { 2490 - "node": ">=4.0" 2491 - } 2492 - }, 2493 - "node_modules/estraverse": { 2494 - "version": "5.3.0", 2495 - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 2496 - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 2497 - "dev": true, 2498 - "license": "BSD-2-Clause", 2499 - "engines": { 2500 - "node": ">=4.0" 2501 - } 2502 - }, 2503 - "node_modules/esutils": { 2504 - "version": "2.0.3", 2505 - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 2506 - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 2507 - "dev": true, 2508 - "license": "BSD-2-Clause", 2509 - "engines": { 2510 - "node": ">=0.10.0" 2511 - } 2512 - }, 2513 - "node_modules/fast-deep-equal": { 2514 - "version": "3.1.3", 2515 - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 2516 - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 2517 - "dev": true, 2518 - "license": "MIT" 2519 - }, 2520 - "node_modules/fast-json-stable-stringify": { 2521 - "version": "2.1.0", 2522 - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 2523 - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 2524 - "dev": true, 2525 - "license": "MIT" 2526 - }, 2527 - "node_modules/fast-levenshtein": { 2528 - "version": "2.0.6", 2529 - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 2530 - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 2531 - "dev": true, 2532 - "license": "MIT" 2533 - }, 2534 - "node_modules/fdir": { 2535 - "version": "6.5.0", 2536 - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 2537 - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 2538 - "dev": true, 2539 - "license": "MIT", 2540 - "engines": { 2541 - "node": ">=12.0.0" 2542 - }, 2543 - "peerDependencies": { 2544 - "picomatch": "^3 || ^4" 2545 - }, 2546 - "peerDependenciesMeta": { 2547 - "picomatch": { 2548 - "optional": true 2549 - } 2550 - } 2551 - }, 2552 - "node_modules/file-entry-cache": { 2553 - "version": "8.0.0", 2554 - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", 2555 - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 2556 - "dev": true, 2557 - "license": "MIT", 2558 - "dependencies": { 2559 - "flat-cache": "^4.0.0" 2560 - }, 2561 - "engines": { 2562 - "node": ">=16.0.0" 2563 - } 2564 - }, 2565 - "node_modules/find-up": { 2566 - "version": "5.0.0", 2567 - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 2568 - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 2569 - "dev": true, 2570 - "license": "MIT", 2571 - "dependencies": { 2572 - "locate-path": "^6.0.0", 2573 - "path-exists": "^4.0.0" 2574 - }, 2575 - "engines": { 2576 - "node": ">=10" 2577 - }, 2578 - "funding": { 2579 - "url": "https://github.com/sponsors/sindresorhus" 2580 - } 2581 - }, 2582 - "node_modules/flat-cache": { 2583 - "version": "4.0.1", 2584 - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", 2585 - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 2586 - "dev": true, 2587 - "license": "MIT", 2588 - "dependencies": { 2589 - "flatted": "^3.2.9", 2590 - "keyv": "^4.5.4" 2591 - }, 2592 - "engines": { 2593 - "node": ">=16" 2594 - } 2595 - }, 2596 - "node_modules/flatted": { 2597 - "version": "3.3.3", 2598 - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", 2599 - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", 2600 - "dev": true, 2601 - "license": "ISC" 2602 - }, 2603 - "node_modules/for-each": { 2604 - "version": "0.3.5", 2605 - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", 2606 - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", 2607 - "dev": true, 2608 - "license": "MIT", 2609 - "dependencies": { 2610 - "is-callable": "^1.2.7" 2611 - }, 2612 - "engines": { 2613 - "node": ">= 0.4" 2614 - }, 2615 - "funding": { 2616 - "url": "https://github.com/sponsors/ljharb" 2617 - } 2618 - }, 2619 - "node_modules/fsevents": { 2620 - "version": "2.3.3", 2621 - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 2622 - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 2623 - "dev": true, 2624 - "hasInstallScript": true, 2625 - "license": "MIT", 2626 - "optional": true, 2627 - "os": [ 2628 - "darwin" 2629 - ], 2630 - "engines": { 2631 - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 2632 - } 2633 - }, 2634 - "node_modules/function-bind": { 2635 - "version": "1.1.2", 2636 - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 2637 - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 2638 - "dev": true, 2639 - "license": "MIT", 2640 - "funding": { 2641 - "url": "https://github.com/sponsors/ljharb" 2642 - } 2643 - }, 2644 - "node_modules/function.prototype.name": { 2645 - "version": "1.1.8", 2646 - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", 2647 - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", 2648 - "dev": true, 2649 - "license": "MIT", 2650 - "dependencies": { 2651 - "call-bind": "^1.0.8", 2652 - "call-bound": "^1.0.3", 2653 - "define-properties": "^1.2.1", 2654 - "functions-have-names": "^1.2.3", 2655 - "hasown": "^2.0.2", 2656 - "is-callable": "^1.2.7" 2657 - }, 2658 - "engines": { 2659 - "node": ">= 0.4" 2660 - }, 2661 - "funding": { 2662 - "url": "https://github.com/sponsors/ljharb" 2663 - } 2664 - }, 2665 - "node_modules/functions-have-names": { 2666 - "version": "1.2.3", 2667 - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 2668 - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", 2669 - "dev": true, 2670 - "license": "MIT", 2671 - "funding": { 2672 - "url": "https://github.com/sponsors/ljharb" 2673 - } 2674 - }, 2675 - "node_modules/generator-function": { 2676 - "version": "2.0.1", 2677 - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", 2678 - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", 2679 - "dev": true, 2680 - "license": "MIT", 2681 - "engines": { 2682 - "node": ">= 0.4" 2683 - } 2684 - }, 2685 - "node_modules/gensync": { 2686 - "version": "1.0.0-beta.2", 2687 - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 2688 - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 2689 - "dev": true, 2690 - "license": "MIT", 2691 - "engines": { 2692 - "node": ">=6.9.0" 2693 - } 2694 - }, 2695 - "node_modules/get-intrinsic": { 2696 - "version": "1.3.0", 2697 - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 2698 - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 2699 - "dev": true, 2700 - "license": "MIT", 2701 - "dependencies": { 2702 - "call-bind-apply-helpers": "^1.0.2", 2703 - "es-define-property": "^1.0.1", 2704 - "es-errors": "^1.3.0", 2705 - "es-object-atoms": "^1.1.1", 2706 - "function-bind": "^1.1.2", 2707 - "get-proto": "^1.0.1", 2708 - "gopd": "^1.2.0", 2709 - "has-symbols": "^1.1.0", 2710 - "hasown": "^2.0.2", 2711 - "math-intrinsics": "^1.1.0" 2712 - }, 2713 - "engines": { 2714 - "node": ">= 0.4" 2715 - }, 2716 - "funding": { 2717 - "url": "https://github.com/sponsors/ljharb" 2718 - } 2719 - }, 2720 - "node_modules/get-proto": { 2721 - "version": "1.0.1", 2722 - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 2723 - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 2724 - "dev": true, 2725 - "license": "MIT", 2726 - "dependencies": { 2727 - "dunder-proto": "^1.0.1", 2728 - "es-object-atoms": "^1.0.0" 2729 - }, 2730 - "engines": { 2731 - "node": ">= 0.4" 2732 - } 2733 - }, 2734 - "node_modules/get-symbol-description": { 2735 - "version": "1.1.0", 2736 - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", 2737 - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", 2738 - "dev": true, 2739 - "license": "MIT", 2740 - "dependencies": { 2741 - "call-bound": "^1.0.3", 2742 - "es-errors": "^1.3.0", 2743 - "get-intrinsic": "^1.2.6" 2744 - }, 2745 - "engines": { 2746 - "node": ">= 0.4" 2747 - }, 2748 - "funding": { 2749 - "url": "https://github.com/sponsors/ljharb" 2750 - } 2751 - }, 2752 - "node_modules/glob-parent": { 2753 - "version": "6.0.2", 2754 - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 2755 - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 2756 - "dev": true, 2757 - "license": "ISC", 2758 - "dependencies": { 2759 - "is-glob": "^4.0.3" 2760 - }, 2761 - "engines": { 2762 - "node": ">=10.13.0" 2763 - } 2764 - }, 2765 - "node_modules/globals": { 2766 - "version": "17.0.0", 2767 - "resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz", 2768 - "integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==", 2769 - "dev": true, 2770 - "license": "MIT", 2771 - "engines": { 2772 - "node": ">=18" 2773 - }, 2774 - "funding": { 2775 - "url": "https://github.com/sponsors/sindresorhus" 2776 - } 2777 - }, 2778 - "node_modules/globalthis": { 2779 - "version": "1.0.4", 2780 - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", 2781 - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", 2782 - "dev": true, 2783 - "license": "MIT", 2784 - "dependencies": { 2785 - "define-properties": "^1.2.1", 2786 - "gopd": "^1.0.1" 2787 - }, 2788 - "engines": { 2789 - "node": ">= 0.4" 2790 - }, 2791 - "funding": { 2792 - "url": "https://github.com/sponsors/ljharb" 2793 - } 2794 - }, 2795 - "node_modules/gopd": { 2796 - "version": "1.2.0", 2797 - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 2798 - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 2799 - "dev": true, 2800 - "license": "MIT", 2801 - "engines": { 2802 - "node": ">= 0.4" 2803 - }, 2804 - "funding": { 2805 - "url": "https://github.com/sponsors/ljharb" 2806 - } 2807 - }, 2808 - "node_modules/has-bigints": { 2809 - "version": "1.1.0", 2810 - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", 2811 - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", 2812 - "dev": true, 2813 - "license": "MIT", 2814 - "engines": { 2815 - "node": ">= 0.4" 2816 - }, 2817 - "funding": { 2818 - "url": "https://github.com/sponsors/ljharb" 2819 - } 2820 - }, 2821 - "node_modules/has-flag": { 2822 - "version": "4.0.0", 2823 - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 2824 - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 2825 - "dev": true, 2826 - "license": "MIT", 2827 - "engines": { 2828 - "node": ">=8" 2829 - } 2830 - }, 2831 - "node_modules/has-property-descriptors": { 2832 - "version": "1.0.2", 2833 - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 2834 - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 2835 - "dev": true, 2836 - "license": "MIT", 2837 - "dependencies": { 2838 - "es-define-property": "^1.0.0" 2839 - }, 2840 - "funding": { 2841 - "url": "https://github.com/sponsors/ljharb" 2842 - } 2843 - }, 2844 - "node_modules/has-proto": { 2845 - "version": "1.2.0", 2846 - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", 2847 - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", 2848 - "dev": true, 2849 - "license": "MIT", 2850 - "dependencies": { 2851 - "dunder-proto": "^1.0.0" 2852 - }, 2853 - "engines": { 2854 - "node": ">= 0.4" 2855 - }, 2856 - "funding": { 2857 - "url": "https://github.com/sponsors/ljharb" 2858 - } 2859 - }, 2860 - "node_modules/has-symbols": { 2861 - "version": "1.1.0", 2862 - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 2863 - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 2864 - "dev": true, 2865 - "license": "MIT", 2866 - "engines": { 2867 - "node": ">= 0.4" 2868 - }, 2869 - "funding": { 2870 - "url": "https://github.com/sponsors/ljharb" 2871 - } 2872 - }, 2873 - "node_modules/has-tostringtag": { 2874 - "version": "1.0.2", 2875 - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", 2876 - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", 2877 - "dev": true, 2878 - "license": "MIT", 2879 - "dependencies": { 2880 - "has-symbols": "^1.0.3" 2881 - }, 2882 - "engines": { 2883 - "node": ">= 0.4" 2884 - }, 2885 - "funding": { 2886 - "url": "https://github.com/sponsors/ljharb" 2887 - } 2888 - }, 2889 - "node_modules/hasown": { 2890 - "version": "2.0.2", 2891 - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 2892 - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 2893 - "dev": true, 2894 - "license": "MIT", 2895 - "dependencies": { 2896 - "function-bind": "^1.1.2" 2897 - }, 2898 - "engines": { 2899 - "node": ">= 0.4" 2900 - } 2901 - }, 2902 - "node_modules/hermes-estree": { 2903 - "version": "0.25.1", 2904 - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", 2905 - "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", 2906 - "dev": true, 2907 - "license": "MIT" 2908 - }, 2909 - "node_modules/hermes-parser": { 2910 - "version": "0.25.1", 2911 - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", 2912 - "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", 2913 - "dev": true, 2914 - "license": "MIT", 2915 - "dependencies": { 2916 - "hermes-estree": "0.25.1" 2917 - } 2918 - }, 2919 - "node_modules/ignore": { 2920 - "version": "5.3.2", 2921 - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", 2922 - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", 2923 - "dev": true, 2924 - "license": "MIT", 2925 - "engines": { 2926 - "node": ">= 4" 2927 - } 2928 - }, 2929 - "node_modules/import-fresh": { 2930 - "version": "3.3.1", 2931 - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", 2932 - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", 2933 - "dev": true, 2934 - "license": "MIT", 2935 - "dependencies": { 2936 - "parent-module": "^1.0.0", 2937 - "resolve-from": "^4.0.0" 2938 - }, 2939 - "engines": { 2940 - "node": ">=6" 2941 - }, 2942 - "funding": { 2943 - "url": "https://github.com/sponsors/sindresorhus" 2944 - } 2945 - }, 2946 - "node_modules/imurmurhash": { 2947 - "version": "0.1.4", 2948 - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 2949 - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 2950 - "dev": true, 2951 - "license": "MIT", 2952 - "engines": { 2953 - "node": ">=0.8.19" 2954 - } 2955 - }, 2956 - "node_modules/internal-slot": { 2957 - "version": "1.1.0", 2958 - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", 2959 - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", 2960 - "dev": true, 2961 - "license": "MIT", 2962 - "dependencies": { 2963 - "es-errors": "^1.3.0", 2964 - "hasown": "^2.0.2", 2965 - "side-channel": "^1.1.0" 2966 - }, 2967 - "engines": { 2968 - "node": ">= 0.4" 2969 - } 2970 - }, 2971 - "node_modules/is-array-buffer": { 2972 - "version": "3.0.5", 2973 - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", 2974 - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", 2975 - "dev": true, 2976 - "license": "MIT", 2977 - "dependencies": { 2978 - "call-bind": "^1.0.8", 2979 - "call-bound": "^1.0.3", 2980 - "get-intrinsic": "^1.2.6" 2981 - }, 2982 - "engines": { 2983 - "node": ">= 0.4" 2984 - }, 2985 - "funding": { 2986 - "url": "https://github.com/sponsors/ljharb" 2987 - } 2988 - }, 2989 - "node_modules/is-async-function": { 2990 - "version": "2.1.1", 2991 - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", 2992 - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", 2993 - "dev": true, 2994 - "license": "MIT", 2995 - "dependencies": { 2996 - "async-function": "^1.0.0", 2997 - "call-bound": "^1.0.3", 2998 - "get-proto": "^1.0.1", 2999 - "has-tostringtag": "^1.0.2", 3000 - "safe-regex-test": "^1.1.0" 3001 - }, 3002 - "engines": { 3003 - "node": ">= 0.4" 3004 - }, 3005 - "funding": { 3006 - "url": "https://github.com/sponsors/ljharb" 3007 - } 3008 - }, 3009 - "node_modules/is-bigint": { 3010 - "version": "1.1.0", 3011 - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", 3012 - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", 3013 - "dev": true, 3014 - "license": "MIT", 3015 - "dependencies": { 3016 - "has-bigints": "^1.0.2" 3017 - }, 3018 - "engines": { 3019 - "node": ">= 0.4" 3020 - }, 3021 - "funding": { 3022 - "url": "https://github.com/sponsors/ljharb" 3023 - } 3024 - }, 3025 - "node_modules/is-boolean-object": { 3026 - "version": "1.2.2", 3027 - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", 3028 - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", 3029 - "dev": true, 3030 - "license": "MIT", 3031 - "dependencies": { 3032 - "call-bound": "^1.0.3", 3033 - "has-tostringtag": "^1.0.2" 3034 - }, 3035 - "engines": { 3036 - "node": ">= 0.4" 3037 - }, 3038 - "funding": { 3039 - "url": "https://github.com/sponsors/ljharb" 3040 - } 3041 - }, 3042 - "node_modules/is-callable": { 3043 - "version": "1.2.7", 3044 - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", 3045 - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", 3046 - "dev": true, 3047 - "license": "MIT", 3048 - "engines": { 3049 - "node": ">= 0.4" 3050 - }, 3051 - "funding": { 3052 - "url": "https://github.com/sponsors/ljharb" 3053 - } 3054 - }, 3055 - "node_modules/is-core-module": { 3056 - "version": "2.16.1", 3057 - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", 3058 - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", 3059 - "dev": true, 3060 - "license": "MIT", 3061 - "dependencies": { 3062 - "hasown": "^2.0.2" 3063 - }, 3064 - "engines": { 3065 - "node": ">= 0.4" 3066 - }, 3067 - "funding": { 3068 - "url": "https://github.com/sponsors/ljharb" 3069 - } 3070 - }, 3071 - "node_modules/is-data-view": { 3072 - "version": "1.0.2", 3073 - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", 3074 - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", 3075 - "dev": true, 3076 - "license": "MIT", 3077 - "dependencies": { 3078 - "call-bound": "^1.0.2", 3079 - "get-intrinsic": "^1.2.6", 3080 - "is-typed-array": "^1.1.13" 3081 - }, 3082 - "engines": { 3083 - "node": ">= 0.4" 3084 - }, 3085 - "funding": { 3086 - "url": "https://github.com/sponsors/ljharb" 3087 - } 3088 - }, 3089 - "node_modules/is-date-object": { 3090 - "version": "1.1.0", 3091 - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", 3092 - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", 3093 - "dev": true, 3094 - "license": "MIT", 3095 - "dependencies": { 3096 - "call-bound": "^1.0.2", 3097 - "has-tostringtag": "^1.0.2" 3098 - }, 3099 - "engines": { 3100 - "node": ">= 0.4" 3101 - }, 3102 - "funding": { 3103 - "url": "https://github.com/sponsors/ljharb" 3104 - } 3105 - }, 3106 - "node_modules/is-extglob": { 3107 - "version": "2.1.1", 3108 - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 3109 - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 3110 - "dev": true, 3111 - "license": "MIT", 3112 - "engines": { 3113 - "node": ">=0.10.0" 3114 - } 3115 - }, 3116 - "node_modules/is-finalizationregistry": { 3117 - "version": "1.1.1", 3118 - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", 3119 - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", 3120 - "dev": true, 3121 - "license": "MIT", 3122 - "dependencies": { 3123 - "call-bound": "^1.0.3" 3124 - }, 3125 - "engines": { 3126 - "node": ">= 0.4" 3127 - }, 3128 - "funding": { 3129 - "url": "https://github.com/sponsors/ljharb" 3130 - } 3131 - }, 3132 - "node_modules/is-generator-function": { 3133 - "version": "1.1.2", 3134 - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", 3135 - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", 3136 - "dev": true, 3137 - "license": "MIT", 3138 - "dependencies": { 3139 - "call-bound": "^1.0.4", 3140 - "generator-function": "^2.0.0", 3141 - "get-proto": "^1.0.1", 3142 - "has-tostringtag": "^1.0.2", 3143 - "safe-regex-test": "^1.1.0" 3144 - }, 3145 - "engines": { 3146 - "node": ">= 0.4" 3147 - }, 3148 - "funding": { 3149 - "url": "https://github.com/sponsors/ljharb" 3150 - } 3151 - }, 3152 - "node_modules/is-glob": { 3153 - "version": "4.0.3", 3154 - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 3155 - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 3156 - "dev": true, 3157 - "license": "MIT", 3158 - "dependencies": { 3159 - "is-extglob": "^2.1.1" 3160 - }, 3161 - "engines": { 3162 - "node": ">=0.10.0" 3163 - } 3164 - }, 3165 - "node_modules/is-map": { 3166 - "version": "2.0.3", 3167 - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", 3168 - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", 3169 - "dev": true, 3170 - "license": "MIT", 3171 - "engines": { 3172 - "node": ">= 0.4" 3173 - }, 3174 - "funding": { 3175 - "url": "https://github.com/sponsors/ljharb" 3176 - } 3177 - }, 3178 - "node_modules/is-negative-zero": { 3179 - "version": "2.0.3", 3180 - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", 3181 - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", 3182 - "dev": true, 3183 - "license": "MIT", 3184 - "engines": { 3185 - "node": ">= 0.4" 3186 - }, 3187 - "funding": { 3188 - "url": "https://github.com/sponsors/ljharb" 3189 - } 3190 - }, 3191 - "node_modules/is-number-object": { 3192 - "version": "1.1.1", 3193 - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", 3194 - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", 3195 - "dev": true, 3196 - "license": "MIT", 3197 - "dependencies": { 3198 - "call-bound": "^1.0.3", 3199 - "has-tostringtag": "^1.0.2" 3200 - }, 3201 - "engines": { 3202 - "node": ">= 0.4" 3203 - }, 3204 - "funding": { 3205 - "url": "https://github.com/sponsors/ljharb" 3206 - } 3207 - }, 3208 - "node_modules/is-regex": { 3209 - "version": "1.2.1", 3210 - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", 3211 - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", 3212 - "dev": true, 3213 - "license": "MIT", 3214 - "dependencies": { 3215 - "call-bound": "^1.0.2", 3216 - "gopd": "^1.2.0", 3217 - "has-tostringtag": "^1.0.2", 3218 - "hasown": "^2.0.2" 3219 - }, 3220 - "engines": { 3221 - "node": ">= 0.4" 3222 - }, 3223 - "funding": { 3224 - "url": "https://github.com/sponsors/ljharb" 3225 - } 3226 - }, 3227 - "node_modules/is-set": { 3228 - "version": "2.0.3", 3229 - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", 3230 - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", 3231 - "dev": true, 3232 - "license": "MIT", 3233 - "engines": { 3234 - "node": ">= 0.4" 3235 - }, 3236 - "funding": { 3237 - "url": "https://github.com/sponsors/ljharb" 3238 - } 3239 - }, 3240 - "node_modules/is-shared-array-buffer": { 3241 - "version": "1.0.4", 3242 - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", 3243 - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", 3244 - "dev": true, 3245 - "license": "MIT", 3246 - "dependencies": { 3247 - "call-bound": "^1.0.3" 3248 - }, 3249 - "engines": { 3250 - "node": ">= 0.4" 3251 - }, 3252 - "funding": { 3253 - "url": "https://github.com/sponsors/ljharb" 3254 - } 3255 - }, 3256 - "node_modules/is-string": { 3257 - "version": "1.1.1", 3258 - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", 3259 - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", 3260 - "dev": true, 3261 - "license": "MIT", 3262 - "dependencies": { 3263 - "call-bound": "^1.0.3", 3264 - "has-tostringtag": "^1.0.2" 3265 - }, 3266 - "engines": { 3267 - "node": ">= 0.4" 3268 - }, 3269 - "funding": { 3270 - "url": "https://github.com/sponsors/ljharb" 3271 - } 3272 - }, 3273 - "node_modules/is-symbol": { 3274 - "version": "1.1.1", 3275 - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", 3276 - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", 3277 - "dev": true, 3278 - "license": "MIT", 3279 - "dependencies": { 3280 - "call-bound": "^1.0.2", 3281 - "has-symbols": "^1.1.0", 3282 - "safe-regex-test": "^1.1.0" 3283 - }, 3284 - "engines": { 3285 - "node": ">= 0.4" 3286 - }, 3287 - "funding": { 3288 - "url": "https://github.com/sponsors/ljharb" 3289 - } 3290 - }, 3291 - "node_modules/is-typed-array": { 3292 - "version": "1.1.15", 3293 - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", 3294 - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", 3295 - "dev": true, 3296 - "license": "MIT", 3297 - "dependencies": { 3298 - "which-typed-array": "^1.1.16" 3299 - }, 3300 - "engines": { 3301 - "node": ">= 0.4" 3302 - }, 3303 - "funding": { 3304 - "url": "https://github.com/sponsors/ljharb" 3305 - } 3306 - }, 3307 - "node_modules/is-weakmap": { 3308 - "version": "2.0.2", 3309 - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", 3310 - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", 3311 - "dev": true, 3312 - "license": "MIT", 3313 - "engines": { 3314 - "node": ">= 0.4" 3315 - }, 3316 - "funding": { 3317 - "url": "https://github.com/sponsors/ljharb" 3318 - } 3319 - }, 3320 - "node_modules/is-weakref": { 3321 - "version": "1.1.1", 3322 - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", 3323 - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", 3324 - "dev": true, 3325 - "license": "MIT", 3326 - "dependencies": { 3327 - "call-bound": "^1.0.3" 3328 - }, 3329 - "engines": { 3330 - "node": ">= 0.4" 3331 - }, 3332 - "funding": { 3333 - "url": "https://github.com/sponsors/ljharb" 3334 - } 3335 - }, 3336 - "node_modules/is-weakset": { 3337 - "version": "2.0.4", 3338 - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", 3339 - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", 3340 - "dev": true, 3341 - "license": "MIT", 3342 - "dependencies": { 3343 - "call-bound": "^1.0.3", 3344 - "get-intrinsic": "^1.2.6" 3345 - }, 3346 - "engines": { 3347 - "node": ">= 0.4" 3348 - }, 3349 - "funding": { 3350 - "url": "https://github.com/sponsors/ljharb" 3351 - } 3352 - }, 3353 - "node_modules/isarray": { 3354 - "version": "2.0.5", 3355 - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 3356 - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", 3357 - "dev": true, 3358 - "license": "MIT" 3359 - }, 3360 - "node_modules/isexe": { 3361 - "version": "2.0.0", 3362 - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 3363 - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 3364 - "dev": true, 3365 - "license": "ISC" 3366 - }, 3367 - "node_modules/iterator.prototype": { 3368 - "version": "1.1.5", 3369 - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", 3370 - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", 3371 - "dev": true, 3372 - "license": "MIT", 3373 - "dependencies": { 3374 - "define-data-property": "^1.1.4", 3375 - "es-object-atoms": "^1.0.0", 3376 - "get-intrinsic": "^1.2.6", 3377 - "get-proto": "^1.0.0", 3378 - "has-symbols": "^1.1.0", 3379 - "set-function-name": "^2.0.2" 3380 - }, 3381 - "engines": { 3382 - "node": ">= 0.4" 3383 - } 3384 - }, 3385 - "node_modules/js-tokens": { 3386 - "version": "4.0.0", 3387 - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 3388 - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 3389 - "license": "MIT" 3390 - }, 3391 - "node_modules/js-yaml": { 3392 - "version": "4.1.1", 3393 - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", 3394 - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", 3395 - "dev": true, 3396 - "license": "MIT", 3397 - "dependencies": { 3398 - "argparse": "^2.0.1" 3399 - }, 3400 - "bin": { 3401 - "js-yaml": "bin/js-yaml.js" 3402 - } 3403 - }, 3404 - "node_modules/jsesc": { 3405 - "version": "3.1.0", 3406 - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", 3407 - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", 3408 - "dev": true, 3409 - "license": "MIT", 3410 - "bin": { 3411 - "jsesc": "bin/jsesc" 3412 - }, 3413 - "engines": { 3414 - "node": ">=6" 3415 - } 3416 - }, 3417 - "node_modules/json-buffer": { 3418 - "version": "3.0.1", 3419 - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 3420 - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 3421 - "dev": true, 3422 - "license": "MIT" 3423 - }, 3424 - "node_modules/json-schema-traverse": { 3425 - "version": "0.4.1", 3426 - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 3427 - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 3428 - "dev": true, 3429 - "license": "MIT" 3430 - }, 3431 - "node_modules/json-stable-stringify-without-jsonify": { 3432 - "version": "1.0.1", 3433 - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 3434 - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 3435 - "dev": true, 3436 - "license": "MIT" 3437 - }, 3438 - "node_modules/json5": { 3439 - "version": "2.2.3", 3440 - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", 3441 - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", 3442 - "dev": true, 3443 - "license": "MIT", 3444 - "bin": { 3445 - "json5": "lib/cli.js" 3446 - }, 3447 - "engines": { 3448 - "node": ">=6" 3449 - } 3450 - }, 3451 - "node_modules/jsx-ast-utils": { 3452 - "version": "3.3.5", 3453 - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", 3454 - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", 3455 - "dev": true, 3456 - "license": "MIT", 3457 - "dependencies": { 3458 - "array-includes": "^3.1.6", 3459 - "array.prototype.flat": "^1.3.1", 3460 - "object.assign": "^4.1.4", 3461 - "object.values": "^1.1.6" 3462 - }, 3463 - "engines": { 3464 - "node": ">=4.0" 3465 - } 3466 - }, 3467 - "node_modules/keyv": { 3468 - "version": "4.5.4", 3469 - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 3470 - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 3471 - "dev": true, 3472 - "license": "MIT", 3473 - "dependencies": { 3474 - "json-buffer": "3.0.1" 3475 - } 3476 - }, 3477 - "node_modules/levn": { 3478 - "version": "0.4.1", 3479 - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 3480 - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 3481 - "dev": true, 3482 - "license": "MIT", 3483 - "dependencies": { 3484 - "prelude-ls": "^1.2.1", 3485 - "type-check": "~0.4.0" 3486 - }, 3487 - "engines": { 3488 - "node": ">= 0.8.0" 3489 - } 3490 - }, 3491 - "node_modules/locate-path": { 3492 - "version": "6.0.0", 3493 - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 3494 - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 3495 - "dev": true, 3496 - "license": "MIT", 3497 - "dependencies": { 3498 - "p-locate": "^5.0.0" 3499 - }, 3500 - "engines": { 3501 - "node": ">=10" 3502 - }, 3503 - "funding": { 3504 - "url": "https://github.com/sponsors/sindresorhus" 3505 - } 3506 - }, 3507 - "node_modules/lodash.merge": { 3508 - "version": "4.6.2", 3509 - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 3510 - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 3511 - "dev": true, 3512 - "license": "MIT" 3513 - }, 3514 - "node_modules/loose-envify": { 3515 - "version": "1.4.0", 3516 - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 3517 - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 3518 - "license": "MIT", 3519 - "dependencies": { 3520 - "js-tokens": "^3.0.0 || ^4.0.0" 3521 - }, 3522 - "bin": { 3523 - "loose-envify": "cli.js" 3524 - } 3525 - }, 3526 - "node_modules/lru-cache": { 3527 - "version": "5.1.1", 3528 - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 3529 - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 3530 - "dev": true, 3531 - "license": "ISC", 3532 - "dependencies": { 3533 - "yallist": "^3.0.2" 3534 - } 3535 - }, 3536 - "node_modules/lucide-react": { 3537 - "version": "0.562.0", 3538 - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz", 3539 - "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==", 3540 - "license": "ISC", 3541 - "peerDependencies": { 3542 - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" 3543 - } 3544 - }, 3545 - "node_modules/math-intrinsics": { 3546 - "version": "1.1.0", 3547 - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 3548 - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 3549 - "dev": true, 3550 - "license": "MIT", 3551 - "engines": { 3552 - "node": ">= 0.4" 3553 - } 3554 - }, 3555 - "node_modules/minimatch": { 3556 - "version": "3.1.2", 3557 - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 3558 - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 3559 - "dev": true, 3560 - "license": "ISC", 3561 - "dependencies": { 3562 - "brace-expansion": "^1.1.7" 3563 - }, 3564 - "engines": { 3565 - "node": "*" 3566 - } 3567 - }, 3568 - "node_modules/ms": { 3569 - "version": "2.1.3", 3570 - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 3571 - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 3572 - "dev": true, 3573 - "license": "MIT" 3574 - }, 3575 - "node_modules/nanoid": { 3576 - "version": "3.3.11", 3577 - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 3578 - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 3579 - "dev": true, 3580 - "funding": [ 3581 - { 3582 - "type": "github", 3583 - "url": "https://github.com/sponsors/ai" 3584 - } 3585 - ], 3586 - "license": "MIT", 3587 - "bin": { 3588 - "nanoid": "bin/nanoid.cjs" 3589 - }, 3590 - "engines": { 3591 - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 3592 - } 3593 - }, 3594 - "node_modules/natural-compare": { 3595 - "version": "1.4.0", 3596 - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 3597 - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 3598 - "dev": true, 3599 - "license": "MIT" 3600 - }, 3601 - "node_modules/node-releases": { 3602 - "version": "2.0.27", 3603 - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", 3604 - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", 3605 - "dev": true, 3606 - "license": "MIT" 3607 - }, 3608 - "node_modules/object-assign": { 3609 - "version": "4.1.1", 3610 - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 3611 - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 3612 - "dev": true, 3613 - "license": "MIT", 3614 - "engines": { 3615 - "node": ">=0.10.0" 3616 - } 3617 - }, 3618 - "node_modules/object-inspect": { 3619 - "version": "1.13.4", 3620 - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", 3621 - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", 3622 - "dev": true, 3623 - "license": "MIT", 3624 - "engines": { 3625 - "node": ">= 0.4" 3626 - }, 3627 - "funding": { 3628 - "url": "https://github.com/sponsors/ljharb" 3629 - } 3630 - }, 3631 - "node_modules/object-keys": { 3632 - "version": "1.1.1", 3633 - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 3634 - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 3635 - "dev": true, 3636 - "license": "MIT", 3637 - "engines": { 3638 - "node": ">= 0.4" 3639 - } 3640 - }, 3641 - "node_modules/object.assign": { 3642 - "version": "4.1.7", 3643 - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", 3644 - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", 3645 - "dev": true, 3646 - "license": "MIT", 3647 - "dependencies": { 3648 - "call-bind": "^1.0.8", 3649 - "call-bound": "^1.0.3", 3650 - "define-properties": "^1.2.1", 3651 - "es-object-atoms": "^1.0.0", 3652 - "has-symbols": "^1.1.0", 3653 - "object-keys": "^1.1.1" 3654 - }, 3655 - "engines": { 3656 - "node": ">= 0.4" 3657 - }, 3658 - "funding": { 3659 - "url": "https://github.com/sponsors/ljharb" 3660 - } 3661 - }, 3662 - "node_modules/object.entries": { 3663 - "version": "1.1.9", 3664 - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", 3665 - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", 3666 - "dev": true, 3667 - "license": "MIT", 3668 - "dependencies": { 3669 - "call-bind": "^1.0.8", 3670 - "call-bound": "^1.0.4", 3671 - "define-properties": "^1.2.1", 3672 - "es-object-atoms": "^1.1.1" 3673 - }, 3674 - "engines": { 3675 - "node": ">= 0.4" 3676 - } 3677 - }, 3678 - "node_modules/object.fromentries": { 3679 - "version": "2.0.8", 3680 - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", 3681 - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", 3682 - "dev": true, 3683 - "license": "MIT", 3684 - "dependencies": { 3685 - "call-bind": "^1.0.7", 3686 - "define-properties": "^1.2.1", 3687 - "es-abstract": "^1.23.2", 3688 - "es-object-atoms": "^1.0.0" 3689 - }, 3690 - "engines": { 3691 - "node": ">= 0.4" 3692 - }, 3693 - "funding": { 3694 - "url": "https://github.com/sponsors/ljharb" 3695 - } 3696 - }, 3697 - "node_modules/object.values": { 3698 - "version": "1.2.1", 3699 - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", 3700 - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", 3701 - "dev": true, 3702 - "license": "MIT", 3703 - "dependencies": { 3704 - "call-bind": "^1.0.8", 3705 - "call-bound": "^1.0.3", 3706 - "define-properties": "^1.2.1", 3707 - "es-object-atoms": "^1.0.0" 3708 - }, 3709 - "engines": { 3710 - "node": ">= 0.4" 3711 - }, 3712 - "funding": { 3713 - "url": "https://github.com/sponsors/ljharb" 3714 - } 3715 - }, 3716 - "node_modules/optionator": { 3717 - "version": "0.9.4", 3718 - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", 3719 - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 3720 - "dev": true, 3721 - "license": "MIT", 3722 - "dependencies": { 3723 - "deep-is": "^0.1.3", 3724 - "fast-levenshtein": "^2.0.6", 3725 - "levn": "^0.4.1", 3726 - "prelude-ls": "^1.2.1", 3727 - "type-check": "^0.4.0", 3728 - "word-wrap": "^1.2.5" 3729 - }, 3730 - "engines": { 3731 - "node": ">= 0.8.0" 3732 - } 3733 - }, 3734 - "node_modules/own-keys": { 3735 - "version": "1.0.1", 3736 - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", 3737 - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", 3738 - "dev": true, 3739 - "license": "MIT", 3740 - "dependencies": { 3741 - "get-intrinsic": "^1.2.6", 3742 - "object-keys": "^1.1.1", 3743 - "safe-push-apply": "^1.0.0" 3744 - }, 3745 - "engines": { 3746 - "node": ">= 0.4" 3747 - }, 3748 - "funding": { 3749 - "url": "https://github.com/sponsors/ljharb" 3750 - } 3751 - }, 3752 - "node_modules/p-limit": { 3753 - "version": "3.1.0", 3754 - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 3755 - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 3756 - "dev": true, 3757 - "license": "MIT", 3758 - "dependencies": { 3759 - "yocto-queue": "^0.1.0" 3760 - }, 3761 - "engines": { 3762 - "node": ">=10" 3763 - }, 3764 - "funding": { 3765 - "url": "https://github.com/sponsors/sindresorhus" 3766 - } 3767 - }, 3768 - "node_modules/p-locate": { 3769 - "version": "5.0.0", 3770 - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 3771 - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 3772 - "dev": true, 3773 - "license": "MIT", 3774 - "dependencies": { 3775 - "p-limit": "^3.0.2" 3776 - }, 3777 - "engines": { 3778 - "node": ">=10" 3779 - }, 3780 - "funding": { 3781 - "url": "https://github.com/sponsors/sindresorhus" 3782 - } 3783 - }, 3784 - "node_modules/parent-module": { 3785 - "version": "1.0.1", 3786 - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 3787 - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 3788 - "dev": true, 3789 - "license": "MIT", 3790 - "dependencies": { 3791 - "callsites": "^3.0.0" 3792 - }, 3793 - "engines": { 3794 - "node": ">=6" 3795 - } 3796 - }, 3797 - "node_modules/path-exists": { 3798 - "version": "4.0.0", 3799 - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 3800 - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 3801 - "dev": true, 3802 - "license": "MIT", 3803 - "engines": { 3804 - "node": ">=8" 3805 - } 3806 - }, 3807 - "node_modules/path-key": { 3808 - "version": "3.1.1", 3809 - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 3810 - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 3811 - "dev": true, 3812 - "license": "MIT", 3813 - "engines": { 3814 - "node": ">=8" 3815 - } 3816 - }, 3817 - "node_modules/path-parse": { 3818 - "version": "1.0.7", 3819 - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 3820 - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 3821 - "dev": true, 3822 - "license": "MIT" 3823 - }, 3824 - "node_modules/picocolors": { 3825 - "version": "1.1.1", 3826 - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 3827 - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 3828 - "dev": true, 3829 - "license": "ISC" 3830 - }, 3831 - "node_modules/picomatch": { 3832 - "version": "4.0.3", 3833 - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 3834 - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 3835 - "dev": true, 3836 - "license": "MIT", 3837 - "peer": true, 3838 - "engines": { 3839 - "node": ">=12" 3840 - }, 3841 - "funding": { 3842 - "url": "https://github.com/sponsors/jonschlinkert" 3843 - } 3844 - }, 3845 - "node_modules/possible-typed-array-names": { 3846 - "version": "1.1.0", 3847 - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", 3848 - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", 3849 - "dev": true, 3850 - "license": "MIT", 3851 - "engines": { 3852 - "node": ">= 0.4" 3853 - } 3854 - }, 3855 - "node_modules/postcss": { 3856 - "version": "8.5.6", 3857 - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", 3858 - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", 3859 - "dev": true, 3860 - "funding": [ 3861 - { 3862 - "type": "opencollective", 3863 - "url": "https://opencollective.com/postcss/" 3864 - }, 3865 - { 3866 - "type": "tidelift", 3867 - "url": "https://tidelift.com/funding/github/npm/postcss" 3868 - }, 3869 - { 3870 - "type": "github", 3871 - "url": "https://github.com/sponsors/ai" 3872 - } 3873 - ], 3874 - "license": "MIT", 3875 - "dependencies": { 3876 - "nanoid": "^3.3.11", 3877 - "picocolors": "^1.1.1", 3878 - "source-map-js": "^1.2.1" 3879 - }, 3880 - "engines": { 3881 - "node": "^10 || ^12 || >=14" 3882 - } 3883 - }, 3884 - "node_modules/prelude-ls": { 3885 - "version": "1.2.1", 3886 - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 3887 - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 3888 - "dev": true, 3889 - "license": "MIT", 3890 - "engines": { 3891 - "node": ">= 0.8.0" 3892 - } 3893 - }, 3894 - "node_modules/prop-types": { 3895 - "version": "15.8.1", 3896 - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", 3897 - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", 3898 - "dev": true, 3899 - "license": "MIT", 3900 - "dependencies": { 3901 - "loose-envify": "^1.4.0", 3902 - "object-assign": "^4.1.1", 3903 - "react-is": "^16.13.1" 3904 - } 3905 - }, 3906 - "node_modules/punycode": { 3907 - "version": "2.3.1", 3908 - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 3909 - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 3910 - "dev": true, 3911 - "license": "MIT", 3912 - "engines": { 3913 - "node": ">=6" 3914 - } 3915 - }, 3916 - "node_modules/react": { 3917 - "version": "18.3.1", 3918 - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", 3919 - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", 3920 - "license": "MIT", 3921 - "peer": true, 3922 - "dependencies": { 3923 - "loose-envify": "^1.1.0" 3924 - }, 3925 - "engines": { 3926 - "node": ">=0.10.0" 3927 - } 3928 - }, 3929 - "node_modules/react-dom": { 3930 - "version": "18.3.1", 3931 - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", 3932 - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", 3933 - "license": "MIT", 3934 - "peer": true, 3935 - "dependencies": { 3936 - "loose-envify": "^1.1.0", 3937 - "scheduler": "^0.23.2" 3938 - }, 3939 - "peerDependencies": { 3940 - "react": "^18.3.1" 3941 - } 3942 - }, 3943 - "node_modules/react-icons": { 3944 - "version": "5.5.0", 3945 - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", 3946 - "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", 3947 - "license": "MIT", 3948 - "peerDependencies": { 3949 - "react": "*" 3950 - } 3951 - }, 3952 - "node_modules/react-is": { 3953 - "version": "16.13.1", 3954 - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 3955 - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", 3956 - "dev": true, 3957 - "license": "MIT" 3958 - }, 3959 - "node_modules/react-refresh": { 3960 - "version": "0.17.0", 3961 - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", 3962 - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", 3963 - "dev": true, 3964 - "license": "MIT", 3965 - "engines": { 3966 - "node": ">=0.10.0" 3967 - } 3968 - }, 3969 - "node_modules/react-router": { 3970 - "version": "6.30.3", 3971 - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", 3972 - "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", 3973 - "license": "MIT", 3974 - "dependencies": { 3975 - "@remix-run/router": "1.23.2" 3976 - }, 3977 - "engines": { 3978 - "node": ">=14.0.0" 3979 - }, 3980 - "peerDependencies": { 3981 - "react": ">=16.8" 3982 - } 3983 - }, 3984 - "node_modules/react-router-dom": { 3985 - "version": "6.30.3", 3986 - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", 3987 - "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", 3988 - "license": "MIT", 3989 - "dependencies": { 3990 - "@remix-run/router": "1.23.2", 3991 - "react-router": "6.30.3" 3992 - }, 3993 - "engines": { 3994 - "node": ">=14.0.0" 3995 - }, 3996 - "peerDependencies": { 3997 - "react": ">=16.8", 3998 - "react-dom": ">=16.8" 3999 - } 4000 - }, 4001 - "node_modules/reflect.getprototypeof": { 4002 - "version": "1.0.10", 4003 - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", 4004 - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", 4005 - "dev": true, 4006 - "license": "MIT", 4007 - "dependencies": { 4008 - "call-bind": "^1.0.8", 4009 - "define-properties": "^1.2.1", 4010 - "es-abstract": "^1.23.9", 4011 - "es-errors": "^1.3.0", 4012 - "es-object-atoms": "^1.0.0", 4013 - "get-intrinsic": "^1.2.7", 4014 - "get-proto": "^1.0.1", 4015 - "which-builtin-type": "^1.2.1" 4016 - }, 4017 - "engines": { 4018 - "node": ">= 0.4" 4019 - }, 4020 - "funding": { 4021 - "url": "https://github.com/sponsors/ljharb" 4022 - } 4023 - }, 4024 - "node_modules/regexp.prototype.flags": { 4025 - "version": "1.5.4", 4026 - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", 4027 - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", 4028 - "dev": true, 4029 - "license": "MIT", 4030 - "dependencies": { 4031 - "call-bind": "^1.0.8", 4032 - "define-properties": "^1.2.1", 4033 - "es-errors": "^1.3.0", 4034 - "get-proto": "^1.0.1", 4035 - "gopd": "^1.2.0", 4036 - "set-function-name": "^2.0.2" 4037 - }, 4038 - "engines": { 4039 - "node": ">= 0.4" 4040 - }, 4041 - "funding": { 4042 - "url": "https://github.com/sponsors/ljharb" 4043 - } 4044 - }, 4045 - "node_modules/resolve": { 4046 - "version": "2.0.0-next.5", 4047 - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", 4048 - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", 4049 - "dev": true, 4050 - "license": "MIT", 4051 - "dependencies": { 4052 - "is-core-module": "^2.13.0", 4053 - "path-parse": "^1.0.7", 4054 - "supports-preserve-symlinks-flag": "^1.0.0" 4055 - }, 4056 - "bin": { 4057 - "resolve": "bin/resolve" 4058 - }, 4059 - "funding": { 4060 - "url": "https://github.com/sponsors/ljharb" 4061 - } 4062 - }, 4063 - "node_modules/resolve-from": { 4064 - "version": "4.0.0", 4065 - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 4066 - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 4067 - "dev": true, 4068 - "license": "MIT", 4069 - "engines": { 4070 - "node": ">=4" 4071 - } 4072 - }, 4073 - "node_modules/rollup": { 4074 - "version": "4.54.0", 4075 - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", 4076 - "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", 4077 - "dev": true, 4078 - "license": "MIT", 4079 - "dependencies": { 4080 - "@types/estree": "1.0.8" 4081 - }, 4082 - "bin": { 4083 - "rollup": "dist/bin/rollup" 4084 - }, 4085 - "engines": { 4086 - "node": ">=18.0.0", 4087 - "npm": ">=8.0.0" 4088 - }, 4089 - "optionalDependencies": { 4090 - "@rollup/rollup-android-arm-eabi": "4.54.0", 4091 - "@rollup/rollup-android-arm64": "4.54.0", 4092 - "@rollup/rollup-darwin-arm64": "4.54.0", 4093 - "@rollup/rollup-darwin-x64": "4.54.0", 4094 - "@rollup/rollup-freebsd-arm64": "4.54.0", 4095 - "@rollup/rollup-freebsd-x64": "4.54.0", 4096 - "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", 4097 - "@rollup/rollup-linux-arm-musleabihf": "4.54.0", 4098 - "@rollup/rollup-linux-arm64-gnu": "4.54.0", 4099 - "@rollup/rollup-linux-arm64-musl": "4.54.0", 4100 - "@rollup/rollup-linux-loong64-gnu": "4.54.0", 4101 - "@rollup/rollup-linux-ppc64-gnu": "4.54.0", 4102 - "@rollup/rollup-linux-riscv64-gnu": "4.54.0", 4103 - "@rollup/rollup-linux-riscv64-musl": "4.54.0", 4104 - "@rollup/rollup-linux-s390x-gnu": "4.54.0", 4105 - "@rollup/rollup-linux-x64-gnu": "4.54.0", 4106 - "@rollup/rollup-linux-x64-musl": "4.54.0", 4107 - "@rollup/rollup-openharmony-arm64": "4.54.0", 4108 - "@rollup/rollup-win32-arm64-msvc": "4.54.0", 4109 - "@rollup/rollup-win32-ia32-msvc": "4.54.0", 4110 - "@rollup/rollup-win32-x64-gnu": "4.54.0", 4111 - "@rollup/rollup-win32-x64-msvc": "4.54.0", 4112 - "fsevents": "~2.3.2" 4113 - } 4114 - }, 4115 - "node_modules/safe-array-concat": { 4116 - "version": "1.1.3", 4117 - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", 4118 - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", 4119 - "dev": true, 4120 - "license": "MIT", 4121 - "dependencies": { 4122 - "call-bind": "^1.0.8", 4123 - "call-bound": "^1.0.2", 4124 - "get-intrinsic": "^1.2.6", 4125 - "has-symbols": "^1.1.0", 4126 - "isarray": "^2.0.5" 4127 - }, 4128 - "engines": { 4129 - "node": ">=0.4" 4130 - }, 4131 - "funding": { 4132 - "url": "https://github.com/sponsors/ljharb" 4133 - } 4134 - }, 4135 - "node_modules/safe-push-apply": { 4136 - "version": "1.0.0", 4137 - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", 4138 - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", 4139 - "dev": true, 4140 - "license": "MIT", 4141 - "dependencies": { 4142 - "es-errors": "^1.3.0", 4143 - "isarray": "^2.0.5" 4144 - }, 4145 - "engines": { 4146 - "node": ">= 0.4" 4147 - }, 4148 - "funding": { 4149 - "url": "https://github.com/sponsors/ljharb" 4150 - } 4151 - }, 4152 - "node_modules/safe-regex-test": { 4153 - "version": "1.1.0", 4154 - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", 4155 - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", 4156 - "dev": true, 4157 - "license": "MIT", 4158 - "dependencies": { 4159 - "call-bound": "^1.0.2", 4160 - "es-errors": "^1.3.0", 4161 - "is-regex": "^1.2.1" 4162 - }, 4163 - "engines": { 4164 - "node": ">= 0.4" 4165 - }, 4166 - "funding": { 4167 - "url": "https://github.com/sponsors/ljharb" 4168 - } 4169 - }, 4170 - "node_modules/scheduler": { 4171 - "version": "0.23.2", 4172 - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", 4173 - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", 4174 - "license": "MIT", 4175 - "dependencies": { 4176 - "loose-envify": "^1.1.0" 4177 - } 4178 - }, 4179 - "node_modules/semver": { 4180 - "version": "6.3.1", 4181 - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", 4182 - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", 4183 - "dev": true, 4184 - "license": "ISC", 4185 - "bin": { 4186 - "semver": "bin/semver.js" 4187 - } 4188 - }, 4189 - "node_modules/set-function-length": { 4190 - "version": "1.2.2", 4191 - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 4192 - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 4193 - "dev": true, 4194 - "license": "MIT", 4195 - "dependencies": { 4196 - "define-data-property": "^1.1.4", 4197 - "es-errors": "^1.3.0", 4198 - "function-bind": "^1.1.2", 4199 - "get-intrinsic": "^1.2.4", 4200 - "gopd": "^1.0.1", 4201 - "has-property-descriptors": "^1.0.2" 4202 - }, 4203 - "engines": { 4204 - "node": ">= 0.4" 4205 - } 4206 - }, 4207 - "node_modules/set-function-name": { 4208 - "version": "2.0.2", 4209 - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", 4210 - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", 4211 - "dev": true, 4212 - "license": "MIT", 4213 - "dependencies": { 4214 - "define-data-property": "^1.1.4", 4215 - "es-errors": "^1.3.0", 4216 - "functions-have-names": "^1.2.3", 4217 - "has-property-descriptors": "^1.0.2" 4218 - }, 4219 - "engines": { 4220 - "node": ">= 0.4" 4221 - } 4222 - }, 4223 - "node_modules/set-proto": { 4224 - "version": "1.0.0", 4225 - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", 4226 - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", 4227 - "dev": true, 4228 - "license": "MIT", 4229 - "dependencies": { 4230 - "dunder-proto": "^1.0.1", 4231 - "es-errors": "^1.3.0", 4232 - "es-object-atoms": "^1.0.0" 4233 - }, 4234 - "engines": { 4235 - "node": ">= 0.4" 4236 - } 4237 - }, 4238 - "node_modules/shebang-command": { 4239 - "version": "2.0.0", 4240 - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 4241 - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 4242 - "dev": true, 4243 - "license": "MIT", 4244 - "dependencies": { 4245 - "shebang-regex": "^3.0.0" 4246 - }, 4247 - "engines": { 4248 - "node": ">=8" 4249 - } 4250 - }, 4251 - "node_modules/shebang-regex": { 4252 - "version": "3.0.0", 4253 - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 4254 - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 4255 - "dev": true, 4256 - "license": "MIT", 4257 - "engines": { 4258 - "node": ">=8" 4259 - } 4260 - }, 4261 - "node_modules/side-channel": { 4262 - "version": "1.1.0", 4263 - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", 4264 - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", 4265 - "dev": true, 4266 - "license": "MIT", 4267 - "dependencies": { 4268 - "es-errors": "^1.3.0", 4269 - "object-inspect": "^1.13.3", 4270 - "side-channel-list": "^1.0.0", 4271 - "side-channel-map": "^1.0.1", 4272 - "side-channel-weakmap": "^1.0.2" 4273 - }, 4274 - "engines": { 4275 - "node": ">= 0.4" 4276 - }, 4277 - "funding": { 4278 - "url": "https://github.com/sponsors/ljharb" 4279 - } 4280 - }, 4281 - "node_modules/side-channel-list": { 4282 - "version": "1.0.0", 4283 - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", 4284 - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", 4285 - "dev": true, 4286 - "license": "MIT", 4287 - "dependencies": { 4288 - "es-errors": "^1.3.0", 4289 - "object-inspect": "^1.13.3" 4290 - }, 4291 - "engines": { 4292 - "node": ">= 0.4" 4293 - }, 4294 - "funding": { 4295 - "url": "https://github.com/sponsors/ljharb" 4296 - } 4297 - }, 4298 - "node_modules/side-channel-map": { 4299 - "version": "1.0.1", 4300 - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", 4301 - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", 4302 - "dev": true, 4303 - "license": "MIT", 4304 - "dependencies": { 4305 - "call-bound": "^1.0.2", 4306 - "es-errors": "^1.3.0", 4307 - "get-intrinsic": "^1.2.5", 4308 - "object-inspect": "^1.13.3" 4309 - }, 4310 - "engines": { 4311 - "node": ">= 0.4" 4312 - }, 4313 - "funding": { 4314 - "url": "https://github.com/sponsors/ljharb" 4315 - } 4316 - }, 4317 - "node_modules/side-channel-weakmap": { 4318 - "version": "1.0.2", 4319 - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", 4320 - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", 4321 - "dev": true, 4322 - "license": "MIT", 4323 - "dependencies": { 4324 - "call-bound": "^1.0.2", 4325 - "es-errors": "^1.3.0", 4326 - "get-intrinsic": "^1.2.5", 4327 - "object-inspect": "^1.13.3", 4328 - "side-channel-map": "^1.0.1" 4329 - }, 4330 - "engines": { 4331 - "node": ">= 0.4" 4332 - }, 4333 - "funding": { 4334 - "url": "https://github.com/sponsors/ljharb" 4335 - } 4336 - }, 4337 - "node_modules/source-map-js": { 4338 - "version": "1.2.1", 4339 - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 4340 - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 4341 - "dev": true, 4342 - "license": "BSD-3-Clause", 4343 - "engines": { 4344 - "node": ">=0.10.0" 4345 - } 4346 - }, 4347 - "node_modules/stop-iteration-iterator": { 4348 - "version": "1.1.0", 4349 - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", 4350 - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", 4351 - "dev": true, 4352 - "license": "MIT", 4353 - "dependencies": { 4354 - "es-errors": "^1.3.0", 4355 - "internal-slot": "^1.1.0" 4356 - }, 4357 - "engines": { 4358 - "node": ">= 0.4" 4359 - } 4360 - }, 4361 - "node_modules/string.prototype.matchall": { 4362 - "version": "4.0.12", 4363 - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", 4364 - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", 4365 - "dev": true, 4366 - "license": "MIT", 4367 - "dependencies": { 4368 - "call-bind": "^1.0.8", 4369 - "call-bound": "^1.0.3", 4370 - "define-properties": "^1.2.1", 4371 - "es-abstract": "^1.23.6", 4372 - "es-errors": "^1.3.0", 4373 - "es-object-atoms": "^1.0.0", 4374 - "get-intrinsic": "^1.2.6", 4375 - "gopd": "^1.2.0", 4376 - "has-symbols": "^1.1.0", 4377 - "internal-slot": "^1.1.0", 4378 - "regexp.prototype.flags": "^1.5.3", 4379 - "set-function-name": "^2.0.2", 4380 - "side-channel": "^1.1.0" 4381 - }, 4382 - "engines": { 4383 - "node": ">= 0.4" 4384 - }, 4385 - "funding": { 4386 - "url": "https://github.com/sponsors/ljharb" 4387 - } 4388 - }, 4389 - "node_modules/string.prototype.repeat": { 4390 - "version": "1.0.0", 4391 - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", 4392 - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", 4393 - "dev": true, 4394 - "license": "MIT", 4395 - "dependencies": { 4396 - "define-properties": "^1.1.3", 4397 - "es-abstract": "^1.17.5" 4398 - } 4399 - }, 4400 - "node_modules/string.prototype.trim": { 4401 - "version": "1.2.10", 4402 - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", 4403 - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", 4404 - "dev": true, 4405 - "license": "MIT", 4406 - "dependencies": { 4407 - "call-bind": "^1.0.8", 4408 - "call-bound": "^1.0.2", 4409 - "define-data-property": "^1.1.4", 4410 - "define-properties": "^1.2.1", 4411 - "es-abstract": "^1.23.5", 4412 - "es-object-atoms": "^1.0.0", 4413 - "has-property-descriptors": "^1.0.2" 4414 - }, 4415 - "engines": { 4416 - "node": ">= 0.4" 4417 - }, 4418 - "funding": { 4419 - "url": "https://github.com/sponsors/ljharb" 4420 - } 4421 - }, 4422 - "node_modules/string.prototype.trimend": { 4423 - "version": "1.0.9", 4424 - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", 4425 - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", 4426 - "dev": true, 4427 - "license": "MIT", 4428 - "dependencies": { 4429 - "call-bind": "^1.0.8", 4430 - "call-bound": "^1.0.2", 4431 - "define-properties": "^1.2.1", 4432 - "es-object-atoms": "^1.0.0" 4433 - }, 4434 - "engines": { 4435 - "node": ">= 0.4" 4436 - }, 4437 - "funding": { 4438 - "url": "https://github.com/sponsors/ljharb" 4439 - } 4440 - }, 4441 - "node_modules/string.prototype.trimstart": { 4442 - "version": "1.0.8", 4443 - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", 4444 - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", 4445 - "dev": true, 4446 - "license": "MIT", 4447 - "dependencies": { 4448 - "call-bind": "^1.0.7", 4449 - "define-properties": "^1.2.1", 4450 - "es-object-atoms": "^1.0.0" 4451 - }, 4452 - "engines": { 4453 - "node": ">= 0.4" 4454 - }, 4455 - "funding": { 4456 - "url": "https://github.com/sponsors/ljharb" 4457 - } 4458 - }, 4459 - "node_modules/strip-json-comments": { 4460 - "version": "3.1.1", 4461 - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 4462 - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 4463 - "dev": true, 4464 - "license": "MIT", 4465 - "engines": { 4466 - "node": ">=8" 4467 - }, 4468 - "funding": { 4469 - "url": "https://github.com/sponsors/sindresorhus" 4470 - } 4471 - }, 4472 - "node_modules/supports-color": { 4473 - "version": "7.2.0", 4474 - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 4475 - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 4476 - "dev": true, 4477 - "license": "MIT", 4478 - "dependencies": { 4479 - "has-flag": "^4.0.0" 4480 - }, 4481 - "engines": { 4482 - "node": ">=8" 4483 - } 4484 - }, 4485 - "node_modules/supports-preserve-symlinks-flag": { 4486 - "version": "1.0.0", 4487 - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 4488 - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 4489 - "dev": true, 4490 - "license": "MIT", 4491 - "engines": { 4492 - "node": ">= 0.4" 4493 - }, 4494 - "funding": { 4495 - "url": "https://github.com/sponsors/ljharb" 4496 - } 4497 - }, 4498 - "node_modules/tinyglobby": { 4499 - "version": "0.2.15", 4500 - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", 4501 - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 4502 - "dev": true, 4503 - "license": "MIT", 4504 - "dependencies": { 4505 - "fdir": "^6.5.0", 4506 - "picomatch": "^4.0.3" 4507 - }, 4508 - "engines": { 4509 - "node": ">=12.0.0" 4510 - }, 4511 - "funding": { 4512 - "url": "https://github.com/sponsors/SuperchupuDev" 4513 - } 4514 - }, 4515 - "node_modules/type-check": { 4516 - "version": "0.4.0", 4517 - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 4518 - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 4519 - "dev": true, 4520 - "license": "MIT", 4521 - "dependencies": { 4522 - "prelude-ls": "^1.2.1" 4523 - }, 4524 - "engines": { 4525 - "node": ">= 0.8.0" 4526 - } 4527 - }, 4528 - "node_modules/typed-array-buffer": { 4529 - "version": "1.0.3", 4530 - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", 4531 - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", 4532 - "dev": true, 4533 - "license": "MIT", 4534 - "dependencies": { 4535 - "call-bound": "^1.0.3", 4536 - "es-errors": "^1.3.0", 4537 - "is-typed-array": "^1.1.14" 4538 - }, 4539 - "engines": { 4540 - "node": ">= 0.4" 4541 - } 4542 - }, 4543 - "node_modules/typed-array-byte-length": { 4544 - "version": "1.0.3", 4545 - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", 4546 - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", 4547 - "dev": true, 4548 - "license": "MIT", 4549 - "dependencies": { 4550 - "call-bind": "^1.0.8", 4551 - "for-each": "^0.3.3", 4552 - "gopd": "^1.2.0", 4553 - "has-proto": "^1.2.0", 4554 - "is-typed-array": "^1.1.14" 4555 - }, 4556 - "engines": { 4557 - "node": ">= 0.4" 4558 - }, 4559 - "funding": { 4560 - "url": "https://github.com/sponsors/ljharb" 4561 - } 4562 - }, 4563 - "node_modules/typed-array-byte-offset": { 4564 - "version": "1.0.4", 4565 - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", 4566 - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", 4567 - "dev": true, 4568 - "license": "MIT", 4569 - "dependencies": { 4570 - "available-typed-arrays": "^1.0.7", 4571 - "call-bind": "^1.0.8", 4572 - "for-each": "^0.3.3", 4573 - "gopd": "^1.2.0", 4574 - "has-proto": "^1.2.0", 4575 - "is-typed-array": "^1.1.15", 4576 - "reflect.getprototypeof": "^1.0.9" 4577 - }, 4578 - "engines": { 4579 - "node": ">= 0.4" 4580 - }, 4581 - "funding": { 4582 - "url": "https://github.com/sponsors/ljharb" 4583 - } 4584 - }, 4585 - "node_modules/typed-array-length": { 4586 - "version": "1.0.7", 4587 - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", 4588 - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", 4589 - "dev": true, 4590 - "license": "MIT", 4591 - "dependencies": { 4592 - "call-bind": "^1.0.7", 4593 - "for-each": "^0.3.3", 4594 - "gopd": "^1.0.1", 4595 - "is-typed-array": "^1.1.13", 4596 - "possible-typed-array-names": "^1.0.0", 4597 - "reflect.getprototypeof": "^1.0.6" 4598 - }, 4599 - "engines": { 4600 - "node": ">= 0.4" 4601 - }, 4602 - "funding": { 4603 - "url": "https://github.com/sponsors/ljharb" 4604 - } 4605 - }, 4606 - "node_modules/unbox-primitive": { 4607 - "version": "1.1.0", 4608 - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", 4609 - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", 4610 - "dev": true, 4611 - "license": "MIT", 4612 - "dependencies": { 4613 - "call-bound": "^1.0.3", 4614 - "has-bigints": "^1.0.2", 4615 - "has-symbols": "^1.1.0", 4616 - "which-boxed-primitive": "^1.1.1" 4617 - }, 4618 - "engines": { 4619 - "node": ">= 0.4" 4620 - }, 4621 - "funding": { 4622 - "url": "https://github.com/sponsors/ljharb" 4623 - } 4624 - }, 4625 - "node_modules/update-browserslist-db": { 4626 - "version": "1.2.3", 4627 - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", 4628 - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", 4629 - "dev": true, 4630 - "funding": [ 4631 - { 4632 - "type": "opencollective", 4633 - "url": "https://opencollective.com/browserslist" 4634 - }, 4635 - { 4636 - "type": "tidelift", 4637 - "url": "https://tidelift.com/funding/github/npm/browserslist" 4638 - }, 4639 - { 4640 - "type": "github", 4641 - "url": "https://github.com/sponsors/ai" 4642 - } 4643 - ], 4644 - "license": "MIT", 4645 - "dependencies": { 4646 - "escalade": "^3.2.0", 4647 - "picocolors": "^1.1.1" 4648 - }, 4649 - "bin": { 4650 - "update-browserslist-db": "cli.js" 4651 - }, 4652 - "peerDependencies": { 4653 - "browserslist": ">= 4.21.0" 4654 - } 4655 - }, 4656 - "node_modules/uri-js": { 4657 - "version": "4.4.1", 4658 - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 4659 - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 4660 - "dev": true, 4661 - "license": "BSD-2-Clause", 4662 - "dependencies": { 4663 - "punycode": "^2.1.0" 4664 - } 4665 - }, 4666 - "node_modules/vite": { 4667 - "version": "6.4.1", 4668 - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", 4669 - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", 4670 - "dev": true, 4671 - "license": "MIT", 4672 - "peer": true, 4673 - "dependencies": { 4674 - "esbuild": "^0.25.0", 4675 - "fdir": "^6.4.4", 4676 - "picomatch": "^4.0.2", 4677 - "postcss": "^8.5.3", 4678 - "rollup": "^4.34.9", 4679 - "tinyglobby": "^0.2.13" 4680 - }, 4681 - "bin": { 4682 - "vite": "bin/vite.js" 4683 - }, 4684 - "engines": { 4685 - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 4686 - }, 4687 - "funding": { 4688 - "url": "https://github.com/vitejs/vite?sponsor=1" 4689 - }, 4690 - "optionalDependencies": { 4691 - "fsevents": "~2.3.3" 4692 - }, 4693 - "peerDependencies": { 4694 - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 4695 - "jiti": ">=1.21.0", 4696 - "less": "*", 4697 - "lightningcss": "^1.21.0", 4698 - "sass": "*", 4699 - "sass-embedded": "*", 4700 - "stylus": "*", 4701 - "sugarss": "*", 4702 - "terser": "^5.16.0", 4703 - "tsx": "^4.8.1", 4704 - "yaml": "^2.4.2" 4705 - }, 4706 - "peerDependenciesMeta": { 4707 - "@types/node": { 4708 - "optional": true 4709 - }, 4710 - "jiti": { 4711 - "optional": true 4712 - }, 4713 - "less": { 4714 - "optional": true 4715 - }, 4716 - "lightningcss": { 4717 - "optional": true 4718 - }, 4719 - "sass": { 4720 - "optional": true 4721 - }, 4722 - "sass-embedded": { 4723 - "optional": true 4724 - }, 4725 - "stylus": { 4726 - "optional": true 4727 - }, 4728 - "sugarss": { 4729 - "optional": true 4730 - }, 4731 - "terser": { 4732 - "optional": true 4733 - }, 4734 - "tsx": { 4735 - "optional": true 4736 - }, 4737 - "yaml": { 4738 - "optional": true 4739 - } 4740 - } 4741 - }, 4742 - "node_modules/which": { 4743 - "version": "2.0.2", 4744 - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 4745 - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 4746 - "dev": true, 4747 - "license": "ISC", 4748 - "dependencies": { 4749 - "isexe": "^2.0.0" 4750 - }, 4751 - "bin": { 4752 - "node-which": "bin/node-which" 4753 - }, 4754 - "engines": { 4755 - "node": ">= 8" 4756 - } 4757 - }, 4758 - "node_modules/which-boxed-primitive": { 4759 - "version": "1.1.1", 4760 - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", 4761 - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", 4762 - "dev": true, 4763 - "license": "MIT", 4764 - "dependencies": { 4765 - "is-bigint": "^1.1.0", 4766 - "is-boolean-object": "^1.2.1", 4767 - "is-number-object": "^1.1.1", 4768 - "is-string": "^1.1.1", 4769 - "is-symbol": "^1.1.1" 4770 - }, 4771 - "engines": { 4772 - "node": ">= 0.4" 4773 - }, 4774 - "funding": { 4775 - "url": "https://github.com/sponsors/ljharb" 4776 - } 4777 - }, 4778 - "node_modules/which-builtin-type": { 4779 - "version": "1.2.1", 4780 - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", 4781 - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", 4782 - "dev": true, 4783 - "license": "MIT", 4784 - "dependencies": { 4785 - "call-bound": "^1.0.2", 4786 - "function.prototype.name": "^1.1.6", 4787 - "has-tostringtag": "^1.0.2", 4788 - "is-async-function": "^2.0.0", 4789 - "is-date-object": "^1.1.0", 4790 - "is-finalizationregistry": "^1.1.0", 4791 - "is-generator-function": "^1.0.10", 4792 - "is-regex": "^1.2.1", 4793 - "is-weakref": "^1.0.2", 4794 - "isarray": "^2.0.5", 4795 - "which-boxed-primitive": "^1.1.0", 4796 - "which-collection": "^1.0.2", 4797 - "which-typed-array": "^1.1.16" 4798 - }, 4799 - "engines": { 4800 - "node": ">= 0.4" 4801 - }, 4802 - "funding": { 4803 - "url": "https://github.com/sponsors/ljharb" 4804 - } 4805 - }, 4806 - "node_modules/which-collection": { 4807 - "version": "1.0.2", 4808 - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", 4809 - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", 4810 - "dev": true, 4811 - "license": "MIT", 4812 - "dependencies": { 4813 - "is-map": "^2.0.3", 4814 - "is-set": "^2.0.3", 4815 - "is-weakmap": "^2.0.2", 4816 - "is-weakset": "^2.0.3" 4817 - }, 4818 - "engines": { 4819 - "node": ">= 0.4" 4820 - }, 4821 - "funding": { 4822 - "url": "https://github.com/sponsors/ljharb" 4823 - } 4824 - }, 4825 - "node_modules/which-typed-array": { 4826 - "version": "1.1.20", 4827 - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", 4828 - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", 4829 - "dev": true, 4830 - "license": "MIT", 4831 - "dependencies": { 4832 - "available-typed-arrays": "^1.0.7", 4833 - "call-bind": "^1.0.8", 4834 - "call-bound": "^1.0.4", 4835 - "for-each": "^0.3.5", 4836 - "get-proto": "^1.0.1", 4837 - "gopd": "^1.2.0", 4838 - "has-tostringtag": "^1.0.2" 4839 - }, 4840 - "engines": { 4841 - "node": ">= 0.4" 4842 - }, 4843 - "funding": { 4844 - "url": "https://github.com/sponsors/ljharb" 4845 - } 4846 - }, 4847 - "node_modules/word-wrap": { 4848 - "version": "1.2.5", 4849 - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 4850 - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 4851 - "dev": true, 4852 - "license": "MIT", 4853 - "engines": { 4854 - "node": ">=0.10.0" 4855 - } 4856 - }, 4857 - "node_modules/yallist": { 4858 - "version": "3.1.1", 4859 - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 4860 - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", 4861 - "dev": true, 4862 - "license": "ISC" 4863 - }, 4864 - "node_modules/yocto-queue": { 4865 - "version": "0.1.0", 4866 - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 4867 - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 4868 - "dev": true, 4869 - "license": "MIT", 4870 - "engines": { 4871 - "node": ">=10" 4872 - }, 4873 - "funding": { 4874 - "url": "https://github.com/sponsors/sindresorhus" 4875 - } 4876 - }, 4877 - "node_modules/zod": { 4878 - "version": "4.3.5", 4879 - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz", 4880 - "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", 4881 - "dev": true, 4882 - "license": "MIT", 4883 - "peer": true, 4884 - "funding": { 4885 - "url": "https://github.com/sponsors/colinhacks" 4886 - } 4887 - }, 4888 - "node_modules/zod-validation-error": { 4889 - "version": "4.0.2", 4890 - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", 4891 - "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", 4892 - "dev": true, 4893 - "license": "MIT", 4894 - "engines": { 4895 - "node": ">=18.0.0" 4896 - }, 4897 - "peerDependencies": { 4898 - "zod": "^3.25.0 || ^4.0.0" 4899 - } 4900 - } 4901 - } 4902 - }
+26 -22
web/package.json
··· 1 1 { 2 - "name": "margin-web", 3 - "private": true, 4 - "version": "0.0.1", 2 + "name": "web", 5 3 "type": "module", 4 + "version": "0.0.1", 6 5 "scripts": { 7 - "dev": "vite", 8 - "build": "vite build", 9 - "lint": "eslint .", 10 - "preview": "vite preview" 6 + "dev": "astro dev", 7 + "build": "astro build", 8 + "preview": "astro preview", 9 + "astro": "astro" 11 10 }, 12 11 "dependencies": { 12 + "@astrojs/node": "^9.5.2", 13 + "@astrojs/react": "^4.4.2", 14 + "@astrojs/tailwind": "^6.0.2", 15 + "@nanostores/react": "^1.0.0", 16 + "@tailwindcss/vite": "^4.1.18", 17 + "astro": "^5.17.1", 18 + "autoprefixer": "^10.4.24", 19 + "clsx": "^2.1.1", 13 20 "date-fns": "^4.1.0", 14 - "lucide-react": "^0.562.0", 15 - "react": "^18.3.1", 16 - "react-dom": "^18.3.1", 17 - "react-icons": "^5.5.0", 18 - "react-router-dom": "^6.28.0" 21 + "lucide-react": "^0.563.0", 22 + "nanostores": "^1.1.0", 23 + "postcss": "^8.5.6", 24 + "react": "^19.2.4", 25 + "react-dom": "^19.2.4", 26 + "react-router-dom": "^7.13.0", 27 + "tailwind-merge": "^3.4.0", 28 + "tailwindcss": "^3.4.19" 19 29 }, 20 30 "devDependencies": { 21 - "@eslint/js": "^9.39.2", 22 - "@types/react": "^18.3.12", 23 - "@types/react-dom": "^18.3.1", 24 - "@vitejs/plugin-react": "^4.3.3", 25 - "eslint": "^9.39.2", 26 - "eslint-plugin-react": "^7.37.5", 27 - "eslint-plugin-react-hooks": "^7.0.1", 28 - "eslint-plugin-react-refresh": "^0.4.26", 29 - "globals": "^17.0.0", 30 - "vite": "^6.0.3" 31 + "@types/react": "^19.2.11", 32 + "@types/react-dom": "^19.2.3", 33 + "react-icons": "^5.5.0", 34 + "typescript": "^5.9.3" 31 35 } 32 36 }
+9
web/public/favicon.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128"> 2 + <path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" /> 3 + <style> 4 + path { fill: #000; } 5 + @media (prefers-color-scheme: dark) { 6 + path { fill: #FFF; } 7 + } 8 + </style> 9 + </svg>
+187
web/src/App.tsx
··· 1 + 2 + import React, { useEffect } from 'react'; 3 + import { BrowserRouter, Routes, Route, Navigate, useParams } from 'react-router-dom'; 4 + import { useStore } from '@nanostores/react'; 5 + import { $user, initAuth } from './store/auth'; 6 + import { $theme } from './store/theme'; 7 + 8 + import AppLayout, { LandingLayout } from './layouts/AppLayout'; 9 + import Feed from './views/Feed'; 10 + import Profile from './views/Profile'; 11 + import Login from './views/Login'; 12 + import Notifications from './views/Notifications'; 13 + import MasonryFeed from './components/MasonryFeed'; 14 + import Collections from './views/Collections'; 15 + import CollectionDetail from './views/CollectionDetail'; 16 + import Settings from './views/Settings'; 17 + import UrlPage from './views/Url'; 18 + import UserUrlPage from './views/UserUrl'; 19 + import NewAnnotationPage from './views/New'; 20 + import AnnotationDetail from './views/AnnotationDetail'; 21 + import Privacy from './views/Privacy'; 22 + import Terms from './views/Terms'; 23 + 24 + 25 + const ProfileWrapper = () => { 26 + const { did } = useParams(); 27 + if (!did) return <Navigate to="/home" replace />; 28 + return <Profile did={did} />; 29 + } 30 + 31 + const SelfProfileWrapper = () => { 32 + const user = useStore($user); 33 + if (!user) return <Navigate to="/login" replace />; 34 + return <Navigate to={`/profile/${user.did}`} replace />; 35 + } 36 + 37 + const CollectionDetailWrapper = () => { 38 + const { handle, rkey } = useParams(); 39 + return <CollectionDetail handle={handle} rkey={rkey} />; 40 + } 41 + 42 + export default function App() { 43 + React.useEffect(() => { 44 + initAuth(); 45 + }, []); 46 + 47 + return ( 48 + <BrowserRouter> 49 + <Routes> 50 + <Route path="/settings" element={ 51 + <AppLayout> 52 + <Settings /> 53 + </AppLayout> 54 + } /> 55 + 56 + <Route path="/login" element={<Login />} /> 57 + 58 + <Route path="/home" element={ 59 + <AppLayout> 60 + <Feed initialType="all" /> 61 + </AppLayout> 62 + } /> 63 + 64 + <Route path="/my-feed" element={<Navigate to="/home" replace />} /> 65 + 66 + <Route path="/notifications" element={ 67 + <AppLayout> 68 + <Notifications /> 69 + </AppLayout> 70 + } /> 71 + 72 + <Route path="/bookmarks" element={ 73 + <AppLayout> 74 + <div className="max-w-2xl mx-auto mb-6 text-center lg:text-left"> 75 + <h1 className="text-3xl font-display font-bold text-surface-900">Bookmarks</h1> 76 + </div> 77 + <MasonryFeed motivation="bookmarking" emptyMessage="You haven't bookmarked anything yet." /> 78 + </AppLayout> 79 + } /> 80 + 81 + <Route path="/highlights" element={ 82 + <AppLayout> 83 + <div className="max-w-2xl mx-auto mb-6 text-center lg:text-left"> 84 + <h1 className="text-3xl font-display font-bold text-surface-900">Highlights</h1> 85 + </div> 86 + <MasonryFeed motivation="highlighting" emptyMessage="You haven't highlighted anything yet." /> 87 + </AppLayout> 88 + } /> 89 + 90 + <Route path="/collections" element={ 91 + <AppLayout> 92 + <Collections /> 93 + </AppLayout> 94 + } /> 95 + 96 + <Route path="/:handle/collection/:rkey" element={ 97 + <AppLayout> 98 + <CollectionDetailWrapper /> 99 + </AppLayout> 100 + } /> 101 + 102 + <Route path="/profile/:did" element={ 103 + <AppLayout> 104 + <ProfileWrapper /> 105 + </AppLayout> 106 + } /> 107 + 108 + <Route path="/profile" element={ 109 + <AppLayout> 110 + <SelfProfileWrapper /> 111 + </AppLayout> 112 + } /> 113 + 114 + <Route path="/url" element={ 115 + <AppLayout> 116 + <UrlPage /> 117 + </AppLayout> 118 + } /> 119 + 120 + <Route path="/new" element={ 121 + <AppLayout> 122 + <NewAnnotationPage /> 123 + </AppLayout> 124 + } /> 125 + 126 + <Route path="/privacy" element={ 127 + <AppLayout> 128 + <Privacy /> 129 + </AppLayout> 130 + } /> 131 + 132 + <Route path="/terms" element={ 133 + <AppLayout> 134 + <Terms /> 135 + </AppLayout> 136 + } /> 137 + 138 + <Route path="/at/:did/:rkey" element={ 139 + <AppLayout> 140 + <AnnotationDetail /> 141 + </AppLayout> 142 + } /> 143 + 144 + <Route path="/annotation/:uri" element={ 145 + <AppLayout> 146 + <AnnotationDetail /> 147 + </AppLayout> 148 + } /> 149 + 150 + <Route path="/collections/:rkey" element={ 151 + <AppLayout> 152 + <CollectionDetail handle={undefined} rkey={undefined} /> 153 + </AppLayout> 154 + } /> 155 + 156 + <Route path="/:handle/annotation/:rkey" element={ 157 + <AppLayout> 158 + <AnnotationDetail /> 159 + </AppLayout> 160 + } /> 161 + 162 + <Route path="/:handle/highlight/:rkey" element={ 163 + <AppLayout> 164 + <AnnotationDetail /> 165 + </AppLayout> 166 + } /> 167 + 168 + <Route path="/:handle/bookmark/:rkey" element={ 169 + <AppLayout> 170 + <AnnotationDetail /> 171 + </AppLayout> 172 + } /> 173 + 174 + <Route path="/:handle/url/*" element={ 175 + <AppLayout> 176 + <UserUrlPage /> 177 + </AppLayout> 178 + } /> 179 + 180 + <Route path="/auth/*" element={<div>Redirecting...</div>} /> 181 + 182 + <Route path="*" element={<Navigate to="/home" replace />} /> 183 + 184 + </Routes> 185 + </BrowserRouter> 186 + ); 187 + }
+674
web/src/api/client.ts
··· 1 + 2 + import { atom } from 'nanostores'; 3 + import type { UserProfile, FeedResponse, AnnotationItem, Collection } from '../types'; 4 + export type { Collection } from '../types'; 5 + 6 + export const sessionAtom = atom<UserProfile | null>(null); 7 + 8 + export async function checkSession(): Promise<UserProfile | null> { 9 + try { 10 + const res = await fetch('/auth/session'); 11 + if (!res.ok) { 12 + sessionAtom.set(null); 13 + return null; 14 + } 15 + const data = await res.json(); 16 + 17 + if (data.authenticated || data.did) { 18 + const baseProfile: UserProfile = { 19 + did: data.did, 20 + handle: data.handle, 21 + displayName: data.displayName, 22 + avatar: data.avatar, 23 + description: data.description, 24 + website: data.website, 25 + links: data.links, 26 + followersCount: data.followersCount, 27 + followsCount: data.followsCount, 28 + postsCount: data.postsCount 29 + }; 30 + 31 + try { 32 + const bskyRes = await fetch(`https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(data.did)}`); 33 + if (bskyRes.ok) { 34 + const bskyData = await bskyRes.json(); 35 + if (bskyData.avatar) baseProfile.avatar = bskyData.avatar; 36 + if (bskyData.displayName) baseProfile.displayName = bskyData.displayName; 37 + } 38 + } catch (e) { 39 + console.warn("Failed to fetch Bsky profile for session", e); 40 + } 41 + 42 + try { 43 + const marginProfile = await getProfile(data.did); 44 + if (marginProfile) { 45 + if (marginProfile.description) baseProfile.description = marginProfile.description; 46 + if (marginProfile.followersCount) baseProfile.followersCount = marginProfile.followersCount; 47 + if (marginProfile.followsCount) baseProfile.followsCount = marginProfile.followsCount; 48 + if (marginProfile.postsCount) baseProfile.postsCount = marginProfile.postsCount; 49 + if (marginProfile.website) baseProfile.website = marginProfile.website; 50 + if (marginProfile.links) baseProfile.links = marginProfile.links; 51 + } 52 + } catch (e) { 53 + } 54 + 55 + sessionAtom.set(baseProfile); 56 + return baseProfile; 57 + } 58 + 59 + sessionAtom.set(null); 60 + return null; 61 + } catch (e) { 62 + sessionAtom.set(null); 63 + return null; 64 + } 65 + } 66 + 67 + async function apiRequest(path: string, options: RequestInit = {}): Promise<Response> { 68 + const headers = { 69 + 'Content-Type': 'application/json', 70 + ...(options.headers || {}), 71 + }; 72 + 73 + const apiPath = path.startsWith('/api') || path.startsWith('/auth') ? path : `/api${path}`; 74 + 75 + const response = await fetch(apiPath, { 76 + ...options, 77 + headers, 78 + }); 79 + 80 + if (response.status === 401) { 81 + sessionAtom.set(null); 82 + window.location.href = '/login'; 83 + } 84 + 85 + return response; 86 + } 87 + 88 + interface GetFeedParams { 89 + source?: string; 90 + type?: string; 91 + limit?: number; 92 + offset?: number; 93 + motivation?: string; 94 + tag?: string; 95 + creator?: string; 96 + } 97 + 98 + function normalizeItem(raw: any): AnnotationItem { 99 + if (raw.type === 'CollectionItem' || raw.collectionUri) { 100 + const inner = raw.annotation || raw.highlight || raw.bookmark || {}; 101 + const normalizedInner = normalizeItem(inner); 102 + 103 + return { 104 + ...normalizedInner, 105 + uri: normalizedInner.uri || raw.uri, 106 + author: normalizedInner.author || raw.author, 107 + collection: raw.collection ? { 108 + uri: raw.collection.uri, 109 + name: raw.collection.name, 110 + icon: raw.collection.icon 111 + } : undefined, 112 + addedBy: raw.creator || raw.author, 113 + createdAt: raw.created || raw.createdAt, 114 + collectionItemUri: raw.uri 115 + }; 116 + } 117 + 118 + let target = raw.target; 119 + if (!target || !target.source) { 120 + const url = raw.url || raw.targetUrl || (typeof raw.target === 'string' ? raw.target : raw.target?.source); 121 + if (url) { 122 + target = { 123 + source: url, 124 + title: raw.title || raw.target?.title, 125 + selector: raw.selector || raw.target?.selector 126 + }; 127 + } 128 + } 129 + 130 + return { 131 + ...raw, 132 + uri: raw.id || raw.uri, 133 + author: raw.creator || raw.author, 134 + createdAt: raw.created || raw.createdAt, 135 + target: target || raw.target, 136 + viewer: raw.viewer || { like: raw.viewerHasLiked ? 'true' : undefined } 137 + }; 138 + } 139 + 140 + export async function getFeed({ source, type = 'all', limit = 50, offset = 0, motivation, tag, creator }: GetFeedParams): Promise<FeedResponse> { 141 + const params = new URLSearchParams(); 142 + if (source) params.append('source', source); 143 + if (type) params.append('type', type); 144 + if (limit) params.append('limit', limit.toString()); 145 + if (offset) params.append('offset', offset.toString()); 146 + if (motivation) params.append('motivation', motivation); 147 + if (tag) params.append('tag', tag); 148 + if (creator) params.append('creator', creator); 149 + 150 + const endpoint = source ? '/api/targets' : '/api/annotations/feed'; 151 + 152 + try { 153 + const res = await apiRequest(`${endpoint}?${params.toString()}`); 154 + if (!res.ok) throw new Error('Failed to fetch feed'); 155 + const data = await res.json(); 156 + return { 157 + cursor: data.cursor, 158 + items: (data.items || []).map(normalizeItem) 159 + }; 160 + } catch (e) { 161 + console.error(e); 162 + return { items: [] }; 163 + } 164 + } 165 + 166 + interface CreateAnnotationParams { 167 + url: string; 168 + text?: string; 169 + title?: string; 170 + selector?: { exact: string; prefix?: string; suffix?: string }; 171 + tags?: string[]; 172 + } 173 + 174 + export async function createAnnotation({ url, text, title, selector, tags }: CreateAnnotationParams) { 175 + try { 176 + const res = await apiRequest('/api/annotations', { 177 + method: 'POST', 178 + body: JSON.stringify({ url, text, title, selector, tags }) 179 + }); 180 + if (!res.ok) throw new Error(await res.text()); 181 + const raw = await res.json(); 182 + return normalizeItem(raw); 183 + } catch (e: any) { 184 + console.error(e); 185 + return { error: e.message }; 186 + } 187 + } 188 + 189 + interface CreateHighlightParams { 190 + url: string; 191 + selector: { exact: string; prefix?: string; suffix?: string }; 192 + color?: string; 193 + tags?: string[]; 194 + title?: string; 195 + } 196 + 197 + export async function createHighlight({ url, selector, color, tags, title }: CreateHighlightParams) { 198 + try { 199 + const res = await apiRequest('/api/highlights', { 200 + method: 'POST', 201 + body: JSON.stringify({ url, selector, color, tags, title }) 202 + }); 203 + if (!res.ok) throw new Error(await res.text()); 204 + const raw = await res.json(); 205 + return normalizeItem(raw); 206 + } catch (e: any) { 207 + console.error(e); 208 + return { error: e.message }; 209 + } 210 + } 211 + 212 + export async function createBookmark({ url, title, description }: { url: string; title?: string; description?: string }) { 213 + try { 214 + const res = await apiRequest('/api/bookmarks', { 215 + method: 'POST', 216 + body: JSON.stringify({ url, title, description }) 217 + }); 218 + if (!res.ok) throw new Error(await res.text()); 219 + const raw = await res.json(); 220 + return normalizeItem(raw); 221 + } catch (e: any) { 222 + console.error(e); 223 + return { error: e.message }; 224 + } 225 + } 226 + 227 + export async function uploadAvatar(file: File): Promise<{ blob: any }> { 228 + const formData = new FormData(); 229 + formData.append('file', file); 230 + const res = await fetch('/api/upload/avatar', { 231 + method: 'POST', 232 + headers: { 233 + 'Authorization': `Bearer ${(await checkSession())?.did}` 234 + }, 235 + body: formData 236 + }); 237 + if (!res.ok) throw new Error('Failed to upload avatar'); 238 + return res.json(); 239 + } 240 + 241 + export async function updateProfile(updates: { displayName?: string; description?: string; avatar?: any; website?: string; links?: string[] }): Promise<boolean> { 242 + try { 243 + const res = await apiRequest('/api/profile', { 244 + method: 'PUT', 245 + body: JSON.stringify(updates) 246 + }); 247 + return res.ok; 248 + } catch (e) { 249 + console.error(e); 250 + return false; 251 + } 252 + } 253 + 254 + export async function likeItem(uri: string, cid: string): Promise<boolean> { 255 + try { 256 + const res = await apiRequest('/api/annotations/like', { 257 + method: 'POST', 258 + body: JSON.stringify({ subjectUri: uri, subjectCid: cid }) 259 + }); 260 + return res.ok; 261 + } catch (e) { 262 + return false; 263 + } 264 + } 265 + 266 + export async function unlikeItem(uri: string): Promise<boolean> { 267 + try { 268 + const res = await apiRequest(`/api/annotations/like?uri=${encodeURIComponent(uri)}`, { 269 + method: 'DELETE' 270 + }); 271 + return res.ok; 272 + } catch (e) { 273 + return false; 274 + } 275 + } 276 + 277 + export async function deleteItem(uri: string, type: string = 'annotation'): Promise<boolean> { 278 + const rkey = uri.split('/').pop(); 279 + let endpoint = '/api/annotations'; 280 + if (uri.includes('highlight')) endpoint = '/api/highlights'; 281 + if (uri.includes('bookmark')) endpoint = '/api/bookmarks'; 282 + 283 + try { 284 + const res = await apiRequest(`${endpoint}?rkey=${rkey}`, { method: 'DELETE' }); 285 + return res.ok; 286 + } catch (e) { 287 + return false; 288 + } 289 + } 290 + 291 + export async function updateAnnotation(uri: string, text: string, tags?: string[]): Promise<boolean> { 292 + try { 293 + const res = await apiRequest(`/api/annotations?uri=${encodeURIComponent(uri)}`, { 294 + method: 'PUT', 295 + body: JSON.stringify({ text, tags }) 296 + }); 297 + return res.ok; 298 + } catch (e) { 299 + return false; 300 + } 301 + } 302 + 303 + export async function updateHighlight(uri: string, color: string, tags?: string[]): Promise<boolean> { 304 + try { 305 + const res = await apiRequest(`/api/highlights?uri=${encodeURIComponent(uri)}`, { 306 + method: 'PUT', 307 + body: JSON.stringify({ color, tags }) 308 + }); 309 + return res.ok; 310 + } catch (e) { 311 + return false; 312 + } 313 + } 314 + 315 + export async function updateBookmark(uri: string, title?: string, description?: string, tags?: string[]): Promise<boolean> { 316 + try { 317 + const res = await apiRequest(`/api/bookmarks?uri=${encodeURIComponent(uri)}`, { 318 + method: 'PUT', 319 + body: JSON.stringify({ title, description, tags }) 320 + }); 321 + return res.ok; 322 + } catch (e) { 323 + return false; 324 + } 325 + } 326 + 327 + export async function getCollectionsContaining(annotationUri: string): Promise<string[]> { 328 + try { 329 + const res = await apiRequest(`/api/collections/containing?uri=${encodeURIComponent(annotationUri)}`); 330 + if (!res.ok) return []; 331 + return await res.json(); 332 + } catch (e) { 333 + return []; 334 + } 335 + } 336 + 337 + export async function getEditHistory(uri: string): Promise<any[]> { 338 + try { 339 + const res = await apiRequest(`/api/annotations/history?uri=${encodeURIComponent(uri)}`); 340 + if (!res.ok) return []; 341 + return await res.json(); 342 + } catch (e) { 343 + return []; 344 + } 345 + } 346 + 347 + export async function getProfile(did: string): Promise<UserProfile | null> { 348 + try { 349 + const res = await apiRequest(`/api/profile/${did}`); 350 + if (!res.ok) return null; 351 + return await res.json(); 352 + } catch (e) { 353 + return null; 354 + } 355 + } 356 + 357 + export interface ActorSearchItem { 358 + did: string; 359 + handle: string; 360 + displayName?: string; 361 + avatar?: string; 362 + } 363 + 364 + export function getAvatarUrl(did?: string, avatar?: string): string | undefined { 365 + if (!avatar && !did) return undefined; 366 + if (avatar && !avatar.includes('cdn.bsky.app')) return avatar; 367 + if (!did) return avatar; 368 + 369 + return `/api/avatar/${encodeURIComponent(did)}`; 370 + } 371 + 372 + export async function searchActors(query: string): Promise<{ actors: ActorSearchItem[] }> { 373 + try { 374 + const res = await fetch(`https://public.api.bsky.app/xrpc/app.bsky.actor.searchActorsTypeahead?q=${encodeURIComponent(query)}&limit=5`); 375 + if (!res.ok) throw new Error('Search failed'); 376 + return await res.json(); 377 + } catch (e) { 378 + return { actors: [] }; 379 + } 380 + } 381 + 382 + export async function resolveHandle(handle: string): Promise<string | null> { 383 + try { 384 + const res = await fetch(`https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`); 385 + if (!res.ok) throw new Error('Failed to resolve handle'); 386 + const data = await res.json(); 387 + return data.did; 388 + } catch (e) { 389 + return null; 390 + } 391 + } 392 + 393 + export async function startLogin(handle: string): Promise<{ authorizationUrl?: string }> { 394 + const res = await apiRequest('/auth/start', { 395 + method: 'POST', 396 + body: JSON.stringify({ handle }) 397 + }); 398 + if (!res.ok) throw new Error('Failed to start login'); 399 + return await res.json(); 400 + } 401 + 402 + export async function startSignup(pdsUrl: string): Promise<{ authorizationUrl?: string }> { 403 + const res = await apiRequest('/auth/signup', { 404 + method: 'POST', 405 + body: JSON.stringify({ pds_url: pdsUrl }) 406 + }); 407 + if (!res.ok) throw new Error('Failed to start signup'); 408 + return await res.json(); 409 + } 410 + 411 + export async function getNotifications(limit = 50, offset = 0): Promise<any[]> { 412 + try { 413 + const res = await apiRequest(`/api/notifications?limit=${limit}&offset=${offset}`); 414 + if (!res.ok) throw new Error('Failed to fetch notifications'); 415 + const data = await res.json(); 416 + return (data.items || []).map((n: any) => ({ 417 + ...n, 418 + subject: n.subject ? normalizeItem(n.subject) : undefined 419 + })); 420 + } catch (e) { 421 + return []; 422 + } 423 + } 424 + 425 + export async function getUnreadNotificationCount(): Promise<number> { 426 + try { 427 + const res = await apiRequest('/api/notifications/count'); 428 + if (!res.ok) return 0; 429 + const data = await res.json(); 430 + return data.count || 0; 431 + } catch (e) { 432 + return 0; 433 + } 434 + } 435 + 436 + export async function markNotificationsRead(): Promise<boolean> { 437 + try { 438 + const res = await apiRequest('/api/notifications/read', { method: 'POST' }); 439 + return res.ok; 440 + } catch (e) { 441 + return false; 442 + } 443 + } 444 + 445 + export interface APIKey { 446 + id: string; 447 + alias: string; 448 + key?: string; 449 + createdAt: string; 450 + } 451 + 452 + export async function getAPIKeys(): Promise<APIKey[]> { 453 + try { 454 + const res = await apiRequest('/api/keys'); 455 + if (!res.ok) return []; 456 + const data = await res.json(); 457 + return Array.isArray(data) ? data : (data.keys || []); 458 + } catch (e) { 459 + return []; 460 + } 461 + } 462 + 463 + export async function createAPIKey(alias: string): Promise<APIKey | null> { 464 + try { 465 + const res = await apiRequest('/api/keys', { 466 + method: 'POST', 467 + body: JSON.stringify({ alias }) 468 + }); 469 + if (!res.ok) return null; 470 + return await res.json(); 471 + } catch (e) { 472 + return null; 473 + } 474 + } 475 + 476 + export async function deleteAPIKey(id: string): Promise<boolean> { 477 + try { 478 + const res = await apiRequest(`/api/keys/${id}`, { method: 'DELETE' }); 479 + return res.ok; 480 + } catch (e) { 481 + return false; 482 + } 483 + } 484 + 485 + export interface Tag { 486 + tag: string; 487 + count: number; 488 + } 489 + 490 + export async function getTrendingTags(limit = 10): Promise<Tag[]> { 491 + try { 492 + const res = await apiRequest(`/api/tags/trending?limit=${limit}`); 493 + if (!res.ok) return []; 494 + const data = await res.json(); 495 + return Array.isArray(data) ? data : (data.tags || []); 496 + } catch (e) { 497 + return []; 498 + } 499 + } 500 + 501 + export async function getCollections(creator?: string): Promise<Collection[]> { 502 + try { 503 + const query = creator ? `?creator=${encodeURIComponent(creator)}` : ''; 504 + const res = await apiRequest(`/api/collections${query}`); 505 + if (!res.ok) throw new Error('Failed to fetch collections'); 506 + const data = await res.json(); 507 + return Array.isArray(data) ? data : (data.items || []); 508 + } catch (e) { 509 + console.error(e); 510 + return []; 511 + } 512 + } 513 + 514 + export async function getCollection(uri: string): Promise<Collection | null> { 515 + try { 516 + const res = await apiRequest(`/api/collection?uri=${encodeURIComponent(uri)}`); 517 + if (!res.ok) throw new Error('Failed to fetch collection'); 518 + return await res.json(); 519 + } catch (e) { 520 + console.error(e); 521 + return null; 522 + } 523 + } 524 + 525 + export async function createCollection(name: string, description?: string, icon?: string): Promise<Collection | null> { 526 + try { 527 + const res = await apiRequest('/api/collections', { 528 + method: 'POST', 529 + body: JSON.stringify({ name, description, icon }) 530 + }); 531 + if (!res.ok) throw new Error('Failed to create collection'); 532 + return await res.json(); 533 + } catch (e) { 534 + console.error(e); 535 + return null; 536 + } 537 + } 538 + 539 + export async function deleteCollection(id: string): Promise<boolean> { 540 + try { 541 + const res = await apiRequest(`/api/collections?uri=${encodeURIComponent(id)}`, { method: 'DELETE' }); // Note: old code used uri param for delete too, despite 'id' arg name here 542 + return res.ok; 543 + } catch (e) { 544 + console.error(e); 545 + return false; 546 + } 547 + } 548 + 549 + export async function getCollectionItems(uri: string): Promise<AnnotationItem[]> { 550 + try { 551 + const res = await apiRequest(`/api/collections/${encodeURIComponent(uri)}/items`); 552 + if (!res.ok) throw new Error('Failed to fetch collection items'); 553 + const data = await res.json(); 554 + return (data || []).map(normalizeItem); 555 + } catch (e) { 556 + console.error(e); 557 + return []; 558 + } 559 + } 560 + 561 + export async function updateCollection(uri: string, name: string, description?: string, icon?: string): Promise<Collection | null> { 562 + try { 563 + const res = await apiRequest(`/api/collections?uri=${encodeURIComponent(uri)}`, { 564 + method: 'PUT', 565 + body: JSON.stringify({ name, description, icon }) 566 + }); 567 + if (!res.ok) throw new Error('Failed to update collection'); 568 + return await res.json(); 569 + } catch (e) { 570 + console.error(e); 571 + return null; 572 + } 573 + } 574 + 575 + export async function addCollectionItem(collectionUri: string, annotationUri: string, position: number = 0): Promise<boolean> { 576 + try { 577 + const res = await apiRequest(`/api/collections/${encodeURIComponent(collectionUri)}/items`, { 578 + method: 'POST', 579 + body: JSON.stringify({ annotationUri, position }) 580 + }); 581 + return res.ok; 582 + } catch (e) { 583 + console.error(e); 584 + return false; 585 + } 586 + } 587 + 588 + export async function removeCollectionItem(itemUri: string): Promise<boolean> { 589 + try { 590 + const res = await apiRequest(`/api/collections/items?uri=${encodeURIComponent(itemUri)}`, { 591 + method: 'DELETE' 592 + }); 593 + return res.ok; 594 + } catch (e) { 595 + console.error(e); 596 + return false; 597 + } 598 + } 599 + 600 + export async function createReply(parentUri: string, parentCid: string, rootUri: string, rootCid: string, text: string): Promise<string | null> { 601 + try { 602 + const res = await apiRequest('/api/annotations/reply', { 603 + method: 'POST', 604 + body: JSON.stringify({ parentUri, parentCid, rootUri, rootCid, text }) 605 + }); 606 + if (!res.ok) throw new Error('Failed to create reply'); 607 + const data = await res.json(); 608 + return data.uri; 609 + } catch (e) { 610 + console.error(e); 611 + return null; 612 + } 613 + } 614 + 615 + export async function deleteReply(uri: string): Promise<boolean> { 616 + try { 617 + const res = await apiRequest(`/api/annotations/reply?uri=${encodeURIComponent(uri)}`, { 618 + method: 'DELETE' 619 + }); 620 + return res.ok; 621 + } catch (e) { 622 + console.error(e); 623 + return false; 624 + } 625 + } 626 + 627 + export async function getAnnotation(uri: string): Promise<AnnotationItem | null> { 628 + try { 629 + const res = await apiRequest(`/api/annotation?uri=${encodeURIComponent(uri)}`); 630 + if (!res.ok) return null; 631 + return normalizeItem(await res.json()); 632 + } catch (e) { 633 + return null; 634 + } 635 + } 636 + 637 + export async function getReplies(uri: string): Promise<{ items: AnnotationItem[] }> { 638 + try { 639 + const res = await apiRequest(`/api/replies?uri=${encodeURIComponent(uri)}`); 640 + if (!res.ok) return { items: [] }; 641 + const data = await res.json(); 642 + return { items: (data.items || []).map(normalizeItem) }; 643 + } catch (e) { 644 + return { items: [] }; 645 + } 646 + } 647 + 648 + export async function getByTarget(url: string, limit = 50, offset = 0): Promise<{ annotations: AnnotationItem[], highlights: AnnotationItem[] }> { 649 + try { 650 + const res = await apiRequest(`/api/targets?source=${encodeURIComponent(url)}&limit=${limit}&offset=${offset}`); 651 + if (!res.ok) return { annotations: [], highlights: [] }; 652 + const data = await res.json(); 653 + return { 654 + annotations: (data.annotations || []).map(normalizeItem), 655 + highlights: (data.highlights || []).map(normalizeItem) 656 + }; 657 + } catch (e) { 658 + return { annotations: [], highlights: [] }; 659 + } 660 + } 661 + 662 + export async function getUserTargetItems(did: string, url: string, limit = 50, offset = 0): Promise<{ annotations: AnnotationItem[], highlights: AnnotationItem[] }> { 663 + try { 664 + const res = await apiRequest(`/api/users/${encodeURIComponent(did)}/targets?source=${encodeURIComponent(url)}&limit=${limit}&offset=${offset}`); 665 + if (!res.ok) return { annotations: [], highlights: [] }; 666 + const data = await res.json(); 667 + return { 668 + annotations: (data.annotations || []).map(normalizeItem), 669 + highlights: (data.highlights || []).map(normalizeItem) 670 + }; 671 + } catch (e) { 672 + return { annotations: [], highlights: [] }; 673 + } 674 + }
+238
web/src/components/AddToCollectionModal.tsx
··· 1 + 2 + import React, { useState, useEffect, useCallback } from 'react'; 3 + import { X, Plus, Check, Loader2, ChevronRight, FolderPlus } from 'lucide-react'; 4 + import { useStore } from '@nanostores/react'; 5 + import { $user } from '../store/auth'; 6 + import { getCollections, addCollectionItem, createCollection, type Collection } from '../api/client'; 7 + 8 + interface AddToCollectionModalProps { 9 + isOpen: boolean; 10 + onClose: () => void; 11 + annotationUri: string; 12 + } 13 + 14 + export default function AddToCollectionModal({ isOpen, onClose, annotationUri }: AddToCollectionModalProps) { 15 + const user = useStore($user); 16 + const [collections, setCollections] = useState<Collection[]>([]); 17 + const [loading, setLoading] = useState(true); 18 + const [addingTo, setAddingTo] = useState<string | null>(null); 19 + const [addedTo, setAddedTo] = useState<Set<string>>(new Set()); 20 + const [error, setError] = useState<string | null>(null); 21 + 22 + const [showNewForm, setShowNewForm] = useState(false); 23 + const [newName, setNewName] = useState(''); 24 + const [creating, setCreating] = useState(false); 25 + 26 + useEffect(() => { 27 + if (isOpen) { 28 + document.body.style.overflow = 'hidden'; 29 + } 30 + return () => { 31 + document.body.style.overflow = 'unset'; 32 + }; 33 + }, [isOpen]); 34 + 35 + const loadCollections = useCallback(async () => { 36 + if (!user) return; 37 + try { 38 + setLoading(true); 39 + const data = await getCollections(user.did); 40 + setCollections(data); 41 + } catch (err) { 42 + console.error(err); 43 + setError('Failed to load collections'); 44 + } finally { 45 + setLoading(false); 46 + } 47 + }, [user]); 48 + 49 + useEffect(() => { 50 + if (isOpen && user) { 51 + loadCollections(); 52 + setError(null); 53 + setAddedTo(new Set()); 54 + } 55 + }, [isOpen, user, loadCollections]); 56 + 57 + const handleAdd = async (collectionUri: string) => { 58 + if (addedTo.has(collectionUri)) return; 59 + 60 + try { 61 + setAddingTo(collectionUri); 62 + await addCollectionItem(collectionUri, annotationUri); 63 + setAddedTo(prev => new Set([...prev, collectionUri])); 64 + } catch (err) { 65 + console.error(err); 66 + setError('Failed to add to collection'); 67 + } finally { 68 + setAddingTo(null); 69 + } 70 + }; 71 + 72 + const handleCreate = async (e: React.FormEvent) => { 73 + e.preventDefault(); 74 + if (!newName.trim()) return; 75 + try { 76 + setCreating(true); 77 + const newCollection = await createCollection(newName.trim()); 78 + if (newCollection) { 79 + setCollections(prev => [newCollection, ...prev]); 80 + setNewName(''); 81 + setShowNewForm(false); 82 + } 83 + } catch (err) { 84 + console.error(err); 85 + setError('Failed to create collection'); 86 + } finally { 87 + setCreating(false); 88 + } 89 + }; 90 + 91 + if (!isOpen) return null; 92 + 93 + return ( 94 + <div 95 + className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm animate-fade-in" 96 + onClick={onClose} 97 + > 98 + <div 99 + className="w-full max-w-md bg-white dark:bg-surface-900 rounded-3xl shadow-2xl overflow-hidden" 100 + onClick={e => e.stopPropagation()} 101 + > 102 + <div className="p-4 flex justify-between items-center border-b border-surface-100 dark:border-surface-800"> 103 + <h2 className="text-xl font-display font-bold text-surface-900 dark:text-white">Add to Collection</h2> 104 + <button 105 + onClick={onClose} 106 + className="p-2 text-surface-400 hover:text-surface-900 dark:hover:text-white hover:bg-surface-50 dark:hover:bg-surface-800 rounded-full transition-colors" 107 + > 108 + <X size={20} /> 109 + </button> 110 + </div> 111 + 112 + <div className="px-6 pb-6 pt-4"> 113 + {loading ? ( 114 + <div className="text-center py-10"> 115 + <Loader2 size={32} className="animate-spin text-primary-600 dark:text-primary-400 mx-auto mb-3" /> 116 + <p className="text-surface-500 dark:text-surface-400 font-medium">Loading collections...</p> 117 + </div> 118 + ) : showNewForm ? ( 119 + <form onSubmit={handleCreate} className="space-y-4"> 120 + <div> 121 + <label className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-1"> 122 + Collection name 123 + </label> 124 + <input 125 + type="text" 126 + className="w-full px-4 py-3 bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-xl focus:border-primary-500 dark:focus:border-primary-400 focus:ring-4 focus:ring-primary-500/10 outline-none transition-all text-surface-900 dark:text-white placeholder-surface-400 dark:placeholder-surface-500" 127 + value={newName} 128 + onChange={e => setNewName(e.target.value)} 129 + placeholder="My Collection" 130 + autoFocus 131 + /> 132 + </div> 133 + 134 + {error && ( 135 + <div className="p-3 bg-red-50 dark:bg-red-900/30 text-red-600 dark:text-red-400 text-sm rounded-lg"> 136 + {error} 137 + </div> 138 + )} 139 + 140 + <div className="flex gap-3 pt-2"> 141 + <button 142 + type="button" 143 + className="flex-1 py-3 bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 text-surface-700 dark:text-surface-200 font-semibold rounded-xl hover:bg-surface-50 dark:hover:bg-surface-700 transition-colors" 144 + onClick={() => { 145 + setShowNewForm(false); 146 + setError(null); 147 + }} 148 + > 149 + Back 150 + </button> 151 + <button 152 + type="submit" 153 + className="flex-1 py-3 bg-primary-600 text-white font-semibold rounded-xl hover:bg-primary-700 transition-colors disabled:opacity-50 flex items-center justify-center gap-2" 154 + disabled={!newName.trim() || creating} 155 + > 156 + {creating && <Loader2 size={16} className="animate-spin" />} 157 + {creating ? 'Creating...' : 'Create'} 158 + </button> 159 + </div> 160 + </form> 161 + ) : ( 162 + <div> 163 + {error && ( 164 + <div className="mb-4 p-3 bg-red-50 dark:bg-red-900/30 text-red-600 dark:text-red-400 text-sm rounded-lg"> 165 + {error} 166 + </div> 167 + )} 168 + 169 + <button 170 + className="w-full flex items-center gap-4 p-4 bg-white dark:bg-surface-800 border-2 border-primary-100 dark:border-primary-900/50 hover:border-primary-300 dark:hover:border-primary-700 rounded-2xl shadow-sm hover:shadow-md transition-all group text-left mb-4" 171 + onClick={() => setShowNewForm(true)} 172 + > 173 + <div className="w-10 h-10 bg-primary-50 dark:bg-primary-900/30 rounded-full flex items-center justify-center text-primary-600 dark:text-primary-400 flex-shrink-0"> 174 + <FolderPlus size={20} /> 175 + </div> 176 + <div className="flex-1 min-w-0"> 177 + <h3 className="font-bold text-surface-900 dark:text-white group-hover:text-primary-700 dark:group-hover:text-primary-400 transition-colors">New Collection</h3> 178 + <span className="text-sm text-surface-500 dark:text-surface-400">Create a new collection</span> 179 + </div> 180 + <ChevronRight size={20} className="text-surface-300 dark:text-surface-600 group-hover:text-primary-500 dark:group-hover:text-primary-400" /> 181 + </button> 182 + 183 + {collections.length === 0 ? ( 184 + <div className="text-center py-6"> 185 + <p className="text-surface-500 dark:text-surface-400">No collections yet</p> 186 + </div> 187 + ) : ( 188 + <div className="space-y-2 max-h-[300px] overflow-y-auto"> 189 + {collections.map(col => { 190 + const isAdded = addedTo.has(col.uri); 191 + const isAdding = addingTo === col.uri; 192 + 193 + return ( 194 + <button 195 + key={col.uri} 196 + onClick={() => handleAdd(col.uri)} 197 + disabled={isAdding || isAdded} 198 + className="w-full flex items-center gap-3 p-3 bg-surface-50 dark:bg-surface-800/50 hover:bg-surface-100 dark:hover:bg-surface-800 rounded-xl transition-colors text-left group disabled:opacity-70" 199 + > 200 + <div className="w-8 h-8 flex items-center justify-center bg-white dark:bg-surface-700 rounded-full shadow-sm text-surface-600 dark:text-surface-300"> 201 + {col.icon ? ( 202 + <span className="text-base">{col.icon}</span> 203 + ) : ( 204 + <span className="font-bold text-xs">{col.name[0]}</span> 205 + )} 206 + </div> 207 + <div className="flex-1 min-w-0"> 208 + <h3 className="text-sm font-bold text-surface-900 dark:text-white">{col.name}</h3> 209 + {col.description && ( 210 + <p className="text-xs text-surface-500 dark:text-surface-400 line-clamp-1">{col.description}</p> 211 + )} 212 + </div> 213 + {isAdding ? ( 214 + <Loader2 size={16} className="animate-spin text-surface-400" /> 215 + ) : isAdded ? ( 216 + <Check size={16} className="text-green-500" /> 217 + ) : ( 218 + <Plus size={16} className="text-surface-300 dark:text-surface-500 group-hover:text-surface-600 dark:group-hover:text-surface-300" /> 219 + )} 220 + </button> 221 + ); 222 + })} 223 + </div> 224 + )} 225 + 226 + <button 227 + onClick={onClose} 228 + className="w-full mt-4 py-3 bg-surface-900 dark:bg-white text-white dark:text-surface-900 font-semibold rounded-xl hover:bg-surface-800 dark:hover:bg-surface-100 transition-colors" 229 + > 230 + Done 231 + </button> 232 + </div> 233 + )} 234 + </div> 235 + </div> 236 + </div> 237 + ); 238 + }
+239
web/src/components/Card.tsx
··· 1 + 2 + import React, { useState } from 'react'; 3 + import { formatDistanceToNow } from 'date-fns'; 4 + import { MessageSquare, Heart, ExternalLink, FolderPlus, Trash2, Edit3, Bookmark as BookmarkIcon, Globe } from 'lucide-react'; 5 + import ShareMenu from './ShareMenu'; 6 + import AddToCollectionModal from './AddToCollectionModal'; 7 + import { clsx } from 'clsx'; 8 + import { likeItem, unlikeItem, deleteItem, getAvatarUrl } from '../api/client'; 9 + import { $user } from '../store/auth'; 10 + import { useStore } from '@nanostores/react'; 11 + import type { AnnotationItem } from '../types'; 12 + import { Link } from 'react-router-dom'; 13 + 14 + interface CardProps { 15 + item: AnnotationItem; 16 + onDelete?: (uri: string) => void; 17 + hideShare?: boolean; 18 + } 19 + 20 + export default function Card({ item, onDelete, hideShare }: CardProps) { 21 + const user = useStore($user); 22 + const isAuthor = user && item.author?.did === user.did; 23 + 24 + const [liked, setLiked] = useState(!!item.viewer?.like); 25 + const [likes, setLikes] = useState(item.likeCount || 0); 26 + const [showCollectionModal, setShowCollectionModal] = useState(false); 27 + 28 + const type = item.motivation === 'highlighting' ? 'highlight' : 29 + item.motivation === 'bookmarking' ? 'bookmark' : 'annotation'; 30 + 31 + const isSemble = item.uri?.includes('network.cosmik') || item.uri?.includes('semble'); 32 + 33 + const handleLike = async () => { 34 + const prev = { liked, likes }; 35 + setLiked(!liked); 36 + setLikes(l => liked ? l - 1 : l + 1); 37 + 38 + const success = liked 39 + ? await unlikeItem(item.uri) 40 + : await likeItem(item.uri, item.cid); 41 + 42 + if (!success) { 43 + setLiked(prev.liked); 44 + setLikes(prev.likes); 45 + } 46 + }; 47 + 48 + const handleDelete = async () => { 49 + if (window.confirm('Delete this item?')) { 50 + const success = await deleteItem(item.uri, type); 51 + if (success && onDelete) onDelete(item.uri); 52 + } 53 + }; 54 + 55 + const timestamp = item.createdAt 56 + ? formatDistanceToNow(new Date(item.createdAt), { addSuffix: false }) 57 + .replace('about ', '') 58 + .replace(' hours', 'h') 59 + .replace(' hour', 'h') 60 + .replace(' minutes', 'm') 61 + .replace(' minute', 'm') 62 + .replace(' days', 'd') 63 + .replace(' day', 'd') 64 + : ''; 65 + 66 + const pageUrl = item.target?.source; 67 + const pageTitle = item.target?.title || (pageUrl ? new URL(pageUrl).hostname : null); 68 + const pageHostname = pageUrl ? new URL(pageUrl).hostname.replace('www.', '') : null; 69 + const isBookmark = type === 'bookmark' && !item.target?.selector && !item.body?.value; 70 + 71 + return ( 72 + <article className="bg-white dark:bg-surface-900 rounded-lg p-4 mb-3 shadow-sm ring-1 ring-black/5 dark:ring-white/5 hover:ring-black/10 dark:hover:ring-white/10 transition-all"> 73 + {item.collection && ( 74 + <Link 75 + to={`/${item.addedBy?.handle || ''}/collection/${item.collection.uri.split('/').pop()}`} 76 + className="inline-flex items-center gap-1 text-xs text-surface-400 dark:text-surface-500 hover:text-primary-600 dark:hover:text-primary-400 mb-2 transition-colors" 77 + > 78 + {item.collection.icon || '📁'} {item.collection.name} 79 + </Link> 80 + )} 81 + 82 + <div className="flex items-start gap-3"> 83 + <Link to={`/profile/${item.author?.did}`} className="shrink-0"> 84 + {getAvatarUrl(item.author?.did, item.author?.avatar) ? ( 85 + <img 86 + src={getAvatarUrl(item.author?.did, item.author?.avatar)} 87 + alt="" 88 + className="w-10 h-10 rounded-full bg-surface-100 dark:bg-surface-800 object-cover" 89 + /> 90 + ) : ( 91 + <div className="w-10 h-10 rounded-full bg-surface-100 dark:bg-surface-800" /> 92 + )} 93 + </Link> 94 + 95 + <div className="flex-1 min-w-0"> 96 + <div className="flex items-center gap-1.5 flex-wrap"> 97 + <Link to={`/profile/${item.author?.did}`} className="font-semibold text-surface-900 dark:text-white text-[15px] hover:underline"> 98 + {item.author?.displayName || item.author?.handle} 99 + </Link> 100 + <span className="text-surface-400 dark:text-surface-500 text-sm"> 101 + @{item.author?.handle} 102 + </span> 103 + <span className="text-surface-300 dark:text-surface-600">·</span> 104 + <span className="text-surface-400 dark:text-surface-500 text-sm"> 105 + {timestamp} 106 + </span> 107 + {isSemble && ( 108 + <span className="inline-flex items-center gap-0.5 text-[10px] text-surface-400 dark:text-surface-500 uppercase font-medium tracking-wide"> 109 + · via <img src="/semble-logo.svg" alt="Semble" className="h-3 inline opacity-70" /> 110 + </span> 111 + )} 112 + </div> 113 + 114 + {pageUrl && !isBookmark && ( 115 + <a 116 + href={pageUrl} 117 + target="_blank" 118 + rel="noopener noreferrer" 119 + className="inline-flex items-center gap-1 text-xs text-primary-600 dark:text-primary-400 hover:underline mt-0.5" 120 + > 121 + <ExternalLink size={10} /> 122 + {pageHostname} 123 + </a> 124 + )} 125 + </div> 126 + </div> 127 + 128 + <div className="mt-3 ml-[52px]"> 129 + {isBookmark && pageUrl && ( 130 + <a 131 + href={pageUrl} 132 + target="_blank" 133 + rel="noopener noreferrer" 134 + className="block p-3 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 hover:border-primary-300 dark:hover:border-primary-600 hover:bg-surface-100 dark:hover:bg-surface-700/50 transition-all group" 135 + > 136 + <div className="flex items-start gap-3"> 137 + <div className="shrink-0 w-10 h-10 bg-surface-200 dark:bg-surface-700 rounded-lg flex items-center justify-center text-surface-400 dark:text-surface-500 group-hover:text-primary-500 dark:group-hover:text-primary-400"> 138 + <Globe size={20} /> 139 + </div> 140 + <div className="flex-1 min-w-0"> 141 + <h3 className="font-medium text-surface-900 dark:text-white text-sm leading-snug group-hover:text-primary-600 dark:group-hover:text-primary-400 line-clamp-2"> 142 + {pageTitle} 143 + </h3> 144 + <p className="text-xs text-surface-500 dark:text-surface-400 mt-1 truncate"> 145 + {pageUrl} 146 + </p> 147 + </div> 148 + <ExternalLink size={14} className="shrink-0 text-surface-300 dark:text-surface-600 group-hover:text-primary-500 dark:group-hover:text-primary-400" /> 149 + </div> 150 + </a> 151 + )} 152 + 153 + {item.target?.selector && ( 154 + <blockquote className={clsx( 155 + "pl-3 border-l-[3px] mb-2 text-[15px] italic text-surface-600 dark:text-surface-300", 156 + item.color === 'yellow' && "border-yellow-400 bg-yellow-50/50 dark:bg-yellow-900/20", 157 + item.color === 'green' && "border-green-400 bg-green-50/50 dark:bg-green-900/20", 158 + item.color === 'red' && "border-red-400 bg-red-50/50 dark:bg-red-900/20", 159 + item.color === 'blue' && "border-blue-400 bg-blue-50/50 dark:bg-blue-900/20", 160 + !item.color && "border-surface-300 dark:border-surface-600" 161 + )}> 162 + "{item.target.selector.exact}" 163 + </blockquote> 164 + )} 165 + 166 + {item.body?.value && ( 167 + <p className="text-surface-900 dark:text-surface-100 whitespace-pre-wrap leading-relaxed text-[15px]"> 168 + {item.body.value} 169 + </p> 170 + )} 171 + </div> 172 + 173 + <div className="flex items-center gap-1 mt-3 ml-[52px]"> 174 + <button 175 + onClick={handleLike} 176 + className={clsx( 177 + "flex items-center gap-1 px-2 py-1.5 rounded-md text-sm transition-colors", 178 + liked 179 + ? "text-red-500" 180 + : "text-surface-400 dark:text-surface-500 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20" 181 + )} 182 + > 183 + <Heart size={16} className={clsx(liked && "fill-current")} /> 184 + {likes > 0 && <span className="text-xs">{likes}</span>} 185 + </button> 186 + 187 + <button className="flex items-center gap-1 px-2 py-1.5 rounded-md text-sm text-surface-400 dark:text-surface-500 hover:text-primary-600 dark:hover:text-primary-400 hover:bg-primary-50 dark:hover:bg-primary-900/20 transition-colors"> 188 + <MessageSquare size={16} /> 189 + {(item.replyCount || 0) > 0 && <span className="text-xs">{item.replyCount}</span>} 190 + </button> 191 + 192 + {user && ( 193 + <button 194 + onClick={() => setShowCollectionModal(true)} 195 + className="flex items-center px-2 py-1.5 rounded-md text-surface-400 dark:text-surface-500 hover:text-primary-600 dark:hover:text-primary-400 hover:bg-primary-50 dark:hover:bg-primary-900/20 transition-colors" 196 + title="Add to Collection" 197 + > 198 + <FolderPlus size={16} /> 199 + </button> 200 + )} 201 + 202 + {!hideShare && ( 203 + <ShareMenu 204 + uri={item.uri} 205 + text={item.body?.value || ''} 206 + handle={item.author?.handle} 207 + type={type} 208 + url={pageUrl} 209 + /> 210 + )} 211 + 212 + {isAuthor && ( 213 + <> 214 + <div className="flex-1" /> 215 + <button 216 + className="flex items-center px-2 py-1.5 rounded-md text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300 hover:bg-surface-50 dark:hover:bg-surface-800 transition-colors" 217 + title="Edit" 218 + > 219 + <Edit3 size={14} /> 220 + </button> 221 + <button 222 + onClick={handleDelete} 223 + className="flex items-center px-2 py-1.5 rounded-md text-surface-400 dark:text-surface-500 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors" 224 + title="Delete" 225 + > 226 + <Trash2 size={14} /> 227 + </button> 228 + </> 229 + )} 230 + </div> 231 + 232 + <AddToCollectionModal 233 + isOpen={showCollectionModal} 234 + onClose={() => setShowCollectionModal(false)} 235 + annotationUri={item.uri} 236 + /> 237 + </article> 238 + ); 239 + }
+124
web/src/components/CollectionIcon.tsx
··· 1 + import React from 'react'; 2 + import { 3 + Folder, 4 + Star, 5 + Heart, 6 + Bookmark, 7 + Lightbulb, 8 + Zap, 9 + Coffee, 10 + Music, 11 + Camera, 12 + Code, 13 + Globe, 14 + Flag, 15 + Tag, 16 + Box, 17 + Archive, 18 + FileText, 19 + Image, 20 + Video, 21 + Mail, 22 + MapPin, 23 + Calendar, 24 + Clock, 25 + Search, 26 + Settings, 27 + User, 28 + Users, 29 + Home, 30 + Briefcase, 31 + Gift, 32 + Award, 33 + Target, 34 + TrendingUp, 35 + Activity, 36 + Cpu, 37 + Database, 38 + Cloud, 39 + Sun, 40 + Moon, 41 + Flame, 42 + Leaf, 43 + } from "lucide-react"; 44 + 45 + export const ICON_MAP: Record<string, React.ElementType> = { 46 + folder: Folder, 47 + star: Star, 48 + heart: Heart, 49 + bookmark: Bookmark, 50 + lightbulb: Lightbulb, 51 + zap: Zap, 52 + coffee: Coffee, 53 + music: Music, 54 + camera: Camera, 55 + code: Code, 56 + globe: Globe, 57 + flag: Flag, 58 + tag: Tag, 59 + box: Box, 60 + archive: Archive, 61 + file: FileText, 62 + image: Image, 63 + video: Video, 64 + mail: Mail, 65 + pin: MapPin, 66 + calendar: Calendar, 67 + clock: Clock, 68 + search: Search, 69 + settings: Settings, 70 + user: User, 71 + users: Users, 72 + home: Home, 73 + briefcase: Briefcase, 74 + gift: Gift, 75 + award: Award, 76 + target: Target, 77 + trending: TrendingUp, 78 + activity: Activity, 79 + cpu: Cpu, 80 + database: Database, 81 + cloud: Cloud, 82 + sun: Sun, 83 + moon: Moon, 84 + flame: Flame, 85 + leaf: Leaf, 86 + }; 87 + 88 + interface CollectionIconProps { 89 + icon?: string; 90 + size?: number; 91 + className?: string; 92 + } 93 + 94 + export default function CollectionIcon({ icon, size = 22, className = "" }: CollectionIconProps) { 95 + if (!icon) { 96 + return <Folder size={size} className={className} />; 97 + } 98 + 99 + if (icon === "icon:semble") { 100 + return ( 101 + <img 102 + src="/semble-logo.svg" 103 + alt="Semble" 104 + style={{ width: size, height: size, objectFit: "contain" }} 105 + className={className} 106 + /> 107 + ); 108 + } 109 + 110 + if (icon.startsWith("icon:")) { 111 + const iconName = icon.replace("icon:", ""); 112 + const IconComponent = ICON_MAP[iconName]; 113 + if (IconComponent) { 114 + return <IconComponent size={size} className={className} />; 115 + } 116 + return <Folder size={size} className={className} />; 117 + } 118 + 119 + return ( 120 + <span style={{ fontSize: `${size * 0.8}px`, lineHeight: 1 }} className={className}> 121 + {icon} 122 + </span> 123 + ); 124 + }
+162
web/src/components/Composer.tsx
··· 1 + 2 + import React, { useState } from 'react'; 3 + import { createAnnotation, createHighlight } from '../api/client'; 4 + import { X } from 'lucide-react'; 5 + 6 + interface ComposerProps { 7 + url: string; 8 + selector?: any; 9 + onSuccess?: () => void; 10 + onCancel?: () => void; 11 + } 12 + 13 + export default function Composer({ url, selector: initialSelector, onSuccess, onCancel }: ComposerProps) { 14 + const [text, setText] = useState(""); 15 + const [quoteText, setQuoteText] = useState(""); 16 + const [tags, setTags] = useState(""); 17 + const [selector, setSelector] = useState(initialSelector); 18 + const [loading, setLoading] = useState(false); 19 + const [error, setError] = useState<string | null>(null); 20 + const [showQuoteInput, setShowQuoteInput] = useState(false); 21 + 22 + const highlightedText = selector?.type === "TextQuoteSelector" ? selector.exact : null; 23 + 24 + const handleSubmit = async (e: React.FormEvent) => { 25 + e.preventDefault(); 26 + if (!text.trim() && !highlightedText && !quoteText.trim()) return; 27 + 28 + try { 29 + setLoading(true); 30 + setError(null); 31 + 32 + let finalSelector = selector; 33 + if (!finalSelector && quoteText.trim()) { 34 + finalSelector = { 35 + type: "TextQuoteSelector", 36 + exact: quoteText.trim(), 37 + }; 38 + } 39 + 40 + const tagList = tags.split(",").map((t) => t.trim()).filter(Boolean); 41 + 42 + if (!text.trim()) { 43 + await createHighlight({ url, selector: finalSelector, color: "yellow", tags: tagList }); 44 + } else { 45 + await createAnnotation({ url, text: text.trim(), selector: finalSelector || undefined, tags: tagList }); 46 + } 47 + 48 + setText(""); 49 + setQuoteText(""); 50 + setSelector(null); 51 + if (onSuccess) onSuccess(); 52 + } catch (err: any) { 53 + setError(err.message || "Failed to post"); 54 + } finally { 55 + setLoading(false); 56 + } 57 + }; 58 + 59 + const handleRemoveSelector = () => { 60 + setSelector(null); 61 + setQuoteText(""); 62 + setShowQuoteInput(false); 63 + }; 64 + 65 + return ( 66 + <form onSubmit={handleSubmit} className="flex flex-col gap-4"> 67 + <div className="flex items-center justify-between"> 68 + <h3 className="text-lg font-bold text-surface-900 dark:text-white">New Annotation</h3> 69 + {url && <div className="text-xs text-surface-400 dark:text-surface-500 max-w-[200px] truncate">{url}</div>} 70 + </div> 71 + 72 + {highlightedText && ( 73 + <div className="relative p-3 bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg"> 74 + <button 75 + type="button" 76 + className="absolute top-2 right-2 text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300" 77 + onClick={handleRemoveSelector} 78 + > 79 + <X size={16} /> 80 + </button> 81 + <blockquote className="italic text-surface-600 dark:text-surface-300 border-l-2 border-primary-400 dark:border-primary-500 pl-3 text-sm"> 82 + "{highlightedText}" 83 + </blockquote> 84 + </div> 85 + )} 86 + 87 + {!highlightedText && ( 88 + <> 89 + {!showQuoteInput ? ( 90 + <button 91 + type="button" 92 + className="text-left text-sm text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 font-medium py-1" 93 + onClick={() => setShowQuoteInput(true)} 94 + > 95 + + Add a quote from the page 96 + </button> 97 + ) : ( 98 + <div className="flex flex-col gap-2"> 99 + <textarea 100 + value={quoteText} 101 + onChange={(e) => setQuoteText(e.target.value)} 102 + placeholder="Paste or type the text you're annotating..." 103 + className="w-full text-sm p-3 bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400 outline-none" 104 + rows={2} 105 + /> 106 + <div className="flex justify-end"> 107 + <button type="button" className="text-xs text-red-500 dark:text-red-400 font-medium" onClick={handleRemoveSelector}> 108 + Remove Quote 109 + </button> 110 + </div> 111 + </div> 112 + )} 113 + </> 114 + )} 115 + 116 + <textarea 117 + value={text} 118 + onChange={(e) => setText(e.target.value)} 119 + placeholder={highlightedText || quoteText ? "Add your comment..." : "Write your annotation..."} 120 + className="w-full p-3 bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400 outline-none min-h-[100px] resize-none" 121 + maxLength={3000} 122 + disabled={loading} 123 + /> 124 + 125 + <input 126 + type="text" 127 + value={tags} 128 + onChange={(e) => setTags(e.target.value)} 129 + placeholder="Tags (comma separated)" 130 + className="w-full p-2.5 bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400 outline-none text-sm" 131 + disabled={loading} 132 + /> 133 + 134 + <div className="flex items-center justify-between pt-2"> 135 + <span className={text.length > 2900 ? "text-red-500 dark:text-red-400 text-xs font-medium" : "text-surface-400 dark:text-surface-500 text-xs"}> 136 + {text.length}/3000 137 + </span> 138 + <div className="flex items-center gap-2"> 139 + {onCancel && ( 140 + <button 141 + type="button" 142 + className="text-sm font-medium text-surface-500 dark:text-surface-400 hover:text-surface-800 dark:hover:text-surface-200 px-3 py-1.5" 143 + onClick={onCancel} 144 + disabled={loading} 145 + > 146 + Cancel 147 + </button> 148 + )} 149 + <button 150 + type="submit" 151 + className="bg-primary-600 hover:bg-primary-700 text-white font-medium px-4 py-1.5 rounded-lg transition-colors disabled:opacity-50 text-sm" 152 + disabled={loading || (!text.trim() && !highlightedText && !quoteText.trim())} 153 + > 154 + {loading ? "..." : "Post"} 155 + </button> 156 + </div> 157 + </div> 158 + 159 + {error && <div className="text-red-500 dark:text-red-400 text-sm text-center bg-red-50 dark:bg-red-900/20 py-2 rounded-lg">{error}</div>} 160 + </form> 161 + ); 162 + }
+205
web/src/components/EditProfileModal.tsx
··· 1 + import React, { useState, useRef } from 'react'; 2 + import { updateProfile, uploadAvatar, getAvatarUrl } from '../api/client'; 3 + import type { UserProfile } from '../types'; 4 + import { Loader2, X, Plus, User as UserIcon } from 'lucide-react'; 5 + 6 + interface EditProfileModalProps { 7 + profile: UserProfile; 8 + onClose: () => void; 9 + onUpdate: (updatedProfile: UserProfile) => void; 10 + } 11 + 12 + export default function EditProfileModal({ profile, onClose, onUpdate }: EditProfileModalProps) { 13 + const [displayName, setDisplayName] = useState(profile.displayName || ''); 14 + const [description, setDescription] = useState(profile.description || ''); 15 + const [website, setWebsite] = useState(profile.website || ''); 16 + const [links, setLinks] = useState<string[]>(profile.links || []); 17 + const [newLink, setNewLink] = useState(''); 18 + 19 + const [avatarBlob, setAvatarBlob] = useState<Blob | null>(null); 20 + const [avatarPreview, setAvatarPreview] = useState<string | null>(null); 21 + const [uploading, setUploading] = useState(false); 22 + 23 + const [saving, setSaving] = useState(false); 24 + const [error, setError] = useState<string | null>(null); 25 + const fileInputRef = useRef<HTMLInputElement>(null); 26 + 27 + const handleAvatarChange = async (e: React.ChangeEvent<HTMLInputElement>) => { 28 + const file = e.target.files?.[0]; 29 + if (!file) return; 30 + 31 + if (!['image/jpeg', 'image/png'].includes(file.type)) { 32 + setError('Please select a JPEG or PNG image'); 33 + return; 34 + } 35 + 36 + if (file.size > 1024 * 1024 * 2) { 37 + setError('Image must be under 2MB'); 38 + return; 39 + } 40 + 41 + setAvatarPreview(URL.createObjectURL(file)); 42 + setAvatarBlob(file); 43 + 44 + setUploading(true); 45 + try { 46 + const result = await uploadAvatar(file); 47 + setAvatarBlob(result.blob); 48 + } catch (err: any) { 49 + setError('Failed to upload: ' + err.message); 50 + setAvatarPreview(null); 51 + } finally { 52 + setUploading(false); 53 + } 54 + }; 55 + 56 + const handleAddLink = () => { 57 + if (!newLink) return; 58 + if (!links.includes(newLink)) { 59 + setLinks([...links, newLink]); 60 + setNewLink(''); 61 + } 62 + }; 63 + 64 + const handleRemoveLink = (index: number) => { 65 + setLinks(links.filter((_, i) => i !== index)); 66 + }; 67 + 68 + const handleSubmit = async (e: React.FormEvent) => { 69 + e.preventDefault(); 70 + setSaving(true); 71 + setError(null); 72 + 73 + try { 74 + await updateProfile({ displayName, description, website, links, avatar: avatarBlob }); 75 + onUpdate({ ...profile, displayName, description, website, links, avatar: avatarPreview || profile.avatar }); 76 + onClose(); 77 + } catch (err: any) { 78 + setError(err.message); 79 + } finally { 80 + setSaving(false); 81 + } 82 + }; 83 + 84 + const currentAvatar = avatarPreview || getAvatarUrl(profile.did, profile.avatar); 85 + 86 + return ( 87 + <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4" onClick={onClose}> 88 + <div className="bg-white dark:bg-surface-900 rounded-xl w-full max-w-md overflow-hidden shadow-2xl ring-1 ring-black/5 dark:ring-white/10" onClick={e => e.stopPropagation()}> 89 + <div className="flex items-center justify-between p-4 border-b border-surface-100 dark:border-surface-800"> 90 + <h2 className="text-lg font-bold text-surface-900 dark:text-white">Edit Profile</h2> 91 + <button onClick={onClose} className="p-1.5 hover:bg-surface-100 dark:hover:bg-surface-800 rounded-lg transition-colors text-surface-500 dark:text-surface-400"> 92 + <X size={18} /> 93 + </button> 94 + </div> 95 + 96 + <form onSubmit={handleSubmit} className="p-5 overflow-y-auto max-h-[80vh]"> 97 + {error && ( 98 + <div className="mb-4 p-3 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-sm border border-red-100 dark:border-red-800"> 99 + {error} 100 + </div> 101 + )} 102 + 103 + <div className="mb-5"> 104 + <label className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-2">Avatar</label> 105 + <div className="flex items-center gap-3"> 106 + <div 107 + className="relative w-16 h-16 rounded-full bg-surface-100 dark:bg-surface-800 overflow-hidden cursor-pointer group border border-surface-200 dark:border-surface-700" 108 + onClick={() => fileInputRef.current?.click()} 109 + > 110 + {currentAvatar ? ( 111 + <img src={currentAvatar} alt="" className="w-full h-full object-cover" /> 112 + ) : ( 113 + <div className="w-full h-full flex items-center justify-center text-surface-400 dark:text-surface-500"> 114 + <UserIcon size={24} /> 115 + </div> 116 + )} 117 + <div className="absolute inset-0 bg-black/40 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"> 118 + <span className="text-white text-xs font-medium">Edit</span> 119 + </div> 120 + </div> 121 + <input ref={fileInputRef} type="file" accept="image/jpeg,image/png" onChange={handleAvatarChange} className="hidden" /> 122 + <button 123 + type="button" 124 + onClick={() => fileInputRef.current?.click()} 125 + className="px-3 py-1.5 rounded-lg bg-surface-100 dark:bg-surface-800 hover:bg-surface-200 dark:hover:bg-surface-700 text-surface-900 dark:text-white font-medium text-sm transition-colors" 126 + disabled={uploading} 127 + > 128 + {uploading ? 'Uploading...' : 'Upload'} 129 + </button> 130 + </div> 131 + </div> 132 + 133 + <div className="mb-4"> 134 + <label className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-1">Display Name</label> 135 + <input 136 + type="text" 137 + value={displayName} 138 + onChange={e => setDisplayName(e.target.value)} 139 + className="w-full px-3 py-2 rounded-lg bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 text-surface-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400" 140 + maxLength={64} 141 + /> 142 + </div> 143 + 144 + <div className="mb-4"> 145 + <label className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-1">Bio</label> 146 + <textarea 147 + value={description} 148 + onChange={e => setDescription(e.target.value)} 149 + className="w-full px-3 py-2 rounded-lg bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 text-surface-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400 min-h-[80px] resize-none" 150 + maxLength={300} 151 + /> 152 + </div> 153 + 154 + <div className="mb-4"> 155 + <label className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-1">Website</label> 156 + <input 157 + type="url" 158 + value={website} 159 + onChange={e => setWebsite(e.target.value)} 160 + placeholder="https://example.com" 161 + className="w-full px-3 py-2 rounded-lg bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400 text-sm" 162 + /> 163 + </div> 164 + 165 + <div className="mb-5"> 166 + <label className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-1">Links</label> 167 + <div className="space-y-2"> 168 + {links.map((link, i) => ( 169 + <div key={i} className="flex items-center gap-2"> 170 + <input type="text" value={link} readOnly className="flex-1 px-3 py-2 rounded-lg bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 text-sm text-surface-600 dark:text-surface-300" /> 171 + <button type="button" onClick={() => handleRemoveLink(i)} className="p-2 text-surface-400 dark:text-surface-500 hover:text-red-500 dark:hover:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg"> 172 + <X size={14} /> 173 + </button> 174 + </div> 175 + ))} 176 + <div className="flex items-center gap-2"> 177 + <input 178 + type="url" 179 + value={newLink} 180 + onChange={e => setNewLink(e.target.value)} 181 + placeholder="Add a link..." 182 + className="flex-1 px-3 py-2 rounded-lg bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400 text-sm" 183 + onKeyDown={e => e.key === 'Enter' && (e.preventDefault(), handleAddLink())} 184 + /> 185 + <button type="button" onClick={handleAddLink} className="p-2 bg-surface-900 dark:bg-surface-700 text-white rounded-lg hover:bg-surface-800 dark:hover:bg-surface-600"> 186 + <Plus size={18} /> 187 + </button> 188 + </div> 189 + </div> 190 + </div> 191 + 192 + <div className="flex items-center justify-end gap-2 pt-4 border-t border-surface-100 dark:border-surface-800"> 193 + <button type="button" onClick={onClose} className="px-4 py-2 rounded-lg text-surface-600 dark:text-surface-300 font-medium hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors" disabled={saving}> 194 + Cancel 195 + </button> 196 + <button type="submit" className="px-4 py-2 rounded-lg bg-primary-600 text-white font-medium hover:bg-primary-700 transition-colors flex items-center gap-2" disabled={saving}> 197 + {saving && <Loader2 size={14} className="animate-spin" />} 198 + {saving ? 'Saving...' : 'Save'} 199 + </button> 200 + </div> 201 + </form> 202 + </div> 203 + </div> 204 + ); 205 + }
+546
web/src/components/Icons.tsx
··· 1 + 2 + import React from 'react'; 3 + import { FaGithub, FaLinkedin } from "react-icons/fa"; 4 + // @ts-ignore 5 + import tangledLogo from "../assets/tangled.svg"; 6 + 7 + interface IconProps { 8 + size?: number; 9 + color?: string; 10 + filled?: boolean; 11 + } 12 + 13 + export function HeartIcon({ filled = false, size = 18 }: IconProps) { 14 + return filled ? ( 15 + <svg 16 + width={size} 17 + height={size} 18 + viewBox="0 0 24 24" 19 + fill="currentColor" 20 + stroke="none" 21 + > 22 + <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" /> 23 + </svg> 24 + ) : ( 25 + <svg 26 + width={size} 27 + height={size} 28 + viewBox="0 0 24 24" 29 + fill="none" 30 + stroke="currentColor" 31 + strokeWidth="2" 32 + strokeLinecap="round" 33 + strokeLinejoin="round" 34 + > 35 + <path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" /> 36 + </svg> 37 + ); 38 + } 39 + 40 + export function MessageIcon({ size = 18 }: IconProps) { 41 + return ( 42 + <svg 43 + width={size} 44 + height={size} 45 + viewBox="0 0 24 24" 46 + fill="none" 47 + stroke="currentColor" 48 + strokeWidth="2" 49 + strokeLinecap="round" 50 + strokeLinejoin="round" 51 + > 52 + <path d="m3 21 1.9-5.7a8.5 8.5 0 1 1 3.8 3.8z" /> 53 + </svg> 54 + ); 55 + } 56 + 57 + export function ShareIcon({ size = 18 }: IconProps) { 58 + return ( 59 + <svg 60 + width={size} 61 + height={size} 62 + viewBox="0 0 24 24" 63 + fill="none" 64 + stroke="currentColor" 65 + strokeWidth="2" 66 + strokeLinecap="round" 67 + strokeLinejoin="round" 68 + > 69 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" /> 70 + <polyline points="16 6 12 2 8 6" /> 71 + <line x1="12" x2="12" y1="2" y2="15" /> 72 + </svg> 73 + ); 74 + } 75 + 76 + export function TrashIcon({ size = 18 }: IconProps) { 77 + return ( 78 + <svg 79 + width={size} 80 + height={size} 81 + viewBox="0 0 24 24" 82 + fill="none" 83 + stroke="currentColor" 84 + strokeWidth="2" 85 + strokeLinecap="round" 86 + strokeLinejoin="round" 87 + > 88 + <path d="M3 6h18" /> 89 + <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" /> 90 + <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" /> 91 + </svg> 92 + ); 93 + } 94 + 95 + export function LinkIcon({ size = 18 }: IconProps) { 96 + return ( 97 + <svg 98 + width={size} 99 + height={size} 100 + viewBox="0 0 24 24" 101 + fill="none" 102 + stroke="currentColor" 103 + strokeWidth="2" 104 + strokeLinecap="round" 105 + strokeLinejoin="round" 106 + > 107 + <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /> 108 + <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /> 109 + </svg> 110 + ); 111 + } 112 + 113 + export function ExternalLinkIcon({ size = 14 }: IconProps) { 114 + return ( 115 + <svg 116 + width={size} 117 + height={size} 118 + viewBox="0 0 24 24" 119 + fill="none" 120 + stroke="currentColor" 121 + strokeWidth="2" 122 + strokeLinecap="round" 123 + strokeLinejoin="round" 124 + > 125 + <path d="M15 3h6v6" /> 126 + <path d="M10 14 21 3" /> 127 + <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /> 128 + </svg> 129 + ); 130 + } 131 + 132 + export function PenIcon({ size = 18 }: IconProps) { 133 + return ( 134 + <svg 135 + width={size} 136 + height={size} 137 + viewBox="0 0 24 24" 138 + fill="none" 139 + stroke="currentColor" 140 + strokeWidth="2" 141 + strokeLinecap="round" 142 + strokeLinejoin="round" 143 + > 144 + <path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" /> 145 + </svg> 146 + ); 147 + } 148 + 149 + export function HighlightIcon({ size = 18 }: IconProps) { 150 + return ( 151 + <svg 152 + width={size} 153 + height={size} 154 + viewBox="0 0 24 24" 155 + fill="none" 156 + stroke="currentColor" 157 + strokeWidth="2" 158 + strokeLinecap="round" 159 + strokeLinejoin="round" 160 + > 161 + <path d="m9 11-6 6v3h9l3-3" /> 162 + <path d="m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4" /> 163 + </svg> 164 + ); 165 + } 166 + 167 + export function BookmarkIcon({ size = 18 }: IconProps) { 168 + return ( 169 + <svg 170 + width={size} 171 + height={size} 172 + viewBox="0 0 24 24" 173 + fill="none" 174 + stroke="currentColor" 175 + strokeWidth="2" 176 + strokeLinecap="round" 177 + strokeLinejoin="round" 178 + > 179 + <path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" /> 180 + </svg> 181 + ); 182 + } 183 + 184 + export function TagIcon({ size = 18 }: IconProps) { 185 + return ( 186 + <svg 187 + width={size} 188 + height={size} 189 + viewBox="0 0 24 24" 190 + fill="none" 191 + stroke="currentColor" 192 + strokeWidth="2" 193 + strokeLinecap="round" 194 + strokeLinejoin="round" 195 + > 196 + <path d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z" /> 197 + <circle cx="7.5" cy="7.5" r=".5" fill="currentColor" /> 198 + </svg> 199 + ); 200 + } 201 + 202 + export function AlertIcon({ size = 18 }: IconProps) { 203 + return ( 204 + <svg 205 + width={size} 206 + height={size} 207 + viewBox="0 0 24 24" 208 + fill="none" 209 + stroke="currentColor" 210 + strokeWidth="2" 211 + strokeLinecap="round" 212 + strokeLinejoin="round" 213 + > 214 + <circle cx="12" cy="12" r="10" /> 215 + <line x1="12" x2="12" y1="8" y2="12" /> 216 + <line x1="12" x2="12.01" y1="16" y2="16" /> 217 + </svg> 218 + ); 219 + } 220 + 221 + export function FileTextIcon({ size = 18 }: IconProps) { 222 + return ( 223 + <svg 224 + width={size} 225 + height={size} 226 + viewBox="0 0 24 24" 227 + fill="none" 228 + stroke="currentColor" 229 + strokeWidth="2" 230 + strokeLinecap="round" 231 + strokeLinejoin="round" 232 + > 233 + <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /> 234 + <path d="M14 2v4a2 2 0 0 0 2 2h4" /> 235 + <path d="M10 9H8" /> 236 + <path d="M16 13H8" /> 237 + <path d="M16 17H8" /> 238 + </svg> 239 + ); 240 + } 241 + 242 + export function SearchIcon({ size = 18 }: IconProps) { 243 + return ( 244 + <svg 245 + width={size} 246 + height={size} 247 + viewBox="0 0 24 24" 248 + fill="none" 249 + stroke="currentColor" 250 + strokeWidth="2" 251 + strokeLinecap="round" 252 + strokeLinejoin="round" 253 + > 254 + <circle cx="11" cy="11" r="8" /> 255 + <path d="m21 21-4.3-4.3" /> 256 + </svg> 257 + ); 258 + } 259 + 260 + export function InboxIcon({ size = 18 }: IconProps) { 261 + return ( 262 + <svg 263 + width={size} 264 + height={size} 265 + viewBox="0 0 24 24" 266 + fill="none" 267 + stroke="currentColor" 268 + strokeWidth="2" 269 + strokeLinecap="round" 270 + strokeLinejoin="round" 271 + > 272 + <polyline points="22 12 16 12 14 15 10 15 8 12 2 12" /> 273 + <path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> 274 + </svg> 275 + ); 276 + } 277 + 278 + export function BlueskyIcon({ size = 18, color = "currentColor" }: IconProps) { 279 + return ( 280 + <svg 281 + xmlns="http://www.w3.org/2000/svg" 282 + viewBox="0 0 512 512" 283 + width={size} 284 + height={size} 285 + > 286 + <path 287 + fill={color} 288 + d="M111.8 62.2C170.2 105.9 233 194.7 256 242.4c23-47.6 85.8-136.4 144.2-180.2c42.1-31.6 110.3-56 110.3 21.8c0 15.5-8.9 130.5-14.1 149.2C478.2 298 412 314.6 353.1 304.5c102.9 17.5 129.1 75.5 72.5 133.5c-107.4 110.2-154.3-27.6-166.3-62.9l0 0c-1.7-4.9-2.6-7.8-3.3-7.8s-1.6 3-3.3 7.8l0 0c-12 35.3-59 173.1-166.3 62.9c-56.5-58-30.4-116 72.5-133.5C100 314.6 33.8 298 15.7 233.1C10.4 214.4 1.5 99.4 1.5 83.9c0-77.8 68.2-53.4 110.3-21.8z" 289 + /> 290 + </svg> 291 + ); 292 + } 293 + 294 + export function MarginIcon({ size = 18 }: IconProps) { 295 + return ( 296 + <svg 297 + width={size} 298 + height={size} 299 + viewBox="0 0 265 231" 300 + fill="currentColor" 301 + xmlns="http://www.w3.org/2000/svg" 302 + > 303 + <path d="M0 230 V0 H199 V65.7156 H149.5 V115.216 H182.5 L199 131.716 V230 Z" /> 304 + <path d="M215 214.224 V230 H264.5 V0 H215.07 V16.2242 H248.5 V214.224 H215 Z" /> 305 + </svg> 306 + ); 307 + } 308 + 309 + export function LogoutIcon({ size = 18 }: IconProps) { 310 + return ( 311 + <svg 312 + width={size} 313 + height={size} 314 + viewBox="0 0 24 24" 315 + fill="none" 316 + stroke="currentColor" 317 + strokeWidth="2" 318 + strokeLinecap="round" 319 + strokeLinejoin="round" 320 + > 321 + <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /> 322 + <polyline points="16 17 21 12 16 7" /> 323 + <line x1="21" x2="9" y1="12" y2="12" /> 324 + </svg> 325 + ); 326 + } 327 + 328 + export function BellIcon({ size = 18 }: IconProps) { 329 + return ( 330 + <svg 331 + width={size} 332 + height={size} 333 + viewBox="0 0 24 24" 334 + fill="none" 335 + stroke="currentColor" 336 + strokeWidth="2" 337 + strokeLinecap="round" 338 + strokeLinejoin="round" 339 + > 340 + <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" /> 341 + <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" /> 342 + </svg> 343 + ); 344 + } 345 + 346 + export function ReplyIcon({ size = 18 }: IconProps) { 347 + return ( 348 + <svg 349 + width={size} 350 + height={size} 351 + viewBox="0 0 24 24" 352 + fill="none" 353 + stroke="currentColor" 354 + strokeWidth="2" 355 + strokeLinecap="round" 356 + strokeLinejoin="round" 357 + > 358 + <polyline points="9 17 4 12 9 7" /> 359 + <path d="M20 18v-2a4 4 0 0 0-4-4H4" /> 360 + </svg> 361 + ); 362 + } 363 + 364 + export function AturiIcon({ size = 18 }: IconProps) { 365 + return ( 366 + <svg 367 + width={size} 368 + height={size} 369 + viewBox="0 0 24 24" 370 + fill="none" 371 + stroke="currentColor" 372 + strokeWidth="2" 373 + strokeLinecap="round" 374 + strokeLinejoin="round" 375 + > 376 + <path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10Z" /> 377 + <path d="M2 21c0-3 1.85-5.36 5.08-6C9.5 14.52 12 13 13 12" /> 378 + </svg> 379 + ); 380 + } 381 + 382 + export function BlackskyIcon({ size = 18 }: IconProps) { 383 + return ( 384 + <svg viewBox="0 0 285 285" width={size} height={size}> 385 + <path 386 + fill="currentColor" 387 + d="M148.846 144.562C148.846 159.75 161.158 172.062 176.346 172.062H207.012V185.865H176.346C161.158 185.865 148.846 198.177 148.846 213.365V243.045H136.029V213.365C136.029 198.177 123.717 185.865 108.529 185.865H77.8633V172.062H108.529C123.717 172.062 136.029 159.75 136.029 144.562V113.896H148.846V144.562Z" 388 + /> 389 + <path 390 + fill="currentColor" 391 + d="M170.946 31.8766C160.207 42.616 160.207 60.0281 170.946 70.7675L192.631 92.4516L182.871 102.212L161.186 80.5275C150.447 69.7881 133.035 69.7881 122.296 80.5275L101.309 101.514L92.2456 92.4509L113.232 71.4642C123.972 60.7248 123.972 43.3128 113.232 32.5733L91.5488 10.8899L101.309 1.12988L122.993 22.814C133.732 33.5533 151.144 33.5534 161.884 22.814L183.568 1.12988L192.631 10.1925L170.946 31.8766Z" 392 + /> 393 + <path 394 + fill="currentColor" 395 + d="M79.0525 75.3259C75.1216 89.9962 83.8276 105.076 98.498 109.006L128.119 116.943L124.547 130.275L94.9267 122.338C80.2564 118.407 65.1772 127.113 61.2463 141.784L53.5643 170.453L41.1837 167.136L48.8654 138.467C52.7963 123.797 44.0902 108.718 29.4199 104.787L-0.201172 96.8497L3.37124 83.5173L32.9923 91.4542C47.6626 95.3851 62.7419 86.679 66.6728 72.0088L74.6098 42.3877L86.9895 45.7048L79.0525 75.3259Z" 396 + /> 397 + <path 398 + fill="currentColor" 399 + d="M218.413 71.4229C222.344 86.093 237.423 94.7992 252.094 90.8683L281.715 82.9313L285.287 96.2628L255.666 104.2C240.995 108.131 232.29 123.21 236.22 137.88L243.902 166.55L231.522 169.867L223.841 141.198C219.91 126.528 204.831 117.822 190.16 121.753L160.539 129.69L156.967 116.357L186.588 108.42C201.258 104.49 209.964 89.4103 206.033 74.74L198.096 45.1189L210.476 41.8018L218.413 71.4229Z" 400 + /> 401 + </svg> 402 + ); 403 + } 404 + 405 + export function NorthskyIcon({ size = 18 }: IconProps) { 406 + return ( 407 + <svg viewBox="0 0 1024 1024" width={size} height={size}> 408 + <defs> 409 + <linearGradient 410 + id="north_a" 411 + x1="564.17" 412 + y1="22.4" 413 + x2="374.54" 414 + y2="1187.29" 415 + gradientUnits="userSpaceOnUse" 416 + gradientTransform="matrix(1 0 0 1.03 31.9 91.01)" 417 + > 418 + <stop offset="0" stopColor="#2affba" /> 419 + <stop offset="0.02" stopColor="#31f4bd" /> 420 + <stop offset="0.14" stopColor="#53bccc" /> 421 + <stop offset="0.25" stopColor="#718ada" /> 422 + <stop offset="0.37" stopColor="#8a5fe5" /> 423 + <stop offset="0.49" stopColor="#9f3def" /> 424 + <stop offset="0.61" stopColor="#af22f6" /> 425 + <stop offset="0.74" stopColor="#bb0ffb" /> 426 + <stop offset="0.87" stopColor="#c204fe" /> 427 + <stop offset="1" stopColor="#c400ff" /> 428 + </linearGradient> 429 + <linearGradient 430 + id="north_b" 431 + x1="554.29" 432 + y1="20.79" 433 + x2="364.65" 434 + y2="1185.68" 435 + xlinkHref="#north_a" 436 + /> 437 + <linearGradient 438 + id="north_c" 439 + x1="561.1" 440 + y1="21.9" 441 + x2="371.47" 442 + y2="1186.79" 443 + xlinkHref="#north_a" 444 + /> 445 + <linearGradient 446 + id="north_d" 447 + x1="530.57" 448 + y1="16.93" 449 + x2="340.93" 450 + y2="1181.82" 451 + xlinkHref="#north_a" 452 + /> 453 + </defs> 454 + <path 455 + d="m275.87 880.64 272-184.16 120.79 114 78.55-56.88 184.6 125.1a485.5 485.5 0 0 0 55.81-138.27c-64.41-21.42-127-48.15-185.92-73.32-97-41.44-188.51-80.52-253.69-80.52-59.57 0-71.53 18.85-89.12 55-16.89 34.55-37.84 77.6-139.69 77.6-81.26 0-159.95-29.93-243.27-61.61-17.07-6.5-34.57-13.14-52.49-19.69A486.06 486.06 0 0 0 95.19 884l91.29-62.16Z" 456 + fill="url(#north_a)" 457 + /> 458 + <path 459 + d="M295.26 506.52c53.69 0 64.49-17.36 80.41-50.63 15.46-32.33 34.7-72.56 128.36-72.56 75 0 154.6 33.2 246.78 71.64 74.85 31.21 156.89 65.34 241 81.63a485.6 485.6 0 0 0-64.23-164.85c-108.88-6-201.82-43.35-284.6-76.69-66.77-26.89-129.69-52.22-182.84-52.22-46.88 0-56.43 15.74-70.55 45.89-13.41 28.65-31.79 67.87-118.24 67.87-44.25 0-90.68-13.48-141-33.11A488.3 488.3 0 0 0 62.86 435.7c8.3 3.38 16.55 6.74 24.68 10.08 76.34 31.22 148.3 60.74 207.72 60.74" 460 + fill="url(#north_b)" 461 + /> 462 + <path 463 + d="M319.2 687.81c61.24 0 73.38-19.09 91.18-55.66 16.7-34.28 37.48-76.95 137.58-76.95 81.4 0 174.78 39.89 282.9 86.09 52.19 22.29 107.38 45.84 163.42 65.43a483 483 0 0 0 2.72-136.5C898.41 554.4 806 516 722.27 481.05c-81.88-34.14-159.08-66.33-218.27-66.33-53.25 0-64 17.29-79.84 50.42-15.51 32.42-34.8 72.77-128.93 72.77-75.08 0-153.29-32-236.08-66l-8.91-3.64A487 487 0 0 0 24 601.68c27.31 9.55 53.55 19.52 79 29.19 80.24 30.55 149.61 56.94 216.2 56.94" 464 + fill="url(#north_c)" 465 + /> 466 + <path 467 + d="M341 279.65c13.49-28.78 31.95-68.19 119.16-68.19 68.59 0 137.73 27.84 210.92 57.32 70.14 28.22 148.13 59.58 233.72 69.37C815.77 218 673 140 511.88 140c-141.15 0-268.24 59.92-357.45 155.62 44 17.32 84.15 29.6 116.89 29.6 46.24 0 55.22-14.79 69.68-45.57" 468 + fill="url(#north_d)" 469 + /> 470 + </svg> 471 + ); 472 + } 473 + 474 + export function TophhieIcon({ size = 18 }: IconProps) { 475 + return ( 476 + <svg 477 + width={size} 478 + height={size} 479 + viewBox="0 0 344 538" 480 + fill="none" 481 + xmlns="http://www.w3.org/2000/svg" 482 + > 483 + <ellipse cx="268.5" cy="455.5" rx="34.5" ry="35.5" fill="currentColor" /> 484 + <ellipse cx="76" cy="75.5" rx="35" ry="35.5" fill="currentColor" /> 485 + <circle cx="268.5" cy="75.5" r="75.5" fill="currentColor" /> 486 + <ellipse cx="76" cy="274.5" rx="76" ry="75.5" fill="currentColor" /> 487 + <ellipse cx="76" cy="462.5" rx="76" ry="75.5" fill="currentColor" /> 488 + <circle cx="268.5" cy="269.5" r="75.5" fill="currentColor" /> 489 + </svg> 490 + ); 491 + } 492 + 493 + export function GithubIcon({ size = 18 }: IconProps) { 494 + return <FaGithub size={size} />; 495 + } 496 + 497 + export function LinkedinIcon({ size = 18 }: IconProps) { 498 + return <FaLinkedin size={size} />; 499 + } 500 + 501 + export function WitchskyIcon({ size = 18 }: IconProps) { 502 + return ( 503 + <svg fill="none" viewBox="0 0 512 512" width={size} height={size}> 504 + <path fill="#ee5346" d="M374.473 57.7173C367.666 50.7995 357.119 49.1209 348.441 53.1659C347.173 53.7567 342.223 56.0864 334.796 59.8613C326.32 64.1696 314.568 70.3869 301.394 78.0596C275.444 93.1728 242.399 114.83 218.408 139.477C185.983 172.786 158.719 225.503 140.029 267.661C130.506 289.144 122.878 308.661 117.629 322.81C116.301 326.389 115.124 329.63 114.104 332.478C87.1783 336.42 64.534 341.641 47.5078 348.101C37.6493 351.84 28.3222 356.491 21.0573 362.538C13.8818 368.511 6.00003 378.262 6.00003 391.822C6.00014 403.222 11.8738 411.777 17.4566 417.235C23.0009 422.655 29.9593 426.793 36.871 430.062C50.8097 436.653 69.5275 441.988 90.8362 446.249C133.828 454.846 192.21 460 256.001 460C319.79 460 378.172 454.846 421.164 446.249C442.472 441.988 461.19 436.653 475.129 430.062C482.041 426.793 488.999 422.655 494.543 417.235C500.039 411.862 505.817 403.489 505.996 392.353L506 391.822L505.995 391.188C505.754 377.959 498.012 368.417 490.945 362.534C483.679 356.485 474.35 351.835 464.491 348.095C446.749 341.366 422.906 335.982 394.476 331.987C393.6 330.57 392.633 328.995 391.595 327.273C386.477 318.777 379.633 306.842 372.737 293.115C358.503 264.781 345.757 232.098 344.756 206.636C343.87 184.121 351.638 154.087 360.819 127.789C365.27 115.041 369.795 103.877 373.207 95.9072C374.909 91.9309 376.325 88.7712 377.302 86.6328C377.79 85.5645 378.167 84.7524 378.416 84.2224C378.54 83.9579 378.632 83.7635 378.69 83.643C378.718 83.5829 378.739 83.5411 378.75 83.5181C378.753 83.5108 378.756 83.5049 378.757 83.5015C382.909 74.8634 381.196 64.5488 374.473 57.7173Z" /> 505 + </svg> 506 + ); 507 + } 508 + 509 + export function CatskyIcon({ size = 18 }: IconProps) { 510 + return ( 511 + <svg fill="none" viewBox="0 0 67.733328 67.733329" width={size} height={size}> 512 + <path fill="#cba7f7" d="m 7.4595521,49.230487 -1.826355,1.186314 -0.00581,0.0064 c -0.6050542,0.41651 -1.129182,0.831427 -1.5159445,1.197382 -0.193382,0.182977 -0.3509469,0.347606 -0.4862911,0.535791 -0.067671,0.0941 -0.1322972,0.188188 -0.1933507,0.352343 -0.061048,0.164157 -0.1411268,0.500074 0.025624,0.844456 l 0.099589,0.200339 c 0.1666616,0.344173 0.4472046,0.428734 0.5969419,0.447854 0.1497358,0.01912 0.2507411,0.0024 0.352923,-0.02039 0.204367,-0.04555 0.4017284,-0.126033 0.6313049,-0.234117 0.4549828,-0.214229 1.0166476,-0.545006 1.6155328,-0.956275 l 0.014617,-0.01049 2.0855152,-1.357536 C 8.3399261,50.711052 7.8735929,49.979321 7.4596148,49.230532 Z" /> 513 + <path fill="#cba7f7" d="m 60.225246,49.199041 c -0.421632,0.744138 -0.895843,1.47112 -1.418104,2.178115 l 2.170542,1.413443 c 0.598885,0.411268 1.160549,0.742047 1.615532,0.956276 0.229578,0.108104 0.426937,0.188564 0.631304,0.234116 0.102186,0.02278 0.2061,0.03951 0.355838,0.02039 0.148897,-0.01901 0.427619,-0.104957 0.594612,-0.444358 l 0.0029,-0.0035 0.09667,-0.20034 h 0.0029 c 0.166756,-0.34438 0.08667,-0.680303 0.02562,-0.844455 -0.06104,-0.164158 -0.125675,-0.258251 -0.193352,-0.352343 -0.135356,-0.188186 -0.293491,-0.352814 -0.486873,-0.535792 -0.386891,-0.366 -0.911016,-0.780916 -1.516073,-1.197426 l -0.0082,-0.007 z" /> 514 + <path fill="#cba7f7" d="m 62.374822,42.996075 c -0.123437,0.919418 -0.330922,1.827482 -0.614997,2.71973 h 2.864745 c 0.698786,0 1.328766,-0.04848 1.817036,-0.1351 0.244137,-0.04331 0.449793,-0.09051 0.645864,-0.172979 0.09803,-0.04122 0.194035,-0.08458 0.315651,-0.190439 0.121618,-0.105868 0.330211,-0.348705 0.330211,-0.746032 v -0.233536 c 0,-0.397326 -0.208544,-0.637282 -0.330211,-0.743122 -0.121662,-0.105838 -0.217613,-0.152159 -0.315651,-0.193351 -0.196079,-0.08238 -0.401748,-0.129732 -0.645864,-0.17296 -0.488229,-0.08645 -1.118333,-0.132208 -1.817036,-0.132208 z" /> 515 + <path fill="#cba7f7" d="m 3.1074004,42.996075 c -0.6987018,0 -1.3264778,0.04576 -1.8147079,0.132208 -0.2441143,0.04324 -0.44978339,0.09059 -0.64586203,0.17296 -0.0980369,0.04118 -0.19398758,0.08751 -0.31565316,0.193351 C 0.20951466,43.600432 0.0015501,43.84039 0.0015501,44.237717 v 0.233535 c 0,0.397326 0.20800926,0.640175 0.32962721,0.746034 0.12161784,0.105867 0.21761904,0.149206 0.31565316,0.190437 0.19606972,0.08246 0.40172683,0.129657 0.64586203,0.172979 0.4882704,0.08663 1.1159226,0.1351 1.8147079,0.1351 H 5.9517617 C 5.6756425,44.822849 5.4740706,43.914705 5.3542351,42.996072 Z" /> 516 + <path fill="#cba7f7" d="m 64.667084,33.5073 c -0.430203,0 -0.690808,0.160181 -1.103618,0.372726 -0.41281,0.212535 -0.895004,0.507161 -1.40529,0.858434 l -0.84038,0.578305 c 0.360074,0.820951 0.644317,1.675211 0.844456,2.560741 l 1.136813,-0.78214 c 0.605058,-0.41651 1.12918,-0.834919 1.515944,-1.200875 0.193382,-0.182976 0.350947,-0.347609 0.486291,-0.535795 0.06767,-0.0941 0.132313,-0.188185 0.193351,-0.352341 0.06104,-0.164157 0.141126,-0.497171 -0.02562,-0.841544 L 65.369444,33.96156 C 65.163418,33.537073 64.829889,33.5073 64.669999,33.5073 Z" /> 517 + <path fill="#cba7f7" d="m 3.0648864,33.5073 c -0.1600423,3.64e-4 -0.4969719,0.0355 -0.7000249,0.45426 l -0.099589,0.203251 c -0.16676,0.344375 -0.089013,0.677388 -0.027951,0.841544 0.061047,0.164157 0.1285982,0.258248 0.1962636,0.352341 0.1353547,0.188186 0.2899962,0.352819 0.4833782,0.535795 0.386764,0.9138003,0.784365 1.518856,1.200875 l 1.1478766,0.78971 c 0.2068,-0.879769 0.5000939,-1.727856 0.8706646,-2.542104 v -5.81e-4 L 5.5761273,34.73846 C 5.065553,34.38699 4.5814871,34.09259 4.1685053,33.880026 3.7555236,33.667462 3.4962107,33.506322 3.0648893,33.5073 Z" /> 518 + <path fill="#cba7f7" d="m 34.206496,25.930929 c -7.358038,0 -14.087814,1.669555 -18.851571,4.452678 -4.763758,2.783122 -7.4049994,6.472247 -7.4049994,10.665932 0,4.229683 2.6374854,8.946766 7.2694834,12.60017 4.631996,3.653402 11.153152,6.176813 18.420538,6.176813 7.267388,0 13.908863,-2.52485 18.657979,-6.185354 4.749117,-3.660501 7.485285,-8.390746 7.485285,-12.591629 0,-4.236884 -2.494219,-7.904081 -7.079874,-10.67732 -4.585655,-2.773237 -11.1388,-4.44129 -18.496841,-4.44129 z" /> 519 + <path fill="#cba7f7" d="m 51.797573,6.1189692 c -0.02945,-7.175e-4 -0.05836,4.17e-5 -0.08736,5.831e-4 -0.143066,0.00254 -0.278681,0.00746 -0.419898,0.094338 -0.483586,0.2975835 -0.980437,0.9277726 -1.446058,1.5345809 -1.170891,1.5259255 -2.372514,3.8701448 -4.229269,7.0095668 -0.839492,1.419423 -2.308256,4.55051 -3.891486,8.089307 4.831393,0.745951 9.148869,2.222975 12.643546,4.336427 2.130458,1.288425 3.976812,2.848736 5.416167,4.643344 C 58.614334,27.483611 57.260351,22.206768 56.421696,19.015263 55.149066,14.172268 54.241403,10.340754 53.185389,8.0524745 52.815225,7.2503647 52.052073,6.1836069 51.974407,6.1337905 51.885945,6.1211124 51.79757,6.1189646 Z" /> 520 + <path fill="#cba7f7" d="m 15.935563,6.1189692 c -0.08837,0.00223 -0.176832,0.014766 -0.254502,0.064642 -0.48854,0.3133308 -0.763154,1.0667562 -1.13332,1.8688677 -1.056011,2.2882791 -1.963673,6.1197931 -3.236303,10.9627891 -0.85539,3.255187 -2.247014,8.680054 -3.4314032,13.071013 1.5346704,-1.910372 3.5390122,-3.56005 5.8517882,-4.91124 3.456591,-2.019439 7.668347,-3.458497 12.320324,-4.231015 C 24.452511,19.365796 22.96466,16.190327 22.117564,14.758042 20.260808,11.61862 19.059771,9.2744012 17.888878,7.7484762 17.423256,7.1416679 16.926404,6.5114787 16.442819,6.2138951 16.301603,6.127059 16.165987,6.1222115 16.02292,6.1195569 c -0.02901,-5.429e-4 -0.0579,-0.0013 -0.08734,-5.847e-4 z" /> 521 + </svg> 522 + ); 523 + } 524 + 525 + export function DeerIcon({ size = 18 }: IconProps) { 526 + return ( 527 + <svg fill="none" viewBox="0 0 512 512" width={size} height={size}> 528 + <path fill="#739f7c" d="m 149.96484,186.56641 46.09766,152.95898 c 0,0 -6.30222,-9.61174 -15.60547,-17.47656 -8.87322,-7.50128 -28.4082,-4.04492 -28.4082,-4.04492 0,0 6.14721,39.88867 15.53125,44.39843 10.71251,5.1482 22.19726,0.16993 22.19726,0.16993 0,0 11.7613,-4.87282 22.82032,31.82421 5.26534,17.47196 15.33258,50.877 20.9707,69.58594 2.16717,7.1913 8.83789,7.25781 8.83789,7.25781 0,0 6.67072,-0.0665 8.83789,-7.25781 5.63812,-18.70894 15.70536,-52.11398 20.9707,-69.58594 11.05902,-36.69703 22.82032,-31.82421 22.82032,-31.82421 0,0 11.48475,4.97827 22.19726,-0.16993 9.38404,-4.50976 15.5332,-44.39843 15.5332,-44.39843 0,0 -19.53693,-3.45636 -28.41015,4.04492 -9.30325,7.86482 -15.60547,17.47656 -15.60547,17.47656 l 46.09766,-152.95898 -49.32618,83.84179 -20.34375,-31.1914 6.35547,54.96875 -23.1582,39.36132 c 0,0 -2.97595,5.06226 -5.94336,4.68946 -0.009,-0.001 -0.0169,0.003 -0.0254,0.01 -0.008,-0.007 -0.0167,-0.0109 -0.0254,-0.01 -2.96741,0.3728 -5.94336,-4.68946 -5.94336,-4.68946 l -23.1582,-39.36132 6.35547,-54.96875 -20.34375,31.1914 z" transform="matrix(2.6921023,0,0,1.7145911,-396.58283,-308.01527)" /> 529 + </svg> 530 + ); 531 + } 532 + 533 + export function TangledIcon({ size = 18 }: IconProps) { 534 + return ( 535 + <div 536 + style={{ 537 + width: size, 538 + height: size, 539 + backgroundColor: "currentColor", 540 + WebkitMask: `url(${tangledLogo.src}) no-repeat center / contain`, 541 + mask: `url(${tangledLogo.src}) no-repeat center / contain`, 542 + display: "inline-block", 543 + }} 544 + /> 545 + ); 546 + }
+71
web/src/components/MasonryFeed.tsx
··· 1 + 2 + import React, { useEffect, useState } from 'react'; 3 + import { getFeed } from '../api/client'; 4 + import Card from './Card'; 5 + import { Loader2 } from 'lucide-react'; 6 + import { useStore } from '@nanostores/react'; 7 + import { $user, initAuth } from '../store/auth'; 8 + import type { AnnotationItem } from '../types'; 9 + 10 + interface MasonryFeedProps { 11 + motivation?: string; 12 + emptyMessage?: string; 13 + } 14 + 15 + export default function MasonryFeed({ 16 + motivation, 17 + emptyMessage = "No items found." 18 + }: MasonryFeedProps) { 19 + const user = useStore($user); 20 + const [items, setItems] = useState<AnnotationItem[]>([]); 21 + const [loading, setLoading] = useState(true); 22 + 23 + useEffect(() => { 24 + initAuth(); 25 + }, []); 26 + 27 + useEffect(() => { 28 + const fetchFeed = async () => { 29 + setLoading(true); 30 + try { 31 + const data = await getFeed({ type: 'my-feed', motivation }); 32 + setItems(data?.items || []); 33 + } catch (e) { 34 + console.error(e); 35 + } finally { 36 + setLoading(false); 37 + } 38 + }; 39 + fetchFeed(); 40 + }, [motivation]); 41 + 42 + const handleDelete = (uri: string) => { 43 + setItems((prev) => prev.filter(i => i.uri !== uri)); 44 + }; 45 + 46 + if (loading) { 47 + return ( 48 + <div className="flex justify-center py-20"> 49 + <Loader2 className="animate-spin text-primary-600 dark:text-primary-400" size={32} /> 50 + </div> 51 + ); 52 + } 53 + 54 + if (items.length === 0) { 55 + return ( 56 + <div className="text-center py-20 text-surface-500 dark:text-surface-400 bg-surface-50/50 dark:bg-surface-800/50 rounded-2xl border border-dashed border-surface-200 dark:border-surface-700"> 57 + <p>{emptyMessage}</p> 58 + </div> 59 + ); 60 + } 61 + 62 + return ( 63 + <div className="columns-1 xl:columns-2 gap-4 animate-fade-in"> 64 + {items.map((item) => ( 65 + <div key={item.uri || item.cid} className="break-inside-avoid mb-4"> 66 + <Card item={item} onDelete={handleDelete} /> 67 + </div> 68 + ))} 69 + </div> 70 + ); 71 + }
+195
web/src/components/MobileNav.tsx
··· 1 + 2 + import React, { useState, useEffect } from 'react'; 3 + import { Link, useLocation } from 'react-router-dom'; 4 + import { useStore } from '@nanostores/react'; 5 + import { $user, logout } from '../store/auth'; 6 + import { getUnreadNotificationCount } from '../api/client'; 7 + import { 8 + Home, 9 + Search, 10 + Folder, 11 + User, 12 + PenSquare, 13 + Bookmark, 14 + Settings, 15 + MoreHorizontal, 16 + LogOut, 17 + Bell, 18 + Highlighter, 19 + X 20 + } from 'lucide-react'; 21 + 22 + export default function MobileNav() { 23 + const user = useStore($user); 24 + const location = useLocation(); 25 + const [isMenuOpen, setIsMenuOpen] = useState(false); 26 + const [unreadCount, setUnreadCount] = useState(0); 27 + 28 + const isAuthenticated = !!user; 29 + 30 + const isActive = (path: string) => { 31 + if (path === '/') return location.pathname === '/'; 32 + return location.pathname.startsWith(path); 33 + }; 34 + 35 + useEffect(() => { 36 + if (isAuthenticated) { 37 + getUnreadNotificationCount() 38 + .then((count) => setUnreadCount(count || 0)) 39 + .catch(() => { }); 40 + } 41 + }, [isAuthenticated]); 42 + 43 + const closeMenu = () => setIsMenuOpen(false); 44 + 45 + return ( 46 + <> 47 + {isMenuOpen && ( 48 + <div 49 + className="fixed inset-0 bg-black/50 z-40 lg:hidden" 50 + onClick={closeMenu} 51 + /> 52 + )} 53 + 54 + {isMenuOpen && ( 55 + <div className="fixed bottom-16 left-0 right-0 bg-white dark:bg-surface-900 rounded-t-2xl shadow-2xl z-50 lg:hidden animate-slide-up"> 56 + <div className="p-4 space-y-1"> 57 + {isAuthenticated && user ? ( 58 + <> 59 + <Link 60 + to={`/profile/${user.did}`} 61 + className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors" 62 + onClick={closeMenu} 63 + > 64 + {user.avatar ? ( 65 + <img src={user.avatar} alt="" className="w-10 h-10 rounded-full object-cover" /> 66 + ) : ( 67 + <div className="w-10 h-10 rounded-full bg-surface-200 dark:bg-surface-700 flex items-center justify-center"> 68 + <User size={18} className="text-surface-500" /> 69 + </div> 70 + )} 71 + <div className="flex flex-col"> 72 + <span className="font-semibold text-surface-900 dark:text-white"> 73 + {user.displayName || user.handle} 74 + </span> 75 + <span className="text-sm text-surface-500">@{user.handle}</span> 76 + </div> 77 + </Link> 78 + 79 + <div className="h-px bg-surface-200 dark:bg-surface-700 my-2" /> 80 + 81 + <Link to="/highlights" className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200" onClick={closeMenu}> 82 + <Highlighter size={20} /> 83 + <span>Highlights</span> 84 + </Link> 85 + 86 + <Link to="/bookmarks" className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200" onClick={closeMenu}> 87 + <Bookmark size={20} /> 88 + <span>Bookmarks</span> 89 + </Link> 90 + 91 + <Link to="/collections" className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200" onClick={closeMenu}> 92 + <Folder size={20} /> 93 + <span>Collections</span> 94 + </Link> 95 + 96 + <Link to="/settings" className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200" onClick={closeMenu}> 97 + <Settings size={20} /> 98 + <span>Settings</span> 99 + </Link> 100 + 101 + <div className="h-px bg-surface-200 dark:bg-surface-700 my-2" /> 102 + 103 + <button 104 + className="flex items-center gap-3 p-3 rounded-xl hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors text-red-600 w-full" 105 + onClick={() => { 106 + logout(); 107 + closeMenu(); 108 + }} 109 + > 110 + <LogOut size={20} /> 111 + <span>Log Out</span> 112 + </button> 113 + </> 114 + ) : ( 115 + <> 116 + <Link to="/login" className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200" onClick={closeMenu}> 117 + <User size={20} /> 118 + <span>Sign In</span> 119 + </Link> 120 + <Link to="/collections" className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200" onClick={closeMenu}> 121 + <Folder size={20} /> 122 + <span>Collections</span> 123 + </Link> 124 + <Link to="/settings" className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200" onClick={closeMenu}> 125 + <Settings size={20} /> 126 + <span>Settings</span> 127 + </Link> 128 + </> 129 + )} 130 + </div> 131 + </div> 132 + )} 133 + 134 + <nav className="fixed bottom-0 left-0 right-0 h-14 bg-white dark:bg-surface-900 border-t border-surface-200 dark:border-surface-700 flex items-center justify-around px-2 z-50 lg:hidden safe-area-bottom"> 135 + <Link 136 + to="/home" 137 + className={`flex flex-col items-center justify-center w-14 h-14 rounded-xl transition-colors ${isActive('/home') ? 'text-primary-600' : 'text-surface-500 hover:text-surface-700' 138 + }`} 139 + onClick={closeMenu} 140 + > 141 + <Home size={24} strokeWidth={1.5} /> 142 + </Link> 143 + 144 + <Link 145 + to="/url" 146 + className={`flex flex-col items-center justify-center w-14 h-14 rounded-xl transition-colors ${isActive('/url') ? 'text-primary-600' : 'text-surface-500 hover:text-surface-700' 147 + }`} 148 + onClick={closeMenu} 149 + > 150 + <Search size={24} strokeWidth={1.5} /> 151 + </Link> 152 + 153 + {isAuthenticated ? ( 154 + <> 155 + <Link 156 + to="/new" 157 + className="flex items-center justify-center w-12 h-12 rounded-full bg-primary-600 text-white shadow-lg hover:bg-primary-500 transition-colors -mt-4" 158 + onClick={closeMenu} 159 + > 160 + <PenSquare size={20} strokeWidth={2} /> 161 + </Link> 162 + 163 + <Link 164 + to="/notifications" 165 + className={`relative flex flex-col items-center justify-center w-14 h-14 rounded-xl transition-colors ${isActive('/notifications') ? 'text-primary-600' : 'text-surface-500 hover:text-surface-700' 166 + }`} 167 + onClick={closeMenu} 168 + > 169 + <Bell size={24} strokeWidth={1.5} /> 170 + {unreadCount > 0 && ( 171 + <span className="absolute top-2 right-2 w-2 h-2 bg-red-500 rounded-full" /> 172 + )} 173 + </Link> 174 + </> 175 + ) : ( 176 + <Link 177 + to="/login" 178 + className="flex items-center justify-center w-12 h-12 rounded-full bg-primary-600 text-white shadow-lg hover:bg-primary-500 transition-colors -mt-4" 179 + onClick={closeMenu} 180 + > 181 + <User size={20} strokeWidth={2} /> 182 + </Link> 183 + )} 184 + 185 + <button 186 + className={`flex flex-col items-center justify-center w-14 h-14 rounded-xl transition-colors ${isMenuOpen ? 'text-primary-600' : 'text-surface-500 hover:text-surface-700' 187 + }`} 188 + onClick={() => setIsMenuOpen(!isMenuOpen)} 189 + > 190 + {isMenuOpen ? <X size={24} strokeWidth={1.5} /> : <MoreHorizontal size={24} strokeWidth={1.5} />} 191 + </button> 192 + </nav> 193 + </> 194 + ); 195 + }
+171
web/src/components/ReplyList.tsx
··· 1 + import React from 'react'; 2 + import { formatDistanceToNow } from 'date-fns'; 3 + import { MessageSquare, Trash2, Reply } from 'lucide-react'; 4 + import type { AnnotationItem, UserProfile } from '../types'; 5 + import { getAvatarUrl } from '../api/client'; 6 + import { clsx } from 'clsx'; 7 + 8 + interface ReplyListProps { 9 + replies: AnnotationItem[]; 10 + rootUri: string; 11 + user: UserProfile | null; 12 + onReply: (reply: AnnotationItem) => void; 13 + onDelete: (reply: AnnotationItem) => void; 14 + isInline?: boolean; 15 + } 16 + 17 + interface ReplyItemProps { 18 + reply: AnnotationItem & { children?: AnnotationItem[] }; 19 + depth: number; 20 + user: UserProfile | null; 21 + onReply: (reply: AnnotationItem) => void; 22 + onDelete: (reply: AnnotationItem) => void; 23 + isInline: boolean; 24 + } 25 + 26 + const ReplyItem: React.FC<ReplyItemProps> = ({ reply, depth = 0, user, onReply, onDelete, isInline }) => { 27 + const author = reply.author || reply.creator || {}; 28 + const isReplyOwner = user?.did && author.did === user.did; 29 + 30 + if (!author.handle && !author.did) return null; 31 + 32 + return ( 33 + <div key={reply.uri || reply.id}> 34 + <div 35 + className={clsx( 36 + "relative mb-2 transition-colors", 37 + isInline ? "flex gap-3" : "rounded-lg", 38 + depth > 0 && "ml-4 pl-3 border-l-2 border-surface-200 dark:border-surface-700" 39 + )} 40 + > 41 + {isInline ? ( 42 + <> 43 + <a href={`/profile/${author.handle}`} className="shrink-0"> 44 + {getAvatarUrl(author.did, author.avatar) ? ( 45 + <img 46 + src={getAvatarUrl(author.did, author.avatar)} 47 + alt="" 48 + className={clsx("rounded-full object-cover bg-surface-200 dark:bg-surface-700", depth > 0 ? "w-6 h-6" : "w-7 h-7")} 49 + /> 50 + ) : ( 51 + <div className={clsx("rounded-full bg-surface-200 dark:bg-surface-700 flex items-center justify-center text-surface-500 dark:text-surface-400 font-bold", depth > 0 ? "w-6 h-6 text-[10px]" : "w-7 h-7 text-xs")}> 52 + {(author.displayName || author.handle || "?")[0]?.toUpperCase()} 53 + </div> 54 + )} 55 + </a> 56 + <div className="flex-1 min-w-0"> 57 + <div className="flex items-baseline gap-2 mb-0.5 flex-wrap"> 58 + <span className={clsx("font-medium text-surface-900 dark:text-white", depth > 0 ? "text-xs" : "text-sm")}> 59 + {author.displayName || author.handle} 60 + </span> 61 + <span className="text-surface-400 dark:text-surface-500 text-xs"> 62 + {reply.createdAt ? formatDistanceToNow(new Date(reply.createdAt), { addSuffix: false }) : ''} 63 + </span> 64 + 65 + <div className="ml-auto flex gap-2"> 66 + <button 67 + onClick={() => onReply(reply)} 68 + className="text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300 transition-colors flex items-center gap-1 text-[10px] uppercase font-medium" 69 + > 70 + <MessageSquare size={12} /> 71 + </button> 72 + {isReplyOwner && ( 73 + <button 74 + onClick={() => onDelete(reply)} 75 + className="text-surface-400 dark:text-surface-500 hover:text-red-500 dark:hover:text-red-400 transition-colors" 76 + > 77 + <Trash2 size={12} /> 78 + </button> 79 + )} 80 + </div> 81 + </div> 82 + <p className={clsx("text-surface-800 dark:text-surface-200 whitespace-pre-wrap leading-relaxed", depth > 0 ? "text-sm" : "text-sm")}> 83 + {reply.text || reply.body?.value} 84 + </p> 85 + </div> 86 + </> 87 + ) : ( 88 + <div className="p-3 bg-white dark:bg-surface-900 rounded-lg ring-1 ring-black/5 dark:ring-white/5"> 89 + <div className="flex items-center gap-2 mb-2"> 90 + <a href={`/profile/${author.handle}`} className="shrink-0"> 91 + {getAvatarUrl(author.did, author.avatar) ? ( 92 + <img src={getAvatarUrl(author.did, author.avatar)} alt="" className="w-7 h-7 rounded-full object-cover bg-surface-200 dark:bg-surface-700" /> 93 + ) : ( 94 + <div className="w-7 h-7 rounded-full bg-surface-200 dark:bg-surface-700 flex items-center justify-center text-surface-500 dark:text-surface-400 font-bold text-xs"> 95 + {(author.displayName || author.handle || "?")[0]?.toUpperCase()} 96 + </div> 97 + )} 98 + </a> 99 + <div className="flex flex-col"> 100 + <span className="font-medium text-surface-900 dark:text-white text-sm">{author.displayName || author.handle}</span> 101 + </div> 102 + <span className="text-surface-400 dark:text-surface-500 text-xs ml-auto"> 103 + {reply.createdAt ? formatDistanceToNow(new Date(reply.createdAt), { addSuffix: false }) : ''} 104 + </span> 105 + </div> 106 + <p className="text-surface-800 dark:text-surface-200 text-sm pl-9 mb-2 whitespace-pre-wrap"> 107 + {reply.text || reply.body?.value} 108 + </p> 109 + <div className="flex items-center justify-end gap-2 pl-9"> 110 + <button onClick={() => onReply(reply)} className="text-surface-400 dark:text-surface-500 hover:text-primary-600 dark:hover:text-primary-400 transition-colors p-1"> 111 + <Reply size={14} /> 112 + </button> 113 + {isReplyOwner && ( 114 + <button onClick={() => onDelete(reply)} className="text-surface-400 dark:text-surface-500 hover:text-red-500 dark:hover:text-red-400 transition-colors p-1"> 115 + <Trash2 size={14} /> 116 + </button> 117 + )} 118 + </div> 119 + </div> 120 + )} 121 + </div> 122 + {reply.children && reply.children.length > 0 && ( 123 + <div className="flex flex-col"> 124 + {reply.children.map((child) => ( 125 + <ReplyItem key={child.uri || child.id} reply={child} depth={depth + 1} user={user} onReply={onReply} onDelete={onDelete} isInline={isInline} /> 126 + ))} 127 + </div> 128 + )} 129 + </div> 130 + ); 131 + } 132 + 133 + export default function ReplyList({ replies, rootUri, user, onReply, onDelete, isInline = false }: ReplyListProps) { 134 + if (!replies || replies.length === 0) { 135 + return ( 136 + <div className="py-8 text-center"> 137 + <p className="text-surface-500 dark:text-surface-400 text-sm">No replies yet</p> 138 + </div> 139 + ); 140 + } 141 + 142 + const buildReplyTree = () => { 143 + const replyMap: Record<string, any> = {}; 144 + const rootReplies: any[] = []; 145 + 146 + replies.forEach((r) => { 147 + replyMap[r.uri || r.id || ''] = { ...r, children: [] }; 148 + }); 149 + 150 + replies.forEach((r) => { 151 + const parentUri = (r as any).reply?.parent?.uri || (r as any).parentUri; 152 + if (parentUri === rootUri || !parentUri || !replyMap[parentUri]) { 153 + rootReplies.push(replyMap[r.uri || r.id || '']); 154 + } else { 155 + replyMap[parentUri].children.push(replyMap[r.uri || r.id || '']); 156 + } 157 + }); 158 + 159 + return rootReplies; 160 + }; 161 + 162 + const replyTree = buildReplyTree(); 163 + 164 + return ( 165 + <div className="flex flex-col gap-1"> 166 + {replyTree.map((reply) => ( 167 + <ReplyItem key={reply.uri || reply.id} reply={reply} depth={0} user={user} onReply={onReply} onDelete={onDelete} isInline={isInline} /> 168 + ))} 169 + </div> 170 + ); 171 + }
+83
web/src/components/RightSidebar.tsx
··· 1 + 2 + import React, { useEffect, useState } from 'react'; 3 + import { ArrowRight, Github, Twitter, ExternalLink, Loader2 } from 'lucide-react'; 4 + import { getTrendingTags, type Tag } from '../api/client'; 5 + 6 + export default function RightSidebar() { 7 + const [tags, setTags] = useState<Tag[]>([]); 8 + const [browser, setBrowser] = useState<'chrome' | 'firefox' | 'other'>('other'); 9 + 10 + useEffect(() => { 11 + const ua = navigator.userAgent.toLowerCase(); 12 + if (ua.includes('firefox')) setBrowser('firefox'); 13 + else if (ua.includes('chrome')) setBrowser('chrome'); 14 + getTrendingTags().then(setTags); 15 + }, []); 16 + 17 + const extensionLink = browser === 'firefox' 18 + ? 'https://addons.mozilla.org/en-US/firefox/addon/margin/' 19 + : 'https://chromewebstore.google.com/detail/margin/cgpmbiiagnehkikhcbnhiagfomajncpa'; 20 + 21 + return ( 22 + <aside className="hidden lg:block w-[280px] shrink-0 sticky top-0 h-screen overflow-y-auto px-4 py-4 border-l border-surface-100/50 dark:border-surface-800/50"> 23 + <div className="space-y-6"> 24 + 25 + <div className="relative"> 26 + <input 27 + type="text" 28 + placeholder="Search Margin..." 29 + className="w-full bg-surface-100 dark:bg-surface-800 rounded-full px-5 py-2.5 text-sm font-medium text-surface-900 dark:text-white placeholder:text-surface-500 dark:placeholder:text-surface-400 focus:outline-none focus:ring-2 focus:ring-primary-500/20 transition-all border-none" 30 + /> 31 + </div> 32 + 33 + <div className="bg-surface-50 dark:bg-surface-900 rounded-2xl p-4 border border-surface-100 dark:border-surface-800"> 34 + <h3 className="font-bold text-base mb-1 text-surface-900 dark:text-white">Get the Extension</h3> 35 + <p className="text-surface-500 dark:text-surface-400 text-sm mb-4 leading-snug"> 36 + Save anything, annotate anywhere. 37 + </p> 38 + <a 39 + href={extensionLink} 40 + target="_blank" 41 + rel="noopener noreferrer" 42 + className="flex items-center justify-center w-full px-4 py-2 bg-surface-900 dark:bg-white text-white dark:text-surface-900 rounded-full hover:bg-black dark:hover:bg-surface-100 transition-all text-sm font-semibold" 43 + > 44 + Download for {browser === 'firefox' ? 'Firefox' : 'Chrome'} 45 + </a> 46 + </div> 47 + 48 + <div className="py-2"> 49 + <h3 className="font-bold text-xl px-2 mb-4 text-surface-900 dark:text-white">Trending</h3> 50 + {tags.length > 0 ? ( 51 + <div className="flex flex-col"> 52 + {tags.map(t => ( 53 + <a key={t.tag} href={`/search?q=${t.tag}`} className="px-2 py-3 hover:bg-surface-50 dark:hover:bg-surface-800 rounded-xl transition-colors group"> 54 + <div className="flex justify-between items-center mb-0.5"> 55 + <span className="text-xs text-surface-500 dark:text-surface-400 font-medium">Trending</span> 56 + <span className="text-xs text-surface-400 dark:text-surface-500 opacity-0 group-hover:opacity-100 transition-opacity">...</span> 57 + </div> 58 + <div className="font-bold text-surface-900 dark:text-white">#{t.tag}</div> 59 + <div className="text-xs text-surface-500 dark:text-surface-400 mt-0.5">{t.count} posts</div> 60 + </a> 61 + ))} 62 + </div> 63 + ) : ( 64 + <div className="px-2"> 65 + <p className="text-sm text-surface-500 dark:text-surface-400">Nothing trending right now.</p> 66 + </div> 67 + )} 68 + </div> 69 + 70 + <div className="px-2 pt-2"> 71 + <div className="flex flex-wrap gap-x-3 gap-y-1 text-[13px] text-surface-400 dark:text-surface-500 leading-relaxed"> 72 + <a href="#" className="hover:underline hover:text-surface-600 dark:hover:text-surface-300">About</a> 73 + <a href="/privacy" className="hover:underline hover:text-surface-600 dark:hover:text-surface-300">Privacy</a> 74 + <a href="/terms" className="hover:underline hover:text-surface-600 dark:hover:text-surface-300">Terms</a> 75 + <a href="https://github.com/margin-at" target="_blank" rel="noreferrer" className="hover:underline hover:text-surface-600 dark:hover:text-surface-300">Code</a> 76 + <span>© 2026 Margin</span> 77 + </div> 78 + </div> 79 + 80 + </div> 81 + </aside> 82 + ); 83 + }
+218
web/src/components/ShareMenu.tsx
··· 1 + 2 + import React, { useState, useRef, useEffect } from "react"; 3 + import { Copy, ExternalLink, Check, Share2, MoreHorizontal } from "lucide-react"; 4 + import { AturiIcon, BlueskyIcon, BlackskyIcon, WitchskyIcon, CatskyIcon, DeerIcon } from "./Icons"; 5 + 6 + const SembleLogo = () => ( 7 + <img src="/semble-logo.svg" alt="Semble" className="w-4 h-4 opacity-90" /> 8 + ); 9 + 10 + const BLUESKY_COLOR = "#1185fe"; 11 + 12 + interface ShareOption { 13 + name: string; 14 + icon: React.ReactNode; 15 + action: () => void; 16 + highlight?: boolean; 17 + } 18 + 19 + interface ShareMenuProps { 20 + uri: string; 21 + text?: string; 22 + customUrl?: string; 23 + handle?: string; 24 + type?: string; 25 + url?: string; 26 + } 27 + 28 + export default function ShareMenu({ uri, text, customUrl, handle, type, url }: ShareMenuProps) { 29 + const [isOpen, setIsOpen] = useState(false); 30 + const [copied, setCopied] = useState<string | null>(null); 31 + const menuRef = useRef<HTMLDivElement>(null); 32 + const buttonRef = useRef<HTMLButtonElement>(null); 33 + const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0, alignRight: false }); 34 + 35 + const getShareUrl = () => { 36 + if (customUrl) return customUrl; 37 + if (!uri) return ""; 38 + 39 + const uriParts = uri.split("/"); 40 + const rkey = uriParts[uriParts.length - 1]; 41 + const did = uriParts[2]; 42 + 43 + if (uri.includes("network.cosmik.card")) return `${window.location.origin}/at/${did}/${rkey}`; 44 + if (handle && type) return `${window.location.origin}/${handle}/${type.toLowerCase()}/${rkey}`; 45 + return `${window.location.origin}/at/${did}/${rkey}`; 46 + }; 47 + 48 + const shareUrl = getShareUrl(); 49 + const isSemble = uri && uri.includes("network.cosmik"); 50 + 51 + const sembleUrl = (() => { 52 + if (!isSemble) return ""; 53 + const parts = uri.split("/"); 54 + const rkey = parts[parts.length - 1]; 55 + const userHandle = handle || (parts.length > 2 ? parts[2] : ""); 56 + 57 + if (uri.includes("network.cosmik.collection")) return `https://semble.so/profile/${userHandle}/collections/${rkey}`; 58 + if (uri.includes("network.cosmik.card") && url) return `https://semble.so/url?id=${encodeURIComponent(url)}`; 59 + return `https://semble.so/profile/${userHandle}`; 60 + })(); 61 + 62 + const handleCopy = async (textToCopy: string, key: string) => { 63 + try { 64 + await navigator.clipboard.writeText(textToCopy); 65 + setCopied(key); 66 + setTimeout(() => { 67 + setCopied(null); 68 + setIsOpen(false); 69 + }, 1000); 70 + } catch { 71 + prompt("Copy this link:", textToCopy); 72 + } 73 + }; 74 + 75 + const handleShareToFork = (domain: string) => { 76 + const composeText = text ? `${text.substring(0, 200)}...\n\n${shareUrl}` : shareUrl; 77 + const composeUrl = `https://${domain}/intent/compose?text=${encodeURIComponent(composeText)}`; 78 + window.open(composeUrl, "_blank"); 79 + setIsOpen(false); 80 + }; 81 + 82 + useEffect(() => { 83 + const handleClickOutside = (e: MouseEvent) => { 84 + if (menuRef.current && !menuRef.current.contains(e.target as Node) && !buttonRef.current?.contains(e.target as Node)) { 85 + setIsOpen(false); 86 + } 87 + }; 88 + if (isOpen) { 89 + document.addEventListener("mousedown", handleClickOutside); 90 + window.addEventListener("scroll", () => setIsOpen(false), true); 91 + window.addEventListener("resize", () => setIsOpen(false)); 92 + } 93 + return () => { 94 + document.removeEventListener("mousedown", handleClickOutside); 95 + window.removeEventListener("scroll", () => setIsOpen(false), true); 96 + window.removeEventListener("resize", () => setIsOpen(false)); 97 + }; 98 + }, [isOpen]); 99 + 100 + const calculatePosition = () => { 101 + if (!buttonRef.current) return; 102 + const rect = buttonRef.current.getBoundingClientRect(); 103 + const menuWidth = 240; 104 + 105 + let top = rect.bottom + 8; 106 + let left = rect.left; 107 + let alignRight = false; 108 + 109 + if (left + menuWidth > window.innerWidth - 16) { 110 + left = rect.right - menuWidth; 111 + alignRight = true; 112 + } 113 + 114 + if (top + 300 > window.innerHeight) { 115 + top = rect.top - 8; 116 + } 117 + 118 + setMenuPosition({ top, left, alignRight }); 119 + }; 120 + 121 + const toggleMenu = () => { 122 + if (!isOpen) calculatePosition(); 123 + setIsOpen(!isOpen); 124 + }; 125 + 126 + const renderMenuItem = (label: string, icon: React.ReactNode, onClick: () => void, isCopied: boolean = false, highlight: boolean = false) => ( 127 + <button 128 + onClick={onClick} 129 + className={`w-full flex items-center gap-3 px-3 py-2.5 text-[14px] font-medium transition-colors rounded-lg group 130 + ${highlight 131 + ? "text-primary-700 dark:text-primary-400 bg-primary-50/50 dark:bg-primary-900/20 hover:bg-primary-50 dark:hover:bg-primary-900/30" 132 + : "text-surface-700 dark:text-surface-200 hover:bg-surface-50 dark:hover:bg-surface-800 hover:text-surface-900 dark:hover:text-white" 133 + }`} 134 + > 135 + <span className={`flex items-center justify-center w-5 h-5 ${highlight ? "text-primary-600 dark:text-primary-400" : "text-surface-400 dark:text-surface-500 group-hover:text-surface-600 dark:group-hover:text-surface-300"}`}> 136 + {isCopied ? <Check size={16} className="text-green-600 dark:text-green-400" /> : icon} 137 + </span> 138 + <span className="flex-1 text-left">{isCopied ? "Copied!" : label}</span> 139 + </button> 140 + ); 141 + 142 + const shareForks = [ 143 + { name: "Bluesky", domain: "bsky.app", icon: <BlueskyIcon size={18} color={BLUESKY_COLOR} /> }, 144 + { name: "Witchsky", domain: "witchsky.app", icon: <WitchskyIcon size={18} /> }, 145 + { name: "Blacksky", domain: "blacksky.community", icon: <BlackskyIcon size={18} /> }, 146 + { name: "Catsky", domain: "catsky.social", icon: <CatskyIcon size={18} /> }, 147 + { name: "Deer", domain: "deer.social", icon: <DeerIcon size={18} /> }, 148 + ]; 149 + 150 + return ( 151 + <div className="relative inline-block"> 152 + <button 153 + ref={buttonRef} 154 + onClick={toggleMenu} 155 + className={`flex items-center gap-1.5 px-2 py-1.5 -ml-2 rounded-md transition-colors ${isOpen ? 'bg-surface-100 dark:bg-surface-800 text-surface-900 dark:text-white' : 'text-surface-500 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white hover:bg-surface-50 dark:hover:bg-surface-800'}`} 156 + title="Share" 157 + > 158 + <Share2 size={18} /> 159 + </button> 160 + 161 + {isOpen && ( 162 + <div 163 + ref={menuRef} 164 + className="fixed z-[1000] w-[260px] bg-white dark:bg-surface-900 rounded-xl shadow-xl ring-1 ring-black/5 dark:ring-white/5 p-1.5 animate-in fade-in zoom-in-95 duration-150 origin-top-left" 165 + style={{ 166 + top: menuPosition.top, 167 + left: menuPosition.left, 168 + transformOrigin: menuPosition.alignRight ? 'top right' : 'top left' 169 + }} 170 + > 171 + <div className="flex flex-col gap-0.5"> 172 + {isSemble ? ( 173 + <> 174 + <div className="px-3 py-2 text-[11px] font-bold text-surface-400 dark:text-surface-500 uppercase tracking-wider flex items-center gap-1.5 select-none"> 175 + <SembleLogo /> 176 + Semble Integration 177 + </div> 178 + {renderMenuItem("Open on Semble", <ExternalLink size={16} />, () => window.open(sembleUrl, "_blank"), false, true)} 179 + {renderMenuItem("Copy Semble Link", <Copy size={16} />, () => handleCopy(sembleUrl, 'semble'), copied === 'semble')} 180 + <div className="h-px bg-surface-100 dark:bg-surface-800 my-1 mx-2" /> 181 + </> 182 + ) : null} 183 + 184 + {renderMenuItem("Copy Link", <Copy size={16} />, () => handleCopy(shareUrl, 'link'), copied === 'link')} 185 + 186 + <div className="px-3 pt-3 pb-1 text-[11px] font-bold text-surface-400 dark:text-surface-500 uppercase tracking-wider select-none"> 187 + Share via App 188 + </div> 189 + 190 + <div className="grid grid-cols-5 gap-1 px-1 mb-1"> 191 + {shareForks.map((fork) => ( 192 + <button 193 + key={fork.domain} 194 + onClick={() => handleShareToFork(fork.domain)} 195 + className="flex items-center justify-center p-2 rounded-lg hover:bg-surface-50 dark:hover:bg-surface-800 hover:scale-105 transition-all text-surface-400 dark:text-surface-500 hover:text-surface-900 dark:hover:text-white" 196 + title={`Share to ${fork.name}`} 197 + > 198 + {fork.icon} 199 + </button> 200 + ))} 201 + </div> 202 + 203 + <div className="h-px bg-surface-100 dark:bg-surface-800 my-1 mx-2" /> 204 + 205 + {renderMenuItem("Copy Universal Link", <AturiIcon size={16} />, () => handleCopy(uri.replace("at://", "https://aturi.to/"), 'aturi'), copied === 'aturi')} 206 + 207 + {navigator.share && ( 208 + renderMenuItem("More Options...", <MoreHorizontal size={16} />, () => { 209 + navigator.share({ title: "Margin", text, url: shareUrl }).catch(() => { }); 210 + setIsOpen(false); 211 + }) 212 + )} 213 + </div> 214 + </div> 215 + )} 216 + </div> 217 + ); 218 + }
+118
web/src/components/Sidebar.tsx
··· 1 + import React from 'react'; 2 + import { Home, Bookmark, PenTool, Settings, LogOut, User, Bell, Sun, Moon, Monitor } from 'lucide-react'; 3 + import { useStore } from '@nanostores/react'; 4 + import { $user, logout } from '../store/auth'; 5 + import { $theme, cycleTheme } from '../store/theme'; 6 + import { getAvatarUrl, getUnreadNotificationCount } from '../api/client'; 7 + import { Link, useLocation } from 'react-router-dom'; 8 + import { useEffect, useState } from 'react'; 9 + 10 + export default function Sidebar() { 11 + const user = useStore($user); 12 + const theme = useStore($theme); 13 + const location = useLocation(); 14 + const currentPath = location.pathname; 15 + const [unreadCount, setUnreadCount] = useState(0); 16 + 17 + useEffect(() => { 18 + if (!user) return; 19 + 20 + const checkNotifications = async () => { 21 + const count = await getUnreadNotificationCount(); 22 + setUnreadCount(count); 23 + }; 24 + 25 + checkNotifications(); 26 + const interval = setInterval(checkNotifications, 30000); 27 + return () => clearInterval(interval); 28 + }, [user]); 29 + 30 + const navItems = [ 31 + { icon: Home, label: 'Feed', href: '/home' }, 32 + { 33 + icon: Bell, 34 + label: 'Activity', 35 + href: '/notifications', 36 + badge: unreadCount > 0 ? unreadCount : undefined 37 + }, 38 + { icon: Bookmark, label: 'Bookmarks', href: '/bookmarks' }, 39 + { icon: PenTool, label: 'Highlights', href: '/highlights' }, 40 + { icon: Settings, label: 'Collections', href: '/collections' }, 41 + ]; 42 + 43 + if (!user) return null; 44 + 45 + return ( 46 + <aside className="sticky top-0 h-screen w-[240px] hidden md:flex flex-col justify-between py-4 px-4 z-50"> 47 + <div className="flex flex-col gap-6"> 48 + <Link to="/home" className="px-3 hover:opacity-80 transition-opacity w-fit"> 49 + <img src="/logo.svg" alt="Margin" className="w-8 h-8 dark:invert" /> 50 + </Link> 51 + 52 + <nav className="flex flex-col gap-1"> 53 + {navItems.map((item) => { 54 + const isActive = currentPath === item.href || currentPath.startsWith(item.href); 55 + return ( 56 + <Link 57 + key={item.href} 58 + to={item.href} 59 + className={`flex items-center gap-4 px-3 py-3 rounded-full transition-all duration-200 text-lg group ${isActive 60 + ? 'font-bold text-surface-900 dark:text-white' 61 + : 'font-medium text-surface-600 dark:text-surface-400 hover:bg-surface-100 dark:hover:bg-surface-800 hover:text-surface-900 dark:hover:text-white' 62 + }`} 63 + > 64 + <item.icon 65 + size={22} 66 + className={`${isActive ? 'stroke-[2.5px] text-primary-600 dark:text-primary-400' : 'stroke-[2px]'}`} 67 + /> 68 + <span>{item.label}</span> 69 + {item.badge && ( 70 + <span className="ml-auto bg-primary-600 text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full min-w-[1.25rem] text-center"> 71 + {item.badge > 99 ? '99+' : item.badge} 72 + </span> 73 + )} 74 + </Link> 75 + ); 76 + })} 77 + </nav> 78 + </div> 79 + 80 + <div className="relative group"> 81 + <Link 82 + to={`/profile/${user.did}`} 83 + className="flex items-center gap-3 p-3 rounded-full hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors w-full" 84 + > 85 + {getAvatarUrl(user.did, user.avatar) ? ( 86 + <img src={getAvatarUrl(user.did, user.avatar)} className="h-10 w-10 rounded-full object-cover bg-surface-100 dark:bg-surface-800" /> 87 + ) : ( 88 + <div className="h-10 w-10 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center text-surface-400 dark:text-surface-500"> 89 + <User size={20} /> 90 + </div> 91 + )} 92 + <div className="flex-1 min-w-0 pr-2"> 93 + <p className="font-bold text-surface-900 dark:text-white truncate text-[15px]">{user.displayName || user.handle}</p> 94 + <p className="text-sm text-surface-500 dark:text-surface-400 truncate">@{user.handle}</p> 95 + </div> 96 + </Link> 97 + 98 + <div className="absolute bottom-full left-0 w-full mb-2 bg-white dark:bg-surface-900 rounded-2xl shadow-xl border border-surface-100 dark:border-surface-800 p-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 transform origin-bottom scale-95 group-hover:scale-100"> 99 + <button 100 + onClick={cycleTheme} 101 + className="flex items-center gap-3 px-4 py-3 rounded-xl hover:bg-surface-50 dark:hover:bg-surface-800 text-sm font-medium text-surface-700 dark:text-surface-200 w-full" 102 + > 103 + {theme === 'light' ? <Sun size={18} /> : theme === 'dark' ? <Moon size={18} /> : <Monitor size={18} />} 104 + {theme === 'light' ? 'Light' : theme === 'dark' ? 'Dark' : 'System'} 105 + </button> 106 + <Link to="/settings" className="flex items-center gap-3 px-4 py-3 rounded-xl hover:bg-surface-50 dark:hover:bg-surface-800 text-sm font-medium text-surface-700 dark:text-surface-200"> 107 + <Settings size={18} /> 108 + Settings 109 + </Link> 110 + <button onClick={logout} className="flex items-center gap-3 px-4 py-3 rounded-xl hover:bg-red-50 dark:hover:bg-red-900/30 text-sm font-medium text-red-600 dark:text-red-400 w-full text-left"> 111 + <LogOut size={18} /> 112 + Log out 113 + </button> 114 + </div> 115 + </div> 116 + </aside> 117 + ); 118 + }
+283
web/src/components/SignUpModal.tsx
··· 1 + 2 + import React, { useState, useEffect } from "react"; 3 + import { X, ChevronRight, Loader2, AlertCircle } from "lucide-react"; 4 + import { 5 + BlackskyIcon, 6 + NorthskyIcon, 7 + BlueskyIcon, 8 + TophhieIcon, 9 + MarginIcon, 10 + } from "./Icons"; 11 + import { startSignup } from "../api/client"; 12 + 13 + interface Provider { 14 + id: string; 15 + name: string; 16 + service: string; 17 + Icon: any; 18 + description: string; 19 + custom?: boolean; 20 + wide?: boolean; 21 + } 22 + 23 + const RECOMMENDED_PROVIDER: Provider = { 24 + id: "margin", 25 + name: "Margin", 26 + service: "https://margin.cafe", 27 + Icon: MarginIcon, 28 + description: "Hosted by Margin, the easiest way to get started", 29 + }; 30 + 31 + const OTHER_PROVIDERS: Provider[] = [ 32 + { 33 + id: "bluesky", 34 + name: "Bluesky", 35 + service: "https://bsky.social", 36 + Icon: BlueskyIcon, 37 + description: "The most popular option on the AT Protocol", 38 + }, 39 + { 40 + id: "blacksky", 41 + name: "Blacksky", 42 + service: "https://blacksky.app", 43 + Icon: BlackskyIcon, 44 + description: "For the Culture. A safe space for users and allies", 45 + }, 46 + { 47 + id: "selfhosted.social", 48 + name: "selfhosted.social", 49 + service: "https://selfhosted.social", 50 + Icon: null, 51 + description: "For hackers, designers, and ATProto enthusiasts.", 52 + }, 53 + { 54 + id: "northsky", 55 + name: "Northsky", 56 + service: "https://northsky.social", 57 + Icon: NorthskyIcon, 58 + description: "A Canadian-based worker-owned cooperative", 59 + }, 60 + { 61 + id: "tophhie", 62 + name: "Tophhie", 63 + service: "https://tophhie.social", 64 + Icon: TophhieIcon, 65 + description: "A welcoming and friendly community", 66 + }, 67 + { 68 + id: "altq", 69 + name: "AltQ", 70 + service: "https://altq.net", 71 + Icon: null, 72 + description: "An independent, self-hosted PDS instance", 73 + }, 74 + { 75 + id: "custom", 76 + name: "Custom", 77 + service: "", 78 + custom: true, 79 + Icon: null, 80 + description: "Connect to your own or another custom PDS", 81 + }, 82 + ]; 83 + 84 + interface SignUpModalProps { 85 + onClose: () => void; 86 + } 87 + 88 + export default function SignUpModal({ onClose }: SignUpModalProps) { 89 + const [showOtherProviders, setShowOtherProviders] = useState(false); 90 + const [showCustomInput, setShowCustomInput] = useState(false); 91 + const [customService, setCustomService] = useState(""); 92 + const [loading, setLoading] = useState(false); 93 + const [error, setError] = useState<string | null>(null); 94 + 95 + useEffect(() => { 96 + document.body.style.overflow = "hidden"; 97 + return () => { 98 + document.body.style.overflow = "unset"; 99 + }; 100 + }, []); 101 + 102 + const handleProviderSelect = async (provider: Provider) => { 103 + if (provider.custom) { 104 + setShowCustomInput(true); 105 + return; 106 + } 107 + 108 + setLoading(true); 109 + setError(null); 110 + 111 + try { 112 + const result = await startSignup(provider.service); 113 + if (result.authorizationUrl) { 114 + window.location.href = result.authorizationUrl; 115 + } 116 + } catch (err) { 117 + console.error(err); 118 + setError("Could not connect to this provider. Please try again."); 119 + setLoading(false); 120 + } 121 + }; 122 + 123 + const handleCustomSubmit = async (e: React.FormEvent) => { 124 + e.preventDefault(); 125 + if (!customService.trim()) return; 126 + 127 + setLoading(true); 128 + setError(null); 129 + 130 + let serviceUrl = customService.trim(); 131 + if (!serviceUrl.startsWith("http")) { 132 + serviceUrl = `https://${serviceUrl}`; 133 + } 134 + 135 + try { 136 + const result = await startSignup(serviceUrl); 137 + if (result.authorizationUrl) { 138 + window.location.href = result.authorizationUrl; 139 + } 140 + } catch (err) { 141 + console.error(err); 142 + setError("Could not connect to this PDS. Please check the URL."); 143 + setLoading(false); 144 + } 145 + }; 146 + 147 + return ( 148 + <div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm animate-fade-in"> 149 + <div className="w-full max-w-md bg-white rounded-3xl shadow-2xl overflow-hidden animate-slide-up"> 150 + <div className="p-4 flex justify-end"> 151 + <button onClick={onClose} className="p-2 text-surface-400 hover:text-surface-900 hover:bg-surface-50 rounded-full transition-colors"> 152 + <X size={20} /> 153 + </button> 154 + </div> 155 + 156 + <div className="px-8 pb-10"> 157 + {loading ? ( 158 + <div className="text-center py-10"> 159 + <Loader2 size={40} className="animate-spin text-primary-600 mx-auto mb-4" /> 160 + <p className="text-surface-600 font-medium">Connecting to provider...</p> 161 + </div> 162 + ) : showCustomInput ? ( 163 + <div> 164 + <h2 className="text-2xl font-display font-bold text-surface-900 mb-6">Custom Provider</h2> 165 + <form onSubmit={handleCustomSubmit} className="space-y-4"> 166 + <div> 167 + <label className="block text-sm font-medium text-surface-700 mb-1"> 168 + PDS address (e.g. pds.example.com) 169 + </label> 170 + <input 171 + type="text" 172 + className="w-full px-4 py-3 bg-surface-50 border border-surface-200 rounded-xl focus:border-primary-500 focus:ring-4 focus:ring-primary-500/10 outline-none transition-all" 173 + value={customService} 174 + onChange={(e) => setCustomService(e.target.value)} 175 + placeholder="pds.example.com" 176 + autoFocus 177 + /> 178 + </div> 179 + 180 + {error && ( 181 + <div className="p-3 bg-red-50 text-red-600 text-sm rounded-lg flex items-center gap-2"> 182 + <AlertCircle size={16} /> 183 + {error} 184 + </div> 185 + )} 186 + 187 + <div className="flex gap-3 pt-4"> 188 + <button 189 + type="button" 190 + className="flex-1 py-3 bg-white border border-surface-200 text-surface-700 font-semibold rounded-xl hover:bg-surface-50 transition-colors" 191 + onClick={() => { 192 + setShowCustomInput(false); 193 + setError(null); 194 + }} 195 + > 196 + Back 197 + </button> 198 + <button 199 + type="submit" 200 + className="flex-1 py-3 bg-primary-600 text-white font-semibold rounded-xl hover:bg-primary-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" 201 + disabled={!customService.trim()} 202 + > 203 + Continue 204 + </button> 205 + </div> 206 + </form> 207 + </div> 208 + ) : ( 209 + <div> 210 + <h2 className="text-2xl font-display font-bold text-surface-900 mb-2">Create your account</h2> 211 + <p className="text-surface-500 mb-6"> 212 + Margin adheres to the AT Protocol. Choose a provider to host your account. 213 + </p> 214 + 215 + {error && ( 216 + <div className="mb-4 p-3 bg-red-50 text-red-600 text-sm rounded-lg flex items-center gap-2"> 217 + <AlertCircle size={16} /> 218 + {error} 219 + </div> 220 + )} 221 + 222 + <div className="mb-6"> 223 + <div className="inline-block px-2 py-0.5 bg-primary-50 text-primary-700 text-xs font-bold uppercase tracking-wider rounded-md mb-2">Recommended</div> 224 + <button 225 + className="w-full flex items-center gap-4 p-4 bg-white border-2 border-primary-100 hover:border-primary-300 rounded-2xl shadow-sm hover:shadow-md transition-all group text-left" 226 + onClick={() => handleProviderSelect(RECOMMENDED_PROVIDER)} 227 + > 228 + <div className="w-12 h-12 bg-primary-50 rounded-full flex items-center justify-center text-primary-600 flex-shrink-0"> 229 + {RECOMMENDED_PROVIDER.Icon && <RECOMMENDED_PROVIDER.Icon size={24} />} 230 + </div> 231 + <div className="flex-1 min-w-0"> 232 + <h3 className="font-bold text-surface-900 group-hover:text-primary-700 transition-colors">{RECOMMENDED_PROVIDER.name}</h3> 233 + <span className="text-sm text-surface-500 line-clamp-1">{RECOMMENDED_PROVIDER.description}</span> 234 + </div> 235 + <ChevronRight size={20} className="text-surface-300 group-hover:text-primary-500" /> 236 + </button> 237 + </div> 238 + 239 + <div className="border-t border-surface-100 pt-4"> 240 + <button 241 + type="button" 242 + className="flex items-center gap-2 text-sm font-medium text-surface-500 hover:text-surface-900 transition-colors mb-4" 243 + onClick={() => setShowOtherProviders(!showOtherProviders)} 244 + > 245 + {showOtherProviders ? "Hide other options" : "More options"} 246 + <ChevronRight 247 + size={14} 248 + className={`transition-transform duration-200 ${showOtherProviders ? "rotate-90" : ""}`} 249 + /> 250 + </button> 251 + 252 + {showOtherProviders && ( 253 + <div className="space-y-2 animate-fade-in"> 254 + {OTHER_PROVIDERS.map((p) => ( 255 + <button 256 + key={p.id} 257 + className="w-full flex items-center gap-3 p-3 bg-surface-50 hover:bg-surface-100 rounded-xl transition-colors text-left group" 258 + onClick={() => handleProviderSelect(p)} 259 + > 260 + <div className="w-8 h-8 flex items-center justify-center bg-white rounded-full shadow-sm text-surface-600"> 261 + {p.Icon ? ( 262 + <p.Icon size={18} /> 263 + ) : ( 264 + <span className="font-bold text-xs">{p.name[0]}</span> 265 + )} 266 + </div> 267 + <div className="flex-1 min-w-0"> 268 + <h3 className="text-sm font-bold text-surface-900">{p.name}</h3> 269 + <p className="text-xs text-surface-500 line-clamp-1">{p.description}</p> 270 + </div> 271 + <ChevronRight size={16} className="text-surface-300 group-hover:text-surface-600" /> 272 + </button> 273 + ))} 274 + </div> 275 + )} 276 + </div> 277 + </div> 278 + )} 279 + </div> 280 + </div> 281 + </div> 282 + ); 283 + }
+41
web/src/layouts/AppLayout.tsx
··· 1 + 2 + import React from 'react'; 3 + import { useStore } from '@nanostores/react'; 4 + import Sidebar from '../components/Sidebar'; 5 + import RightSidebar from '../components/RightSidebar'; 6 + import MobileNav from '../components/MobileNav'; 7 + import { $theme } from '../store/theme'; 8 + 9 + interface AppLayoutProps { 10 + children: React.ReactNode; 11 + } 12 + 13 + export default function AppLayout({ children }: AppLayoutProps) { 14 + useStore($theme); 15 + 16 + return ( 17 + <div className="min-h-screen bg-surface-50 dark:bg-surface-950 flex"> 18 + <Sidebar /> 19 + 20 + <div className="flex-1 min-w-0 transition-all duration-300"> 21 + <div className="flex w-full max-w-[1100px] mx-auto"> 22 + <main className="flex-1 w-full min-w-0 py-6 px-3 md:px-6 lg:px-8 pb-20 lg:pb-6"> 23 + {children} 24 + </main> 25 + 26 + <RightSidebar /> 27 + </div> 28 + </div> 29 + 30 + <MobileNav /> 31 + </div> 32 + ); 33 + } 34 + 35 + export function LandingLayout({ children }: AppLayoutProps) { 36 + return ( 37 + <div className="min-h-screen bg-white dark:bg-surface-950"> 38 + {children} 39 + </div> 40 + ); 41 + }
+31
web/src/layouts/Layout.astro
··· 1 + 2 + --- 3 + import Sidebar from '../components/Sidebar.jsx'; 4 + import '../styles/global.css'; 5 + 6 + const { title = 'Margin' } = Astro.props; 7 + const currentPath = Astro.url.pathname; 8 + --- 9 + 10 + <!DOCTYPE html> 11 + <html lang="en"> 12 + <head> 13 + <meta charset="UTF-8" /> 14 + <meta name="description" content="Margin - Annotate the web" /> 15 + <meta name="viewport" content="width=device-width" /> 16 + <link rel="icon" type="image/svg+xml" href="/logo.svg" /> 17 + <link rel="preconnect" href="https://fonts.googleapis.com"> 18 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 19 + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@500;600;700&display=swap" rel="stylesheet"> 20 + <meta name="generator" content={Astro.generator} /> 21 + <title>{title}</title> 22 + </head> 23 + <body class="bg-surface-50 min-h-screen text-surface-900"> 24 + <div class="flex"> 25 + <Sidebar client:load currentPath={currentPath} /> 26 + <main class="flex-1 md:ml-64 p-4 md:p-8 max-w-4xl mx-auto w-full"> 27 + <slot /> 28 + </main> 29 + </div> 30 + </body> 31 + </html>
+23
web/src/pages/index.astro
··· 1 + 2 + --- 3 + import App from '../App'; 4 + import '../styles/global.css'; 5 + --- 6 + 7 + <!DOCTYPE html> 8 + <html lang="en"> 9 + <head> 10 + <meta charset="UTF-8" /> 11 + <meta name="description" content="Margin - Annotate the web" /> 12 + <meta name="viewport" content="width=device-width" /> 13 + <link rel="icon" type="image/svg+xml" href="/logo.svg" /> 14 + <link rel="preconnect" href="https://fonts.googleapis.com"> 15 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 16 + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@500;600;700&display=swap" rel="stylesheet"> 17 + <meta name="generator" content={Astro.generator} /> 18 + <title>Margin</title> 19 + </head> 20 + <body class="bg-surface-50 min-h-screen text-surface-900"> 21 + <App client:only="react" /> 22 + </body> 23 + </html>
+20
web/src/store/auth.ts
··· 1 + 2 + import { atom } from 'nanostores'; 3 + import { checkSession } from '../api/client'; 4 + import type { UserProfile } from '../types'; 5 + 6 + export const $user = atom<UserProfile | null>(null); 7 + export const $isLoading = atom<boolean>(true); 8 + 9 + export async function initAuth() { 10 + $isLoading.set(true); 11 + const session = await checkSession(); 12 + $user.set(session); 13 + $isLoading.set(false); 14 + } 15 + 16 + export function logout() { 17 + fetch('/auth/logout', { method: 'POST' }).then(() => { 18 + window.location.href = '/'; 19 + }); 20 + }
+78
web/src/store/theme.ts
··· 1 + 2 + import { atom, onMount } from 'nanostores'; 3 + 4 + export type Theme = 'light' | 'dark' | 'system'; 5 + export type Layout = 'sidebar' | 'compact'; 6 + 7 + export const $theme = atom<Theme>('system'); 8 + export const $layout = atom<Layout>('sidebar'); 9 + 10 + onMount($theme, () => { 11 + if (typeof window !== 'undefined') { 12 + const stored = localStorage.getItem('theme') as Theme | null; 13 + if (stored && ['light', 'dark', 'system'].includes(stored)) { 14 + $theme.set(stored); 15 + } 16 + applyTheme($theme.get()); 17 + } 18 + 19 + return $theme.subscribe((theme) => { 20 + if (typeof window !== 'undefined') { 21 + localStorage.setItem('theme', theme); 22 + applyTheme(theme); 23 + } 24 + }); 25 + }); 26 + 27 + onMount($layout, () => { 28 + if (typeof window !== 'undefined') { 29 + const stored = localStorage.getItem('layout_preference') as Layout | null; 30 + if (stored && ['sidebar', 'compact'].includes(stored)) { 31 + $layout.set(stored); 32 + } 33 + } 34 + 35 + return $layout.subscribe((layout) => { 36 + if (typeof window !== 'undefined') { 37 + localStorage.setItem('layout_preference', layout); 38 + } 39 + }); 40 + }); 41 + 42 + function applyTheme(theme: Theme) { 43 + const root = window.document.documentElement; 44 + root.classList.remove('light', 'dark'); 45 + delete root.dataset.theme; 46 + 47 + if (theme === 'system') { 48 + const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches 49 + ? 'dark' 50 + : 'light'; 51 + root.dataset.theme = systemTheme; 52 + } else { 53 + root.dataset.theme = theme; 54 + } 55 + } 56 + 57 + if (typeof window !== 'undefined') { 58 + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); 59 + mediaQuery.addEventListener('change', () => { 60 + if ($theme.get() === 'system') { 61 + applyTheme('system'); 62 + } 63 + }); 64 + } 65 + 66 + export function setTheme(theme: Theme) { 67 + $theme.set(theme); 68 + } 69 + 70 + export function setLayout(layout: Layout) { 71 + $layout.set(layout); 72 + } 73 + 74 + export function cycleTheme() { 75 + const current = $theme.get(); 76 + const next: Theme = current === 'system' ? 'light' : current === 'light' ? 'dark' : 'system'; 77 + $theme.set(next); 78 + }
+23
web/src/styles/global.css
··· 1 + @tailwind base; 2 + @tailwind components; 3 + @tailwind utilities; 4 + 5 + @layer base { 6 + html { 7 + font-family: 'Inter', system-ui, sans-serif; 8 + background-color: #fafafa; 9 + color: #18181b; 10 + -webkit-font-smoothing: antialiased; 11 + -moz-osx-font-smoothing: grayscale; 12 + } 13 + 14 + h1, 15 + h2, 16 + h3, 17 + h4, 18 + h5, 19 + h6 { 20 + font-family: 'Outfit', sans-serif; 21 + letter-spacing: -0.02em; 22 + } 23 + }
+108
web/src/types.ts
··· 1 + 2 + export interface UserProfile { 3 + did: string; 4 + handle: string; 5 + displayName?: string; 6 + description?: string; 7 + avatar?: string; 8 + banner?: string; 9 + website?: string; 10 + links?: string[]; 11 + followersCount?: number; 12 + followsCount?: number; 13 + postsCount?: number; 14 + } 15 + 16 + export interface Selector { 17 + exact: string; 18 + prefix?: string; 19 + suffix?: string; 20 + start?: number; 21 + end?: number; 22 + } 23 + 24 + export interface Target { 25 + source: string; 26 + title?: string; 27 + selector?: Selector; 28 + } 29 + 30 + export interface AnnotationBody { 31 + type: 'TextualBody'; 32 + value: string; 33 + format: 'text/plain'; 34 + } 35 + 36 + export interface Param { 37 + id: string; 38 + value: string; 39 + } 40 + 41 + export interface AnnotationItem { 42 + uri: string; 43 + id?: string; 44 + cid: string; 45 + author: UserProfile; 46 + creator?: UserProfile; 47 + target: Target; 48 + body?: AnnotationBody; 49 + motivation: 'highlighting' | 'commenting' | 'bookmarking' | string; 50 + type?: string; 51 + createdAt: string; 52 + text?: string; 53 + title?: string; 54 + color?: 'yellow' | 'green' | 'red' | 'blue'; 55 + tags?: string[]; 56 + likeCount?: number; 57 + replyCount?: number; 58 + repostCount?: number; 59 + children?: AnnotationItem[]; 60 + viewer?: { 61 + like?: string; 62 + } 63 + collection?: { 64 + uri: string; 65 + name: string; 66 + icon?: string; 67 + }; 68 + addedBy?: UserProfile; 69 + collectionItemUri?: string; 70 + } 71 + 72 + export type ActorSearchItem = UserProfile; 73 + 74 + export interface FeedResponse { 75 + cursor?: string; 76 + items: AnnotationItem[]; 77 + } 78 + 79 + export interface NotificationItem { 80 + id: number; 81 + recipient: UserProfile; 82 + actor: UserProfile; 83 + type: 'reply' | 'quote' | 'highlight' | 'bookmark' | 'annotation'; 84 + subjectUri: string; 85 + subject?: any; 86 + createdAt: string; 87 + readAt?: string; 88 + } 89 + 90 + export interface Collection { 91 + id: string; 92 + uri: string; 93 + name: string; 94 + description?: string; 95 + icon?: string; 96 + creator: UserProfile; 97 + createdAt: string; 98 + itemCount: number; 99 + items?: AnnotationItem[]; 100 + } 101 + 102 + export interface CollectionItem { 103 + id: string; 104 + collectionId: string; 105 + subjectUri: string; 106 + createdAt: string; 107 + annotation?: AnnotationItem; 108 + }
+234
web/src/views/AnnotationDetail.tsx
··· 1 + import React, { useEffect, useState } from 'react'; 2 + import { useParams, Link, useLocation, useNavigate } from 'react-router-dom'; 3 + import { useStore } from '@nanostores/react'; 4 + import { $user } from '../store/auth'; 5 + import { getAnnotation, getReplies, resolveHandle, createReply, deleteReply } from '../api/client'; 6 + import type { AnnotationItem } from '../types'; 7 + import Card from '../components/Card'; 8 + import ReplyList from '../components/ReplyList'; 9 + import { Loader2, MessageSquare, ArrowLeft, X, AlertTriangle } from 'lucide-react'; 10 + import { clsx } from 'clsx'; 11 + import { getAvatarUrl } from '../api/client'; 12 + 13 + export default function AnnotationDetail() { 14 + const { uri, did, rkey, handle, type } = useParams(); 15 + const location = useLocation(); 16 + const navigate = useNavigate(); 17 + const user = useStore($user); 18 + 19 + const [annotation, setAnnotation] = useState<AnnotationItem | null>(null); 20 + const [replies, setReplies] = useState<AnnotationItem[]>([]); 21 + const [loading, setLoading] = useState(true); 22 + const [error, setError] = useState<string | null>(null); 23 + 24 + const [replyText, setReplyText] = useState(""); 25 + const [posting, setPosting] = useState(false); 26 + const [replyingTo, setReplyingTo] = useState<AnnotationItem | null>(null); 27 + 28 + const [targetUri, setTargetUri] = useState<string | null>(uri || null); 29 + 30 + useEffect(() => { 31 + async function resolve() { 32 + if (uri) { 33 + setTargetUri(decodeURIComponent(uri)); 34 + return; 35 + } 36 + 37 + if (handle && rkey) { 38 + let collection = "at.margin.annotation"; 39 + if (type === "highlight" || location.pathname.includes("/highlight/")) collection = "at.margin.highlight"; 40 + if (type === "bookmark" || location.pathname.includes("/bookmark/")) collection = "at.margin.bookmark"; 41 + 42 + try { 43 + const resolvedDid = await resolveHandle(handle); 44 + if (resolvedDid) { 45 + setTargetUri(`at://${resolvedDid}/${collection}/${rkey}`); 46 + } else { 47 + throw new Error("Could not resolve handle"); 48 + } 49 + } catch (e: any) { 50 + setError("Failed to resolve handle: " + e.message); 51 + setLoading(false); 52 + } 53 + } else if (did && rkey) { 54 + setTargetUri(`at://${did}/at.margin.annotation/${rkey}`); 55 + } else { 56 + const pathParts = location.pathname.split("/"); 57 + const atIndex = pathParts.indexOf("at"); 58 + if (atIndex !== -1 && pathParts[atIndex + 1] && pathParts[atIndex + 2]) { 59 + setTargetUri(`at://${pathParts[atIndex + 1]}/at.margin.annotation/${pathParts[atIndex + 2]}`); 60 + } 61 + } 62 + } 63 + resolve(); 64 + }, [uri, did, rkey, handle, type, location.pathname]); 65 + 66 + const refreshReplies = async () => { 67 + if (!targetUri) return; 68 + const repliesData = await getReplies(targetUri); 69 + setReplies(repliesData.items || []); 70 + }; 71 + 72 + useEffect(() => { 73 + async function fetchData() { 74 + if (!targetUri) return; 75 + 76 + try { 77 + setLoading(true); 78 + const [annData, repliesData] = await Promise.all([ 79 + getAnnotation(targetUri), 80 + getReplies(targetUri).catch(() => ({ items: [] as AnnotationItem[] })), 81 + ]); 82 + 83 + if (!annData) { 84 + setError("Annotation not found"); 85 + } else { 86 + setAnnotation(annData); 87 + setReplies(repliesData.items || []); 88 + } 89 + } catch (err: any) { 90 + setError(err.message); 91 + } finally { 92 + setLoading(false); 93 + } 94 + } 95 + fetchData(); 96 + }, [targetUri]); 97 + 98 + const handleReply = async (e?: React.FormEvent) => { 99 + if (e) e.preventDefault(); 100 + if (!replyText.trim() || !annotation || !targetUri) return; 101 + 102 + try { 103 + setPosting(true); 104 + const parentUri = replyingTo ? (replyingTo.uri || replyingTo.id) : targetUri; 105 + const parentCid = replyingTo ? (replyingTo.cid) : annotation.cid; 106 + 107 + if (!parentUri || !parentCid || !annotation.cid) throw new Error("Missing parent info"); 108 + 109 + await createReply(parentUri, parentCid, targetUri, annotation.cid, replyText); 110 + 111 + setReplyText(""); 112 + setReplyingTo(null); 113 + await refreshReplies(); 114 + } catch (err: any) { 115 + alert("Failed to post reply: " + err.message); 116 + } finally { 117 + setPosting(false); 118 + } 119 + }; 120 + 121 + const handleDeleteReply = async (reply: AnnotationItem) => { 122 + if (!window.confirm("Delete this reply?")) return; 123 + try { 124 + await deleteReply(reply.uri || reply.id!); 125 + await refreshReplies(); 126 + } catch (err: any) { 127 + alert("Failed to delete: " + err.message); 128 + } 129 + }; 130 + 131 + if (loading) { 132 + return ( 133 + <div className="flex justify-center py-20"> 134 + <Loader2 className="animate-spin text-primary-600 dark:text-primary-400" size={32} /> 135 + </div> 136 + ); 137 + } 138 + 139 + if (error || !annotation) { 140 + return ( 141 + <div className="max-w-md mx-auto py-12 px-4 text-center"> 142 + <div className="w-14 h-14 bg-surface-100 dark:bg-surface-800 rounded-full flex items-center justify-center mx-auto mb-4 text-surface-400 dark:text-surface-500"> 143 + <AlertTriangle size={28} /> 144 + </div> 145 + <h3 className="text-xl font-bold text-surface-900 dark:text-white mb-2">Not found</h3> 146 + <p className="text-surface-500 dark:text-surface-400 text-sm mb-6">{error || "This may have been deleted."}</p> 147 + <Link to="/home" className="px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg transition-colors"> 148 + Back to Feed 149 + </Link> 150 + </div> 151 + ); 152 + } 153 + 154 + return ( 155 + <div className="max-w-2xl mx-auto pb-20"> 156 + <div className="mb-4"> 157 + <Link to="/home" className="inline-flex items-center gap-1.5 text-sm font-medium text-surface-500 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white transition-colors"> 158 + <ArrowLeft size={16} /> 159 + Back 160 + </Link> 161 + </div> 162 + 163 + <Card item={annotation} onDelete={() => navigate("/home")} /> 164 + 165 + {annotation.type !== 'Bookmark' && annotation.type !== 'Highlight' && !annotation.motivation?.includes('bookmark') && !annotation.motivation?.includes('highlight') && ( 166 + <div className="mt-6"> 167 + <h3 className="flex items-center gap-2 text-sm font-semibold text-surface-500 dark:text-surface-400 uppercase tracking-wider mb-4"> 168 + <MessageSquare size={16} /> 169 + Replies ({replies.length}) 170 + </h3> 171 + 172 + {user ? ( 173 + <div className="bg-white dark:bg-surface-900 rounded-xl ring-1 ring-black/5 dark:ring-white/5 p-4 mb-4"> 174 + {replyingTo && ( 175 + <div className="flex items-center justify-between bg-surface-50 dark:bg-surface-800 px-3 py-2 rounded-lg mb-3 border border-surface-200 dark:border-surface-700"> 176 + <span className="text-sm text-surface-600 dark:text-surface-300"> 177 + Replying to <span className="font-medium text-surface-900 dark:text-white">@{(replyingTo.author || replyingTo.creator)?.handle || "unknown"}</span> 178 + </span> 179 + <button onClick={() => setReplyingTo(null)} className="text-surface-400 dark:text-surface-500 hover:text-surface-900 dark:hover:text-white p-1"> 180 + <X size={14} /> 181 + </button> 182 + </div> 183 + )} 184 + <div className="flex gap-3"> 185 + {getAvatarUrl(user.did, user.avatar) ? ( 186 + <img src={getAvatarUrl(user.did, user.avatar)} alt="" className="w-8 h-8 rounded-full object-cover bg-surface-100 dark:bg-surface-800" /> 187 + ) : ( 188 + <div className="w-8 h-8 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center text-xs font-bold text-surface-400 dark:text-surface-500"> 189 + {user.handle?.[0]?.toUpperCase()} 190 + </div> 191 + )} 192 + <div className="flex-1"> 193 + <textarea 194 + value={replyText} 195 + onChange={(e) => setReplyText(e.target.value)} 196 + placeholder="Write a reply..." 197 + className="w-full p-0 border-0 focus:ring-0 text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 resize-none min-h-[40px] appearance-none bg-transparent leading-relaxed" 198 + rows={2} 199 + disabled={posting} 200 + /> 201 + <div className="flex justify-end mt-2 pt-2 border-t border-surface-100 dark:border-surface-800"> 202 + <button 203 + className="px-4 py-1.5 bg-primary-600 hover:bg-primary-700 text-white text-sm font-medium rounded-full transition-colors disabled:opacity-50" 204 + disabled={posting || !replyText.trim()} 205 + onClick={() => handleReply()} 206 + > 207 + {posting ? "..." : "Reply"} 208 + </button> 209 + </div> 210 + </div> 211 + </div> 212 + </div> 213 + ) : ( 214 + <div className="bg-surface-50 dark:bg-surface-800/50 rounded-xl p-5 text-center mb-4 border border-dashed border-surface-200 dark:border-surface-700"> 215 + <p className="text-surface-500 dark:text-surface-400 text-sm mb-2">Sign in to reply</p> 216 + <Link to="/login" className="text-primary-600 dark:text-primary-400 font-medium hover:underline text-sm"> 217 + Log in 218 + </Link> 219 + </div> 220 + )} 221 + 222 + <ReplyList 223 + replies={replies} 224 + rootUri={targetUri || ""} 225 + user={user} 226 + onReply={(reply) => setReplyingTo(reply)} 227 + onDelete={handleDeleteReply} 228 + isInline={false} 229 + /> 230 + </div> 231 + )} 232 + </div> 233 + ); 234 + }
+159
web/src/views/CollectionDetail.tsx
··· 1 + import React, { useEffect, useState } from 'react'; 2 + import { getCollection, getCollectionItems, deleteCollection, removeCollectionItem, resolveHandle } from '../api/client'; 3 + import { Loader2, ArrowLeft, Trash2, Plus } from 'lucide-react'; 4 + import CollectionIcon from '../components/CollectionIcon'; 5 + import ShareMenu from '../components/ShareMenu'; 6 + import Card from '../components/Card'; 7 + import { useStore } from '@nanostores/react'; 8 + import { $user } from '../store/auth'; 9 + import type { Collection, AnnotationItem } from '../types'; 10 + 11 + interface CollectionDetailProps { 12 + handle?: string; 13 + rkey?: string; 14 + uri?: string; 15 + } 16 + 17 + export default function CollectionDetail({ handle, rkey, uri }: CollectionDetailProps) { 18 + const user = useStore($user); 19 + const [collection, setCollection] = useState<Collection | null>(null); 20 + const [items, setItems] = useState<AnnotationItem[]>([]); 21 + const [loading, setLoading] = useState(true); 22 + const [error, setError] = useState<string | null>(null); 23 + 24 + useEffect(() => { 25 + loadData(); 26 + }, [handle, rkey, uri]); 27 + 28 + const loadData = async () => { 29 + setLoading(true); 30 + try { 31 + let targetUri = uri; 32 + if (!targetUri && handle && rkey) { 33 + if (handle.startsWith('did:')) { 34 + targetUri = `at://${handle}/at.margin.collection/${rkey}`; 35 + } else { 36 + const did = await resolveHandle(handle); 37 + if (did) { 38 + targetUri = `at://${did}/at.margin.collection/${rkey}`; 39 + } else { 40 + setError("Collection not found"); 41 + setLoading(false); 42 + return; 43 + } 44 + } 45 + } 46 + 47 + if (targetUri) { 48 + const col = await getCollection(targetUri); 49 + if (col) { 50 + setCollection(col); 51 + const colItems = await getCollectionItems(col.uri); 52 + setItems(colItems); 53 + } else { 54 + setError("Collection not found"); 55 + } 56 + } 57 + } catch (e) { 58 + setError("Failed to load collection"); 59 + } finally { 60 + setLoading(false); 61 + } 62 + }; 63 + 64 + const handleDelete = async () => { 65 + if (!collection) return; 66 + if (window.confirm('Delete this collection?')) { 67 + await deleteCollection(collection.id); 68 + window.location.href = '/collections'; 69 + } 70 + }; 71 + 72 + const handleRemoveItem = async (item: AnnotationItem) => { 73 + if (!item.collectionItemUri) return; 74 + if (!window.confirm('Remove from collection?')) return; 75 + const success = await removeCollectionItem(item.collectionItemUri); 76 + if (success) { 77 + setItems(prev => prev.filter(i => i.collectionItemUri !== item.collectionItemUri)); 78 + } 79 + }; 80 + 81 + if (loading) { 82 + return ( 83 + <div className="flex justify-center py-20"> 84 + <Loader2 className="animate-spin text-primary-600 dark:text-primary-400" size={32} /> 85 + </div> 86 + ); 87 + } 88 + 89 + if (error || !collection) { 90 + return <div className="text-center py-20 text-red-500 dark:text-red-400">{error || "Collection not found"}</div>; 91 + } 92 + 93 + const isOwner = user?.did === collection.creator?.did; 94 + 95 + return ( 96 + <div className="animate-fade-in max-w-2xl mx-auto"> 97 + <a href="/collections" className="inline-flex items-center gap-1.5 text-sm font-medium text-surface-500 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white mb-4 transition-colors"> 98 + <ArrowLeft size={16} /> 99 + Collections 100 + </a> 101 + 102 + <div className="bg-white dark:bg-surface-900 rounded-xl p-4 ring-1 ring-black/5 dark:ring-white/5 mb-4"> 103 + <div className="flex items-start gap-3"> 104 + <div className="p-2 bg-primary-50 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 rounded-lg"> 105 + <CollectionIcon icon={collection.icon} size={24} /> 106 + </div> 107 + <div className="flex-1 min-w-0"> 108 + <h1 className="text-xl font-bold text-surface-900 dark:text-white truncate">{collection.name}</h1> 109 + {collection.description && ( 110 + <p className="text-surface-600 dark:text-surface-300 text-sm mt-1">{collection.description}</p> 111 + )} 112 + <div className="flex items-center gap-2 mt-2 text-xs text-surface-500 dark:text-surface-400"> 113 + <span className="font-medium bg-surface-100 dark:bg-surface-800 px-2 py-0.5 rounded"> 114 + {items.length} items 115 + </span> 116 + <span>by {collection.creator.displayName || collection.creator.handle}</span> 117 + </div> 118 + </div> 119 + <div className="flex items-center gap-1"> 120 + <ShareMenu 121 + uri={collection.uri} 122 + handle={collection.creator.handle} 123 + type="Collection" 124 + text={collection.name} 125 + /> 126 + {isOwner && ( 127 + <button onClick={handleDelete} className="p-2 text-surface-400 dark:text-surface-500 hover:text-red-500 dark:hover:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg transition-colors"> 128 + <Trash2 size={18} /> 129 + </button> 130 + )} 131 + </div> 132 + </div> 133 + </div> 134 + 135 + <div className="space-y-2"> 136 + {items.length === 0 ? ( 137 + <div className="text-center py-12 text-surface-500 dark:text-surface-400 bg-surface-50 dark:bg-surface-800/50 rounded-xl border border-dashed border-surface-200 dark:border-surface-700"> 138 + <Plus size={28} className="mx-auto mb-2 text-surface-300 dark:text-surface-600" /> 139 + <p className="text-sm">Collection is empty</p> 140 + </div> 141 + ) : ( 142 + items.map(item => ( 143 + <div key={item.uri} className="relative group"> 144 + <Card item={item} hideShare /> 145 + {isOwner && item.collectionItemUri && ( 146 + <button 147 + className="absolute top-3 right-3 p-1.5 bg-white/90 dark:bg-surface-800/90 backdrop-blur text-surface-400 dark:text-surface-500 hover:text-red-500 dark:hover:text-red-400 rounded-lg shadow-sm opacity-0 group-hover:opacity-100 transition-all" 148 + onClick={() => handleRemoveItem(item)} 149 + > 150 + <Trash2 size={14} /> 151 + </button> 152 + )} 153 + </div> 154 + )) 155 + )} 156 + </div> 157 + </div> 158 + ); 159 + }
+196
web/src/views/Collections.tsx
··· 1 + import React, { useEffect, useState } from 'react'; 2 + import { getCollections, createCollection, deleteCollection } from '../api/client'; 3 + import { Loader2, Plus, Folder, Trash2, X } from 'lucide-react'; 4 + import CollectionIcon, { ICON_MAP } from '../components/CollectionIcon'; 5 + import { useStore } from '@nanostores/react'; 6 + import { $user } from '../store/auth'; 7 + import type { Collection } from '../types'; 8 + import { formatDistanceToNow } from 'date-fns'; 9 + import { clsx } from 'clsx'; 10 + 11 + export default function Collections() { 12 + const user = useStore($user); 13 + const [collections, setCollections] = useState<Collection[]>([]); 14 + const [loading, setLoading] = useState(true); 15 + const [showCreateModal, setShowCreateModal] = useState(false); 16 + const [newItemName, setNewItemName] = useState(''); 17 + const [newItemDesc, setNewItemDesc] = useState(''); 18 + const [newItemIcon, setNewItemIcon] = useState('folder'); 19 + 20 + useEffect(() => { 21 + loadCollections(); 22 + }, []); 23 + 24 + const loadCollections = async () => { 25 + setLoading(true); 26 + const data = await getCollections(); 27 + setCollections(data); 28 + setLoading(false); 29 + }; 30 + 31 + const handleCreate = async (e: React.FormEvent) => { 32 + e.preventDefault(); 33 + if (!newItemName.trim()) return; 34 + 35 + const res = await createCollection(newItemName, newItemDesc); 36 + if (res) { 37 + setCollections([res, ...collections]); 38 + setShowCreateModal(false); 39 + setNewItemName(''); 40 + setNewItemDesc(''); 41 + setNewItemIcon('folder'); 42 + loadCollections(); 43 + } 44 + }; 45 + 46 + const handleDelete = async (id: string, e: React.MouseEvent) => { 47 + e.preventDefault(); 48 + if (window.confirm('Delete this collection?')) { 49 + const success = await deleteCollection(id); 50 + if (success) { 51 + setCollections(prev => prev.filter(c => c.id !== id)); 52 + } 53 + } 54 + }; 55 + 56 + if (loading) { 57 + return ( 58 + <div className="flex justify-center py-20"> 59 + <Loader2 className="animate-spin text-primary-600 dark:text-primary-400" size={32} /> 60 + </div> 61 + ); 62 + } 63 + 64 + return ( 65 + <div className="max-w-2xl mx-auto animate-fade-in"> 66 + <div className="flex items-center justify-between mb-6"> 67 + <div> 68 + <h1 className="text-2xl font-display font-bold text-surface-900 dark:text-white">Collections</h1> 69 + <p className="text-surface-500 dark:text-surface-400 text-sm mt-0.5">Organize your annotations and highlights</p> 70 + </div> 71 + <button 72 + onClick={() => setShowCreateModal(true)} 73 + className="flex items-center gap-1.5 px-3 py-2 bg-primary-600 text-white rounded-lg font-medium text-sm hover:bg-primary-500 transition-colors" 74 + > 75 + <Plus size={16} /> 76 + New 77 + </button> 78 + </div> 79 + 80 + {collections.length === 0 ? ( 81 + <div className="text-center py-16 text-surface-500 dark:text-surface-400 bg-surface-50/50 dark:bg-surface-800/50 rounded-xl border border-dashed border-surface-200 dark:border-surface-700"> 82 + <Folder size={40} className="mx-auto mb-3 text-surface-300 dark:text-surface-600" /> 83 + <p className="mb-3">No collections yet</p> 84 + <button 85 + onClick={() => setShowCreateModal(true)} 86 + className="text-primary-600 dark:text-primary-400 font-medium hover:underline" 87 + > 88 + Create your first collection 89 + </button> 90 + </div> 91 + ) : ( 92 + <div className="space-y-2"> 93 + {collections.map(collection => ( 94 + <a 95 + key={collection.id} 96 + href={`/${collection.creator?.handle || user?.handle}/collection/${collection.uri.split('/').pop()}`} 97 + className="group flex items-center gap-3 bg-white dark:bg-surface-900 rounded-lg p-3 ring-1 ring-black/5 dark:ring-white/5 hover:ring-primary-500/30 dark:hover:ring-primary-400/30 transition-all" 98 + > 99 + <div className="p-2 bg-primary-50 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 rounded-lg"> 100 + <CollectionIcon icon={collection.icon} size={20} /> 101 + </div> 102 + <div className="flex-1 min-w-0"> 103 + <h3 className="font-medium text-surface-900 dark:text-white truncate">{collection.name}</h3> 104 + <p className="text-xs text-surface-500 dark:text-surface-400"> 105 + {collection.itemCount} items · {collection.createdAt && formatDistanceToNow(new Date(collection.createdAt), { addSuffix: true })} 106 + </p> 107 + </div> 108 + {!collection.uri.includes('network.cosmik') && ( 109 + <button 110 + onClick={(e) => handleDelete(collection.id, e)} 111 + className="p-2 text-surface-400 dark:text-surface-500 hover:text-red-500 dark:hover:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg transition-colors opacity-0 group-hover:opacity-100" 112 + > 113 + <Trash2 size={16} /> 114 + </button> 115 + )} 116 + </a> 117 + ))} 118 + </div> 119 + )} 120 + 121 + {showCreateModal && ( 122 + <div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm"> 123 + <div className="bg-white dark:bg-surface-900 rounded-xl shadow-xl max-w-md w-full p-5 animate-scale-in ring-1 ring-black/5 dark:ring-white/10"> 124 + <div className="flex items-center justify-between mb-4"> 125 + <h2 className="text-lg font-bold text-surface-900 dark:text-white">New Collection</h2> 126 + <button onClick={() => setShowCreateModal(false)} className="p-1.5 text-surface-400 dark:text-surface-500 hover:bg-surface-100 dark:hover:bg-surface-800 rounded-lg"> 127 + <X size={18} /> 128 + </button> 129 + </div> 130 + <form onSubmit={handleCreate}> 131 + <div className="mb-4"> 132 + <label className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-1">Name</label> 133 + <input 134 + type="text" 135 + value={newItemName} 136 + onChange={e => setNewItemName(e.target.value)} 137 + className="w-full px-3 py-2 bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400" 138 + placeholder="e.g. Design Inspiration" 139 + autoFocus 140 + required 141 + /> 142 + </div> 143 + <div className="mb-4"> 144 + <label className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-1">Icon</label> 145 + <div className="grid grid-cols-6 gap-2 p-2 bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg max-h-32 overflow-y-auto"> 146 + {Object.keys(ICON_MAP).map(key => { 147 + const Icon = ICON_MAP[key]; 148 + return ( 149 + <button 150 + key={key} 151 + type="button" 152 + onClick={() => setNewItemIcon(key)} 153 + className={clsx( 154 + "p-2 rounded-lg flex items-center justify-center transition-colors", 155 + newItemIcon === key 156 + ? "bg-primary-100 dark:bg-primary-900/50 text-primary-600 dark:text-primary-400 ring-1 ring-primary-500" 157 + : "hover:bg-surface-100 dark:hover:bg-surface-700 text-surface-500 dark:text-surface-400" 158 + )} 159 + > 160 + <Icon size={18} /> 161 + </button> 162 + ); 163 + })} 164 + </div> 165 + </div> 166 + <div className="mb-5"> 167 + <label className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-1">Description</label> 168 + <textarea 169 + value={newItemDesc} 170 + onChange={e => setNewItemDesc(e.target.value)} 171 + className="w-full px-3 py-2 bg-white dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400 min-h-[60px] resize-none" 172 + placeholder="What's this collection for?" 173 + /> 174 + </div> 175 + <div className="flex justify-end gap-2"> 176 + <button 177 + type="button" 178 + onClick={() => setShowCreateModal(false)} 179 + className="px-4 py-2 text-surface-600 dark:text-surface-300 font-medium hover:bg-surface-100 dark:hover:bg-surface-800 rounded-lg transition-colors" 180 + > 181 + Cancel 182 + </button> 183 + <button 184 + type="submit" 185 + className="px-4 py-2 bg-primary-600 text-white font-medium rounded-lg hover:bg-primary-500 transition-colors" 186 + > 187 + Create 188 + </button> 189 + </div> 190 + </form> 191 + </div> 192 + </div> 193 + )} 194 + </div> 195 + ); 196 + }
+110
web/src/views/Feed.tsx
··· 1 + 2 + import React, { useEffect, useState } from 'react'; 3 + import { getFeed } from '../api/client'; 4 + import Card from '../components/Card'; 5 + import { Loader2 } from 'lucide-react'; 6 + import { useStore } from '@nanostores/react'; 7 + import { $user, initAuth } from '../store/auth'; 8 + import type { AnnotationItem } from '../types'; 9 + import { clsx } from 'clsx'; 10 + 11 + interface FeedProps { 12 + initialType?: string; 13 + motivation?: string; 14 + showTabs?: boolean; 15 + emptyMessage?: string; 16 + } 17 + 18 + export default function Feed({ 19 + initialType = 'all', 20 + motivation, 21 + showTabs = true, 22 + emptyMessage = "No items found." 23 + }: FeedProps) { 24 + const user = useStore($user); 25 + const [items, setItems] = useState<AnnotationItem[]>([]); 26 + const [loading, setLoading] = useState(true); 27 + const [activeTab, setActiveTab] = useState(initialType); 28 + 29 + useEffect(() => { 30 + initAuth(); 31 + }, []); 32 + 33 + useEffect(() => { 34 + const fetchFeed = async () => { 35 + setLoading(true); 36 + try { 37 + const type = activeTab === 'all' ? 'popular' : activeTab; 38 + const data = await getFeed({ type, motivation }); 39 + setItems(data?.items || []); 40 + } catch (e) { 41 + console.error(e); 42 + } finally { 43 + setLoading(false); 44 + } 45 + }; 46 + fetchFeed(); 47 + }, [activeTab, motivation]); 48 + 49 + const handleDelete = (uri: string) => { 50 + setItems((prev) => prev.filter(i => i.uri !== uri)); 51 + }; 52 + 53 + const tabs = [ 54 + { id: 'all', label: 'Popular' }, 55 + { id: 'shelved', label: 'Shelved' }, 56 + { id: 'margin', label: 'Margin' }, 57 + { id: 'semble', label: 'Semble' }, 58 + ]; 59 + 60 + if (!user && !loading) { 61 + return ( 62 + <div className="text-center py-20"> 63 + <h2 className="text-2xl font-display font-bold mb-4 tracking-tight text-surface-900 dark:text-white">Welcome to Margin</h2> 64 + <p className="text-surface-500 dark:text-surface-400 mb-8 text-lg">Your curated corner of the internet.</p> 65 + <a href="/login" className="px-6 py-3 bg-primary-600 text-white rounded-xl font-semibold shadow-sm hover:bg-primary-500 transition-colors"> 66 + Log In 67 + </a> 68 + </div> 69 + ) 70 + } 71 + 72 + return ( 73 + <div className="max-w-2xl mx-auto"> 74 + {showTabs && ( 75 + <div className="flex gap-1 bg-surface-100 dark:bg-surface-800 p-1 rounded-lg mb-6 w-fit"> 76 + {tabs.map(tab => ( 77 + <button 78 + key={tab.id} 79 + onClick={() => setActiveTab(tab.id)} 80 + className={clsx( 81 + "px-3 py-1.5 text-sm font-medium rounded-md transition-all", 82 + activeTab === tab.id 83 + ? "bg-white dark:bg-surface-700 text-surface-900 dark:text-white shadow-sm" 84 + : "text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-200" 85 + )} 86 + > 87 + {tab.label} 88 + </button> 89 + ))} 90 + </div> 91 + )} 92 + 93 + {loading ? ( 94 + <div className="flex justify-center py-20"> 95 + <Loader2 className="animate-spin text-primary-600 dark:text-primary-400" size={32} /> 96 + </div> 97 + ) : items.length > 0 ? ( 98 + <div className="animate-fade-in"> 99 + {items.map((item) => ( 100 + <Card key={item.uri || item.cid} item={item} onDelete={handleDelete} /> 101 + ))} 102 + </div> 103 + ) : ( 104 + <div className="text-center py-20 text-surface-500 dark:text-surface-400 bg-surface-50/50 dark:bg-surface-800/50 rounded-2xl border border-dashed border-surface-200 dark:border-surface-700"> 105 + <p>{emptyMessage}</p> 106 + </div> 107 + )} 108 + </div> 109 + ); 110 + }
+238
web/src/views/Login.tsx
··· 1 + 2 + import React, { useState, useEffect, useRef } from 'react'; 3 + import { Link } from 'react-router-dom'; 4 + import { Loader2, AtSign } from 'lucide-react'; 5 + import { BlueskyIcon, MarginIcon } from '../components/Icons'; 6 + import SignUpModal from '../components/SignUpModal'; 7 + import { searchActors, startLogin, getAvatarUrl, type ActorSearchItem } from '../api/client'; 8 + 9 + export default function Login() { 10 + const [handle, setHandle] = useState(''); 11 + const [suggestions, setSuggestions] = useState<ActorSearchItem[]>([]); 12 + const [showSuggestions, setShowSuggestions] = useState(false); 13 + const [loading, setLoading] = useState(false); 14 + const [error, setError] = useState<string | null>(null); 15 + const [selectedIndex, setSelectedIndex] = useState(-1); 16 + const [showSignUp, setShowSignUp] = useState(false); 17 + 18 + const inputRef = useRef<HTMLInputElement>(null); 19 + const suggestionsRef = useRef<HTMLDivElement>(null); 20 + const isSelectionRef = useRef(false); 21 + 22 + const [providerIndex, setProviderIndex] = useState(0); 23 + const [morphClass, setMorphClass] = useState("opacity-100 translate-y-0 blur-0"); 24 + const providers = [ 25 + "AT Protocol", 26 + "Margin", 27 + "Bluesky", 28 + "Blacksky", 29 + "Tangled", 30 + "Northsky", 31 + "witchcraft.systems", 32 + "tophhie.social", 33 + "altq.net", 34 + ]; 35 + 36 + useEffect(() => { 37 + const cycleText = () => { 38 + setMorphClass("opacity-0 translate-y-2 blur-sm"); 39 + setTimeout(() => { 40 + setProviderIndex((prev) => (prev + 1) % providers.length); 41 + setMorphClass("opacity-100 translate-y-0 blur-0"); 42 + }, 400); 43 + }; 44 + const interval = setInterval(cycleText, 3000); 45 + return () => clearInterval(interval); 46 + }, [providers.length]); 47 + 48 + useEffect(() => { 49 + if (handle.length >= 3) { 50 + if (isSelectionRef.current) { 51 + isSelectionRef.current = false; 52 + return; 53 + } 54 + const timer = setTimeout(async () => { 55 + try { 56 + if (!handle.includes('.')) { 57 + const data = await searchActors(handle); 58 + setSuggestions(data.actors || []); 59 + setShowSuggestions(true); 60 + setSelectedIndex(-1); 61 + } 62 + } catch (e) { 63 + console.error("Search failed:", e); 64 + } 65 + }, 300); 66 + return () => clearTimeout(timer); 67 + } else { 68 + setSuggestions([]); 69 + setShowSuggestions(false); 70 + } 71 + }, [handle]); 72 + 73 + useEffect(() => { 74 + const handleClickOutside = (e: MouseEvent) => { 75 + if ( 76 + suggestionsRef.current && 77 + !suggestionsRef.current.contains(e.target as Node) && 78 + inputRef.current && 79 + !inputRef.current.contains(e.target as Node) 80 + ) { 81 + setShowSuggestions(false); 82 + } 83 + }; 84 + document.addEventListener("mousedown", handleClickOutside); 85 + return () => document.removeEventListener("mousedown", handleClickOutside); 86 + }, []); 87 + 88 + const handleKeyDown = (e: React.KeyboardEvent) => { 89 + if (!showSuggestions || suggestions.length === 0) return; 90 + 91 + if (e.key === "ArrowDown") { 92 + e.preventDefault(); 93 + setSelectedIndex((prev) => Math.min(prev + 1, suggestions.length - 1)); 94 + } else if (e.key === "ArrowUp") { 95 + e.preventDefault(); 96 + setSelectedIndex((prev) => Math.max(prev - 1, -1)); 97 + } else if (e.key === "Enter" && selectedIndex >= 0) { 98 + e.preventDefault(); 99 + selectSuggestion(suggestions[selectedIndex]); 100 + } else if (e.key === "Escape") { 101 + setShowSuggestions(false); 102 + } 103 + }; 104 + 105 + const selectSuggestion = (actor: ActorSearchItem) => { 106 + isSelectionRef.current = true; 107 + setHandle(actor.handle); 108 + setSuggestions([]); 109 + setShowSuggestions(false); 110 + inputRef.current?.blur(); 111 + }; 112 + 113 + const handleSubmit = async (e: React.FormEvent) => { 114 + e.preventDefault(); 115 + if (!handle.trim()) return; 116 + 117 + setLoading(true); 118 + setError(null); 119 + 120 + try { 121 + const result = await startLogin(handle.trim()); 122 + if (result.authorizationUrl) { 123 + window.location.href = result.authorizationUrl; 124 + } 125 + } catch (err: any) { 126 + setError(err.message || 'Failed to initiate login. Please try again.'); 127 + setLoading(false); 128 + } 129 + }; 130 + 131 + return ( 132 + <div className="min-h-screen flex items-center justify-center bg-surface-50 p-4"> 133 + <div className="w-full max-w-[440px] flex flex-col items-center"> 134 + 135 + <div className="flex items-center justify-center gap-6 mb-12"> 136 + <MarginIcon size={60} /> 137 + <span className="text-3xl font-light text-surface-300 pb-1">×</span> 138 + <div className="text-[#0285FF]"> 139 + <BlueskyIcon size={60} /> 140 + </div> 141 + </div> 142 + 143 + <h1 className="text-2xl font-bold font-display text-surface-900 mb-8 text-center leading-relaxed"> 144 + Sign in with your <br /> 145 + <span className={`inline-block transition-all duration-400 ease-out text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-indigo-600 ${morphClass}`}> 146 + {providers[providerIndex]} 147 + </span>{" "} 148 + handle 149 + </h1> 150 + 151 + <form onSubmit={handleSubmit} className="w-full flex flex-col gap-5"> 152 + <div className="relative"> 153 + <div className="absolute left-4 top-1/2 -translate-y-1/2 text-surface-400"> 154 + <AtSign size={20} className="stroke-[2.5]" /> 155 + </div> 156 + <input 157 + ref={inputRef} 158 + type="text" 159 + value={handle} 160 + onChange={(e) => setHandle(e.target.value)} 161 + onKeyDown={handleKeyDown} 162 + onFocus={() => handle.length >= 3 && suggestions.length > 0 && !handle.includes(".") && setShowSuggestions(true)} 163 + placeholder="handle.bsky.social" 164 + className="w-full pl-12 pr-4 py-3.5 bg-white border border-surface-200 rounded-xl shadow-sm outline-none focus:border-primary-500 focus:ring-4 focus:ring-primary-500/10 transition-all font-medium text-lg text-surface-900 placeholder:text-surface-400" 165 + autoCapitalize="none" 166 + autoCorrect="off" 167 + autoComplete="off" 168 + spellCheck={false} 169 + disabled={loading} 170 + /> 171 + 172 + {showSuggestions && suggestions.length > 0 && ( 173 + <div ref={suggestionsRef} className="absolute top-[calc(100%+8px)] left-0 right-0 bg-white/90 backdrop-blur-xl border border-surface-200 rounded-xl shadow-xl overflow-hidden z-50 animate-fade-in max-h-[300px] overflow-y-auto"> 174 + {suggestions.map((actor, index) => ( 175 + <button 176 + key={actor.did} 177 + type="button" 178 + className={`w-full flex items-center gap-3 px-4 py-3 border-b border-surface-100 last:border-0 hover:bg-surface-50 transition-colors text-left ${index === selectedIndex ? 'bg-surface-50' : ''}`} 179 + onClick={() => selectSuggestion(actor)} 180 + > 181 + {actor.avatar ? ( 182 + <img src={actor.avatar} alt={actor.handle} className="w-9 h-9 rounded-full bg-surface-100 object-cover shrink-0" /> 183 + ) : ( 184 + <div className="w-9 h-9 rounded-full bg-surface-100 flex items-center justify-center text-xs font-bold shrink-0"> 185 + {(actor.displayName || actor.handle).slice(0, 2).toUpperCase()} 186 + </div> 187 + )} 188 + <div className="min-w-0"> 189 + <div className="font-semibold text-surface-900 truncate text-sm"> 190 + {actor.displayName || actor.handle} 191 + </div> 192 + <div className="text-surface-500 text-xs truncate">@{actor.handle}</div> 193 + </div> 194 + </button> 195 + ))} 196 + </div> 197 + )} 198 + </div> 199 + 200 + {error && ( 201 + <div className="p-3 bg-red-50 text-red-600 text-sm rounded-lg border border-red-100 text-center font-medium"> 202 + {error} 203 + </div> 204 + )} 205 + 206 + <button 207 + type="submit" 208 + disabled={loading || !handle} 209 + className="w-full py-3.5 bg-surface-900 hover:bg-surface-800 text-white rounded-xl font-bold text-lg shadow-lg shadow-surface-900/10 disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center justify-center gap-2" 210 + > 211 + {loading ? "Connecting..." : "Continue"} 212 + </button> 213 + 214 + <p className="text-center text-sm text-surface-400 mt-2"> 215 + By signing in, you agree to our <Link to="/terms" className="text-surface-900 hover:underline">Terms of Service</Link> and <Link to="/privacy" className="text-surface-900 hover:underline">Privacy Policy</Link>. 216 + </p> 217 + 218 + <div className="flex items-center gap-4 py-2"> 219 + <div className="h-px bg-surface-200 flex-1" /> 220 + <span className="text-xs font-bold text-surface-400 uppercase tracking-wider">or</span> 221 + <div className="h-px bg-surface-200 flex-1" /> 222 + </div> 223 + 224 + <button 225 + type="button" 226 + onClick={() => setShowSignUp(true)} 227 + className="w-full py-3.5 bg-transparent border-2 border-surface-200 hover:border-surface-400 hover:bg-surface-50 text-surface-700 rounded-xl font-bold transition-all" 228 + > 229 + Create New Account 230 + </button> 231 + </form> 232 + 233 + </div> 234 + 235 + {showSignUp && <SignUpModal onClose={() => setShowSignUp(false)} />} 236 + </div> 237 + ); 238 + }
+91
web/src/views/New.tsx
··· 1 + 2 + import React, { useState } from 'react'; 3 + import { useNavigate, useSearchParams, Link } from 'react-router-dom'; 4 + import { useStore } from '@nanostores/react'; 5 + import { $user } from '../store/auth'; 6 + import Composer from '../components/Composer'; 7 + 8 + export default function NewAnnotationPage() { 9 + const user = useStore($user); 10 + const navigate = useNavigate(); 11 + const [searchParams] = useSearchParams(); 12 + 13 + const initialUrl = searchParams.get("url") || ""; 14 + 15 + let initialSelector: any = null; 16 + const selectorParam = searchParams.get("selector"); 17 + if (selectorParam) { 18 + try { 19 + initialSelector = JSON.parse(selectorParam); 20 + } catch (e) { 21 + console.error("Failed to parse selector:", e); 22 + } 23 + } 24 + 25 + const legacyQuote = searchParams.get("quote") || ""; 26 + if (legacyQuote && !initialSelector) { 27 + initialSelector = { 28 + type: "TextQuoteSelector", 29 + exact: legacyQuote, 30 + }; 31 + } 32 + 33 + const [url, setUrl] = useState(initialUrl); 34 + 35 + if (!user) { 36 + return ( 37 + <div className="max-w-sm mx-auto py-16 px-4"> 38 + <div className="bg-white dark:bg-surface-900 rounded-xl ring-1 ring-black/5 dark:ring-white/5 p-6 text-center"> 39 + <h2 className="text-xl font-bold text-surface-900 dark:text-white mb-2">Sign in to create</h2> 40 + <p className="text-surface-500 dark:text-surface-400 text-sm mb-5">You need a Bluesky account</p> 41 + <Link to="/login" className="block w-full py-2.5 px-4 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg transition-colors"> 42 + Sign in with Bluesky 43 + </Link> 44 + </div> 45 + </div> 46 + ); 47 + } 48 + 49 + const handleSuccess = () => { 50 + navigate("/home"); 51 + }; 52 + 53 + return ( 54 + <div className="max-w-2xl mx-auto pb-20"> 55 + <div className="mb-6 text-center sm:text-left"> 56 + <h1 className="text-2xl font-display font-bold text-surface-900 dark:text-white mb-1">New Annotation</h1> 57 + <p className="text-surface-500 dark:text-surface-400">Write in the margins of the web</p> 58 + </div> 59 + 60 + {!initialUrl && ( 61 + <div className="mb-4"> 62 + <label htmlFor="url-input" className="block text-sm font-medium text-surface-700 dark:text-surface-300 mb-1.5"> 63 + URL to annotate 64 + </label> 65 + <input 66 + id="url-input" 67 + type="url" 68 + value={url} 69 + onChange={(e) => setUrl(e.target.value)} 70 + placeholder="https://example.com/article" 71 + className="w-full p-3 bg-white dark:bg-surface-900 border border-surface-200 dark:border-surface-700 rounded-lg text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400 outline-none transition-all" 72 + required 73 + /> 74 + </div> 75 + )} 76 + 77 + <div className="bg-white dark:bg-surface-900 rounded-xl ring-1 ring-black/5 dark:ring-white/5 p-5"> 78 + <Composer 79 + url={ 80 + (url || initialUrl) && !/^(?:f|ht)tps?:\/\//.test(url || initialUrl) 81 + ? `https://${url || initialUrl}` 82 + : url || initialUrl 83 + } 84 + selector={initialSelector} 85 + onSuccess={handleSuccess} 86 + onCancel={() => navigate(-1)} 87 + /> 88 + </div> 89 + </div> 90 + ); 91 + }
+105
web/src/views/Notifications.tsx
··· 1 + 2 + import React, { useEffect, useState } from 'react'; 3 + import { getNotifications, markNotificationsRead } from '../api/client'; 4 + import type { NotificationItem } from '../types'; 5 + import { Heart, MessageCircle, Star, User } from 'lucide-react'; 6 + import Card from '../components/Card'; 7 + import { formatDistanceToNow } from 'date-fns'; 8 + import { clsx } from 'clsx'; 9 + 10 + const NotificationIcon = ({ type }: { type: string }) => { 11 + switch (type) { 12 + case 'like': return <Heart size={18} className="text-red-500 fill-current" />; 13 + case 'reply': return <MessageCircle size={18} className="text-blue-500 fill-current" />; 14 + case 'follow': return <User size={18} className="text-primary-500 fill-current" />; 15 + case 'highlight': return <Star size={18} className="text-yellow-500 fill-current" />; 16 + default: return <Star size={18} className="text-surface-400 dark:text-surface-500" />; 17 + } 18 + }; 19 + 20 + export default function Notifications() { 21 + const [notifications, setNotifications] = useState<NotificationItem[]>([]); 22 + const [loading, setLoading] = useState(true); 23 + 24 + useEffect(() => { 25 + loadNotifications(); 26 + }, []); 27 + 28 + const loadNotifications = async () => { 29 + setLoading(true); 30 + const data = await getNotifications(); 31 + setNotifications(data); 32 + setLoading(false); 33 + markNotificationsRead(); 34 + }; 35 + 36 + if (loading) { 37 + return <div className="p-8 text-center text-surface-500 dark:text-surface-400">Loading activity...</div>; 38 + } 39 + 40 + if (notifications.length === 0) { 41 + return ( 42 + <div className="flex flex-col items-center justify-center py-16 bg-white dark:bg-surface-900 rounded-xl ring-1 ring-black/5 dark:ring-white/5"> 43 + <div className="w-12 h-12 bg-surface-100 dark:bg-surface-800 rounded-full flex items-center justify-center mb-3"> 44 + <Star size={24} className="text-surface-400 dark:text-surface-500" /> 45 + </div> 46 + <h3 className="text-lg font-semibold text-surface-900 dark:text-white mb-1">No activity yet</h3> 47 + <p className="text-surface-500 dark:text-surface-400 text-sm">Interactions with your content will appear here</p> 48 + </div> 49 + ); 50 + } 51 + 52 + return ( 53 + <div className="max-w-2xl mx-auto"> 54 + <h1 className="text-2xl font-display font-bold text-surface-900 dark:text-white mb-4">Activity</h1> 55 + <div className="space-y-2"> 56 + {notifications.map((n) => ( 57 + <div 58 + key={n.id} 59 + className={clsx( 60 + "bg-white dark:bg-surface-900 ring-1 ring-black/5 dark:ring-white/5 rounded-lg p-3 transition-colors", 61 + !n.readAt && "ring-primary-500/20 dark:ring-primary-400/20 bg-primary-50/30 dark:bg-primary-900/10" 62 + )} 63 + > 64 + <div className="flex gap-3"> 65 + <div className="shrink-0 mt-0.5"> 66 + <NotificationIcon type={n.type} /> 67 + </div> 68 + <div className="flex-1 min-w-0"> 69 + <div className="flex items-center gap-2 flex-wrap"> 70 + {n.actor.avatar ? ( 71 + <img src={n.actor.avatar} alt="" className="w-6 h-6 rounded-full" /> 72 + ) : ( 73 + <div className="w-6 h-6 rounded-full bg-surface-100 dark:bg-surface-800" /> 74 + )} 75 + <span className="font-medium text-surface-900 dark:text-white text-sm truncate"> 76 + {n.actor.displayName || n.actor.handle} 77 + </span> 78 + <span className="text-surface-500 dark:text-surface-400 text-sm"> 79 + {n.type === 'like' && 'liked your post'} 80 + {n.type === 'reply' && 'replied to you'} 81 + {n.type === 'follow' && 'followed you'} 82 + {n.type === 'highlight' && 'highlighted'} 83 + </span> 84 + <span className="text-surface-400 dark:text-surface-500 text-xs ml-auto"> 85 + {formatDistanceToNow(new Date(n.createdAt), { addSuffix: false })} 86 + </span> 87 + </div> 88 + 89 + {n.subject && ( 90 + <div className="mt-2 pl-3 border-l-2 border-surface-200 dark:border-surface-700"> 91 + {n.type === 'reply' ? ( 92 + <p className="text-surface-600 dark:text-surface-300 text-sm">{n.subject.text}</p> 93 + ) : ( 94 + <Card item={n.subject} /> 95 + )} 96 + </div> 97 + )} 98 + </div> 99 + </div> 100 + </div> 101 + ))} 102 + </div> 103 + </div> 104 + ); 105 + }
+122
web/src/views/Privacy.tsx
··· 1 + 2 + import React from 'react'; 3 + import { ArrowLeft } from 'lucide-react'; 4 + import { Link } from 'react-router-dom'; 5 + 6 + export default function Privacy() { 7 + return ( 8 + <div className="max-w-3xl mx-auto py-12 px-4"> 9 + <Link to="/home" className="inline-flex items-center gap-2 text-sm font-medium text-surface-500 hover:text-surface-900 transition-colors mb-8"> 10 + <ArrowLeft size={18} /> 11 + <span>Home</span> 12 + </Link> 13 + 14 + <div className="prose prose-surface max-w-none"> 15 + <h1 className="font-display font-bold text-3xl mb-2 text-surface-900">Privacy Policy</h1> 16 + <p className="text-surface-500 mb-8">Last updated: January 11, 2026</p> 17 + 18 + <section className="mb-8"> 19 + <h2 className="text-xl font-bold text-surface-900 mb-4">Overview</h2> 20 + <p className="text-surface-700 leading-relaxed"> 21 + Margin ("we", "our", or "us") is a web annotation tool that lets you highlight, annotate, and bookmark any webpage. Your data is stored on the decentralized AT Protocol network, giving you ownership and control over your content. 22 + </p> 23 + </section> 24 + 25 + <section className="mb-8"> 26 + <h2 className="text-xl font-bold text-surface-900 mb-4">Data We Collect</h2> 27 + <h3 className="text-lg font-semibold text-surface-900 mb-2">Account Information</h3> 28 + <p className="text-surface-700 mb-4"> 29 + When you log in with your Bluesky/AT Protocol account, we access your: 30 + </p> 31 + <ul className="list-disc pl-5 mb-4 text-surface-700 space-y-1"> 32 + <li>Decentralized Identifier (DID)</li> 33 + <li>Handle (username)</li> 34 + <li>Display name and avatar (for showing your profile)</li> 35 + </ul> 36 + 37 + <h3 className="text-lg font-semibold text-surface-900 mb-2">Annotations & Content</h3> 38 + <p className="text-surface-700 mb-4">When you use Margin, we store:</p> 39 + <ul className="list-disc pl-5 mb-4 text-surface-700 space-y-1"> 40 + <li>URLs of pages you annotate</li> 41 + <li>Text you highlight or select</li> 42 + <li>Annotations and comments you create</li> 43 + <li>Bookmarks you save</li> 44 + <li>Collections you organize content into</li> 45 + </ul> 46 + 47 + <h3 className="text-lg font-semibold text-surface-900 mb-2">Authentication</h3> 48 + <p className="text-surface-700 mb-4"> 49 + We store OAuth session tokens locally in your browser to keep you logged in. These tokens are used solely for authenticating API requests. 50 + </p> 51 + </section> 52 + 53 + <section className="mb-8"> 54 + <h2 className="text-xl font-bold text-surface-900 mb-4">How We Use Your Data</h2> 55 + <p className="text-surface-700 mb-4">Your data is used exclusively to:</p> 56 + <ul className="list-disc pl-5 mb-4 text-surface-700 space-y-1"> 57 + <li>Display your annotations on webpages</li> 58 + <li>Sync your content across devices</li> 59 + <li>Show your public annotations to other users</li> 60 + <li>Enable social features like replies and likes</li> 61 + </ul> 62 + </section> 63 + 64 + <section className="mb-8"> 65 + <h2 className="text-xl font-bold text-surface-900 mb-4">Data Storage</h2> 66 + <p className="text-surface-700 mb-4"> 67 + Your annotations are stored on the AT Protocol network through your Personal Data Server (PDS). This means: 68 + </p> 69 + <ul className="list-disc pl-5 mb-4 text-surface-700 space-y-1"> 70 + <li>You own your data</li> 71 + <li>You can export or delete it at any time</li> 72 + <li>Your data is portable across AT Protocol services</li> 73 + </ul> 74 + <p className="text-surface-700"> 75 + We also maintain a local index of annotations to provide faster search and discovery features. 76 + </p> 77 + </section> 78 + 79 + <section className="mb-8"> 80 + <h2 className="text-xl font-bold text-surface-900 mb-4">Data Sharing</h2> 81 + <p className="text-surface-700 mb-4"> 82 + <strong>We do not sell your data.</strong> We do not share your data with third parties for advertising or marketing purposes. 83 + </p> 84 + <p className="text-surface-700 mb-4">Your public annotations may be visible to:</p> 85 + <ul className="list-disc pl-5 mb-4 text-surface-700 space-y-1"> 86 + <li>Other Margin users viewing the same webpage</li> 87 + <li>Anyone on the AT Protocol network (for public content)</li> 88 + </ul> 89 + </section> 90 + 91 + <section className="mb-8"> 92 + <h2 className="text-xl font-bold text-surface-900 mb-4">Browser Extension Permissions</h2> 93 + <p className="text-surface-700 mb-4">The Margin browser extension requires certain permissions:</p> 94 + <ul className="list-disc pl-5 mb-4 text-surface-700 space-y-1"> 95 + <li><strong>All URLs:</strong> To display and create annotations on any webpage</li> 96 + <li><strong>Storage:</strong> To save your preferences and session locally</li> 97 + <li><strong>Cookies:</strong> To maintain your logged-in session</li> 98 + <li><strong>Tabs:</strong> To know which page you're viewing</li> 99 + </ul> 100 + </section> 101 + 102 + <section className="mb-8"> 103 + <h2 className="text-xl font-bold text-surface-900 mb-4">Your Rights</h2> 104 + <p className="text-surface-700 mb-4">You can:</p> 105 + <ul className="list-disc pl-5 mb-4 text-surface-700 space-y-1"> 106 + <li>Delete any annotation, highlight, or bookmark you've created</li> 107 + <li>Delete your collections</li> 108 + <li>Export your data from your PDS</li> 109 + <li>Revoke the extension's access at any time</li> 110 + </ul> 111 + </section> 112 + 113 + <section className="mb-8"> 114 + <h2 className="text-xl font-bold text-surface-900 mb-4">Contact</h2> 115 + <p className="text-surface-700"> 116 + For privacy questions or concerns, contact us at <a href="mailto:hello@margin.at" className="text-primary-600 hover:text-primary-700 hover:underline">hello@margin.at</a> 117 + </p> 118 + </section> 119 + </div> 120 + </div> 121 + ); 122 + }
+271
web/src/views/Profile.tsx
··· 1 + import React, { useEffect, useState } from 'react'; 2 + import { getProfile, getFeed, getAvatarUrl, getCollections } from '../api/client'; 3 + import Card from '../components/Card'; 4 + import { Loader2, User as UserIcon, Edit2, Grid, Bookmark, PenTool, MessageSquare, Folder, Link as LinkIcon, Globe } from 'lucide-react'; 5 + import type { UserProfile, AnnotationItem, Collection } from '../types'; 6 + import { useStore } from '@nanostores/react'; 7 + import { $user } from '../store/auth'; 8 + import EditProfileModal from '../components/EditProfileModal'; 9 + import CollectionIcon from '../components/CollectionIcon'; 10 + import { Link } from 'react-router-dom'; 11 + import { formatDistanceToNow } from 'date-fns'; 12 + import { clsx } from 'clsx'; 13 + 14 + interface ProfileProps { 15 + did: string; 16 + } 17 + 18 + type Tab = 'annotations' | 'highlights' | 'bookmarks' | 'collections'; 19 + 20 + export default function Profile({ did }: ProfileProps) { 21 + const [profile, setProfile] = useState<UserProfile | null>(null); 22 + const [loading, setLoading] = useState(true); 23 + const [activeTab, setActiveTab] = useState<Tab>('annotations'); 24 + 25 + const [annotations, setAnnotations] = useState<AnnotationItem[]>([]); 26 + const [highlights, setHighlights] = useState<AnnotationItem[]>([]); 27 + const [bookmarks, setBookmarks] = useState<AnnotationItem[]>([]); 28 + const [collections, setCollections] = useState<Collection[]>([]); 29 + const [dataLoading, setDataLoading] = useState(false); 30 + 31 + const user = useStore($user); 32 + const isOwner = user?.did === did; 33 + const [showEdit, setShowEdit] = useState(false); 34 + 35 + useEffect(() => { 36 + const loadProfile = async () => { 37 + setLoading(true); 38 + try { 39 + const marginPromise = getProfile(did); 40 + const bskyPromise = fetch(`https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(did)}`) 41 + .then(res => res.ok ? res.json() : null) 42 + .catch(() => null); 43 + 44 + const [marginData, bskyData] = await Promise.all([marginPromise, bskyPromise]); 45 + 46 + const merged: UserProfile = { 47 + did: did, 48 + handle: bskyData?.handle || marginData?.handle || '', 49 + displayName: bskyData?.displayName || marginData?.displayName, 50 + avatar: bskyData?.avatar || marginData?.avatar, 51 + description: bskyData?.description || marginData?.description, 52 + banner: bskyData?.banner || marginData?.banner, 53 + website: marginData?.website, 54 + links: marginData?.links || [], 55 + followersCount: bskyData?.followersCount || marginData?.followersCount, 56 + followsCount: bskyData?.followsCount || marginData?.followsCount, 57 + postsCount: bskyData?.postsCount || marginData?.postsCount 58 + }; 59 + 60 + setProfile(merged); 61 + } catch (e) { 62 + console.error("Profile load failed", e); 63 + } finally { 64 + setLoading(false); 65 + } 66 + }; 67 + if (did) loadProfile(); 68 + }, [did]); 69 + 70 + useEffect(() => { 71 + const loadTabContent = async () => { 72 + if (!did) return; 73 + setDataLoading(true); 74 + try { 75 + if (activeTab === 'annotations') { 76 + const res = await getFeed({ creator: did, motivation: 'commenting', limit: 50 }); 77 + setAnnotations(res.items || []); 78 + } else if (activeTab === 'highlights') { 79 + const res = await getFeed({ creator: did, motivation: 'highlighting', limit: 50 }); 80 + setHighlights(res.items || []); 81 + } else if (activeTab === 'bookmarks') { 82 + const res = await getFeed({ creator: did, motivation: 'bookmarking', limit: 50 }); 83 + setBookmarks(res.items || []); 84 + } else if (activeTab === 'collections') { 85 + const res = await getCollections(did); 86 + setCollections(res); 87 + } 88 + } catch (e) { 89 + console.error(e); 90 + } finally { 91 + setDataLoading(false); 92 + } 93 + }; 94 + loadTabContent(); 95 + }, [did, activeTab]); 96 + 97 + if (loading) { 98 + return ( 99 + <div className="flex justify-center py-20"> 100 + <Loader2 className="animate-spin text-primary-600 dark:text-primary-400" size={32} /> 101 + </div> 102 + ); 103 + } 104 + 105 + if (!profile) { 106 + return ( 107 + <div className="text-center py-20 text-surface-500 dark:text-surface-400"> 108 + <p>User not found.</p> 109 + </div> 110 + ); 111 + } 112 + 113 + const currentAvatar = getAvatarUrl(profile.did, profile.avatar); 114 + 115 + const tabs = [ 116 + { id: 'annotations', label: 'Notes', icon: MessageSquare }, 117 + { id: 'highlights', label: 'Highlights', icon: PenTool }, 118 + { id: 'bookmarks', label: 'Bookmarks', icon: Bookmark }, 119 + { id: 'collections', label: 'Collections', icon: Grid }, 120 + ]; 121 + 122 + return ( 123 + <div className="max-w-2xl mx-auto"> 124 + <div className="bg-white dark:bg-surface-900 rounded-xl shadow-sm ring-1 ring-black/5 dark:ring-white/5 p-4 mb-4"> 125 + <div className="flex items-start gap-4"> 126 + <div className="shrink-0"> 127 + {currentAvatar ? ( 128 + <img src={currentAvatar} alt={profile.handle} className="w-14 h-14 sm:w-16 sm:h-16 rounded-full object-cover ring-2 ring-surface-100 dark:ring-surface-800" /> 129 + ) : ( 130 + <div className="w-14 h-14 sm:w-16 sm:h-16 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center text-surface-400 dark:text-surface-500"> 131 + <UserIcon size={28} /> 132 + </div> 133 + )} 134 + </div> 135 + 136 + <div className="flex-1 min-w-0"> 137 + <div className="flex items-start justify-between gap-2"> 138 + <div className="min-w-0"> 139 + <h1 className="text-lg sm:text-xl font-bold text-surface-900 dark:text-white truncate"> 140 + {profile.displayName || profile.handle} 141 + </h1> 142 + <p className="text-surface-500 dark:text-surface-400 text-sm">@{profile.handle}</p> 143 + </div> 144 + {isOwner && ( 145 + <button 146 + onClick={() => setShowEdit(true)} 147 + className="shrink-0 px-2.5 py-1.5 text-sm font-medium text-surface-600 dark:text-surface-300 bg-surface-100 dark:bg-surface-800 hover:bg-surface-200 dark:hover:bg-surface-700 rounded-lg transition-colors flex items-center gap-1" 148 + > 149 + <Edit2 size={14} /> 150 + <span className="hidden sm:inline">Edit</span> 151 + </button> 152 + )} 153 + </div> 154 + 155 + {profile.description && ( 156 + <p className="text-surface-600 dark:text-surface-300 text-sm mt-2 line-clamp-2">{profile.description}</p> 157 + )} 158 + 159 + <div className="flex items-center gap-4 mt-2 text-xs text-surface-500 dark:text-surface-400"> 160 + <span><strong className="text-surface-700 dark:text-surface-200">{profile.followersCount || 0}</strong> followers</span> 161 + <span><strong className="text-surface-700 dark:text-surface-200">{profile.followsCount || 0}</strong> following</span> 162 + </div> 163 + </div> 164 + </div> 165 + </div> 166 + 167 + <div className="flex gap-1 bg-surface-100 dark:bg-surface-800 p-1 rounded-lg mb-4 overflow-x-auto"> 168 + {tabs.map((tab) => ( 169 + <button 170 + key={tab.id} 171 + onClick={() => setActiveTab(tab.id as Tab)} 172 + className={clsx( 173 + "flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-md transition-all whitespace-nowrap", 174 + activeTab === tab.id 175 + ? "bg-white dark:bg-surface-700 text-surface-900 dark:text-white shadow-sm" 176 + : "text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-200" 177 + )} 178 + > 179 + <tab.icon size={14} /> 180 + <span className="hidden sm:inline">{tab.label}</span> 181 + </button> 182 + ))} 183 + </div> 184 + 185 + <div className="min-h-[200px]"> 186 + {dataLoading ? ( 187 + <div className="flex justify-center py-12"> 188 + <Loader2 className="animate-spin text-surface-400" size={24} /> 189 + </div> 190 + ) : ( 191 + <> 192 + {activeTab === 'collections' ? ( 193 + collections.length === 0 ? ( 194 + <EmptyState type="collections" isOwner={isOwner} /> 195 + ) : ( 196 + <div className="grid grid-cols-1 gap-2"> 197 + {collections.map(collection => ( 198 + <Link 199 + key={collection.id} 200 + to={`/${collection.creator?.handle || profile.handle}/collection/${collection.uri.split('/').pop()}`} 201 + className="group bg-white dark:bg-surface-900 rounded-lg p-3 shadow-sm ring-1 ring-black/5 dark:ring-white/5 hover:ring-primary-300 dark:hover:ring-primary-600 transition-all flex items-center gap-3" 202 + > 203 + <div className="p-2 bg-primary-50 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 rounded-lg"> 204 + <CollectionIcon icon={collection.icon} size={18} /> 205 + </div> 206 + <div className="flex-1 min-w-0"> 207 + <h3 className="font-medium text-surface-900 dark:text-white truncate group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors"> 208 + {collection.name} 209 + </h3> 210 + <p className="text-xs text-surface-500 dark:text-surface-400"> 211 + {collection.itemCount} items 212 + </p> 213 + </div> 214 + </Link> 215 + ))} 216 + </div> 217 + ) 218 + ) : ( 219 + <> 220 + {(activeTab === 'annotations' ? annotations : activeTab === 'highlights' ? highlights : bookmarks).length > 0 ? ( 221 + <div> 222 + {(activeTab === 'annotations' ? annotations : activeTab === 'highlights' ? highlights : bookmarks).map((item) => ( 223 + <Card key={item.uri || item.cid} item={item} /> 224 + ))} 225 + </div> 226 + ) : ( 227 + <EmptyState type={activeTab} isOwner={isOwner} /> 228 + )} 229 + </> 230 + )} 231 + </> 232 + )} 233 + </div> 234 + 235 + {showEdit && profile && ( 236 + <EditProfileModal 237 + profile={profile} 238 + onClose={() => setShowEdit(false)} 239 + onUpdate={(updated) => setProfile(updated)} 240 + /> 241 + )} 242 + </div> 243 + ); 244 + } 245 + 246 + function EmptyState({ type, isOwner }: { type: string, isOwner: boolean }) { 247 + const messages: Record<string, string> = { 248 + annotations: isOwner ? "No notes yet" : "No notes", 249 + highlights: isOwner ? "No highlights yet" : "No highlights", 250 + bookmarks: isOwner ? "No bookmarks yet" : "No bookmarks", 251 + collections: isOwner ? "No collections yet" : "No collections" 252 + }; 253 + 254 + const icons: Record<string, any> = { 255 + annotations: MessageSquare, 256 + highlights: PenTool, 257 + bookmarks: Bookmark, 258 + collections: Folder 259 + }; 260 + 261 + const Icon = icons[type] || MessageSquare; 262 + 263 + return ( 264 + <div className="text-center py-10 flex flex-col items-center"> 265 + <div className="w-10 h-10 bg-surface-100 dark:bg-surface-800 rounded-full flex items-center justify-center text-surface-400 dark:text-surface-500 mb-2"> 266 + <Icon size={20} /> 267 + </div> 268 + <p className="text-surface-500 dark:text-surface-400 text-sm">{messages[type]}</p> 269 + </div> 270 + ); 271 + }
+156
web/src/views/Settings.tsx
··· 1 + 2 + import React, { useEffect, useState } from 'react'; 3 + import { useStore } from '@nanostores/react'; 4 + import { $user } from '../store/auth'; 5 + import { getAPIKeys, createAPIKey, deleteAPIKey, type APIKey } from '../api/client'; 6 + import { Copy, Trash2, Key, Plus, Check, User as UserIcon } from 'lucide-react'; 7 + 8 + export default function Settings() { 9 + const user = useStore($user); 10 + const [keys, setKeys] = useState<APIKey[]>([]); 11 + const [loading, setLoading] = useState(true); 12 + const [newKeyName, setNewKeyName] = useState(''); 13 + const [createdKey, setCreatedKey] = useState<string | null>(null); 14 + const [justCopied, setJustCopied] = useState(false); 15 + 16 + useEffect(() => { 17 + loadKeys(); 18 + }, []); 19 + 20 + const loadKeys = async () => { 21 + setLoading(true); 22 + const data = await getAPIKeys(); 23 + setKeys(data); 24 + setLoading(false); 25 + }; 26 + 27 + const handleCreate = async (e: React.FormEvent) => { 28 + e.preventDefault(); 29 + if (!newKeyName.trim()) return; 30 + 31 + const res = await createAPIKey(newKeyName); 32 + if (res) { 33 + setKeys([res, ...keys]); 34 + setCreatedKey(res.key || null); 35 + setNewKeyName(''); 36 + } 37 + }; 38 + 39 + const handleDelete = async (id: string) => { 40 + if (window.confirm('Revoke this key? Apps using it will stop working.')) { 41 + const success = await deleteAPIKey(id); 42 + if (success) { 43 + setKeys(prev => prev.filter(k => k.id !== id)); 44 + } 45 + } 46 + }; 47 + 48 + const copyToClipboard = async (text: string) => { 49 + await navigator.clipboard.writeText(text); 50 + setJustCopied(true); 51 + setTimeout(() => setJustCopied(false), 2000); 52 + }; 53 + 54 + if (!user) return null; 55 + 56 + return ( 57 + <div className="max-w-2xl mx-auto animate-fade-in"> 58 + <h1 className="text-2xl font-display font-bold text-surface-900 dark:text-white mb-6">Settings</h1> 59 + 60 + <div className="space-y-4"> 61 + <section className="bg-white dark:bg-surface-900 rounded-xl ring-1 ring-black/5 dark:ring-white/5 p-4"> 62 + <h2 className="text-sm font-semibold text-surface-500 dark:text-surface-400 uppercase tracking-wider mb-3">Profile</h2> 63 + <div className="flex gap-4 items-center"> 64 + {user.avatar ? ( 65 + <img src={user.avatar} className="w-14 h-14 rounded-full bg-surface-100 dark:bg-surface-800 object-cover" /> 66 + ) : ( 67 + <div className="w-14 h-14 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center text-surface-400 dark:text-surface-500"> 68 + <UserIcon size={24} /> 69 + </div> 70 + )} 71 + <div> 72 + <p className="font-medium text-surface-900 dark:text-white">{user.displayName || user.handle}</p> 73 + <p className="text-sm text-surface-500 dark:text-surface-400">@{user.handle}</p> 74 + </div> 75 + </div> 76 + </section> 77 + 78 + <section className="bg-white dark:bg-surface-900 rounded-xl ring-1 ring-black/5 dark:ring-white/5 p-4"> 79 + <h2 className="text-sm font-semibold text-surface-500 dark:text-surface-400 uppercase tracking-wider mb-1">API Keys</h2> 80 + <p className="text-xs text-surface-400 dark:text-surface-500 mb-4">For the browser extension and other apps</p> 81 + 82 + <form onSubmit={handleCreate} className="flex gap-2 mb-4"> 83 + <input 84 + type="text" 85 + value={newKeyName} 86 + onChange={e => setNewKeyName(e.target.value)} 87 + placeholder="Key name, e.g. Chrome Extension" 88 + className="flex-1 px-3 py-2 bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500 dark:focus:border-primary-400 text-sm" 89 + /> 90 + <button 91 + type="submit" 92 + disabled={!newKeyName.trim()} 93 + className="px-3 py-2 bg-primary-600 text-white font-medium rounded-lg hover:bg-primary-500 disabled:opacity-50 transition-colors text-sm flex items-center gap-1.5" 94 + > 95 + <Plus size={16} /> 96 + Generate 97 + </button> 98 + </form> 99 + 100 + {createdKey && ( 101 + <div className="mb-4 p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg"> 102 + <div className="flex items-start gap-2"> 103 + <Key size={16} className="text-green-600 dark:text-green-400 mt-0.5 shrink-0" /> 104 + <div className="flex-1 min-w-0"> 105 + <p className="text-green-800 dark:text-green-200 text-xs mb-2">Copy now - you won't see this again!</p> 106 + <div className="flex items-center gap-2"> 107 + <code className="flex-1 bg-white dark:bg-surface-900 border border-green-200 dark:border-green-800 px-2 py-1.5 rounded text-xs font-mono text-green-900 dark:text-green-100 break-all"> 108 + {createdKey} 109 + </code> 110 + <button 111 + onClick={() => copyToClipboard(createdKey)} 112 + className="p-1.5 text-green-700 dark:text-green-400 hover:bg-green-100 dark:hover:bg-green-900/40 rounded transition-colors" 113 + > 114 + {justCopied ? <Check size={16} /> : <Copy size={16} />} 115 + </button> 116 + </div> 117 + </div> 118 + </div> 119 + </div> 120 + )} 121 + 122 + {loading ? ( 123 + <div className="space-y-2"> 124 + <div className="h-12 bg-surface-100 dark:bg-surface-800 rounded-lg animate-pulse" /> 125 + <div className="h-12 bg-surface-100 dark:bg-surface-800 rounded-lg animate-pulse" /> 126 + </div> 127 + ) : keys.length === 0 ? ( 128 + <p className="text-center text-surface-500 dark:text-surface-400 text-sm py-6">No API keys yet</p> 129 + ) : ( 130 + <div className="space-y-2"> 131 + {keys.map(key => ( 132 + <div key={key.id} className="flex items-center justify-between p-3 bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-lg group"> 133 + <div className="flex items-center gap-3"> 134 + <Key size={16} className="text-surface-400 dark:text-surface-500" /> 135 + <div> 136 + <p className="font-medium text-surface-900 dark:text-white text-sm">{key.alias}</p> 137 + <p className="text-xs text-surface-500 dark:text-surface-400"> 138 + Created {new Date(key.createdAt).toLocaleDateString()} 139 + </p> 140 + </div> 141 + </div> 142 + <button 143 + onClick={() => handleDelete(key.id)} 144 + className="p-1.5 text-surface-400 dark:text-surface-500 hover:text-red-500 dark:hover:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg transition-colors opacity-0 group-hover:opacity-100" 145 + > 146 + <Trash2 size={16} /> 147 + </button> 148 + </div> 149 + ))} 150 + </div> 151 + )} 152 + </section> 153 + </div> 154 + </div> 155 + ); 156 + }
+70
web/src/views/Terms.tsx
··· 1 + 2 + import React from 'react'; 3 + import { ArrowLeft } from 'lucide-react'; 4 + import { Link } from 'react-router-dom'; 5 + 6 + export default function Terms() { 7 + return ( 8 + <div className="max-w-3xl mx-auto py-12 px-4"> 9 + <Link to="/home" className="inline-flex items-center gap-2 text-sm font-medium text-surface-500 hover:text-surface-900 transition-colors mb-8"> 10 + <ArrowLeft size={18} /> 11 + <span>Home</span> 12 + </Link> 13 + 14 + <div className="prose prose-surface max-w-none"> 15 + <h1 className="font-display font-bold text-3xl mb-2 text-surface-900">Terms of Service</h1> 16 + <p className="text-surface-500 mb-8">Last updated: January 17, 2026</p> 17 + 18 + <section className="mb-8"> 19 + <h2 className="text-xl font-bold text-surface-900 mb-4">Overview</h2> 20 + <p className="text-surface-700 leading-relaxed"> 21 + Margin is an open-source project. By using our service, you agree to these terms ("Terms"). If you do not agree to these Terms, please do not use the Service. 22 + </p> 23 + </section> 24 + 25 + <section className="mb-8"> 26 + <h2 className="text-xl font-bold text-surface-900 mb-4">Open Source</h2> 27 + <p className="text-surface-700 leading-relaxed"> 28 + Margin is open source software. The code is available publicly and is provided "as is", without warranty of any kind, express or implied. 29 + </p> 30 + </section> 31 + 32 + <section className="mb-8"> 33 + <h2 className="text-xl font-bold text-surface-900 mb-4">User Conduct</h2> 34 + <p className="text-surface-700 mb-4"> 35 + You are responsible for your use of the Service and for any content you provide, including compliance with applicable laws, rules, and regulations. 36 + </p> 37 + <p className="text-surface-700 mb-4"> 38 + We reserve the right to remove any content that violates these terms, including but not limited to: 39 + </p> 40 + <ul className="list-disc pl-5 mb-4 text-surface-700 space-y-1"> 41 + <li>Illegal content</li> 42 + <li>Harassment or hate speech</li> 43 + <li>Spam or malicious content</li> 44 + </ul> 45 + </section> 46 + 47 + <section className="mb-8"> 48 + <h2 className="text-xl font-bold text-surface-900 mb-4">Decentralized Nature</h2> 49 + <p className="text-surface-700 leading-relaxed"> 50 + Margin interacts with the AT Protocol network. We do not control the network itself or the data stored on your Personal Data Server (PDS). Please refer to the terms of your PDS provider for data storage policies. 51 + </p> 52 + </section> 53 + 54 + <section className="mb-8"> 55 + <h2 className="text-xl font-bold text-surface-900 mb-4">Disclaimer</h2> 56 + <p className="text-surface-700 leading-relaxed uppercase"> 57 + THE SERVICE IS PROVIDED "AS IS" AND "AS AVAILABLE". WE DISCLAIM ALL CONDITIONS, REPRESENTATIONS AND WARRANTIES NOT EXPRESSLY SET OUT IN THESE TERMS. 58 + </p> 59 + </section> 60 + 61 + <section className="mb-8"> 62 + <h2 className="text-xl font-bold text-surface-900 mb-4">Contact</h2> 63 + <p className="text-surface-700"> 64 + For questions about these Terms, please contact us at <a href="mailto:hello@margin.at" className="text-primary-600 hover:text-primary-700 hover:underline">hello@margin.at</a> 65 + </p> 66 + </section> 67 + </div> 68 + </div> 69 + ); 70 + }
+354
web/src/views/Url.tsx
··· 1 + import React, { useState, useEffect, useRef } from 'react'; 2 + import { useNavigate, Link } from 'react-router-dom'; 3 + import { useStore } from '@nanostores/react'; 4 + import { $user } from '../store/auth'; 5 + import { getByTarget, searchActors, resolveHandle } from '../api/client'; 6 + import type { AnnotationItem, ActorSearchItem } from '../types'; 7 + import Card from '../components/Card'; 8 + import { Search, PenTool, Highlighter, Loader2, AlertTriangle, ExternalLink, Copy, Check, Link as LinkIcon, Clock, Globe } from 'lucide-react'; 9 + import { clsx } from 'clsx'; 10 + import { getAvatarUrl } from '../api/client'; 11 + 12 + export default function UrlPage() { 13 + const user = useStore($user); 14 + const navigate = useNavigate(); 15 + const [url, setUrl] = useState(""); 16 + const [annotations, setAnnotations] = useState<AnnotationItem[]>([]); 17 + const [highlights, setHighlights] = useState<AnnotationItem[]>([]); 18 + const [loading, setLoading] = useState(false); 19 + const [searched, setSearched] = useState(false); 20 + const [error, setError] = useState<string | null>(null); 21 + const [activeTab, setActiveTab] = useState<'all' | 'annotations' | 'highlights'>('all'); 22 + const [copied, setCopied] = useState(false); 23 + 24 + const [suggestions, setSuggestions] = useState<ActorSearchItem[]>([]); 25 + const [showSuggestions, setShowSuggestions] = useState(false); 26 + const [selectedIndex, setSelectedIndex] = useState(-1); 27 + const inputRef = useRef<HTMLInputElement>(null); 28 + const suggestionsRef = useRef<HTMLDivElement>(null); 29 + 30 + const [recentSearches, setRecentSearches] = useState<string[]>([]); 31 + 32 + useEffect(() => { 33 + const stored = localStorage.getItem('margin-recent-searches'); 34 + if (stored) { 35 + try { 36 + setRecentSearches(JSON.parse(stored).slice(0, 5)); 37 + } catch { } 38 + } 39 + }, []); 40 + 41 + const saveRecentSearch = (query: string) => { 42 + const updated = [query, ...recentSearches.filter(s => s !== query)].slice(0, 5); 43 + setRecentSearches(updated); 44 + localStorage.setItem('margin-recent-searches', JSON.stringify(updated)); 45 + }; 46 + 47 + useEffect(() => { 48 + const timer = setTimeout(async () => { 49 + const isUrl = url.includes("http") || url.includes("://"); 50 + if (url.length >= 2 && !isUrl) { 51 + try { 52 + const data = await searchActors(url); 53 + setSuggestions(data.actors || []); 54 + setShowSuggestions(true); 55 + } catch { } 56 + } else { 57 + setSuggestions([]); 58 + setShowSuggestions(false); 59 + } 60 + }, 300); 61 + return () => clearTimeout(timer); 62 + }, [url]); 63 + 64 + useEffect(() => { 65 + const handleClickOutside = (e: MouseEvent) => { 66 + if ( 67 + suggestionsRef.current && 68 + !suggestionsRef.current.contains(e.target as Node) && 69 + inputRef.current && 70 + !inputRef.current.contains(e.target as Node) 71 + ) { 72 + setShowSuggestions(false); 73 + } 74 + }; 75 + document.addEventListener("mousedown", handleClickOutside); 76 + return () => document.removeEventListener("mousedown", handleClickOutside); 77 + }, []); 78 + 79 + const handleKeyDown = (e: React.KeyboardEvent) => { 80 + if (!showSuggestions || suggestions.length === 0) return; 81 + 82 + if (e.key === "ArrowDown") { 83 + e.preventDefault(); 84 + setSelectedIndex((prev) => Math.min(prev + 1, suggestions.length - 1)); 85 + } else if (e.key === "ArrowUp") { 86 + e.preventDefault(); 87 + setSelectedIndex((prev) => Math.max(prev - 1, -1)); 88 + } else if (e.key === "Enter" && selectedIndex >= 0) { 89 + e.preventDefault(); 90 + selectSuggestion(suggestions[selectedIndex]); 91 + } else if (e.key === "Escape") { 92 + setShowSuggestions(false); 93 + } 94 + }; 95 + 96 + const selectSuggestion = (actor: ActorSearchItem) => { 97 + navigate(`/profile/${encodeURIComponent(actor.handle)}`); 98 + }; 99 + 100 + const handleSearch = async (e: React.FormEvent) => { 101 + e.preventDefault(); 102 + if (!url.trim()) return; 103 + 104 + setLoading(true); 105 + setError(null); 106 + setSearched(true); 107 + setAnnotations([]); 108 + setHighlights([]); 109 + 110 + const isProtocol = url.startsWith("http://") || url.startsWith("https://"); 111 + if (!isProtocol) { 112 + try { 113 + const actorRes = await searchActors(url); 114 + if (actorRes?.actors?.length > 0) { 115 + const match = actorRes.actors[0]; 116 + navigate(`/profile/${encodeURIComponent(match.handle)}`); 117 + return; 118 + } 119 + } catch { } 120 + } 121 + 122 + try { 123 + const data = await getByTarget(url); 124 + setAnnotations(data.annotations || []); 125 + setHighlights(data.highlights || []); 126 + saveRecentSearch(url); 127 + } catch (err: any) { 128 + setError(err.message); 129 + } finally { 130 + setLoading(false); 131 + setShowSuggestions(false); 132 + } 133 + }; 134 + 135 + const myAnnotations = user 136 + ? annotations.filter((a) => (a.author?.did || a.creator?.did) === user.did) 137 + : []; 138 + const myHighlights = user 139 + ? highlights.filter((h) => (h.author?.did || h.creator?.did) === user.did) 140 + : []; 141 + const myItemsCount = myAnnotations.length + myHighlights.length; 142 + 143 + const getShareUrl = () => { 144 + if (!user?.handle || !url) return null; 145 + return `${window.location.origin}/${user.handle}/url/${encodeURIComponent(url)}`; 146 + }; 147 + 148 + const handleCopyShareLink = async () => { 149 + const shareUrl = getShareUrl(); 150 + if (!shareUrl) return; 151 + try { 152 + await navigator.clipboard.writeText(shareUrl); 153 + setCopied(true); 154 + setTimeout(() => setCopied(false), 2000); 155 + } catch { 156 + prompt("Copy this link:", shareUrl); 157 + } 158 + }; 159 + 160 + const totalItems = annotations.length + highlights.length; 161 + 162 + const renderResults = () => { 163 + if (activeTab === "annotations" && annotations.length === 0) { 164 + return ( 165 + <div className="flex flex-col items-center justify-center p-12 text-center bg-surface-50 dark:bg-surface-800/50 border border-dashed border-surface-200 dark:border-surface-700 rounded-2xl"> 166 + <div className="w-12 h-12 bg-surface-100 dark:bg-surface-700 rounded-full flex items-center justify-center text-surface-400 dark:text-surface-500 mb-4"> 167 + <PenTool size={24} /> 168 + </div> 169 + <h3 className="text-lg font-medium text-surface-600 dark:text-surface-300">No annotations</h3> 170 + </div> 171 + ); 172 + } 173 + 174 + if (activeTab === "highlights" && highlights.length === 0) { 175 + return ( 176 + <div className="flex flex-col items-center justify-center p-12 text-center bg-surface-50 dark:bg-surface-800/50 border border-dashed border-surface-200 dark:border-surface-700 rounded-2xl"> 177 + <div className="w-12 h-12 bg-surface-100 dark:bg-surface-700 rounded-full flex items-center justify-center text-surface-400 dark:text-surface-500 mb-4"> 178 + <Highlighter size={24} /> 179 + </div> 180 + <h3 className="text-lg font-medium text-surface-600 dark:text-surface-300">No highlights</h3> 181 + </div> 182 + ); 183 + } 184 + 185 + return ( 186 + <div className="space-y-3"> 187 + {(activeTab === "all" || activeTab === "annotations") && 188 + annotations.map((a) => <Card key={a.uri} item={a} />)} 189 + {(activeTab === "all" || activeTab === "highlights") && 190 + highlights.map((h) => <Card key={h.uri} item={h} />)} 191 + </div> 192 + ); 193 + }; 194 + 195 + return ( 196 + <div className="max-w-2xl mx-auto pb-20"> 197 + <div className="mb-8 text-center"> 198 + <h1 className="text-3xl font-display font-bold text-surface-900 dark:text-white mb-2">Explore</h1> 199 + <p className="text-surface-500 dark:text-surface-400"> 200 + Search for a URL or find a user 201 + </p> 202 + </div> 203 + 204 + <form onSubmit={handleSearch} className="mb-6 relative z-10"> 205 + <div className="relative"> 206 + <div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none"> 207 + {loading ? ( 208 + <Loader2 className="animate-spin text-primary-500" size={20} /> 209 + ) : ( 210 + <Search className="text-surface-400 dark:text-surface-500" size={20} /> 211 + )} 212 + </div> 213 + <input 214 + ref={inputRef} 215 + type="text" 216 + value={url} 217 + onChange={(e) => setUrl(e.target.value)} 218 + onKeyDown={handleKeyDown} 219 + placeholder="https://... or @handle" 220 + className="w-full pl-12 pr-24 py-3.5 bg-white dark:bg-surface-900 border border-surface-200 dark:border-surface-700 rounded-xl shadow-sm focus:ring-4 focus:ring-primary-500/10 focus:border-primary-500 dark:focus:border-primary-400 outline-none transition-all text-surface-900 dark:text-white placeholder:text-surface-400 dark:placeholder:text-surface-500" 221 + autoComplete="off" 222 + required 223 + /> 224 + <div className="absolute inset-y-1.5 right-1.5"> 225 + <button 226 + type="submit" 227 + className="h-full px-5 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg transition-colors disabled:opacity-70" 228 + disabled={loading} 229 + > 230 + Search 231 + </button> 232 + </div> 233 + </div> 234 + 235 + {showSuggestions && suggestions.length > 0 && ( 236 + <div 237 + ref={suggestionsRef} 238 + className="absolute top-full left-0 right-0 mt-2 bg-white dark:bg-surface-900 rounded-xl shadow-xl border border-surface-200 dark:border-surface-700 overflow-hidden max-h-[280px] overflow-y-auto" 239 + > 240 + {suggestions.map((actor, index) => ( 241 + <button 242 + key={actor.did} 243 + type="button" 244 + className={clsx( 245 + "w-full text-left px-4 py-3 flex items-center gap-3 transition-colors border-b border-surface-100 dark:border-surface-800 last:border-0", 246 + index === selectedIndex ? "bg-surface-50 dark:bg-surface-800" : "hover:bg-surface-50 dark:hover:bg-surface-800" 247 + )} 248 + onClick={() => selectSuggestion(actor)} 249 + > 250 + {getAvatarUrl(actor.did, actor.avatar) ? ( 251 + <img src={getAvatarUrl(actor.did, actor.avatar)} alt="" className="w-10 h-10 rounded-full object-cover bg-surface-100 dark:bg-surface-800" /> 252 + ) : ( 253 + <div className="w-10 h-10 rounded-full bg-surface-100 dark:bg-surface-800 flex items-center justify-center font-bold text-surface-500 dark:text-surface-400"> 254 + {(actor.displayName || actor.handle || "?")[0]?.toUpperCase()} 255 + </div> 256 + )} 257 + <div className="flex flex-col"> 258 + <span className="font-semibold text-surface-900 dark:text-white">{actor.displayName || actor.handle}</span> 259 + <span className="text-sm text-surface-500 dark:text-surface-400">@{actor.handle}</span> 260 + </div> 261 + </button> 262 + ))} 263 + </div> 264 + )} 265 + </form> 266 + 267 + {!searched && recentSearches.length > 0 && ( 268 + <div className="mb-8"> 269 + <h3 className="text-sm font-medium text-surface-500 dark:text-surface-400 mb-3 flex items-center gap-2"> 270 + <Clock size={14} /> Recent 271 + </h3> 272 + <div className="flex flex-wrap gap-2"> 273 + {recentSearches.map((q, i) => ( 274 + <button 275 + key={i} 276 + onClick={() => { setUrl(q); }} 277 + className="px-3 py-1.5 bg-surface-100 dark:bg-surface-800 hover:bg-surface-200 dark:hover:bg-surface-700 rounded-lg text-sm text-surface-700 dark:text-surface-300 transition-colors flex items-center gap-1.5 max-w-[200px] truncate" 278 + > 279 + <Globe size={12} className="shrink-0" /> 280 + <span className="truncate">{q.replace(/^https?:\/\//, '')}</span> 281 + </button> 282 + ))} 283 + </div> 284 + </div> 285 + )} 286 + 287 + {error && ( 288 + <div className="mb-6 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 p-4 rounded-xl flex items-start gap-3 border border-red-100 dark:border-red-900/30"> 289 + <AlertTriangle className="shrink-0 mt-0.5" size={18} /> 290 + <p>{error}</p> 291 + </div> 292 + )} 293 + 294 + {searched && !loading && !error && totalItems === 0 && ( 295 + <div className="text-center py-12"> 296 + <div className="w-14 h-14 bg-surface-100 dark:bg-surface-800 rounded-full flex items-center justify-center mx-auto mb-4 text-surface-400 dark:text-surface-500"> 297 + <Search size={28} /> 298 + </div> 299 + <h3 className="text-xl font-bold text-surface-900 dark:text-white mb-2">No items found</h3> 300 + <p className="text-surface-500 dark:text-surface-400 text-sm"> 301 + Be the first to annotate this URL! 302 + </p> 303 + </div> 304 + )} 305 + 306 + {searched && totalItems > 0 && ( 307 + <div className="animate-fade-in"> 308 + <div className="flex items-center justify-between gap-4 mb-4"> 309 + <h2 className="text-lg font-bold text-surface-900 dark:text-white"> 310 + {totalItems} result{totalItems !== 1 ? "s" : ""} 311 + </h2> 312 + <div className="flex bg-surface-100 dark:bg-surface-800 p-1 rounded-lg"> 313 + {[ 314 + { id: 'all', label: `All (${totalItems})` }, 315 + { id: 'annotations', label: `Notes (${annotations.length})` }, 316 + { id: 'highlights', label: `Highlights (${highlights.length})` } 317 + ].map(tab => ( 318 + <button 319 + key={tab.id} 320 + className={clsx( 321 + "px-3 py-1.5 rounded-md text-sm font-medium transition-all", 322 + activeTab === tab.id 323 + ? "bg-white dark:bg-surface-700 text-surface-900 dark:text-white shadow-sm" 324 + : "text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-200" 325 + )} 326 + onClick={() => setActiveTab(tab.id as any)} 327 + > 328 + {tab.label} 329 + </button> 330 + ))} 331 + </div> 332 + </div> 333 + 334 + {user && myItemsCount > 0 && ( 335 + <div className="bg-primary-50 dark:bg-primary-900/20 border border-primary-100 dark:border-primary-900/30 rounded-xl p-3 mb-4 flex items-center justify-between gap-3"> 336 + <span className="text-sm text-primary-900 dark:text-primary-100 font-medium"> 337 + You have {myItemsCount} note{myItemsCount !== 1 ? 's' : ''} here 338 + </span> 339 + <button 340 + onClick={handleCopyShareLink} 341 + className="flex items-center gap-1.5 px-3 py-1.5 bg-primary-600 hover:bg-primary-700 text-white text-sm font-medium rounded-lg transition-colors" 342 + > 343 + {copied ? <Check size={14} /> : <Copy size={14} />} 344 + {copied ? "Copied!" : "Share"} 345 + </button> 346 + </div> 347 + )} 348 + 349 + {renderResults()} 350 + </div> 351 + )} 352 + </div> 353 + ); 354 + }
+220
web/src/views/UserUrl.tsx
··· 1 + import React, { useState, useEffect } from 'react'; 2 + import { useParams, Link } from 'react-router-dom'; 3 + import { getUserTargetItems } from '../api/client'; 4 + import type { AnnotationItem, UserProfile } from '../types'; 5 + import Card from '../components/Card'; 6 + import { PenTool, Highlighter, Search, AlertTriangle, ExternalLink } from 'lucide-react'; 7 + import { clsx } from 'clsx'; 8 + import { getAvatarUrl } from '../api/client'; 9 + 10 + export default function UserUrlPage() { 11 + const params = useParams(); 12 + const handle = params.handle; 13 + const urlPath = params['*']; 14 + const targetUrl = urlPath || ""; 15 + 16 + const [profile, setProfile] = useState<UserProfile | null>(null); 17 + const [annotations, setAnnotations] = useState<AnnotationItem[]>([]); 18 + const [highlights, setHighlights] = useState<AnnotationItem[]>([]); 19 + const [loading, setLoading] = useState(true); 20 + const [error, setError] = useState<string | null>(null); 21 + const [activeTab, setActiveTab] = useState<'all' | 'annotations' | 'highlights'>('all'); 22 + 23 + useEffect(() => { 24 + async function fetchData() { 25 + if (!targetUrl || !handle) { 26 + setLoading(false); 27 + return; 28 + } 29 + 30 + try { 31 + setLoading(true); 32 + setError(null); 33 + 34 + const profileRes = await fetch( 35 + `https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(handle)}` 36 + ); 37 + 38 + let did = handle; 39 + if (profileRes.ok) { 40 + const profileData = await profileRes.json(); 41 + setProfile(profileData); 42 + did = profileData.did; 43 + } 44 + 45 + const decodedUrl = decodeURIComponent(targetUrl); 46 + 47 + const data = await getUserTargetItems(did, decodedUrl); 48 + setAnnotations(data.annotations || []); 49 + setHighlights(data.highlights || []); 50 + } catch (err: any) { 51 + setError(err.message); 52 + } finally { 53 + setLoading(false); 54 + } 55 + } 56 + fetchData(); 57 + }, [handle, targetUrl]); 58 + 59 + const displayName = profile?.displayName || profile?.handle || handle; 60 + const displayHandle = profile?.handle || (handle?.startsWith("did:") ? null : handle); 61 + const avatarUrl = getAvatarUrl(profile?.did, profile?.avatar); 62 + 63 + const getInitial = () => { 64 + return (displayName || displayHandle || "??")?.substring(0, 2).toUpperCase(); 65 + }; 66 + 67 + const totalItems = annotations.length + highlights.length; 68 + const bskyProfileUrl = displayHandle 69 + ? `https://bsky.app/profile/${displayHandle}` 70 + : `https://bsky.app/profile/${handle}`; 71 + 72 + const renderResults = () => { 73 + if (activeTab === "annotations" && annotations.length === 0) { 74 + return ( 75 + <div className="flex flex-col items-center justify-center p-12 text-center bg-surface-50 border border-dashed border-surface-200 rounded-2xl"> 76 + <div className="w-12 h-12 bg-surface-100 rounded-full flex items-center justify-center text-surface-400 mb-4"> 77 + <PenTool size={24} /> 78 + </div> 79 + <h3 className="text-lg font-medium text-surface-600">No annotations</h3> 80 + </div> 81 + ); 82 + } 83 + 84 + if (activeTab === "highlights" && highlights.length === 0) { 85 + return ( 86 + <div className="flex flex-col items-center justify-center p-12 text-center bg-surface-50 border border-dashed border-surface-200 rounded-2xl"> 87 + <div className="w-12 h-12 bg-surface-100 rounded-full flex items-center justify-center text-surface-400 mb-4"> 88 + <Highlighter size={24} /> 89 + </div> 90 + <h3 className="text-lg font-medium text-surface-600">No highlights</h3> 91 + </div> 92 + ); 93 + } 94 + 95 + return ( 96 + <div className="space-y-6"> 97 + {(activeTab === "all" || activeTab === "annotations") && 98 + annotations.map((a) => <Card key={a.uri} item={a} />)} 99 + {(activeTab === "all" || activeTab === "highlights") && 100 + highlights.map((h) => <Card key={h.uri} item={h} />)} 101 + </div> 102 + ); 103 + }; 104 + 105 + if (!targetUrl) { 106 + return ( 107 + <div className="max-w-2xl mx-auto py-20 text-center"> 108 + <div className="w-16 h-16 bg-surface-100 rounded-full flex items-center justify-center mx-auto mb-4 text-surface-400"> 109 + <Search size={32} /> 110 + </div> 111 + <h3 className="text-xl font-bold text-surface-900 mb-2">No URL specified</h3> 112 + <p className="text-surface-500">Please provide a URL to view annotations.</p> 113 + </div> 114 + ); 115 + } 116 + 117 + return ( 118 + <div className="max-w-3xl mx-auto pb-20"> 119 + <header className="flex items-center gap-6 mb-8 p-6 bg-white rounded-2xl border border-surface-200 shadow-sm"> 120 + <a 121 + href={bskyProfileUrl} 122 + target="_blank" 123 + rel="noopener noreferrer" 124 + className="shrink-0 hover:opacity-80 transition-opacity" 125 + > 126 + {avatarUrl ? ( 127 + <img src={avatarUrl} alt={displayName} className="w-20 h-20 rounded-full object-cover border-4 border-surface-50" /> 128 + ) : ( 129 + <div className="w-20 h-20 rounded-full bg-surface-100 flex items-center justify-center text-2xl font-bold text-surface-500 border-4 border-surface-50"> 130 + {getInitial()} 131 + </div> 132 + )} 133 + </a> 134 + <div className="flex-1"> 135 + <h1 className="text-2xl font-bold text-surface-900 mb-1">{displayName}</h1> 136 + {displayHandle && ( 137 + <a 138 + href={bskyProfileUrl} 139 + target="_blank" 140 + rel="noopener noreferrer" 141 + className="text-surface-500 hover:text-primary-600 transition-colors bg-surface-50 hover:bg-primary-50 px-2 py-1 rounded-md text-sm inline-flex items-center gap-1" 142 + > 143 + @{displayHandle} <ExternalLink size={12} /> 144 + </a> 145 + )} 146 + </div> 147 + </header> 148 + 149 + <div className="mb-8 p-4 bg-surface-50 border border-surface-200 rounded-xl flex flex-col sm:flex-row sm:items-center gap-4"> 150 + <span className="text-sm font-semibold text-surface-500 uppercase tracking-wide">Annotations on:</span> 151 + <a 152 + href={decodeURIComponent(targetUrl)} 153 + target="_blank" 154 + rel="noopener noreferrer" 155 + className="text-primary-600 hover:text-primary-700 hover:underline font-medium truncate flex-1 block" 156 + > 157 + {decodeURIComponent(targetUrl)} 158 + </a> 159 + </div> 160 + 161 + {loading && ( 162 + <div className="flex justify-center py-12"> 163 + <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div> 164 + </div> 165 + )} 166 + 167 + {error && ( 168 + <div className="mb-8 bg-red-50 text-red-600 p-4 rounded-xl flex items-start gap-3 border border-red-100"> 169 + <AlertTriangle className="shrink-0 mt-0.5" size={18} /> 170 + <p>{error}</p> 171 + </div> 172 + )} 173 + 174 + {!loading && !error && totalItems === 0 && ( 175 + <div className="text-center py-16 bg-surface-50 rounded-2xl border border-dashed border-surface-200"> 176 + <div className="w-12 h-12 bg-surface-100 rounded-full flex items-center justify-center mx-auto mb-4 text-surface-400"> 177 + <PenTool size={24} /> 178 + </div> 179 + <h3 className="text-lg font-bold text-surface-900 mb-1">No items found</h3> 180 + <p className="text-surface-500"> 181 + {displayName} hasn&apos;t annotated this page yet. 182 + </p> 183 + </div> 184 + )} 185 + 186 + {!loading && !error && totalItems > 0 && ( 187 + <div className="animate-fade-in"> 188 + <div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6"> 189 + <h2 className="text-xl font-bold text-surface-900"> 190 + {totalItems} item{totalItems !== 1 ? "s" : ""} 191 + </h2> 192 + <div className="flex bg-surface-100 p-1 rounded-xl self-start md:self-auto"> 193 + <button 194 + className={clsx("px-4 py-1.5 rounded-lg text-sm font-medium transition-all", activeTab === 'all' ? "bg-white text-surface-900 shadow-sm" : "text-surface-500 hover:text-surface-700")} 195 + onClick={() => setActiveTab('all')} 196 + > 197 + All ({totalItems}) 198 + </button> 199 + <button 200 + className={clsx("px-4 py-1.5 rounded-lg text-sm font-medium transition-all", activeTab === 'annotations' ? "bg-white text-surface-900 shadow-sm" : "text-surface-500 hover:text-surface-700")} 201 + onClick={() => setActiveTab('annotations')} 202 + > 203 + Annotations ({annotations.length}) 204 + </button> 205 + <button 206 + className={clsx("px-4 py-1.5 rounded-lg text-sm font-medium transition-all", activeTab === 'highlights' ? "bg-white text-surface-900 shadow-sm" : "text-surface-500 hover:text-surface-700")} 207 + onClick={() => setActiveTab('highlights')} 208 + > 209 + Highlights ({highlights.length}) 210 + </button> 211 + </div> 212 + </div> 213 + <div className="space-y-6"> 214 + {renderResults()} 215 + </div> 216 + </div> 217 + )} 218 + </div> 219 + ); 220 + }
+60
web/tailwind.config.mjs
··· 1 + 2 + /** @type {import('tailwindcss').Config} */ 3 + import defaultTheme from 'tailwindcss/defaultTheme'; 4 + 5 + export default { 6 + darkMode: ['selector', '[data-theme="dark"]'], 7 + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], 8 + theme: { 9 + extend: { 10 + fontFamily: { 11 + sans: ['Inter', ...defaultTheme.fontFamily.sans], 12 + display: ['Outfit', ...defaultTheme.fontFamily.sans], 13 + }, 14 + colors: { 15 + primary: { 16 + 50: '#f0f7ff', 17 + 100: '#e0effe', 18 + 200: '#bae0fd', 19 + 300: '#7cc2fc', 20 + 400: '#36a2fa', 21 + 500: '#0083f5', 22 + 600: '#0066d6', 23 + 700: '#0051ab', 24 + 800: '#00458d', 25 + 900: '#063a70', 26 + 950: '#04254d', 27 + }, 28 + surface: { 29 + 50: '#fafafa', 30 + 100: '#f4f4f5', 31 + 200: '#e4e4e7', 32 + 300: '#d4d4d8', 33 + 400: '#a1a1aa', 34 + 500: '#71717a', 35 + 600: '#52525b', 36 + 700: '#3f3f46', 37 + 800: '#27272a', 38 + 900: '#18181b', 39 + 950: '#09090b', 40 + } 41 + }, 42 + animation: { 43 + 'fade-in': 'fadeIn 0.3s ease-out', 44 + }, 45 + keyframes: { 46 + fadeIn: { 47 + '0%': { opacity: '0' }, 48 + '100%': { opacity: '1' }, 49 + } 50 + }, 51 + boxShadow: { 52 + 'sm': '0 1px 2px 0 rgb(0 0 0 / 0.05)', 53 + 'DEFAULT': '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)', 54 + 'md': '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', 55 + 'lg': '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)', 56 + } 57 + }, 58 + }, 59 + plugins: [], 60 + }
+14
web/tsconfig.json
··· 1 + { 2 + "extends": "astro/tsconfigs/strict", 3 + "include": [ 4 + ".astro/types.d.ts", 5 + "**/*" 6 + ], 7 + "exclude": [ 8 + "dist" 9 + ], 10 + "compilerOptions": { 11 + "jsx": "react-jsx", 12 + "jsxImportSource": "react" 13 + } 14 + }