@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,662 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { css, html, LitElement, nothing } from 'lit';
5
+ import { customElement, property, state } from 'lit/decorators.js';
6
+
7
+ import type { SignerAdapter } from '../types.js';
8
+ import { actionButtonStyles, sharedStyles, stateStyles } from './styles.js';
9
+ import {
10
+ emitEvent,
11
+ formatAddress,
12
+ getErrorMessage,
13
+ isPairableAdapter,
14
+ toggleSetItem,
15
+ } from './utils.js';
16
+ import './dev-wallet-dropdown.js';
17
+
18
+ interface AvailableAccount {
19
+ adapterId: string;
20
+ adapterName: string;
21
+ address: string;
22
+ scheme: string;
23
+ alias?: string | null;
24
+ }
25
+
26
+ @customElement('dev-wallet-new-account')
27
+ export class DevWalletNewAccount extends LitElement {
28
+ static override styles = [
29
+ sharedStyles,
30
+ actionButtonStyles,
31
+ stateStyles,
32
+ css`
33
+ :host {
34
+ display: block;
35
+ }
36
+
37
+ dialog:not([open]) {
38
+ display: none;
39
+ }
40
+
41
+ dialog {
42
+ width: 320px;
43
+ max-width: calc(100vw - 32px);
44
+ max-height: min(420px, 80vh);
45
+ border-radius: var(--dev-wallet-radius-xl);
46
+ background: var(--dev-wallet-background);
47
+ border: 1px solid var(--dev-wallet-border);
48
+ box-shadow: var(--dev-wallet-shadow-lg);
49
+ padding: 20px;
50
+ display: flex;
51
+ flex-direction: column;
52
+ color: inherit;
53
+ }
54
+
55
+ dialog::backdrop {
56
+ background: color-mix(in oklab, oklch(0 0 0) 50%, transparent);
57
+ }
58
+
59
+ .dialog-title {
60
+ font-size: 16px;
61
+ font-weight: var(--dev-wallet-font-weight-semibold);
62
+ color: var(--dev-wallet-foreground);
63
+ margin-bottom: 16px;
64
+ }
65
+
66
+ .tabs {
67
+ display: flex;
68
+ gap: 4px;
69
+ margin-bottom: 16px;
70
+ background: var(--dev-wallet-secondary);
71
+ border-radius: var(--dev-wallet-radius-sm);
72
+ padding: 3px;
73
+ }
74
+
75
+ .tab {
76
+ flex: 1;
77
+ padding: 6px 12px;
78
+ border-radius: var(--dev-wallet-radius-xs);
79
+ font-size: 12px;
80
+ font-weight: var(--dev-wallet-font-weight-medium);
81
+ color: var(--dev-wallet-muted-foreground);
82
+ text-align: center;
83
+ transition: all 0.15s;
84
+ }
85
+
86
+ .tab.active {
87
+ background: var(--dev-wallet-background);
88
+ color: var(--dev-wallet-foreground);
89
+ box-shadow: var(--dev-wallet-shadow-sm);
90
+ }
91
+
92
+ .tab:not(.active):hover {
93
+ color: var(--dev-wallet-foreground);
94
+ }
95
+
96
+ .field {
97
+ margin-bottom: 12px;
98
+ }
99
+
100
+ .field-label {
101
+ display: block;
102
+ font-size: 12px;
103
+ font-weight: var(--dev-wallet-font-weight-medium);
104
+ color: var(--dev-wallet-muted-foreground);
105
+ margin-bottom: 4px;
106
+ }
107
+
108
+ .field-input {
109
+ width: 100%;
110
+ padding: 8px 10px;
111
+ border-radius: var(--dev-wallet-radius-sm);
112
+ border: 1px solid var(--dev-wallet-input);
113
+ background: var(--dev-wallet-secondary);
114
+ color: var(--dev-wallet-foreground);
115
+ font-size: 14px;
116
+ font-family: inherit;
117
+ outline: none;
118
+ box-sizing: border-box;
119
+ }
120
+
121
+ .field-input:focus {
122
+ border-color: var(--dev-wallet-primary);
123
+ outline: 2px solid color-mix(in oklab, var(--dev-wallet-ring) 50%, transparent);
124
+ outline-offset: -1px;
125
+ }
126
+
127
+ .select-trigger {
128
+ width: 100%;
129
+ padding: 8px 10px;
130
+ border-radius: var(--dev-wallet-radius-sm);
131
+ border: 1px solid var(--dev-wallet-input);
132
+ background: var(--dev-wallet-secondary);
133
+ color: var(--dev-wallet-foreground);
134
+ font-size: 14px;
135
+ font-family: inherit;
136
+ box-sizing: border-box;
137
+ display: flex;
138
+ align-items: center;
139
+ justify-content: space-between;
140
+ cursor: pointer;
141
+ text-align: left;
142
+ }
143
+
144
+ .select-trigger:hover {
145
+ border-color: var(--dev-wallet-primary);
146
+ }
147
+
148
+ .select-trigger .chevron {
149
+ font-size: 10px;
150
+ color: var(--dev-wallet-muted-foreground);
151
+ transition: transform 0.15s;
152
+ }
153
+
154
+ .select-trigger.open .chevron {
155
+ transform: rotate(180deg);
156
+ }
157
+
158
+ .select-option {
159
+ width: 100%;
160
+ padding: 8px 10px;
161
+ font-size: 14px;
162
+ font-family: inherit;
163
+ color: var(--dev-wallet-foreground);
164
+ background: transparent;
165
+ text-align: left;
166
+ cursor: pointer;
167
+ transition: background 0.1s;
168
+ }
169
+
170
+ .select-option:hover {
171
+ background: color-mix(in oklab, var(--dev-wallet-primary) 15%, transparent);
172
+ }
173
+
174
+ .select-option.selected {
175
+ color: var(--dev-wallet-primary);
176
+ font-weight: var(--dev-wallet-font-weight-medium);
177
+ }
178
+
179
+ .import-list {
180
+ display: flex;
181
+ flex-direction: column;
182
+ gap: 4px;
183
+ overflow-y: auto;
184
+ max-height: 240px;
185
+ min-height: 60px;
186
+ }
187
+
188
+ .import-item {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 10px;
192
+ padding: 8px 10px;
193
+ border-radius: var(--dev-wallet-radius-sm);
194
+ border: 1px solid var(--dev-wallet-border);
195
+ background: var(--dev-wallet-secondary);
196
+ width: 100%;
197
+ text-align: left;
198
+ transition: border-color 0.15s;
199
+ }
200
+
201
+ .import-item:hover {
202
+ border-color: var(--dev-wallet-primary);
203
+ }
204
+
205
+ .import-item.selected {
206
+ border-color: var(--dev-wallet-primary);
207
+ background: color-mix(in oklab, var(--dev-wallet-primary) 10%, var(--dev-wallet-secondary));
208
+ }
209
+
210
+ .import-item:disabled {
211
+ opacity: 0.5;
212
+ cursor: not-allowed;
213
+ }
214
+
215
+ .import-checkbox {
216
+ width: 16px;
217
+ height: 16px;
218
+ border-radius: var(--dev-wallet-radius-2xs);
219
+ border: 2px solid var(--dev-wallet-border);
220
+ flex-shrink: 0;
221
+ display: flex;
222
+ align-items: center;
223
+ justify-content: center;
224
+ font-size: 10px;
225
+ color: var(--dev-wallet-primary-foreground);
226
+ transition: all 0.15s;
227
+ }
228
+
229
+ .import-checkbox.checked {
230
+ background: var(--dev-wallet-primary);
231
+ border-color: var(--dev-wallet-primary);
232
+ }
233
+
234
+ .import-item-info {
235
+ flex: 1;
236
+ min-width: 0;
237
+ }
238
+
239
+ .import-item-label {
240
+ font-size: 13px;
241
+ font-weight: var(--dev-wallet-font-weight-medium);
242
+ color: var(--dev-wallet-foreground);
243
+ }
244
+
245
+ .import-item-address {
246
+ font-size: 11px;
247
+ color: var(--dev-wallet-muted-foreground);
248
+ font-family: var(--dev-wallet-font-mono);
249
+ overflow: hidden;
250
+ text-overflow: ellipsis;
251
+ white-space: nowrap;
252
+ }
253
+
254
+ .import-item-badge {
255
+ font-size: 10px;
256
+ padding: 1px 6px;
257
+ border-radius: var(--dev-wallet-radius-xs);
258
+ background: var(--dev-wallet-background);
259
+ border: 1px solid var(--dev-wallet-border);
260
+ color: var(--dev-wallet-muted-foreground);
261
+ font-weight: var(--dev-wallet-font-weight-medium);
262
+ text-transform: uppercase;
263
+ letter-spacing: 0.3px;
264
+ white-space: nowrap;
265
+ flex-shrink: 0;
266
+ }
267
+
268
+ .empty-state,
269
+ .loading {
270
+ padding: 20px;
271
+ }
272
+
273
+ .actions {
274
+ margin-top: 16px;
275
+ }
276
+
277
+ .error {
278
+ color: var(--dev-wallet-destructive);
279
+ font-size: 12px;
280
+ margin-top: 8px;
281
+ }
282
+ `,
283
+ ];
284
+
285
+ @property({ attribute: false })
286
+ adapters: SignerAdapter[] = [];
287
+
288
+ @property({ type: Boolean })
289
+ open = false;
290
+
291
+ @state()
292
+ private _mode: 'create' | 'import' = 'create';
293
+
294
+ @state()
295
+ private _label = '';
296
+
297
+ @state()
298
+ private _selectedAdapterId = '';
299
+
300
+ @state()
301
+ private _creating = false;
302
+
303
+ @state()
304
+ private _error: string | null = null;
305
+
306
+ @state()
307
+ private _availableAccounts: AvailableAccount[] = [];
308
+
309
+ @state()
310
+ private _loadingImports = false;
311
+
312
+ @state()
313
+ private _dropdownOpen = false;
314
+
315
+ @state()
316
+ private _selectedImports = new Set<string>();
317
+
318
+ get #creatableAdapters(): SignerAdapter[] {
319
+ return this.adapters.filter((a) => 'createAccount' in a && a.createAccount);
320
+ }
321
+
322
+ get #importableAdapters(): SignerAdapter[] {
323
+ return this.adapters.filter(
324
+ (a) =>
325
+ 'importAccount' in a &&
326
+ a.importAccount &&
327
+ 'listAvailableAccounts' in a &&
328
+ a.listAvailableAccounts &&
329
+ // Skip adapters that require pairing but aren't paired yet
330
+ (!isPairableAdapter(a) || a.isPaired),
331
+ );
332
+ }
333
+
334
+ get #hasUnpairedImportableAdapters(): boolean {
335
+ return this.adapters.some(
336
+ (a) =>
337
+ 'importAccount' in a &&
338
+ a.importAccount &&
339
+ 'listAvailableAccounts' in a &&
340
+ a.listAvailableAccounts &&
341
+ isPairableAdapter(a) &&
342
+ !a.isPaired,
343
+ );
344
+ }
345
+
346
+ override updated(changed: Map<string, unknown>) {
347
+ if (changed.has('open')) {
348
+ const dialog = this.shadowRoot?.querySelector('dialog');
349
+ if (this.open && dialog && !dialog.open) {
350
+ const canCreate = this.#creatableAdapters.length > 0;
351
+ const canImport = this.#importableAdapters.length > 0;
352
+ if (canImport && !canCreate) {
353
+ this._mode = 'import';
354
+ } else {
355
+ this._mode = 'create';
356
+ }
357
+ if (canImport) {
358
+ this.#loadAvailableAccounts();
359
+ }
360
+ dialog.showModal();
361
+ } else if (!this.open && dialog?.open) {
362
+ dialog.close();
363
+ }
364
+ }
365
+ }
366
+
367
+ override render() {
368
+ const canCreate = this.#creatableAdapters.length > 0;
369
+ const canImport = this.#importableAdapters.length > 0;
370
+ const showTabs = canCreate && canImport;
371
+
372
+ return html`
373
+ <dialog part="dialog" @cancel=${this.#handleDialogCancel}>
374
+ <div class="dialog-title">Add Account</div>
375
+ ${showTabs
376
+ ? html`
377
+ <div class="tabs">
378
+ <button
379
+ class="tab ${this._mode === 'create' ? 'active' : ''}"
380
+ @click=${() => (this._mode = 'create')}
381
+ >
382
+ Create
383
+ </button>
384
+ <button
385
+ class="tab ${this._mode === 'import' ? 'active' : ''}"
386
+ @click=${() => (this._mode = 'import')}
387
+ >
388
+ Import
389
+ </button>
390
+ </div>
391
+ `
392
+ : nothing}
393
+ ${this._mode === 'create' ? this.#renderCreateForm() : this.#renderImportList()}
394
+ ${this._error
395
+ ? html`<div class="error" part="error-message">${this._error}</div>`
396
+ : nothing}
397
+ </dialog>
398
+ `;
399
+ }
400
+
401
+ #handleDialogCancel(e: Event) {
402
+ e.preventDefault();
403
+ if (!this._creating) {
404
+ this.#close();
405
+ }
406
+ }
407
+
408
+ #renderCreateForm() {
409
+ const creatableAdapters = this.#creatableAdapters;
410
+ const showPicker = creatableAdapters.length > 1;
411
+
412
+ const selectedAdapter =
413
+ creatableAdapters.find((a) => a.id === this._selectedAdapterId) ?? creatableAdapters[0];
414
+
415
+ return html`
416
+ ${showPicker
417
+ ? html`
418
+ <div class="field">
419
+ <label class="field-label">Type</label>
420
+ <dev-wallet-dropdown
421
+ full-width
422
+ .open=${this._dropdownOpen}
423
+ @close=${() => (this._dropdownOpen = false)}
424
+ >
425
+ <button
426
+ slot="trigger"
427
+ class="select-trigger ${this._dropdownOpen ? 'open' : ''}"
428
+ @click=${() => (this._dropdownOpen = !this._dropdownOpen)}
429
+ >
430
+ <span>${selectedAdapter?.name ?? 'Select...'}</span>
431
+ <span class="chevron">▼</span>
432
+ </button>
433
+ <div slot="popover">
434
+ ${creatableAdapters.map(
435
+ (a) => html`
436
+ <button
437
+ class="select-option ${a.id === (selectedAdapter?.id ?? '')
438
+ ? 'selected'
439
+ : ''}"
440
+ @click=${() => {
441
+ this._selectedAdapterId = a.id;
442
+ this._dropdownOpen = false;
443
+ this._error = null;
444
+ }}
445
+ >
446
+ ${a.name}
447
+ </button>
448
+ `,
449
+ )}
450
+ </div>
451
+ </dev-wallet-dropdown>
452
+ </div>
453
+ `
454
+ : nothing}
455
+ <div class="field">
456
+ <label class="field-label">Label</label>
457
+ <input
458
+ class="field-input"
459
+ part="label-input"
460
+ type="text"
461
+ placeholder="e.g. Test Account"
462
+ .value=${this._label}
463
+ @input=${this.#handleLabelInput}
464
+ @keydown=${this.#handleKeydown}
465
+ />
466
+ </div>
467
+ <div class="actions">
468
+ <button class="btn btn-cancel" part="cancel-button" @click=${this.#handleCancel}>
469
+ Cancel
470
+ </button>
471
+ <button
472
+ class="btn btn-create"
473
+ part="create-button"
474
+ ?disabled=${this._creating}
475
+ @click=${this.#handleCreate}
476
+ >
477
+ ${this._creating ? 'Creating...' : 'Create'}
478
+ </button>
479
+ </div>
480
+ `;
481
+ }
482
+
483
+ #renderImportList() {
484
+ if (this._loadingImports) {
485
+ return html`<div class="loading">Loading accounts...</div>
486
+ <div class="actions">
487
+ <button class="btn btn-cancel" part="cancel-button" @click=${this.#handleCancel}>
488
+ Cancel
489
+ </button>
490
+ </div>`;
491
+ }
492
+
493
+ if (this._availableAccounts.length === 0) {
494
+ return html`<div class="empty-state">
495
+ ${this.#hasUnpairedImportableAdapters
496
+ ? 'Open the token URL from your terminal to connect CLI accounts.'
497
+ : 'No accounts available to import'}
498
+ </div>
499
+ <div class="actions">
500
+ <button class="btn btn-cancel" part="cancel-button" @click=${this.#handleCancel}>
501
+ Cancel
502
+ </button>
503
+ </div>`;
504
+ }
505
+
506
+ return html`
507
+ <div class="import-list">
508
+ ${this._availableAccounts.map((account) => {
509
+ const selected = this._selectedImports.has(account.address);
510
+ return html`
511
+ <button
512
+ class="import-item ${selected ? 'selected' : ''}"
513
+ role="checkbox"
514
+ aria-checked="${selected}"
515
+ ?disabled=${this._creating}
516
+ @click=${() => this.#toggleImportSelection(account.address)}
517
+ >
518
+ <span class="import-checkbox ${selected ? 'checked' : ''}"
519
+ >${selected ? '✓' : ''}</span
520
+ >
521
+ <div class="import-item-info">
522
+ <div class="import-item-label">
523
+ ${account.alias || formatAddress(account.address)}
524
+ </div>
525
+ <div class="import-item-address">${formatAddress(account.address)}</div>
526
+ </div>
527
+ <span class="import-item-badge">${account.scheme}</span>
528
+ </button>
529
+ `;
530
+ })}
531
+ </div>
532
+ <div class="actions">
533
+ <button class="btn btn-cancel" part="cancel-button" @click=${this.#handleCancel}>
534
+ Cancel
535
+ </button>
536
+ <button
537
+ class="btn btn-create"
538
+ part="create-button"
539
+ ?disabled=${this._creating || this._selectedImports.size === 0}
540
+ @click=${this.#handleImportSelected}
541
+ >
542
+ ${this._creating
543
+ ? 'Importing...'
544
+ : `Import${this._selectedImports.size > 0 ? ` (${this._selectedImports.size})` : ''}`}
545
+ </button>
546
+ </div>
547
+ `;
548
+ }
549
+
550
+ async #loadAvailableAccounts() {
551
+ this._loadingImports = true;
552
+ this._availableAccounts = [];
553
+
554
+ try {
555
+ const results: AvailableAccount[] = [];
556
+ for (const adapter of this.#importableAdapters) {
557
+ const accounts = await adapter.listAvailableAccounts!();
558
+ for (const account of accounts) {
559
+ results.push({
560
+ adapterId: adapter.id,
561
+ adapterName: adapter.name,
562
+ address: account.address,
563
+ scheme: account.scheme,
564
+ alias: 'alias' in account ? (account.alias as string | null) : null,
565
+ });
566
+ }
567
+ }
568
+ this._availableAccounts = results;
569
+ } catch (error) {
570
+ this._error = getErrorMessage(error, 'Failed to load accounts');
571
+ } finally {
572
+ this._loadingImports = false;
573
+ }
574
+ }
575
+
576
+ #handleLabelInput(e: InputEvent) {
577
+ this._label = (e.target as HTMLInputElement).value;
578
+ this._error = null;
579
+ }
580
+
581
+ #handleKeydown(e: KeyboardEvent) {
582
+ if (e.key === 'Enter' && !this._creating) {
583
+ this.#handleCreate();
584
+ } else if (e.key === 'Escape') {
585
+ this.#close();
586
+ }
587
+ }
588
+
589
+ #handleCancel() {
590
+ this.#close();
591
+ }
592
+
593
+ async #handleCreate() {
594
+ const creatableAdapters = this.#creatableAdapters;
595
+ if (creatableAdapters.length === 0) return;
596
+
597
+ const adapter =
598
+ creatableAdapters.find((a) => a.id === this._selectedAdapterId) ?? creatableAdapters[0];
599
+ if (!adapter?.createAccount) return;
600
+
601
+ this._creating = true;
602
+ this._error = null;
603
+
604
+ try {
605
+ const options: Record<string, unknown> = {};
606
+ if (this._label.trim()) {
607
+ options.label = this._label.trim();
608
+ }
609
+ await adapter.createAccount(options);
610
+ this.#close();
611
+ emitEvent(this, 'account-created');
612
+ } catch (error) {
613
+ this._error = getErrorMessage(error, 'Failed to create account');
614
+ } finally {
615
+ this._creating = false;
616
+ }
617
+ }
618
+
619
+ #toggleImportSelection(address: string) {
620
+ this._selectedImports = toggleSetItem(this._selectedImports, address);
621
+ }
622
+
623
+ async #handleImportSelected() {
624
+ const selected = this._availableAccounts.filter((a) => this._selectedImports.has(a.address));
625
+ if (selected.length === 0) return;
626
+
627
+ this._creating = true;
628
+ this._error = null;
629
+
630
+ try {
631
+ for (const account of selected) {
632
+ const adapter = this.#importableAdapters.find((a) => a.id === account.adapterId);
633
+ if (!adapter?.importAccount) continue;
634
+ await adapter.importAccount({ address: account.address });
635
+ }
636
+ this.#close();
637
+ emitEvent(this, 'account-created');
638
+ } catch (error) {
639
+ this._error = getErrorMessage(error, 'Failed to import account');
640
+ } finally {
641
+ this._creating = false;
642
+ }
643
+ }
644
+
645
+ #close() {
646
+ this._label = '';
647
+ this._selectedAdapterId = '';
648
+ this._error = null;
649
+ this._availableAccounts = [];
650
+ this._selectedImports = new Set();
651
+ this._mode = 'create';
652
+ this._dropdownOpen = false;
653
+ // Don't mutate `this.open` — let the parent control it via the `close` event
654
+ emitEvent(this, 'close');
655
+ }
656
+ }
657
+
658
+ declare global {
659
+ interface HTMLElementTagNameMap {
660
+ 'dev-wallet-new-account': DevWalletNewAccount;
661
+ }
662
+ }