@mysten-incubation/dev-wallet 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/README.md +250 -0
  2. package/dist/_virtual/_@oxc-project_runtime@0.107.0/helpers/decorate.mjs +10 -0
  3. package/dist/adapters/base-adapter.d.mts +33 -0
  4. package/dist/adapters/base-adapter.d.mts.map +1 -0
  5. package/dist/adapters/base-adapter.mjs +77 -0
  6. package/dist/adapters/base-adapter.mjs.map +1 -0
  7. package/dist/adapters/browser.d.mts +8 -0
  8. package/dist/adapters/browser.mjs +8 -0
  9. package/dist/adapters/build-managed-account.d.mts +12 -0
  10. package/dist/adapters/build-managed-account.d.mts.map +1 -0
  11. package/dist/adapters/build-managed-account.mjs +30 -0
  12. package/dist/adapters/build-managed-account.mjs.map +1 -0
  13. package/dist/adapters/idb-store.mjs +73 -0
  14. package/dist/adapters/idb-store.mjs.map +1 -0
  15. package/dist/adapters/in-memory-adapter.d.mts +16 -0
  16. package/dist/adapters/in-memory-adapter.d.mts.map +1 -0
  17. package/dist/adapters/in-memory-adapter.mjs +44 -0
  18. package/dist/adapters/in-memory-adapter.mjs.map +1 -0
  19. package/dist/adapters/passkey-adapter.d.mts +31 -0
  20. package/dist/adapters/passkey-adapter.d.mts.map +1 -0
  21. package/dist/adapters/passkey-adapter.mjs +87 -0
  22. package/dist/adapters/passkey-adapter.mjs.map +1 -0
  23. package/dist/adapters/remote-cli-adapter.d.mts +65 -0
  24. package/dist/adapters/remote-cli-adapter.d.mts.map +1 -0
  25. package/dist/adapters/remote-cli-adapter.mjs +299 -0
  26. package/dist/adapters/remote-cli-adapter.mjs.map +1 -0
  27. package/dist/adapters/webcrypto-adapter.d.mts +20 -0
  28. package/dist/adapters/webcrypto-adapter.d.mts.map +1 -0
  29. package/dist/adapters/webcrypto-adapter.mjs +68 -0
  30. package/dist/adapters/webcrypto-adapter.mjs.map +1 -0
  31. package/dist/bin/cli.d.mts +1 -0
  32. package/dist/bin/cli.mjs +183 -0
  33. package/dist/bin/cli.mjs.map +1 -0
  34. package/dist/client/dev-wallet-client.d.mts +57 -0
  35. package/dist/client/dev-wallet-client.d.mts.map +1 -0
  36. package/dist/client/dev-wallet-client.mjs +223 -0
  37. package/dist/client/dev-wallet-client.mjs.map +1 -0
  38. package/dist/client/index.d.mts +3 -0
  39. package/dist/client/index.mjs +4 -0
  40. package/dist/client/request-handler.d.mts +42 -0
  41. package/dist/client/request-handler.d.mts.map +1 -0
  42. package/dist/client/request-handler.mjs +115 -0
  43. package/dist/client/request-handler.mjs.map +1 -0
  44. package/dist/index.d.mts +5 -0
  45. package/dist/index.mjs +4 -0
  46. package/dist/react/components.d.mts +32 -0
  47. package/dist/react/components.d.mts.map +1 -0
  48. package/dist/react/components.mjs +53 -0
  49. package/dist/react/components.mjs.map +1 -0
  50. package/dist/react/context.d.mts +22 -0
  51. package/dist/react/context.d.mts.map +1 -0
  52. package/dist/react/context.mjs +25 -0
  53. package/dist/react/context.mjs.map +1 -0
  54. package/dist/react/index.d.mts +4 -0
  55. package/dist/react/index.mjs +5 -0
  56. package/dist/react/useDevWallet.d.mts +38 -0
  57. package/dist/react/useDevWallet.d.mts.map +1 -0
  58. package/dist/react/useDevWallet.mjs +89 -0
  59. package/dist/react/useDevWallet.mjs.map +1 -0
  60. package/dist/server/cli-signing-middleware.d.mts +22 -0
  61. package/dist/server/cli-signing-middleware.d.mts.map +1 -0
  62. package/dist/server/cli-signing-middleware.mjs +117 -0
  63. package/dist/server/cli-signing-middleware.mjs.map +1 -0
  64. package/dist/server/index.d.mts +2 -0
  65. package/dist/server/index.mjs +3 -0
  66. package/dist/standalone/assets/in-memory-adapter-CBF4h2KD.js +1 -0
  67. package/dist/standalone/assets/main-VE4olb-Q.js +2841 -0
  68. package/dist/standalone/assets/webcrypto-adapter-DysAubFb.js +1 -0
  69. package/dist/standalone/bookmarklet.js +5 -0
  70. package/dist/standalone/index.html +48 -0
  71. package/dist/types.d.mts +55 -0
  72. package/dist/types.d.mts.map +1 -0
  73. package/dist/ui/copy-controller.mjs +43 -0
  74. package/dist/ui/copy-controller.mjs.map +1 -0
  75. package/dist/ui/dev-wallet-account-selector.d.mts +24 -0
  76. package/dist/ui/dev-wallet-account-selector.d.mts.map +1 -0
  77. package/dist/ui/dev-wallet-account-selector.mjs +268 -0
  78. package/dist/ui/dev-wallet-account-selector.mjs.map +1 -0
  79. package/dist/ui/dev-wallet-accounts.d.mts +29 -0
  80. package/dist/ui/dev-wallet-accounts.d.mts.map +1 -0
  81. package/dist/ui/dev-wallet-accounts.mjs +524 -0
  82. package/dist/ui/dev-wallet-accounts.mjs.map +1 -0
  83. package/dist/ui/dev-wallet-balances.d.mts +26 -0
  84. package/dist/ui/dev-wallet-balances.d.mts.map +1 -0
  85. package/dist/ui/dev-wallet-balances.mjs +130 -0
  86. package/dist/ui/dev-wallet-balances.mjs.map +1 -0
  87. package/dist/ui/dev-wallet-connect.d.mts +31 -0
  88. package/dist/ui/dev-wallet-connect.d.mts.map +1 -0
  89. package/dist/ui/dev-wallet-connect.mjs +225 -0
  90. package/dist/ui/dev-wallet-connect.mjs.map +1 -0
  91. package/dist/ui/dev-wallet-dropdown.d.mts +27 -0
  92. package/dist/ui/dev-wallet-dropdown.d.mts.map +1 -0
  93. package/dist/ui/dev-wallet-dropdown.mjs +121 -0
  94. package/dist/ui/dev-wallet-dropdown.mjs.map +1 -0
  95. package/dist/ui/dev-wallet-network-badge.d.mts +21 -0
  96. package/dist/ui/dev-wallet-network-badge.d.mts.map +1 -0
  97. package/dist/ui/dev-wallet-network-badge.mjs +121 -0
  98. package/dist/ui/dev-wallet-network-badge.mjs.map +1 -0
  99. package/dist/ui/dev-wallet-new-account.d.mts +31 -0
  100. package/dist/ui/dev-wallet-new-account.d.mts.map +1 -0
  101. package/dist/ui/dev-wallet-new-account.mjs +577 -0
  102. package/dist/ui/dev-wallet-new-account.mjs.map +1 -0
  103. package/dist/ui/dev-wallet-objects.d.mts +26 -0
  104. package/dist/ui/dev-wallet-objects.d.mts.map +1 -0
  105. package/dist/ui/dev-wallet-objects.mjs +276 -0
  106. package/dist/ui/dev-wallet-objects.mjs.map +1 -0
  107. package/dist/ui/dev-wallet-panel.d.mts +22 -0
  108. package/dist/ui/dev-wallet-panel.d.mts.map +1 -0
  109. package/dist/ui/dev-wallet-panel.mjs +192 -0
  110. package/dist/ui/dev-wallet-panel.mjs.map +1 -0
  111. package/dist/ui/dev-wallet-popup.d.mts +36 -0
  112. package/dist/ui/dev-wallet-popup.d.mts.map +1 -0
  113. package/dist/ui/dev-wallet-popup.mjs +137 -0
  114. package/dist/ui/dev-wallet-popup.mjs.map +1 -0
  115. package/dist/ui/dev-wallet-settings.d.mts +33 -0
  116. package/dist/ui/dev-wallet-settings.d.mts.map +1 -0
  117. package/dist/ui/dev-wallet-settings.mjs +635 -0
  118. package/dist/ui/dev-wallet-settings.mjs.map +1 -0
  119. package/dist/ui/dev-wallet-signing-modal.d.mts +32 -0
  120. package/dist/ui/dev-wallet-signing-modal.d.mts.map +1 -0
  121. package/dist/ui/dev-wallet-signing-modal.mjs +115 -0
  122. package/dist/ui/dev-wallet-signing-modal.mjs.map +1 -0
  123. package/dist/ui/dev-wallet-signing.d.mts +25 -0
  124. package/dist/ui/dev-wallet-signing.d.mts.map +1 -0
  125. package/dist/ui/dev-wallet-signing.mjs +544 -0
  126. package/dist/ui/dev-wallet-signing.mjs.map +1 -0
  127. package/dist/ui/dev-wallet-standalone.d.mts +23 -0
  128. package/dist/ui/dev-wallet-standalone.d.mts.map +1 -0
  129. package/dist/ui/dev-wallet-standalone.mjs +129 -0
  130. package/dist/ui/dev-wallet-standalone.mjs.map +1 -0
  131. package/dist/ui/dev-wallet-tab-bar.d.mts +19 -0
  132. package/dist/ui/dev-wallet-tab-bar.d.mts.map +1 -0
  133. package/dist/ui/dev-wallet-tab-bar.mjs +143 -0
  134. package/dist/ui/dev-wallet-tab-bar.mjs.map +1 -0
  135. package/dist/ui/index.d.mts +17 -0
  136. package/dist/ui/index.mjs +18 -0
  137. package/dist/ui/mount.d.mts +11 -0
  138. package/dist/ui/mount.d.mts.map +1 -0
  139. package/dist/ui/mount.mjs +17 -0
  140. package/dist/ui/mount.mjs.map +1 -0
  141. package/dist/ui/styles.mjs +250 -0
  142. package/dist/ui/styles.mjs.map +1 -0
  143. package/dist/ui/transaction-analyzer.mjs +58 -0
  144. package/dist/ui/transaction-analyzer.mjs.map +1 -0
  145. package/dist/ui/utils.mjs +98 -0
  146. package/dist/ui/utils.mjs.map +1 -0
  147. package/dist/ui/wallet-controller.mjs +275 -0
  148. package/dist/ui/wallet-controller.mjs.map +1 -0
  149. package/dist/wallet/constants.mjs +11 -0
  150. package/dist/wallet/constants.mjs.map +1 -0
  151. package/dist/wallet/dev-wallet.d.mts +118 -0
  152. package/dist/wallet/dev-wallet.d.mts.map +1 -0
  153. package/dist/wallet/dev-wallet.mjs +424 -0
  154. package/dist/wallet/dev-wallet.mjs.map +1 -0
  155. package/dist/wallet/initializer.d.mts +63 -0
  156. package/dist/wallet/initializer.d.mts.map +1 -0
  157. package/dist/wallet/initializer.mjs +75 -0
  158. package/dist/wallet/initializer.mjs.map +1 -0
  159. package/dist/wallet/signing.d.mts +35 -0
  160. package/dist/wallet/signing.d.mts.map +1 -0
  161. package/dist/wallet/signing.mjs +69 -0
  162. package/dist/wallet/signing.mjs.map +1 -0
  163. package/package.json +124 -0
  164. package/src/adapters/base-adapter.ts +93 -0
  165. package/src/adapters/browser.ts +15 -0
  166. package/src/adapters/build-managed-account.ts +33 -0
  167. package/src/adapters/idb-store.ts +87 -0
  168. package/src/adapters/in-memory-adapter.ts +51 -0
  169. package/src/adapters/passkey-adapter.ts +120 -0
  170. package/src/adapters/remote-cli-adapter.ts +388 -0
  171. package/src/adapters/webcrypto-adapter.ts +96 -0
  172. package/src/app/bookmarklet-entry.ts +77 -0
  173. package/src/app/index.html +48 -0
  174. package/src/app/main.ts +245 -0
  175. package/src/app/tsconfig.json +3 -0
  176. package/src/bin/cli.ts +214 -0
  177. package/src/client/dev-wallet-client.ts +280 -0
  178. package/src/client/index.ts +7 -0
  179. package/src/client/request-handler.ts +161 -0
  180. package/src/index.ts +19 -0
  181. package/src/react/components.ts +64 -0
  182. package/src/react/context.ts +38 -0
  183. package/src/react/index.ts +16 -0
  184. package/src/react/useDevWallet.ts +118 -0
  185. package/src/server/cli-signing-middleware.ts +146 -0
  186. package/src/server/index.ts +8 -0
  187. package/src/types.ts +58 -0
  188. package/src/ui/copy-controller.ts +49 -0
  189. package/src/ui/dev-wallet-account-selector.ts +283 -0
  190. package/src/ui/dev-wallet-accounts.ts +578 -0
  191. package/src/ui/dev-wallet-balances.ts +171 -0
  192. package/src/ui/dev-wallet-connect.ts +259 -0
  193. package/src/ui/dev-wallet-dropdown.ts +138 -0
  194. package/src/ui/dev-wallet-network-badge.ts +128 -0
  195. package/src/ui/dev-wallet-new-account.ts +662 -0
  196. package/src/ui/dev-wallet-objects.ts +336 -0
  197. package/src/ui/dev-wallet-panel.ts +207 -0
  198. package/src/ui/dev-wallet-popup.ts +169 -0
  199. package/src/ui/dev-wallet-settings.ts +692 -0
  200. package/src/ui/dev-wallet-signing-modal.ts +137 -0
  201. package/src/ui/dev-wallet-signing.ts +624 -0
  202. package/src/ui/dev-wallet-standalone.ts +136 -0
  203. package/src/ui/dev-wallet-tab-bar.ts +151 -0
  204. package/src/ui/index.ts +26 -0
  205. package/src/ui/mount.ts +21 -0
  206. package/src/ui/styles.ts +252 -0
  207. package/src/ui/transaction-analyzer.ts +60 -0
  208. package/src/ui/utils.ts +118 -0
  209. package/src/ui/wallet-controller.ts +340 -0
  210. package/src/wallet/constants.ts +17 -0
  211. package/src/wallet/dev-wallet.ts +597 -0
  212. package/src/wallet/initializer.ts +124 -0
  213. package/src/wallet/signing.ts +85 -0
@@ -0,0 +1,578 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { ReadonlyWalletAccount } from '@mysten/wallet-standard';
5
+ import { css, html, LitElement, nothing } from 'lit';
6
+ import { customElement, property, state } from 'lit/decorators.js';
7
+
8
+ import type { SignerAdapter } from '../types.js';
9
+ import { CopyController } from './copy-controller.js';
10
+ import { actionButtonStyles, sectionHeaderStyles, sharedStyles } from './styles.js';
11
+ import { emitEvent, findAdapterForAddress, formatAddress, getErrorMessage } from './utils.js';
12
+ import './dev-wallet-new-account.js';
13
+
14
+ @customElement('dev-wallet-accounts')
15
+ export class DevWalletAccounts extends LitElement {
16
+ static override styles = [
17
+ sharedStyles,
18
+ sectionHeaderStyles,
19
+ actionButtonStyles,
20
+ css`
21
+ :host {
22
+ display: block;
23
+ }
24
+
25
+ .accounts-header {
26
+ display: flex;
27
+ justify-content: space-between;
28
+ align-items: center;
29
+ margin-bottom: 12px;
30
+ }
31
+
32
+ .accounts-header .section-header {
33
+ margin-bottom: 0;
34
+ }
35
+
36
+ .add-btn {
37
+ font-size: 12px;
38
+ color: var(--dev-wallet-primary);
39
+ padding: 4px 8px;
40
+ border-radius: var(--dev-wallet-radius-sm);
41
+ }
42
+
43
+ .add-btn:hover {
44
+ background: color-mix(in oklab, var(--dev-wallet-primary) 15%, transparent);
45
+ }
46
+
47
+ .account-list {
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: 4px;
51
+ }
52
+
53
+ .account-item {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 10px;
57
+ padding: 10px 12px;
58
+ border-radius: var(--dev-wallet-radius-sm);
59
+ border: 1px solid var(--dev-wallet-border);
60
+ background: var(--dev-wallet-secondary);
61
+ width: 100%;
62
+ text-align: left;
63
+ }
64
+
65
+ .account-item:hover {
66
+ border-color: var(--dev-wallet-primary);
67
+ }
68
+
69
+ .account-item.active {
70
+ border-color: var(--dev-wallet-primary);
71
+ background: color-mix(in oklab, var(--dev-wallet-primary) 10%, var(--dev-wallet-secondary));
72
+ }
73
+
74
+ .account-avatar {
75
+ width: 32px;
76
+ height: 32px;
77
+ border-radius: 50%;
78
+ background: var(--dev-wallet-primary);
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ font-size: 14px;
83
+ font-weight: var(--dev-wallet-font-weight-semibold);
84
+ color: var(--dev-wallet-primary-foreground);
85
+ flex-shrink: 0;
86
+ }
87
+
88
+ .account-info {
89
+ flex: 1;
90
+ min-width: 0;
91
+ }
92
+
93
+ .account-label {
94
+ font-size: 14px;
95
+ font-weight: var(--dev-wallet-font-weight-medium);
96
+ color: var(--dev-wallet-foreground);
97
+ }
98
+
99
+ .account-address {
100
+ font-size: 12px;
101
+ color: var(--dev-wallet-muted-foreground);
102
+ font-family: var(--dev-wallet-font-mono);
103
+ overflow: hidden;
104
+ text-overflow: ellipsis;
105
+ white-space: nowrap;
106
+ cursor: pointer;
107
+ border-radius: var(--dev-wallet-radius-2xs);
108
+ padding: 1px 2px;
109
+ transition: background 0.15s;
110
+ }
111
+
112
+ .account-address:hover {
113
+ background: color-mix(in oklab, var(--dev-wallet-primary) 15%, transparent);
114
+ }
115
+
116
+ .account-address.copied {
117
+ color: var(--dev-wallet-positive);
118
+ }
119
+
120
+ .account-label-row {
121
+ display: flex;
122
+ align-items: center;
123
+ gap: 4px;
124
+ }
125
+
126
+ .edit-label-btn {
127
+ width: 18px;
128
+ height: 18px;
129
+ display: inline-flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ border-radius: var(--dev-wallet-radius-2xs);
133
+ font-size: 11px;
134
+ color: var(--dev-wallet-muted-foreground);
135
+ opacity: 0;
136
+ transition: opacity 0.15s;
137
+ }
138
+
139
+ .account-item:hover .edit-label-btn {
140
+ opacity: 1;
141
+ }
142
+
143
+ .edit-label-btn:hover {
144
+ background: var(--dev-wallet-border);
145
+ color: var(--dev-wallet-foreground);
146
+ }
147
+
148
+ .edit-label-input {
149
+ padding: 2px 6px;
150
+ border-radius: var(--dev-wallet-radius-2xs);
151
+ border: 1px solid var(--dev-wallet-primary);
152
+ background: var(--dev-wallet-background);
153
+ color: var(--dev-wallet-foreground);
154
+ font-size: 13px;
155
+ font-family: inherit;
156
+ outline: none;
157
+ width: 100%;
158
+ }
159
+
160
+ .account-badge {
161
+ font-size: 10px;
162
+ padding: 1px 6px;
163
+ border-radius: var(--dev-wallet-radius-xs);
164
+ background: var(--dev-wallet-secondary);
165
+ border: 1px solid var(--dev-wallet-border);
166
+ color: var(--dev-wallet-muted-foreground);
167
+ font-weight: var(--dev-wallet-font-weight-medium);
168
+ text-transform: uppercase;
169
+ letter-spacing: 0.3px;
170
+ white-space: nowrap;
171
+ }
172
+
173
+ .delete-btn {
174
+ width: 18px;
175
+ height: 18px;
176
+ display: inline-flex;
177
+ align-items: center;
178
+ justify-content: center;
179
+ border-radius: var(--dev-wallet-radius-2xs);
180
+ font-size: 11px;
181
+ color: var(--dev-wallet-muted-foreground);
182
+ opacity: 0;
183
+ transition: opacity 0.15s;
184
+ flex-shrink: 0;
185
+ }
186
+
187
+ .account-item:hover .delete-btn {
188
+ opacity: 1;
189
+ }
190
+
191
+ .delete-btn:hover {
192
+ background: color-mix(in oklab, var(--dev-wallet-destructive) 20%, transparent);
193
+ color: var(--dev-wallet-destructive);
194
+ }
195
+
196
+ .confirm-dialog:not([open]) {
197
+ display: none;
198
+ }
199
+
200
+ .confirm-dialog {
201
+ width: 300px;
202
+ max-width: calc(100vw - 32px);
203
+ border-radius: var(--dev-wallet-radius-xl);
204
+ background: var(--dev-wallet-background);
205
+ border: 1px solid var(--dev-wallet-border);
206
+ box-shadow: var(--dev-wallet-shadow-lg);
207
+ padding: 20px;
208
+ display: flex;
209
+ flex-direction: column;
210
+ color: inherit;
211
+ }
212
+
213
+ .confirm-dialog::backdrop {
214
+ background: color-mix(in oklab, oklch(0 0 0) 50%, transparent);
215
+ }
216
+
217
+ .confirm-title {
218
+ font-size: 16px;
219
+ font-weight: var(--dev-wallet-font-weight-semibold);
220
+ color: var(--dev-wallet-foreground);
221
+ margin-bottom: 12px;
222
+ }
223
+
224
+ .confirm-body {
225
+ font-size: 13px;
226
+ color: var(--dev-wallet-muted-foreground);
227
+ margin-bottom: 8px;
228
+ line-height: 1.4;
229
+ }
230
+
231
+ .confirm-account {
232
+ padding: 8px 10px;
233
+ border-radius: var(--dev-wallet-radius-sm);
234
+ background: var(--dev-wallet-secondary);
235
+ border: 1px solid var(--dev-wallet-border);
236
+ margin-bottom: 16px;
237
+ }
238
+
239
+ .confirm-account-label {
240
+ font-size: 13px;
241
+ font-weight: var(--dev-wallet-font-weight-medium);
242
+ color: var(--dev-wallet-foreground);
243
+ }
244
+
245
+ .confirm-account-address {
246
+ font-size: 11px;
247
+ color: var(--dev-wallet-muted-foreground);
248
+ font-family: var(--dev-wallet-font-mono);
249
+ }
250
+
251
+ .confirm-actions {
252
+ display: flex;
253
+ gap: 8px;
254
+ }
255
+
256
+ .confirm-error {
257
+ color: var(--dev-wallet-destructive);
258
+ font-size: 12px;
259
+ margin-bottom: 8px;
260
+ }
261
+
262
+ .empty-state {
263
+ text-align: center;
264
+ padding: 24px;
265
+ color: var(--dev-wallet-muted-foreground);
266
+ font-size: 14px;
267
+ }
268
+ `,
269
+ ];
270
+
271
+ @property({ attribute: false })
272
+ accounts: readonly ReadonlyWalletAccount[] = [];
273
+
274
+ @property({ attribute: false })
275
+ adapters: SignerAdapter[] = [];
276
+
277
+ @property({ type: String })
278
+ activeAddress = '';
279
+
280
+ @state()
281
+ private _dialogOpen = false;
282
+
283
+ #copy = new CopyController(this);
284
+
285
+ @state()
286
+ private _editingAddress: string | null = null;
287
+
288
+ @state()
289
+ private _editingLabel = '';
290
+
291
+ @state()
292
+ private _confirmDeleteAddress: string | null = null;
293
+
294
+ @state()
295
+ private _deleting = false;
296
+
297
+ @state()
298
+ private _deleteError: string | null = null;
299
+
300
+ override render() {
301
+ const canAdd = this.adapters.some(
302
+ (a) =>
303
+ ('createAccount' in a && a.createAccount) ||
304
+ ('importAccount' in a &&
305
+ a.importAccount &&
306
+ 'listAvailableAccounts' in a &&
307
+ a.listAvailableAccounts),
308
+ );
309
+
310
+ return html`
311
+ <div class="accounts-header">
312
+ <h3 class="section-header">Accounts</h3>
313
+ ${canAdd
314
+ ? html`<button class="add-btn" part="add-button" @click=${this.#openDialog}>
315
+ + Add
316
+ </button>`
317
+ : nothing}
318
+ </div>
319
+ ${this.accounts.length === 0
320
+ ? html`<div class="empty-state" part="empty-state">No accounts yet</div>`
321
+ : html`
322
+ <div
323
+ class="account-list"
324
+ part="account-list"
325
+ role="listbox"
326
+ aria-label="Wallet accounts"
327
+ >
328
+ ${this.accounts.map(
329
+ (account, index) => html`
330
+ <button
331
+ class="account-item ${account.address === this.activeAddress ? 'active' : ''}"
332
+ role="option"
333
+ aria-selected=${account.address === this.activeAddress}
334
+ @click=${() => this.#selectAccount(account)}
335
+ >
336
+ <div class="account-avatar">${index + 1}</div>
337
+ <div class="account-info">
338
+ ${this._editingAddress === account.address
339
+ ? html`<input
340
+ class="edit-label-input"
341
+ type="text"
342
+ aria-label="Rename account"
343
+ .value=${this._editingLabel}
344
+ @input=${(e: InputEvent) => {
345
+ this._editingLabel = (e.target as HTMLInputElement).value;
346
+ }}
347
+ @keydown=${(e: KeyboardEvent) => {
348
+ e.stopPropagation();
349
+ if (e.key === 'Enter') this.#saveLabel(account.address);
350
+ if (e.key === 'Escape') this.#cancelEditLabel();
351
+ }}
352
+ @click=${(e: Event) => e.stopPropagation()}
353
+ />`
354
+ : html`<div class="account-label-row">
355
+ <span class="account-label">
356
+ ${this.#getAccountLabel(account.address, index)}
357
+ </span>
358
+ <span class="account-badge"
359
+ >${this.#getAdapterName(account.address)}</span
360
+ >
361
+ ${this.#canRename(account.address)
362
+ ? html`<button
363
+ class="edit-label-btn"
364
+ title="Rename"
365
+ aria-label="Rename account"
366
+ @click=${(e: Event) => {
367
+ e.stopPropagation();
368
+ this.#startEditLabel(account.address, index);
369
+ }}
370
+ >
371
+ &#9998;
372
+ </button>`
373
+ : nothing}
374
+ </div>`}
375
+ <div
376
+ class="account-address ${this.#copy.isCopied(account.address)
377
+ ? 'copied'
378
+ : ''}"
379
+ title="Click to copy"
380
+ role="button"
381
+ tabindex="0"
382
+ aria-label="Copy address"
383
+ @click=${(e: Event) => {
384
+ e.stopPropagation();
385
+ this.#copy.copy(account.address);
386
+ }}
387
+ @keydown=${(e: KeyboardEvent) => {
388
+ if (e.key === 'Enter' || e.key === ' ') {
389
+ e.preventDefault();
390
+ e.stopPropagation();
391
+ this.#copy.copy(account.address);
392
+ }
393
+ }}
394
+ >
395
+ ${this.#copy.isCopied(account.address)
396
+ ? 'Copied!'
397
+ : formatAddress(account.address)}
398
+ </div>
399
+ </div>
400
+ ${this.#canRemove(account.address)
401
+ ? html`<button
402
+ class="delete-btn"
403
+ title="Remove account"
404
+ aria-label="Remove account"
405
+ @click=${(e: Event) => {
406
+ e.stopPropagation();
407
+ this.#promptDelete(account.address);
408
+ }}
409
+ >
410
+ &#128465;
411
+ </button>`
412
+ : nothing}
413
+ </button>
414
+ `,
415
+ )}
416
+ </div>
417
+ `}
418
+ ${this.#renderConfirmDialog()}
419
+ <dev-wallet-new-account
420
+ .adapters=${this.adapters}
421
+ .open=${this._dialogOpen}
422
+ @close=${this.#closeDialog}
423
+ ></dev-wallet-new-account>
424
+ `;
425
+ }
426
+
427
+ #getAccountLabel(address: string, index: number): string {
428
+ const adapter = findAdapterForAddress(this.adapters, address);
429
+ if (!adapter) return `Account ${index + 1}`;
430
+ const managed = adapter.getAccount(address);
431
+ return managed?.label ?? `Account ${index + 1}`;
432
+ }
433
+
434
+ #canRename(address: string): boolean {
435
+ const adapter = findAdapterForAddress(this.adapters, address);
436
+ return !!(adapter && adapter.renameAccount);
437
+ }
438
+
439
+ #getAdapterName(address: string): string {
440
+ return findAdapterForAddress(this.adapters, address)?.name ?? 'Unknown';
441
+ }
442
+
443
+ #selectAccount(account: ReadonlyWalletAccount) {
444
+ emitEvent(this, 'account-selected', { account });
445
+ }
446
+
447
+ #startEditLabel(address: string, index: number) {
448
+ this._editingAddress = address;
449
+ this._editingLabel = this.#getAccountLabel(address, index);
450
+ }
451
+
452
+ async #saveLabel(address: string) {
453
+ const label = this._editingLabel.trim();
454
+ if (!label) {
455
+ this.#cancelEditLabel();
456
+ return;
457
+ }
458
+ try {
459
+ for (const adapter of this.adapters) {
460
+ if (adapter.getAccount(address) && adapter.renameAccount) {
461
+ await adapter.renameAccount(address, label);
462
+ break;
463
+ }
464
+ }
465
+ } catch (e) {
466
+ console.error('[dev-wallet] rename failed:', e);
467
+ }
468
+ this._editingAddress = null;
469
+ this._editingLabel = '';
470
+ emitEvent(this, 'account-renamed', { address, label });
471
+ }
472
+
473
+ #cancelEditLabel() {
474
+ this._editingAddress = null;
475
+ this._editingLabel = '';
476
+ }
477
+
478
+ #canRemove(address: string): boolean {
479
+ const adapter = findAdapterForAddress(this.adapters, address);
480
+ return !!(adapter && adapter.removeAccount);
481
+ }
482
+
483
+ #isImportedAccount(address: string): boolean {
484
+ const adapter = findAdapterForAddress(this.adapters, address);
485
+ if (!adapter) return false;
486
+ return !('createAccount' in adapter && adapter.createAccount);
487
+ }
488
+
489
+ #promptDelete(address: string) {
490
+ this._confirmDeleteAddress = address;
491
+ this._deleteError = null;
492
+ this.updateComplete.then(() => {
493
+ const dialog = this.shadowRoot?.querySelector<HTMLDialogElement>('.confirm-dialog');
494
+ if (dialog && !dialog.open) dialog.showModal();
495
+ });
496
+ }
497
+
498
+ #cancelDelete() {
499
+ const dialog = this.shadowRoot?.querySelector<HTMLDialogElement>('.confirm-dialog');
500
+ if (dialog?.open) dialog.close();
501
+ this._confirmDeleteAddress = null;
502
+ this._deleting = false;
503
+ this._deleteError = null;
504
+ }
505
+
506
+ async #confirmDelete() {
507
+ const address = this._confirmDeleteAddress;
508
+ if (!address) return;
509
+
510
+ this._deleting = true;
511
+ this._deleteError = null;
512
+
513
+ try {
514
+ const adapter = findAdapterForAddress(this.adapters, address);
515
+ if (!adapter?.removeAccount) throw new Error('Adapter does not support removal');
516
+ const removed = await adapter.removeAccount(address);
517
+ if (!removed) throw new Error('Account not found');
518
+ this.#cancelDelete();
519
+ emitEvent(this, 'account-removed', { address });
520
+ } catch (error) {
521
+ this._deleteError = getErrorMessage(error, 'Failed to remove account');
522
+ this._deleting = false;
523
+ }
524
+ }
525
+
526
+ #renderConfirmDialog() {
527
+ if (!this._confirmDeleteAddress) return nothing;
528
+
529
+ const address = this._confirmDeleteAddress;
530
+ const index = this.accounts.findIndex((a) => a.address === address);
531
+ const label = index >= 0 ? this.#getAccountLabel(address, index) : 'Unknown';
532
+ const imported = this.#isImportedAccount(address);
533
+
534
+ return html`
535
+ <dialog
536
+ class="confirm-dialog"
537
+ @cancel=${(e: Event) => {
538
+ e.preventDefault();
539
+ this.#cancelDelete();
540
+ }}
541
+ >
542
+ <div class="confirm-title">Remove Account</div>
543
+ <div class="confirm-body">
544
+ ${imported
545
+ ? 'This will remove the imported account from your wallet. You can re-import it later.'
546
+ : 'This will permanently delete this account and its keys. This cannot be undone.'}
547
+ </div>
548
+ <div class="confirm-account">
549
+ <div class="confirm-account-label">${label}</div>
550
+ <div class="confirm-account-address">${formatAddress(address)}</div>
551
+ </div>
552
+ ${this._deleteError ? html`<div class="confirm-error">${this._deleteError}</div>` : nothing}
553
+ <div class="confirm-actions">
554
+ <button class="btn btn-cancel" ?disabled=${this._deleting} @click=${this.#cancelDelete}>
555
+ Cancel
556
+ </button>
557
+ <button class="btn btn-reject" ?disabled=${this._deleting} @click=${this.#confirmDelete}>
558
+ ${this._deleting ? 'Removing...' : 'Remove'}
559
+ </button>
560
+ </div>
561
+ </dialog>
562
+ `;
563
+ }
564
+
565
+ #openDialog() {
566
+ this._dialogOpen = true;
567
+ }
568
+
569
+ #closeDialog() {
570
+ this._dialogOpen = false;
571
+ }
572
+ }
573
+
574
+ declare global {
575
+ interface HTMLElementTagNameMap {
576
+ 'dev-wallet-accounts': DevWalletAccounts;
577
+ }
578
+ }