chore: added vue 85bde091
Steve · 2025-10-18 12:54 19 file(s) · +1174 −3
examples/vite-vue/.gitignore (added) +24 −0
1 +
# Logs
2 +
logs
3 +
*.log
4 +
npm-debug.log*
5 +
yarn-debug.log*
6 +
yarn-error.log*
7 +
pnpm-debug.log*
8 +
lerna-debug.log*
9 +
10 +
node_modules
11 +
dist
12 +
dist-ssr
13 +
*.local
14 +
15 +
# Editor directories and files
16 +
.vscode/*
17 +
!.vscode/extensions.json
18 +
.idea
19 +
.DS_Store
20 +
*.suo
21 +
*.ntvs*
22 +
*.njsproj
23 +
*.sln
24 +
*.sw?
examples/vite-vue/README.md (added) +5 −0
1 +
# Vue 3 + TypeScript + Vite
2 +
3 +
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
4 +
5 +
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
examples/vite-vue/bun.lock (added) +208 −0
1 +
{
2 +
  "lockfileVersion": 1,
3 +
  "workspaces": {
4 +
    "": {
5 +
      "name": "vite-vue",
6 +
      "dependencies": {
7 +
        "vue": "^3.5.22",
8 +
      },
9 +
      "devDependencies": {
10 +
        "@types/node": "^24.6.0",
11 +
        "@vitejs/plugin-vue": "^6.0.1",
12 +
        "@vue/tsconfig": "^0.8.1",
13 +
        "typescript": "~5.9.3",
14 +
        "vite": "^7.1.7",
15 +
        "vue-tsc": "^3.1.0",
16 +
      },
17 +
    },
18 +
  },
19 +
  "packages": {
20 +
    "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
21 +
22 +
    "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
23 +
24 +
    "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="],
25 +
26 +
    "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
27 +
28 +
    "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="],
29 +
30 +
    "@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="],
31 +
32 +
    "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="],
33 +
34 +
    "@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="],
35 +
36 +
    "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="],
37 +
38 +
    "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="],
39 +
40 +
    "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="],
41 +
42 +
    "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="],
43 +
44 +
    "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="],
45 +
46 +
    "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="],
47 +
48 +
    "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="],
49 +
50 +
    "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="],
51 +
52 +
    "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="],
53 +
54 +
    "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="],
55 +
56 +
    "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="],
57 +
58 +
    "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="],
59 +
60 +
    "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="],
61 +
62 +
    "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="],
63 +
64 +
    "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="],
65 +
66 +
    "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="],
67 +
68 +
    "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="],
69 +
70 +
    "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="],
71 +
72 +
    "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="],
73 +
74 +
    "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="],
75 +
76 +
    "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="],
77 +
78 +
    "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="],
79 +
80 +
    "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
81 +
82 +
    "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.29", "", {}, "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q=="],
83 +
84 +
    "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
85 +
86 +
    "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="],
87 +
88 +
    "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="],
89 +
90 +
    "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="],
91 +
92 +
    "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="],
93 +
94 +
    "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="],
95 +
96 +
    "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="],
97 +
98 +
    "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="],
99 +
100 +
    "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="],
101 +
102 +
    "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="],
103 +
104 +
    "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="],
105 +
106 +
    "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="],
107 +
108 +
    "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="],
109 +
110 +
    "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="],
111 +
112 +
    "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="],
113 +
114 +
    "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="],
115 +
116 +
    "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="],
117 +
118 +
    "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="],
119 +
120 +
    "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="],
121 +
122 +
    "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="],
123 +
124 +
    "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="],
125 +
126 +
    "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
127 +
128 +
    "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
129 +
130 +
    "@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="],
131 +
132 +
    "@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.1", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-beta.29" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", "vue": "^3.2.25" } }, "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw=="],
133 +
134 +
    "@volar/language-core": ["@volar/language-core@2.4.23", "", { "dependencies": { "@volar/source-map": "2.4.23" } }, "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ=="],
135 +
136 +
    "@volar/source-map": ["@volar/source-map@2.4.23", "", {}, "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q=="],
137 +
138 +
    "@volar/typescript": ["@volar/typescript@2.4.23", "", { "dependencies": { "@volar/language-core": "2.4.23", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag=="],
139 +
140 +
    "@vue/compiler-core": ["@vue/compiler-core@3.5.22", "", { "dependencies": { "@babel/parser": "^7.28.4", "@vue/shared": "3.5.22", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ=="],
141 +
142 +
    "@vue/compiler-dom": ["@vue/compiler-dom@3.5.22", "", { "dependencies": { "@vue/compiler-core": "3.5.22", "@vue/shared": "3.5.22" } }, "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA=="],
143 +
144 +
    "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.22", "", { "dependencies": { "@babel/parser": "^7.28.4", "@vue/compiler-core": "3.5.22", "@vue/compiler-dom": "3.5.22", "@vue/compiler-ssr": "3.5.22", "@vue/shared": "3.5.22", "estree-walker": "^2.0.2", "magic-string": "^0.30.19", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ=="],
145 +
146 +
    "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.22", "", { "dependencies": { "@vue/compiler-dom": "3.5.22", "@vue/shared": "3.5.22" } }, "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww=="],
147 +
148 +
    "@vue/language-core": ["@vue/language-core@3.1.1", "", { "dependencies": { "@volar/language-core": "2.4.23", "@vue/compiler-dom": "^3.5.0", "@vue/shared": "^3.5.0", "alien-signals": "^3.0.0", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-qjMY3Q+hUCjdH+jLrQapqgpsJ0rd/2mAY02lZoHG3VFJZZZKLjAlV+Oo9QmWIT4jh8+Rx8RUGUi++d7T9Wb6Mw=="],
149 +
150 +
    "@vue/reactivity": ["@vue/reactivity@3.5.22", "", { "dependencies": { "@vue/shared": "3.5.22" } }, "sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A=="],
151 +
152 +
    "@vue/runtime-core": ["@vue/runtime-core@3.5.22", "", { "dependencies": { "@vue/reactivity": "3.5.22", "@vue/shared": "3.5.22" } }, "sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ=="],
153 +
154 +
    "@vue/runtime-dom": ["@vue/runtime-dom@3.5.22", "", { "dependencies": { "@vue/reactivity": "3.5.22", "@vue/runtime-core": "3.5.22", "@vue/shared": "3.5.22", "csstype": "^3.1.3" } }, "sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww=="],
155 +
156 +
    "@vue/server-renderer": ["@vue/server-renderer@3.5.22", "", { "dependencies": { "@vue/compiler-ssr": "3.5.22", "@vue/shared": "3.5.22" }, "peerDependencies": { "vue": "3.5.22" } }, "sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ=="],
157 +
158 +
    "@vue/shared": ["@vue/shared@3.5.22", "", {}, "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w=="],
159 +
160 +
    "@vue/tsconfig": ["@vue/tsconfig@0.8.1", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g=="],
161 +
162 +
    "alien-signals": ["alien-signals@3.0.1", "", {}, "sha512-ec02Wv5iOg7yG979PH9ykv5KN/KHznOxMlKy/Jr8lnBo3T94d4MUGo7FVdM8B2fM0e94twzEcWCyWzfIyeV19g=="],
163 +
164 +
    "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
165 +
166 +
    "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
167 +
168 +
    "esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="],
169 +
170 +
    "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
171 +
172 +
    "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
173 +
174 +
    "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
175 +
176 +
    "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
177 +
178 +
    "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="],
179 +
180 +
    "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
181 +
182 +
    "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
183 +
184 +
    "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
185 +
186 +
    "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
187 +
188 +
    "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=="],
189 +
190 +
    "rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
191 +
192 +
    "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
193 +
194 +
    "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
195 +
196 +
    "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
197 +
198 +
    "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="],
199 +
200 +
    "vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "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-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="],
201 +
202 +
    "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
203 +
204 +
    "vue": ["vue@3.5.22", "", { "dependencies": { "@vue/compiler-dom": "3.5.22", "@vue/compiler-sfc": "3.5.22", "@vue/runtime-dom": "3.5.22", "@vue/server-renderer": "3.5.22", "@vue/shared": "3.5.22" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ=="],
205 +
206 +
    "vue-tsc": ["vue-tsc@3.1.1", "", { "dependencies": { "@volar/typescript": "2.4.23", "@vue/language-core": "3.1.1" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-fyixKxFniOVgn+L/4+g8zCG6dflLLt01Agz9jl3TO45Bgk87NZJRmJVPsiK+ouq3LB91jJCbOV+pDkzYTxbI7A=="],
207 +
  }
208 +
}
examples/vite-vue/index.html (added) +13 −0
1 +
<!doctype html>
2 +
<html lang="en">
3 +
  <head>
4 +
    <meta charset="UTF-8" />
5 +
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6 +
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 +
    <title>vite-vue</title>
8 +
  </head>
9 +
  <body>
10 +
    <div id="app"></div>
11 +
    <script type="module" src="/src/main.ts"></script>
12 +
  </body>
13 +
</html>
examples/vite-vue/norns.json (added) +5 −0
1 +
{
2 +
  "components": "src/components",
3 +
  "includeTypes": true,
4 +
  "framework": "vue"
5 +
}
examples/vite-vue/package.json (added) +22 −0
1 +
{
2 +
  "name": "vite-vue",
3 +
  "private": true,
4 +
  "version": "0.0.0",
5 +
  "type": "module",
6 +
  "scripts": {
7 +
    "dev": "vite",
8 +
    "build": "vue-tsc -b && vite build",
9 +
    "preview": "vite preview"
10 +
  },
11 +
  "dependencies": {
12 +
    "vue": "^3.5.22"
13 +
  },
14 +
  "devDependencies": {
15 +
    "@types/node": "^24.6.0",
16 +
    "@vitejs/plugin-vue": "^6.0.1",
17 +
    "@vue/tsconfig": "^0.8.1",
18 +
    "typescript": "~5.9.3",
19 +
    "vite": "^7.1.7",
20 +
    "vue-tsc": "^3.1.0"
21 +
  }
22 +
}
examples/vite-vue/public/vite.svg (added) +1 −0
1 +
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
examples/vite-vue/src/App.vue (added) +32 −0
1 +
<script setup lang="ts">
2 +
import './components/connect-wallet'
3 +
</script>
4 +
5 +
<template>
6 +
  <div>
7 +
    <a href="https://vite.dev" target="_blank">
8 +
      <img src="/vite.svg" class="logo" alt="Vite logo" />
9 +
    </a>
10 +
    <a href="https://vuejs.org/" target="_blank">
11 +
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
12 +
    </a>
13 +
  </div>
14 +
  <connect-wallet
15 +
    chain-id='11155111'
16 +
  ></connect-wallet>
17 +
</template>
18 +
19 +
<style scoped>
20 +
.logo {
21 +
  height: 6em;
22 +
  padding: 1.5em;
23 +
  will-change: filter;
24 +
  transition: filter 300ms;
25 +
}
26 +
.logo:hover {
27 +
  filter: drop-shadow(0 0 2em #646cffaa);
28 +
}
29 +
.logo.vue:hover {
30 +
  filter: drop-shadow(0 0 2em #42b883aa);
31 +
}
32 +
</style>
examples/vite-vue/src/assets/vue.svg (added) +1 −0
1 +
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
examples/vite-vue/src/components/connect-wallet.js (added) +632 −0
1 +
/**
2 +
 * @fileoverview ConnectWallet Web Component - A customizable wallet connection component
3 +
 * that supports Ethereum wallet integration with ENS resolution, balance display, and
4 +
 * multi-chain support.
5 +
 *
6 +
 */
7 +
8 +
/**
9 +
 * ConnectWallet - A Web Component for Ethereum wallet connection and management
10 +
 *
11 +
 * This component provides a complete wallet connection interface with the following features:
12 +
 * - Connect/disconnect wallet functionality
13 +
 * - ENS name resolution and avatar display
14 +
 * - Balance fetching and display
15 +
 * - Multi-chain support with automatic switching
16 +
 * - Customizable styling through attributes
17 +
 * - Popover interface for wallet management
18 +
 *
19 +
 * @class ConnectWallet
20 +
 * @extends HTMLElement
21 +
 *
22 +
 * @example
23 +
 * // Basic usage
24 +
 * <connect-wallet></connect-wallet>
25 +
 *
26 +
 * @example
27 +
 * // With custom styling and chain
28 +
 * <connect-wallet
29 +
 *   chain-id="0x89"
30 +
 *   primary="#4F46E5"
31 +
 *   background="#1F2937"
32 +
 *   border-radius="8px">
33 +
 * </connect-wallet>
34 +
 *
35 +
 * @fires ConnectWallet#wallet-connected - Fired when wallet is successfully connected
36 +
 * @fires ConnectWallet#wallet-disconnected - Fired when wallet is disconnected
37 +
 * @fires ConnectWallet#wallet-error - Fired when wallet connection fails
38 +
 */
39 +
40 +
class ConnectWallet extends HTMLElement {
41 +
	// Constructor and lifecycle methods
42 +
	constructor() {
43 +
		super();
44 +
		this.attachShadow({ mode: "open" });
45 +
		this.connected = false;
46 +
		this.address = "";
47 +
		this.ensData = null;
48 +
		this.loading = false;
49 +
		this.chainId = "0x1";
50 +
		this.currentChainId = null;
51 +
		this.showPopover = false;
52 +
		this.balance = "0";
53 +
		this.copySuccess = false;
54 +
55 +
		// React-friendly callback properties
56 +
		this.onWalletConnected = null;
57 +
		this.onWalletDisconnected = null;
58 +
		this.onWalletError = null;
59 +
	}
60 +
61 +
	static get observedAttributes() {
62 +
		return [
63 +
			"chain-id",
64 +
			"background",
65 +
			"foreground",
66 +
			"primary",
67 +
			"secondary",
68 +
			"border-radius",
69 +
		];
70 +
	}
71 +
72 +
	attributeChangedCallback(name, oldValue, newValue) {
73 +
		if (name === "chain-id" && oldValue !== newValue) {
74 +
			this.chainId = this.normalizeChainId(newValue);
75 +
			if (this.connected) {
76 +
				this.checkAndSwitchChain();
77 +
			}
78 +
		} else if (
79 +
			[
80 +
				"background",
81 +
				"foreground",
82 +
				"primary",
83 +
				"secondary",
84 +
				"border-radius",
85 +
			].includes(name) &&
86 +
			oldValue !== newValue
87 +
		) {
88 +
			this.render();
89 +
		}
90 +
	}
91 +
92 +
	connectedCallback() {
93 +
		const chainIdAttr = this.getAttribute("chain-id");
94 +
		this.chainId = this.normalizeChainId(chainIdAttr);
95 +
		this.render();
96 +
	}
97 +
98 +
	// Wallet connection methods
99 +
	async connect() {
100 +
		if (window.ethereum) {
101 +
			try {
102 +
				this.loading = true;
103 +
				this.render();
104 +
105 +
				const accounts = await window.ethereum.request({
106 +
					method: "eth_requestAccounts",
107 +
				});
108 +
109 +
				this.address = accounts[0];
110 +
111 +
				this.currentChainId = await window.ethereum.request({
112 +
					method: "eth_chainId",
113 +
				});
114 +
115 +
				if (this.chainId && this.chainId !== this.currentChainId) {
116 +
					await this.switchChain(this.chainId);
117 +
				}
118 +
119 +
				this.connected = true;
120 +
121 +
				await Promise.all([this.fetchEnsData(), this.fetchBalance()]);
122 +
123 +
				this.loading = false;
124 +
				this.render();
125 +
126 +
				const eventDetail = {
127 +
					address: this.address,
128 +
					ensData: this.ensData,
129 +
					chainId: this.currentChainId,
130 +
				};
131 +
132 +
				// Dispatch custom event for vanilla JS and other frameworks
133 +
				this.dispatchEvent(
134 +
					new CustomEvent("wallet-connected", {
135 +
						detail: eventDetail,
136 +
					}),
137 +
				);
138 +
139 +
				// Call callback property if set (React-friendly)
140 +
				if (typeof this.onWalletConnected === "function") {
141 +
					this.onWalletConnected(eventDetail);
142 +
				}
143 +
			} catch (error) {
144 +
				console.error("Connection failed", error);
145 +
				this.loading = false;
146 +
				this.render();
147 +
148 +
				const errorDetail = { error: error.message };
149 +
150 +
				// Dispatch custom event for vanilla JS and other frameworks
151 +
				this.dispatchEvent(
152 +
					new CustomEvent("wallet-error", {
153 +
						detail: errorDetail,
154 +
					}),
155 +
				);
156 +
157 +
				// Call callback property if set (React-friendly)
158 +
				if (typeof this.onWalletError === "function") {
159 +
					this.onWalletError(errorDetail);
160 +
				}
161 +
			}
162 +
		} else {
163 +
			alert("Please install a wallet extension like MetaMask");
164 +
		}
165 +
	}
166 +
167 +
	disconnect() {
168 +
		this.connected = false;
169 +
		this.address = "";
170 +
		this.ensData = null;
171 +
		this.currentChainId = null;
172 +
		this.balance = "0";
173 +
		this.showPopover = false;
174 +
		this.copySuccess = false;
175 +
		this.render();
176 +
177 +
		// Dispatch custom event for vanilla JS and other frameworks
178 +
		this.dispatchEvent(new CustomEvent("wallet-disconnected"));
179 +
180 +
		// Call callback property if set (React-friendly)
181 +
		if (typeof this.onWalletDisconnected === "function") {
182 +
			this.onWalletDisconnected();
183 +
		}
184 +
	}
185 +
186 +
	// Chain management methods
187 +
	/**
188 +
	 * Converts a numeric chain ID to hex format
189 +
	 * @param {string|number} chainId - Chain ID in numeric or hex format
190 +
	 * @returns {string} Chain ID in hex format (e.g., "0x2105")
191 +
	 */
192 +
	normalizeChainId(chainId) {
193 +
		if (!chainId) return "0x1";
194 +
195 +
		const chainIdStr = String(chainId);
196 +
197 +
		// If it's already in hex format (starts with 0x), return as-is
198 +
		if (chainIdStr.startsWith("0x")) {
199 +
			return chainIdStr.toLowerCase();
200 +
		}
201 +
202 +
		// Convert numeric string to hex
203 +
		const numericChainId = parseInt(chainIdStr, 10);
204 +
		if (isNaN(numericChainId)) {
205 +
			console.warn(`Invalid chain ID: ${chainId}, defaulting to 0x1`);
206 +
			return "0x1";
207 +
		}
208 +
209 +
		return `0x${numericChainId.toString(16)}`;
210 +
	}
211 +
212 +
	async switchChain(chainId) {
213 +
		try {
214 +
			await window.ethereum.request({
215 +
				method: "wallet_switchEthereumChain",
216 +
				params: [{ chainId }],
217 +
			});
218 +
			this.currentChainId = chainId;
219 +
		} catch (switchError) {
220 +
			throw new Error(`Failed to switch chain: ${switchError.message}`);
221 +
		}
222 +
	}
223 +
224 +
	async checkAndSwitchChain() {
225 +
		if (window.ethereum && this.chainId && this.connected) {
226 +
			const currentChain = await window.ethereum.request({
227 +
				method: "eth_chainId",
228 +
			});
229 +
230 +
			if (currentChain !== this.chainId) {
231 +
				try {
232 +
					await this.switchChain(this.chainId);
233 +
					this.render();
234 +
				} catch (error) {
235 +
					console.error("Failed to switch chain:", error);
236 +
				}
237 +
			}
238 +
		}
239 +
	}
240 +
241 +
	getChainName(chainId) {
242 +
		const chainNames = {
243 +
			"0x1": "Ethereum",
244 +
			"0x89": "Polygon",
245 +
			"0xa": "Optimism",
246 +
			"0xa4b1": "Arbitrum",
247 +
			"0x2105": "Base",
248 +
		};
249 +
		return chainNames[chainId] || `Chain ${chainId}`;
250 +
	}
251 +
252 +
	// Data fetching methods
253 +
	async fetchEnsData() {
254 +
		try {
255 +
			const response = await fetch(`https://api.ensdata.net/${this.address}`);
256 +
			if (response.ok) {
257 +
				this.ensData = await response.json();
258 +
				console.log("ENS data loaded:", this.ensData);
259 +
			} else {
260 +
				console.log("No ENS data found for this address");
261 +
				this.ensData = null;
262 +
			}
263 +
		} catch (error) {
264 +
			console.error("Failed to fetch ENS data", error);
265 +
			this.ensData = null;
266 +
		}
267 +
	}
268 +
269 +
	async fetchBalance() {
270 +
		try {
271 +
			const balanceWei = await window.ethereum.request({
272 +
				method: "eth_getBalance",
273 +
				params: [this.address, "latest"],
274 +
			});
275 +
276 +
			const balanceEth = parseInt(balanceWei, 16) / Math.pow(10, 18);
277 +
			this.balance = balanceEth.toFixed(4);
278 +
		} catch (error) {
279 +
			console.error("Failed to fetch balance", error);
280 +
			this.balance = "0";
281 +
		}
282 +
	}
283 +
284 +
	// UI helper methods
285 +
	getDisplayName() {
286 +
		if (this.ensData?.ens_primary) {
287 +
			return this.ensData.ens_primary;
288 +
		}
289 +
		return this.truncateAddress(this.address);
290 +
	}
291 +
292 +
	truncateAddress(addr) {
293 +
		if (!addr) return "";
294 +
		return addr.slice(0, 5) + "..." + addr.slice(-5);
295 +
	}
296 +
297 +
	async copyAddress() {
298 +
		try {
299 +
			await navigator.clipboard.writeText(this.address);
300 +
			this.copySuccess = true;
301 +
			this.showPopoverElement();
302 +
303 +
			setTimeout(() => {
304 +
				this.copySuccess = false;
305 +
				this.showPopoverElement();
306 +
			}, 1000);
307 +
		} catch (error) {
308 +
			console.error("Failed to copy address", error);
309 +
		}
310 +
	}
311 +
312 +
	// Popover management methods
313 +
	togglePopover() {
314 +
		this.showPopover = !this.showPopover;
315 +
		if (this.showPopover) {
316 +
			this.showPopoverElement();
317 +
		} else {
318 +
			this.hidePopoverElement();
319 +
		}
320 +
	}
321 +
322 +
	hidePopover() {
323 +
		if (this.showPopover) {
324 +
			this.showPopover = false;
325 +
			this.hidePopoverElement();
326 +
		}
327 +
	}
328 +
329 +
	showPopoverElement() {
330 +
		const profileContainer =
331 +
			this.shadowRoot.querySelector(".profile-container");
332 +
		if (!profileContainer) return;
333 +
334 +
		const existingPopover = profileContainer.querySelector(".popover");
335 +
		if (existingPopover) {
336 +
			existingPopover.remove();
337 +
		}
338 +
339 +
		const popover = document.createElement("div");
340 +
		popover.className = "popover";
341 +
342 +
		const copyIcon = this.copySuccess
343 +
			? `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`
344 +
			: `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 2V1H10V2H5ZM4.75 0C4.33579 0 4 0.335786 4 0.75V1H3.5C2.67157 1 2 1.67157 2 2.5V12.5C2 13.3284 2.67157 14 3.5 14H7V13H3.5C3.22386 13 3 12.7761 3 12.5V2.5C3 2.22386 3.22386 2 3.5 2H4V2.25C4 2.66421 4.33579 3 4.75 3H10.25C10.6642 3 11 2.66421 11 2.25V2H11.5C11.7761 2 12 2.22386 12 2.5V7H13V2.5C13 1.67157 12.3284 1 11.5 1H11V0.75C11 0.335786 10.6642 0 10.25 0H4.75ZM9 8.5C9 8.77614 8.77614 9 8.5 9C8.22386 9 8 8.77614 8 8.5C8 8.22386 8.22386 8 8.5 8C8.77614 8 9 8.22386 9 8.5ZM10.5 9C10.7761 9 11 8.77614 11 8.5C11 8.22386 10.7761 8 10.5 8C10.2239 8 10 8.22386 10 8.5C10 8.77614 10.2239 9 10.5 9ZM13 8.5C13 8.77614 12.7761 9 12.5 9C12.2239 9 12 8.77614 12 8.5C12 8.22386 12.2239 8 12.5 8C12.7761 8 13 8.22386 13 8.5ZM14.5 9C14.7761 9 15 8.77614 15 8.5C15 8.22386 14.7761 8 14.5 8C14.2239 8 14 8.22386 14 8.5C14 8.77614 14.2239 9 14.5 9ZM15 10.5C15 10.7761 14.7761 11 14.5 11C14.2239 11 14 10.7761 14 10.5C14 10.2239 14.2239 10 14.5 10C14.7761 10 15 10.2239 15 10.5ZM14.5 13C14.7761 13 15 12.7761 15 12.5C15 12.2239 14.7761 12 14.5 12C14.2239 12 14 12.2239 14 12.5C14 12.7761 14.2239 13 14.5 13ZM14.5 15C14.7761 15 15 14.7761 15 14.5C15 14.2239 14.7761 14 14.5 14C14.2239 14 14 14.2239 14 14.5C14 14.7761 14.2239 15 14.5 15ZM8.5 11C8.77614 11 9 10.7761 9 10.5C9 10.2239 8.77614 10 8.5 10C8.22386 10 8 10.2239 8 10.5C8 10.7761 8.22386 11 8.5 11ZM9 12.5C9 12.7761 8.77614 13 8.5 13C8.22386 13 8 12.7761 8 12.5C8 12.2239 8.22386 12 8.5 12C8.77614 12 9 12.2239 9 12.5ZM8.5 15C8.77614 15 9 14.7761 9 14.5C9 14.2239 8.77614 14 8.5 14C8.22386 14 8 14.2239 8 14.5C8 14.7761 8.22386 15 8.5 15ZM11 14.5C11 14.7761 10.7761 15 10.5 15C10.2239 15 10 14.7761 10 14.5C10 14.2239 10.2239 14 10.5 14C10.7761 14 11 14.2239 11 14.5ZM12.5 15C12.7761 15 13 14.7761 13 14.5C13 14.2239 12.7761 14 12.5 14C12.2239 14 12 14.2239 12 14.5C12 12.7761 12.2239 15 12.5 15Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`;
345 +
346 +
		const copyText = this.copySuccess ? "Copied!" : "Copy Address";
347 +
		popover.innerHTML = `
348 +
			<button class="popover-button copy-button">
349 +
				<span>${copyIcon}</span>
350 +
				${copyText}
351 +
			</button>
352 +
			<button class="popover-button disconnect-button">
353 +
				<span><svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 1C2.44771 1 2 1.44772 2 2V13C2 13.5523 2.44772 14 3 14H10.5C10.7761 14 11 13.7761 11 13.5C11 13.2239 10.7761 13 10.5 13H3V2L10.5 2C10.7761 2 11 1.77614 11 1.5C11 1.22386 10.7761 1 10.5 1H3ZM12.6036 4.89645C12.4083 4.70118 12.0917 4.70118 11.8964 4.89645C11.7012 5.09171 11.7012 5.40829 11.8964 5.60355L13.2929 7H6.5C6.22386 7 6 7.22386 6 7.5C6 7.77614 6.22386 8 6.5 8H13.2929L11.8964 9.39645C11.7012 9.59171 11.7012 9.90829 11.8964 10.1036C12.0917 10.2988 12.4083 10.2988 12.6036 10.1036L14.8536 7.85355C15.0488 7.65829 15.0488 7.34171 14.8536 7.14645L12.6036 4.89645Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></span>
354 +
				Disconnect
355 +
			</button>
356 +
		`;
357 +
358 +
		popover.querySelector(".copy-button").addEventListener("click", (e) => {
359 +
			e.stopPropagation();
360 +
			this.copyAddress();
361 +
		});
362 +
363 +
		popover
364 +
			.querySelector(".disconnect-button")
365 +
			.addEventListener("click", (e) => {
366 +
				e.stopPropagation();
367 +
				this.disconnect();
368 +
			});
369 +
370 +
		profileContainer.appendChild(popover);
371 +
372 +
		setTimeout(() => {
373 +
			document.addEventListener("click", this.hidePopover.bind(this), {
374 +
				once: true,
375 +
			});
376 +
		}, 0);
377 +
	}
378 +
379 +
	hidePopoverElement() {
380 +
		const profileContainer =
381 +
			this.shadowRoot.querySelector(".profile-container");
382 +
		if (!profileContainer) return;
383 +
384 +
		const popover = profileContainer.querySelector(".popover");
385 +
		if (popover) {
386 +
			popover.remove();
387 +
		}
388 +
	}
389 +
390 +
	// Color helper methods
391 +
	getCSSVariable(name, defaultValue) {
392 +
		return this.getAttribute(name) || defaultValue;
393 +
	}
394 +
395 +
	// Render methods and styling
396 +
	render() {
397 +
		const background = this.getCSSVariable("background", "#232323");
398 +
		const foreground = this.getCSSVariable("foreground", "#ffffff");
399 +
		const primary = this.getCSSVariable("primary", "#5F8787");
400 +
		const secondary = this.getCSSVariable("secondary", "#6F9797");
401 +
		const borderRadius = this.getCSSVariable("border-radius", "4px");
402 +
403 +
		this.shadowRoot.innerHTML = `
404 +
			<style>
405 +
				:host {
406 +
					--color-background: ${background};
407 +
					--color-foreground: ${foreground};
408 +
					--color-primary: ${primary};
409 +
					--color-secondary: ${secondary};
410 +
					--border-radius: ${borderRadius};
411 +
					--bg-color: ${this.connected ? "var(--color-background)" : "var(--color-primary)"};
412 +
					--bg-hover-color: ${this.connected ? "var(--color-background)" : "var(--color-secondary)"};
413 +
					display: inline-block;
414 +
				}
415 +
416 +
				button {
417 +
					padding: 10px 20px;
418 +
					background: var(--bg-color);
419 +
					color: var(--color-foreground);
420 +
					border: none;
421 +
					border-radius: var(--border-radius);
422 +
					cursor: pointer;
423 +
					font-size: 16px;
424 +
					transition: background-color 0.3s ease;
425 +
				}
426 +
427 +
				button:hover {
428 +
					background: var(--bg-hover-color);
429 +
				}
430 +
431 +
				button:disabled {
432 +
					opacity: 0.7;
433 +
					cursor: not-allowed;
434 +
				}
435 +
436 +
				.profile-container {
437 +
					position: relative;
438 +
					display: inline-block;
439 +
					font-family: sans-serif;
440 +
				}
441 +
442 +
				.profile {
443 +
					display: flex;
444 +
					align-items: center;
445 +
					gap: 8px;
446 +
					padding: 10px 20px;
447 +
					background: var(--bg-color);
448 +
					border-radius: var(--border-radius);
449 +
					color: var(--color-foreground);
450 +
					min-width: auto;
451 +
					transition: background-color 0.3s ease;
452 +
					cursor: pointer;
453 +
				}
454 +
455 +
				.profile:hover {
456 +
					background: var(--bg-hover-color);
457 +
				}
458 +
459 +
				.popover {
460 +
					position: absolute;
461 +
					top: 100%;
462 +
					left: 0;
463 +
					right: 0;
464 +
					background: var(--bg-color);
465 +
					border: 1px solid rgba(255, 255, 255, 0.1);
466 +
					border-radius: var(--border-radius);
467 +
					box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
468 +
					z-index: 1000;
469 +
					margin-top: 4px;
470 +
					overflow: hidden;
471 +
				}
472 +
473 +
				.popover-button {
474 +
					display: flex;
475 +
					align-items: center;
476 +
					gap: 8px;
477 +
					width: 100%;
478 +
					padding: 10px 16px;
479 +
					background: var(--bg-color);
480 +
					border: none;
481 +
					color: var(--color-foreground);
482 +
					font-size: 14px;
483 +
					cursor: pointer;
484 +
					transition: background-color 0.2s ease;
485 +
				}
486 +
487 +
				.popover-button:hover {
488 +
					background: var(--bg-hover-color);
489 +
				}
490 +
491 +
				.popover-button:not(:last-child) {
492 +
					border-bottom: 1px solid rgba(255, 255, 255, 0.1);
493 +
				}
494 +
495 +
				.popover-button span {
496 +
					font-size: 16px;
497 +
				}
498 +
499 +
				.avatar {
500 +
					width: 32px;
501 +
					height: 32px;
502 +
					border-radius: 50%;
503 +
					object-fit: cover;
504 +
				}
505 +
506 +
				.avatar-placeholder {
507 +
					width: 32px;
508 +
					height: 32px;
509 +
					border-radius: 50%;
510 +
					background: linear-gradient(45deg, var(--color-primary), var(--color-secondary));
511 +
					display: flex;
512 +
					align-items: center;
513 +
					justify-content: center;
514 +
					color: var(--color-foreground);
515 +
					font-weight: bold;
516 +
					font-size: 12px;
517 +
				}
518 +
519 +
				.profile-info {
520 +
					flex: 1;
521 +
					min-width: 0;
522 +
				}
523 +
524 +
				.profile-info h4 {
525 +
					margin: 0 0 2px 0;
526 +
					font-size: 14px;
527 +
					font-weight: 600;
528 +
					white-space: nowrap;
529 +
					overflow: hidden;
530 +
					text-overflow: ellipsis;
531 +
				}
532 +
533 +
				.profile-info p {
534 +
					margin: 0;
535 +
					font-size: 12px;
536 +
					opacity: 0.8;
537 +
					white-space: nowrap;
538 +
					overflow: hidden;
539 +
					text-overflow: ellipsis;
540 +
					font-family: monospace;
541 +
				}
542 +
543 +
				.loading {
544 +
					display: flex;
545 +
					align-items: center;
546 +
					gap: 8px;
547 +
				}
548 +
549 +
				.spinner {
550 +
					width: 16px;
551 +
					height: 16px;
552 +
					border: 2px solid rgba(255, 255, 255, 0.3);
553 +
					border-top: 2px solid var(--color-foreground);
554 +
					border-radius: 50%;
555 +
					animation: spin 1s linear infinite;
556 +
				}
557 +
558 +
				@keyframes spin {
559 +
					from { transform: rotate(0deg); }
560 +
					to { transform: rotate(360deg); }
561 +
				}
562 +
			</style>
563 +
		`;
564 +
565 +
		if (this.loading) {
566 +
			this.renderLoading();
567 +
		} else if (this.connected) {
568 +
			this.renderProfile();
569 +
		} else {
570 +
			this.renderConnectButton();
571 +
		}
572 +
	}
573 +
574 +
	renderProfile() {
575 +
		const profileContainer = document.createElement("div");
576 +
		profileContainer.className = "profile-container";
577 +
578 +
		const profileDiv = document.createElement("div");
579 +
		profileDiv.className = "profile";
580 +
581 +
		const avatar = this.ensData?.avatar_small;
582 +
		const displayName = this.getDisplayName();
583 +
584 +
		let avatarElement = "";
585 +
		if (avatar) {
586 +
			avatarElement = `<img src="${avatar}" alt="Avatar" class="avatar" onerror="this.style.display='none'">`;
587 +
		} else {
588 +
			avatarElement = `<div class="avatar-placeholder"></div>`;
589 +
		}
590 +
591 +
		profileDiv.innerHTML = `
592 +
			${avatarElement}
593 +
			<div class="profile-info">
594 +
				<h4>${displayName}</h4>
595 +
				<p>${this.balance} ETH</p>
596 +
			</div>
597 +
		`;
598 +
599 +
		profileDiv.addEventListener("click", (e) => {
600 +
			e.stopPropagation();
601 +
			this.togglePopover();
602 +
		});
603 +
604 +
		profileContainer.appendChild(profileDiv);
605 +
		this.shadowRoot.appendChild(profileContainer);
606 +
607 +
		if (this.showPopover) {
608 +
			this.showPopoverElement();
609 +
		}
610 +
	}
611 +
612 +
	renderLoading() {
613 +
		const button = document.createElement("button");
614 +
		button.disabled = true;
615 +
		button.innerHTML = `
616 +
			<div class="loading">
617 +
				<div class="spinner"></div>
618 +
				<span>Connecting...</span>
619 +
			</div>
620 +
		`;
621 +
		this.shadowRoot.appendChild(button);
622 +
	}
623 +
624 +
	renderConnectButton() {
625 +
		const button = document.createElement("button");
626 +
		button.textContent = "Connect Wallet";
627 +
		button.addEventListener("click", () => this.connect());
628 +
		this.shadowRoot.appendChild(button);
629 +
	}
630 +
}
631 +
632 +
customElements.define("connect-wallet", ConnectWallet);
examples/vite-vue/src/components/custom-elements-vue.ts (added) +37 −0
1 +
declare module 'vue' {
2 +
  export interface GlobalComponents {
3 +
    'contract-call': {
4 +
      'contract-address'?: string;
5 +
      'chain-id'?: string;
6 +
      'method-name'?: string;
7 +
      'method-args'?: string;
8 +
      'abi-url'?: string;
9 +
      'abi'?: string;
10 +
      'button-text'?: string;
11 +
      'background'?: string;
12 +
      'foreground'?: string;
13 +
      'primary'?: string;
14 +
      'secondary'?: string;
15 +
      'border-radius'?: string;
16 +
      'error-color'?: string;
17 +
      'success-color'?: string;
18 +
      'onAbiLoaded'?: (event: CustomEvent) => void;
19 +
      'onAbiError'?: (event: CustomEvent) => void;
20 +
      'onContractCallSuccess'?: (event: CustomEvent) => void;
21 +
      'onContractCallError'?: (event: CustomEvent) => void;
22 +
    };
23 +
    'connect-wallet': {
24 +
      'chain-id'?: string;
25 +
      'background'?: string;
26 +
      'foreground'?: string;
27 +
      'primary'?: string;
28 +
      'secondary'?: string;
29 +
      'border-radius'?: string;
30 +
      'onWalletConnected'?: (event: CustomEvent) => void;
31 +
      'onWalletError'?: (event: CustomEvent) => void;
32 +
      'onWalletDisconnected'?: (event: CustomEvent) => void;
33 +
    };
34 +
  }
35 +
}
36 +
37 +
export {};
examples/vite-vue/src/main.ts (added) +5 −0
1 +
import { createApp } from 'vue'
2 +
import './style.css'
3 +
import App from './App.vue'
4 +
5 +
createApp(App).mount('#app')
examples/vite-vue/src/style.css (added) +79 −0
1 +
:root {
2 +
  font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3 +
  line-height: 1.5;
4 +
  font-weight: 400;
5 +
6 +
  color-scheme: light dark;
7 +
  color: rgba(255, 255, 255, 0.87);
8 +
  background-color: #242424;
9 +
10 +
  font-synthesis: none;
11 +
  text-rendering: optimizeLegibility;
12 +
  -webkit-font-smoothing: antialiased;
13 +
  -moz-osx-font-smoothing: grayscale;
14 +
}
15 +
16 +
a {
17 +
  font-weight: 500;
18 +
  color: #646cff;
19 +
  text-decoration: inherit;
20 +
}
21 +
a:hover {
22 +
  color: #535bf2;
23 +
}
24 +
25 +
body {
26 +
  margin: 0;
27 +
  display: flex;
28 +
  place-items: center;
29 +
  min-width: 320px;
30 +
  min-height: 100vh;
31 +
}
32 +
33 +
h1 {
34 +
  font-size: 3.2em;
35 +
  line-height: 1.1;
36 +
}
37 +
38 +
button {
39 +
  border-radius: 8px;
40 +
  border: 1px solid transparent;
41 +
  padding: 0.6em 1.2em;
42 +
  font-size: 1em;
43 +
  font-weight: 500;
44 +
  font-family: inherit;
45 +
  background-color: #1a1a1a;
46 +
  cursor: pointer;
47 +
  transition: border-color 0.25s;
48 +
}
49 +
button:hover {
50 +
  border-color: #646cff;
51 +
}
52 +
button:focus,
53 +
button:focus-visible {
54 +
  outline: 4px auto -webkit-focus-ring-color;
55 +
}
56 +
57 +
.card {
58 +
  padding: 2em;
59 +
}
60 +
61 +
#app {
62 +
  max-width: 1280px;
63 +
  margin: 0 auto;
64 +
  padding: 2rem;
65 +
  text-align: center;
66 +
}
67 +
68 +
@media (prefers-color-scheme: light) {
69 +
  :root {
70 +
    color: #213547;
71 +
    background-color: #ffffff;
72 +
  }
73 +
  a:hover {
74 +
    color: #747bff;
75 +
  }
76 +
  button {
77 +
    background-color: #f9f9f9;
78 +
  }
79 +
}
examples/vite-vue/tsconfig.app.json (added) +16 −0
1 +
{
2 +
  "extends": "@vue/tsconfig/tsconfig.dom.json",
3 +
  "compilerOptions": {
4 +
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 +
    "types": ["vite/client"],
6 +
7 +
    /* Linting */
8 +
    "strict": true,
9 +
    "noUnusedLocals": true,
10 +
    "noUnusedParameters": true,
11 +
    "erasableSyntaxOnly": true,
12 +
    "noFallthroughCasesInSwitch": true,
13 +
    "noUncheckedSideEffectImports": true
14 +
  },
15 +
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
16 +
}
examples/vite-vue/tsconfig.json (added) +7 −0
1 +
{
2 +
  "files": [],
3 +
  "references": [
4 +
    { "path": "./tsconfig.app.json" },
5 +
    { "path": "./tsconfig.node.json" }
6 +
  ]
7 +
}
examples/vite-vue/tsconfig.node.json (added) +26 −0
1 +
{
2 +
  "compilerOptions": {
3 +
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4 +
    "target": "ES2023",
5 +
    "lib": ["ES2023"],
6 +
    "module": "ESNext",
7 +
    "types": ["node"],
8 +
    "skipLibCheck": true,
9 +
10 +
    /* Bundler mode */
11 +
    "moduleResolution": "bundler",
12 +
    "allowImportingTsExtensions": true,
13 +
    "verbatimModuleSyntax": true,
14 +
    "moduleDetection": "force",
15 +
    "noEmit": true,
16 +
17 +
    /* Linting */
18 +
    "strict": true,
19 +
    "noUnusedLocals": true,
20 +
    "noUnusedParameters": true,
21 +
    "erasableSyntaxOnly": true,
22 +
    "noFallthroughCasesInSwitch": true,
23 +
    "noUncheckedSideEffectImports": true
24 +
  },
25 +
  "include": ["vite.config.ts"]
26 +
}
examples/vite-vue/vite.config.ts (added) +7 −0
1 +
import { defineConfig } from 'vite'
2 +
import vue from '@vitejs/plugin-vue'
3 +
4 +
// https://vite.dev/config/
5 +
export default defineConfig({
6 +
  plugins: [vue()],
7 +
})
scripts/build-types.ts +44 −0
7 7
const OUTPUT_DIR = "dist";
8 8
const REACT_OUTPUT = "custom-elements-jsx.ts";
9 9
const SVELTE_OUTPUT = "custom-elements-svelte.ts";
10 +
const VUE_OUTPUT = "custom-elements-vue.ts";
10 11
const TYPESCRIPT_OUTPUT = "custom-elements.ts";
11 12
12 13
interface ComponentInfo {
194 195
`;
195 196
}
196 197
198 +
function generateVueTypes(components: ComponentInfo[]): string {
199 +
	const globalComponents = components
200 +
		.map((comp) => {
201 +
			const attributeProps = comp.attributes
202 +
				.map((attr) => `      '${attr}'?: string;`)
203 +
				.join("\n");
204 +
205 +
			const eventHandlers = comp.events
206 +
				.map((event) => {
207 +
					// Convert to camelCase for Vue's @event syntax
208 +
					const camelEvent = event
209 +
						.split("-")
210 +
						.map((word, i) =>
211 +
							i === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1),
212 +
						)
213 +
						.join("");
214 +
					return `      'on${camelEvent.charAt(0).toUpperCase() + camelEvent.slice(1)}'?: (event: CustomEvent) => void;`;
215 +
				})
216 +
				.join("\n");
217 +
218 +
			const allProps = [attributeProps, eventHandlers]
219 +
				.filter((p) => p)
220 +
				.join("\n");
221 +
222 +
			return `    '${comp.tagName}': {\n${allProps}\n    };`;
223 +
		})
224 +
		.join("\n");
225 +
226 +
	return `declare module 'vue' {
227 +
  export interface GlobalComponents {
228 +
${globalComponents}
229 +
  }
230 +
}
231 +
232 +
export {};
233 +
`;
234 +
}
235 +
197 236
function toPascalCase(str: string): string {
198 237
	return str
199 238
		.split("-")
230 269
		const svelteTypesCode = generateSvelteTypes(components);
231 270
		await writeFile(join(OUTPUT_DIR, SVELTE_OUTPUT), svelteTypesCode);
232 271
		console.log(`✅ Generated Svelte types: ${SVELTE_OUTPUT}`);
272 +
273 +
		// Generate Vue types
274 +
		const vueTypesCode = generateVueTypes(components);
275 +
		await writeFile(join(OUTPUT_DIR, VUE_OUTPUT), vueTypesCode);
276 +
		console.log(`✅ Generated Vue types: ${VUE_OUTPUT}`);
233 277
234 278
		// Generate TypeScript types
235 279
		const tsTypesCode = generateTypeScriptTypes(components);
src/index.ts +10 −3
21 21
interface NornsConfig {
22 22
	components: string;
23 23
	includeTypes?: boolean;
24 -
	framework?: "typescript" | "react" | "svelte";
24 +
	framework?: "typescript" | "react" | "svelte" | "vue";
25 25
}
26 26
27 27
const DEFAULT_CONFIG: NornsConfig = {
110 110
		includeTypesResponse.toLowerCase() !== "no";
111 111
112 112
	// Get framework selection
113 -
	let framework: "typescript" | "react" | "svelte" = "typescript";
113 +
	let framework: "typescript" | "react" | "svelte" | "vue" = "typescript";
114 114
	if (includeTypes) {
115 115
		console.log(colors.blue("\n▸ Select your framework:"));
116 116
		console.log(colors.cyan("  1. TypeScript (standard)"));
117 117
		console.log(colors.cyan("  2. React"));
118 118
		console.log(colors.cyan("  3. Svelte"));
119 +
		console.log(colors.cyan("  4. Vue"));
119 120
120 -
		const frameworkChoice = await promptUser("Enter your choice (1-3)", "1");
121 +
		const frameworkChoice = await promptUser("Enter your choice (1-4)", "1");
121 122
122 123
		switch (frameworkChoice) {
123 124
			case "1":
128 129
				break;
129 130
			case "3":
130 131
				framework = "svelte";
132 +
				break;
133 +
			case "4":
134 +
				framework = "vue";
131 135
				break;
132 136
			default:
133 137
				console.log(
247 251
					break;
248 252
				case "svelte":
249 253
					typesFileName = "custom-elements-svelte.ts";
254 +
					break;
255 +
				case "vue":
256 +
					typesFileName = "custom-elements-vue.ts";
250 257
					break;
251 258
				case "typescript":
252 259
				default: