ethagent 1.0.8 → 1.1.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 (30) hide show
  1. package/package.json +1 -2
  2. package/src/chat/ChatScreen.tsx +5 -4
  3. package/src/chat/ContinuityEditReviewView.tsx +3 -3
  4. package/src/chat/commands.ts +5 -5
  5. package/src/cli/ResetConfirmView.tsx +12 -13
  6. package/src/cli/main.tsx +27 -30
  7. package/src/cli/reset.ts +7 -8
  8. package/src/cli/updateNotice.ts +52 -0
  9. package/src/identity/continuity/envelope.ts +11 -5
  10. package/src/identity/continuity/storage.ts +1 -1
  11. package/src/identity/hub/IdentityHub.tsx +6 -7
  12. package/src/identity/hub/identityHubModel.ts +12 -12
  13. package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +39 -34
  14. package/src/identity/hub/screens/CreateFlow.tsx +4 -4
  15. package/src/identity/hub/screens/DetailsScreen.tsx +2 -2
  16. package/src/identity/hub/screens/EditProfileFlow.tsx +5 -5
  17. package/src/identity/hub/screens/ErrorScreen.tsx +2 -2
  18. package/src/identity/hub/screens/IdentitySummary.tsx +32 -12
  19. package/src/identity/hub/screens/MenuScreen.tsx +17 -17
  20. package/src/identity/hub/screens/NetworkScreen.tsx +7 -3
  21. package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +9 -7
  22. package/src/identity/hub/screens/RestoreFlow.tsx +2 -2
  23. package/src/identity/hub/screens/StorageCredentialScreen.tsx +5 -5
  24. package/src/identity/wallet/wallet-page/wallet.html +1095 -966
  25. package/src/models/ModelPicker.tsx +71 -71
  26. package/src/models/llamacppPreflight.ts +1 -1
  27. package/src/models/modelPickerOptions.ts +22 -22
  28. package/src/storage/factoryReset.ts +17 -20
  29. package/src/tools/privateContinuityEditTool.ts +1 -1
  30. package/src/ui/BrandSplash.tsx +7 -4
@@ -1,44 +1,44 @@
1
- <!doctype html>
2
- <html>
3
-
4
- <head>
5
- <meta charset="utf-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1">
7
- <title>wallet request</title>
8
- <style>
9
- *,
10
- *::before,
11
- *::after {
12
- box-sizing: border-box;
13
- }
14
-
15
- :root {
16
- interpolate-size: allow-keywords;
17
- --font-ui: Inter, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
18
- --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "DejaVu Sans Mono", "Liberation Mono", monospace;
19
- --font-display: Inter, "SF Pro Display", "Segoe UI", system-ui, sans-serif;
20
- --c-gray-900: #1f2330;
21
- --c-gray-800: #2f364a;
22
- --c-gray-700: #49526a;
23
- --c-gray-500: #6f7890;
24
- --c-gray-400: #949bb0;
25
- --c-blue-400: #5f8fff;
26
- --c-blue-500: #3f76ff;
27
- --c-blue-700: #2954c6;
28
- --c-danger: #e5484d;
29
- --c-success: #1aa86b;
30
- --ease-standard: cubic-bezier(0.2, 0.0, 0, 1);
31
- --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
32
- --fg-1: #1f2330;
33
- }
34
-
35
- html,
36
- body {
37
- height: 100%;
38
- margin: 0;
39
- overflow: hidden;
40
- }
41
-
1
+ <!doctype html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <title>wallet request</title>
8
+ <style>
9
+ *,
10
+ *::before,
11
+ *::after {
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ :root {
16
+ interpolate-size: allow-keywords;
17
+ --font-ui: Inter, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
18
+ --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "DejaVu Sans Mono", "Liberation Mono", monospace;
19
+ --font-display: Inter, "SF Pro Display", "Segoe UI", system-ui, sans-serif;
20
+ --c-gray-900: #1f2330;
21
+ --c-gray-800: #2f364a;
22
+ --c-gray-700: #49526a;
23
+ --c-gray-500: #6f7890;
24
+ --c-gray-400: #949bb0;
25
+ --c-blue-400: #5f8fff;
26
+ --c-blue-500: #3f76ff;
27
+ --c-blue-700: #2954c6;
28
+ --c-danger: #e5484d;
29
+ --c-success: #1aa86b;
30
+ --ease-standard: cubic-bezier(0.2, 0.0, 0, 1);
31
+ --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
32
+ --fg-1: #1f2330;
33
+ }
34
+
35
+ html,
36
+ body {
37
+ height: 100%;
38
+ margin: 0;
39
+ overflow: hidden;
40
+ }
41
+
42
42
  body {
43
43
  position: relative;
44
44
  font-family: var(--font-ui);
@@ -46,12 +46,12 @@
46
46
  background: #fbfaf6;
47
47
  display: grid;
48
48
  place-items: center;
49
- padding: 24px;
50
- }
51
-
52
- body::before {
53
- content: "";
54
- position: fixed;
49
+ padding: clamp(18px, 3vw, 28px);
50
+ }
51
+
52
+ body::before {
53
+ content: "";
54
+ position: fixed;
55
55
  inset: 0;
56
56
  z-index: 0;
57
57
  background:
@@ -62,11 +62,11 @@
62
62
  radial-gradient(60% 55% at 85% 85%, rgba(212, 230, 245, 0.85), transparent 65%),
63
63
  linear-gradient(180deg, #fef3ee 0%, #fef7e8 40%, #f1f9ee 75%, #ecf3fb 100%);
64
64
  filter: saturate(1.05);
65
- }
66
-
67
- body::after {
68
- content: "";
69
- position: fixed;
65
+ }
66
+
67
+ body::after {
68
+ content: "";
69
+ position: fixed;
70
70
  inset: 0;
71
71
  z-index: 0;
72
72
  pointer-events: none;
@@ -74,1009 +74,1138 @@
74
74
  background-size: 3px 3px;
75
75
  mix-blend-mode: multiply;
76
76
  opacity: 0.6;
77
- }
78
-
79
- main {
80
- position: relative;
81
- z-index: 1;
82
- width: min(720px, 100%);
83
- max-height: calc(100vh - 48px);
84
- display: flex;
85
- flex-direction: column;
86
- background: rgba(255, 255, 255, 0.95);
87
- backdrop-filter: blur(40px) saturate(1.4);
88
- -webkit-backdrop-filter: blur(40px) saturate(1.4);
89
- border: 1px solid rgba(255, 255, 255, 0.7);
90
- border-radius: 28px;
91
- box-shadow:
92
- 0 1px 0 rgba(255, 255, 255, 0.9) inset,
93
- 0 -1px 0 rgba(255, 255, 255, 0.4) inset,
94
- 0 30px 60px -20px rgba(31, 35, 48, 0.25),
95
- 0 12px 24px -8px rgba(31, 35, 48, 0.12);
96
- overflow: hidden;
97
- height: fit-content;
98
- }
99
-
100
- main::before {
101
- content: "";
102
- position: absolute;
103
- top: 0;
104
- left: 12%;
105
- right: 12%;
106
- height: 1px;
107
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.95), transparent);
108
- pointer-events: none;
109
- }
110
-
111
- .chrome {
77
+ }
78
+
79
+ main {
80
+ position: relative;
81
+ z-index: 1;
82
+ width: min(700px, 100%);
83
+ max-height: calc(100dvh - 36px);
84
+ display: flex;
85
+ flex-direction: column;
86
+ background: rgba(255, 255, 255, 0.95);
87
+ backdrop-filter: blur(40px) saturate(1.4);
88
+ -webkit-backdrop-filter: blur(40px) saturate(1.4);
89
+ border: 1px solid rgba(255, 255, 255, 0.7);
90
+ border-radius: 26px;
91
+ box-shadow:
92
+ 0 1px 0 rgba(255, 255, 255, 0.9) inset,
93
+ 0 -1px 0 rgba(255, 255, 255, 0.4) inset,
94
+ 0 30px 60px -20px rgba(31, 35, 48, 0.25),
95
+ 0 12px 24px -8px rgba(31, 35, 48, 0.12);
96
+ overflow: hidden;
97
+ height: fit-content;
98
+ }
99
+
100
+ main::before {
101
+ content: "";
102
+ position: absolute;
103
+ top: 0;
104
+ left: 12%;
105
+ right: 12%;
106
+ height: 1px;
107
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.95), transparent);
108
+ pointer-events: none;
109
+ }
110
+
111
+ .chrome {
112
112
  display: flex;
113
113
  align-items: center;
114
114
  gap: 8px;
115
- padding: 14px 18px;
116
- border-bottom: 1px solid rgba(255, 255, 255, 0.5);
117
- background: linear-gradient(180deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.2));
118
- flex: none;
119
- }
120
-
121
- .light {
122
- width: 12px;
123
- height: 12px;
124
- border-radius: 999px;
125
- box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.08), 0 1px 0 rgba(255, 255, 255, 0.6) inset;
126
- }
127
-
128
- .light.r {
129
- background: #ff5f57;
130
- }
131
-
132
- .light.y {
133
- background: #febc2e;
134
- }
135
-
136
- .light.g {
137
- background: #28c840;
138
- }
139
-
140
- .chrome-title {
115
+ padding: 14px 20px;
116
+ border-bottom: 1px solid rgba(255, 255, 255, 0.5);
117
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.2));
118
+ flex: none;
119
+ }
120
+
121
+ .light {
122
+ width: 10px;
123
+ height: 10px;
124
+ border-radius: 999px;
125
+ box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.08), 0 1px 0 rgba(255, 255, 255, 0.6) inset;
126
+ }
127
+
128
+ .light.r {
129
+ background: #ff5f57;
130
+ }
131
+
132
+ .light.y {
133
+ background: #febc2e;
134
+ }
135
+
136
+ .light.g {
137
+ background: #28c840;
138
+ }
139
+
140
+ .chrome-title {
141
141
  flex: 1;
142
142
  text-align: center;
143
- font-size: 12px;
144
- color: rgba(31, 35, 48, 0.55);
145
- font-weight: 500;
146
- letter-spacing: 0.01em;
147
- margin-right: 56px;
148
- }
149
-
150
- .body {
143
+ font-size: 11.5px;
144
+ color: rgba(31, 35, 48, 0.55);
145
+ font-weight: 500;
146
+ letter-spacing: 0.01em;
147
+ margin-right: 56px;
148
+ }
149
+
150
+ .body {
151
151
  flex: 1;
152
152
  min-height: 0;
153
- padding: 18px 28px 22px;
153
+ padding: clamp(20px, 3vw, 28px) clamp(22px, 4vw, 32px) clamp(22px, 3vw, 30px);
154
154
  display: flex;
155
155
  flex-direction: column;
156
- gap: 14px;
157
- overflow: hidden;
158
- }
159
-
160
- .splash-wrap {
156
+ gap: 15px;
157
+ overflow: hidden;
158
+ }
159
+
160
+ .splash-wrap {
161
161
  flex: none;
162
162
  display: flex;
163
163
  justify-content: center;
164
164
  margin: 6px 0 2px;
165
165
  padding: 4px 0;
166
- }
167
-
168
- .splash {
166
+ }
167
+
168
+ .splash {
169
169
  font-family: var(--font-mono);
170
- font-size: 11px;
171
- font-weight: 800;
172
- line-height: 1.05;
173
- letter-spacing: 0;
174
- white-space: pre;
175
- margin: 0;
176
- color: var(--c-gray-700);
177
- text-align: center;
178
- transform: scaleX(0.75);
179
- transform-origin: 50% 50%;
180
- }
181
-
182
- .spinner {
183
- display: inline-block;
184
- width: 14px;
185
- height: 14px;
186
- border-radius: 999px;
187
- border: 1.6px solid color-mix(in srgb, var(--accent) 30%, transparent);
188
- border-top-color: var(--accent);
189
- animation: spin 0.9s linear infinite;
190
- }
191
-
192
- @keyframes spin {
193
- to {
194
- transform: rotate(360deg);
195
- }
196
- }
197
-
198
- [data-flow] {
199
- --accent: #6366f1;
200
- --accent-soft: rgba(99, 102, 241, 0.12);
201
- --glow: rgba(99, 102, 241, 0.35);
202
- }
203
-
204
- main,
205
- .head .label,
170
+ font-size: clamp(9.5px, 1.35vw, 11px);
171
+ font-weight: 800;
172
+ line-height: 1.05;
173
+ letter-spacing: 0;
174
+ white-space: pre;
175
+ margin: 0;
176
+ color: var(--c-gray-700);
177
+ text-align: center;
178
+ transform: scaleX(0.75);
179
+ transform-origin: 50% 50%;
180
+ }
181
+
182
+ .spinner {
183
+ display: inline-block;
184
+ width: 14px;
185
+ height: 14px;
186
+ border-radius: 999px;
187
+ border: 1.6px solid color-mix(in srgb, var(--accent) 30%, transparent);
188
+ border-top-color: var(--accent);
189
+ animation: spin 0.9s linear infinite;
190
+ }
191
+
192
+ @keyframes spin {
193
+ to {
194
+ transform: rotate(360deg);
195
+ }
196
+ }
197
+
198
+ [data-flow] {
199
+ --accent: #6366f1;
200
+ --accent-soft: rgba(99, 102, 241, 0.12);
201
+ --glow: rgba(99, 102, 241, 0.35);
202
+ }
203
+
204
+ main,
205
+ .head .label,
206
206
  .status,
207
207
  .status-line,
208
208
  .status-hint,
209
209
  .flow-detail,
210
- .tx-summary,
211
210
  #error-block-slot,
212
211
  .net-pill {
213
212
  transition: height 480ms var(--ease-standard), background-color 320ms var(--ease-standard), border-color 320ms var(--ease-standard), color 320ms var(--ease-standard), box-shadow 320ms var(--ease-standard);
214
- }
215
-
216
- .status-line,
217
- .status-hint {
218
- transition: opacity 280ms var(--ease-standard), transform 280ms var(--ease-standard), color 320ms var(--ease-standard);
219
- }
220
-
221
- .status-line.is-changing,
222
- .status-hint.is-changing {
223
- opacity: 0;
224
- transform: translateY(-3px);
225
- }
226
-
227
- .head {
228
- display: flex;
213
+ }
214
+
215
+ .status-line,
216
+ .status-hint {
217
+ transition: opacity 280ms var(--ease-standard), transform 280ms var(--ease-standard), color 320ms var(--ease-standard);
218
+ }
219
+
220
+ .status-line.is-changing,
221
+ .status-hint.is-changing {
222
+ opacity: 0;
223
+ transform: translateY(-3px);
224
+ }
225
+
226
+ .head {
227
+ display: flex;
229
228
  align-items: center;
230
229
  justify-content: space-between;
231
230
  gap: 12px;
232
- }
233
-
234
- .head .label {
235
- font-family: var(--font-ui);
236
- color: var(--c-gray-800);
237
- font-size: 12.5px;
238
- font-weight: 500;
239
- letter-spacing: 0;
240
- text-transform: capitalize;
241
- display: inline-flex;
242
- align-items: center;
243
- gap: 8px;
244
- padding: 5px 12px 5px 10px;
245
- background: rgba(255, 255, 255, 0.6);
246
- border-radius: 999px;
247
- border: 1px solid rgba(255, 255, 255, 0.85);
248
- box-shadow: 0 1px 2px rgba(31, 35, 48, 0.05), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
249
- }
250
-
251
- .head .label::before {
252
- content: "";
253
- width: 6px;
254
- height: 6px;
255
- border-radius: 999px;
256
- background: var(--accent);
257
- box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
258
- }
259
-
260
- .flow-title {
261
- font-family: var(--font-display);
262
- font-size: 18px;
231
+ }
232
+
233
+ .head .label {
234
+ font-family: var(--font-ui);
235
+ color: var(--c-gray-800);
236
+ font-size: 11.5px;
237
+ font-weight: 500;
238
+ letter-spacing: 0;
239
+ text-transform: capitalize;
240
+ display: inline-flex;
241
+ align-items: center;
242
+ gap: 6px;
243
+ padding: 5px 13px 5px 10px;
244
+ background: rgba(255, 255, 255, 0.6);
245
+ border-radius: 999px;
246
+ border: 1px solid rgba(255, 255, 255, 0.85);
247
+ box-shadow: 0 1px 2px rgba(31, 35, 48, 0.05), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
248
+ }
249
+
250
+ .head .label::before {
251
+ content: "";
252
+ width: 6px;
253
+ height: 6px;
254
+ border-radius: 999px;
255
+ background: var(--accent);
256
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
257
+ }
258
+
259
+ .flow-title {
260
+ font-family: var(--font-display);
261
+ font-size: clamp(18px, 2.6vw, 20px);
263
262
  font-weight: 600;
264
- line-height: 1.18;
265
- letter-spacing: 0;
266
- margin: 0;
267
- color: var(--c-gray-900);
268
- text-wrap: balance;
269
- }
270
-
271
- .details {
263
+ line-height: 1.22;
264
+ letter-spacing: 0;
265
+ margin: 0;
266
+ color: var(--c-gray-900);
267
+ text-wrap: balance;
268
+ }
269
+
270
+ .details {
272
271
  display: flex;
273
272
  flex-direction: column;
274
- gap: 8px;
275
- padding: 12px 14px;
276
- background: rgba(255, 255, 255, 0.45);
277
- backdrop-filter: blur(16px);
278
- border: 1px solid rgba(255, 255, 255, 0.85);
273
+ gap: 9px;
274
+ padding: 14px 16px;
275
+ background: rgba(255, 255, 255, 0.45);
276
+ backdrop-filter: blur(16px);
277
+ border: 1px solid rgba(255, 255, 255, 0.85);
279
278
  border-radius: 14px;
280
- box-shadow: 0 4px 16px -4px rgba(31, 35, 48, 0.08), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
281
- max-height: 200px;
282
- opacity: 1;
283
- margin-top: 0;
284
- overflow: hidden;
285
- transition: max-height 480ms var(--ease-standard), padding 480ms var(--ease-standard), margin 480ms var(--ease-standard), opacity 480ms var(--ease-standard), border-color 480ms var(--ease-standard), box-shadow 480ms var(--ease-standard);
286
- }
287
-
288
- .details[hidden] {
289
- display: flex;
290
- max-height: 0;
291
- opacity: 0;
292
- padding-top: 0;
293
- padding-bottom: 0;
294
- margin-top: -14px;
295
- border-color: transparent;
296
- box-shadow: none;
297
- pointer-events: none;
298
- }
299
-
300
- .flow-detail {
301
- display: grid;
302
- grid-template-columns: 88px 1fr;
279
+ box-shadow: 0 4px 16px -4px rgba(31, 35, 48, 0.08), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
280
+ max-height: 210px;
281
+ opacity: 1;
282
+ margin-top: 0;
283
+ overflow: hidden;
284
+ transition: max-height 480ms var(--ease-standard), padding 480ms var(--ease-standard), margin 480ms var(--ease-standard), opacity 480ms var(--ease-standard), border-color 480ms var(--ease-standard), box-shadow 480ms var(--ease-standard);
285
+ }
286
+
287
+ .details[hidden] {
288
+ display: flex;
289
+ max-height: 0;
290
+ opacity: 0;
291
+ padding-top: 0;
292
+ padding-bottom: 0;
293
+ margin-top: -14px;
294
+ border-color: transparent;
295
+ box-shadow: none;
296
+ pointer-events: none;
297
+ }
298
+
299
+ .flow-detail {
300
+ display: grid;
301
+ grid-template-columns: 96px 1fr;
303
302
  align-items: center;
304
303
  gap: 12px;
305
- font-size: 13px;
306
- color: var(--c-gray-700);
307
- margin: 0;
308
- line-height: 1.4;
309
- }
310
-
311
- .flow-detail[hidden] {
312
- display: none;
313
- }
314
-
315
- .flow-detail .key {
316
- font-family: var(--font-mono);
317
- font-size: 10.5px;
318
- font-weight: 500;
319
- color: var(--c-gray-500);
320
- text-transform: lowercase;
321
- letter-spacing: 0.05em;
304
+ font-size: 13.5px;
305
+ color: var(--c-gray-700);
306
+ margin: 0;
307
+ line-height: 1.35;
308
+ }
309
+
310
+ .flow-detail[hidden] {
311
+ display: none;
312
+ }
313
+
314
+ .flow-detail .key {
315
+ font-family: var(--font-mono);
316
+ font-size: 10px;
317
+ font-weight: 500;
318
+ color: var(--c-gray-500);
319
+ text-transform: lowercase;
320
+ letter-spacing: 0.05em;
322
321
  padding: 3px 8px;
323
- background: rgba(15, 17, 23, 0.04);
324
- border: 1px solid rgba(15, 17, 23, 0.06);
325
- border-radius: 6px;
326
- justify-self: start;
327
- min-width: 0;
328
- }
329
-
330
- .flow-detail span:last-child {
331
- overflow: hidden;
332
- text-overflow: ellipsis;
333
- white-space: nowrap;
334
- }
335
-
336
- .status {
322
+ background: rgba(15, 17, 23, 0.04);
323
+ border: 1px solid rgba(15, 17, 23, 0.06);
324
+ border-radius: 6px;
325
+ justify-self: start;
326
+ min-width: 0;
327
+ }
328
+
329
+ .flow-detail span:last-child {
330
+ overflow: hidden;
331
+ text-overflow: ellipsis;
332
+ white-space: nowrap;
333
+ }
334
+
335
+ .status {
337
336
  display: flex;
338
337
  flex-direction: column;
339
- gap: 6px;
340
- padding: 14px 16px;
341
- background: rgba(255, 255, 255, 0.45);
342
- backdrop-filter: blur(16px);
343
- border: 1px solid rgba(255, 255, 255, 0.85);
338
+ gap: 7px;
339
+ padding: 15px 17px;
340
+ background: rgba(255, 255, 255, 0.45);
341
+ backdrop-filter: blur(16px);
342
+ border: 1px solid rgba(255, 255, 255, 0.85);
344
343
  border-radius: 14px;
345
- box-shadow: 0 8px 24px -6px rgba(31, 35, 48, 0.08), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
346
- }
347
-
348
- .status-line {
344
+ box-shadow: 0 8px 24px -6px rgba(31, 35, 48, 0.08), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
345
+ }
346
+
347
+ .status-line {
349
348
  display: flex;
350
349
  align-items: center;
351
350
  gap: 12px;
351
+ font-size: 13.5px;
352
+ font-weight: 500;
353
+ color: var(--c-gray-900);
354
+ margin: 0;
355
+ }
356
+
357
+ .status-line .marker {
358
+ font-family: var(--font-mono);
352
359
  font-size: 13px;
353
- font-weight: 500;
354
- color: var(--c-gray-900);
355
- margin: 0;
356
- }
357
-
358
- .status-line .marker {
359
- font-family: var(--font-mono);
360
- font-size: 13px;
361
- width: 28px;
362
- height: 28px;
363
- display: inline-flex;
364
- align-items: center;
365
- justify-content: center;
366
- border-radius: 999px;
367
- background: var(--accent-soft);
368
- color: var(--accent);
369
- flex: none;
370
- font-variant-numeric: tabular-nums;
371
- font-weight: 600;
372
- box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 15%, transparent) inset;
373
- }
374
-
375
- .status-hint {
376
- font-size: 12px;
360
+ width: 29px;
361
+ height: 29px;
362
+ display: inline-flex;
363
+ align-items: center;
364
+ justify-content: center;
365
+ border-radius: 999px;
366
+ background: var(--accent-soft);
367
+ color: var(--accent);
368
+ flex: none;
369
+ font-variant-numeric: tabular-nums;
370
+ font-weight: 600;
371
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 15%, transparent) inset;
372
+ }
373
+
374
+ .status-hint {
375
+ font-size: 12.5px;
377
376
  color: var(--c-gray-500);
378
- margin: 0 0 0 40px;
379
- line-height: 1.45;
380
- }
381
-
382
- .tx-summary {
383
- display: grid;
384
- grid-template-columns: max-content minmax(0, 1fr);
385
- align-items: center;
386
- column-gap: 14px;
387
- row-gap: 6px;
388
- padding: 12px 16px;
389
- background: rgba(255, 255, 255, 0.45);
390
- backdrop-filter: blur(16px);
391
- border: 1px solid rgba(255, 255, 255, 0.85);
377
+ margin: 0 0 0 41px;
378
+ line-height: 1.4;
379
+ }
380
+
381
+ #error-block-slot:empty {
382
+ display: block;
383
+ max-height: 0;
384
+ opacity: 0;
385
+ padding-top: 0;
386
+ padding-bottom: 0;
387
+ margin-top: -14px;
388
+ border-color: transparent;
389
+ box-shadow: none;
390
+ pointer-events: none;
391
+ }
392
+
393
+ #error-block-slot {
394
+ padding: 12px 14px;
395
+ background: rgba(255, 255, 255, 0.45);
396
+ backdrop-filter: blur(16px);
397
+ border: 1px solid rgba(255, 255, 255, 0.85);
392
398
  border-radius: 14px;
393
- box-shadow: 0 4px 16px -4px rgba(31, 35, 48, 0.08), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
394
- max-height: 240px;
395
- opacity: 1;
396
- margin-top: 0;
397
- overflow: hidden;
398
- transition: max-height 480ms var(--ease-standard), padding 480ms var(--ease-standard), margin 480ms var(--ease-standard), opacity 480ms var(--ease-standard), background-color 320ms var(--ease-standard), border-color 320ms var(--ease-standard);
399
- }
400
-
401
- .tx-summary[hidden] {
402
- display: grid;
403
- max-height: 0;
404
- opacity: 0;
405
- padding-top: 0;
406
- padding-bottom: 0;
407
- margin-top: -14px;
408
- border-color: transparent;
409
- background-color: transparent;
410
- pointer-events: none;
411
- }
412
-
413
- .tx-key {
414
- font-family: var(--font-mono);
415
- font-size: 11px;
416
- color: var(--c-gray-500);
417
- text-transform: lowercase;
418
- }
419
-
420
- .tx-value {
399
+ box-shadow: 0 8px 24px -6px rgba(31, 35, 48, 0.08), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
400
+ max-height: 400px;
401
+ opacity: 1;
402
+ margin-top: 0;
403
+ overflow: hidden;
404
+ transition: max-height 480ms var(--ease-standard), padding 480ms var(--ease-standard), margin 480ms var(--ease-standard), opacity 480ms var(--ease-standard), border-color 320ms var(--ease-standard), box-shadow 320ms var(--ease-standard);
405
+ }
406
+
407
+ .error-title {
408
+ color: var(--c-gray-900);
421
409
  font-size: 12px;
422
- color: var(--c-gray-800);
423
- overflow: hidden;
424
- text-overflow: ellipsis;
425
- white-space: nowrap;
426
- }
427
-
428
- .tx-value.muted {
429
- color: var(--c-gray-400);
430
- }
431
-
432
- #error-block-slot:empty {
433
- display: block;
434
- max-height: 0;
435
- opacity: 0;
436
- padding-top: 0;
437
- padding-bottom: 0;
438
- margin-top: -14px;
439
- border-color: transparent;
440
- box-shadow: none;
441
- pointer-events: none;
442
- }
443
-
444
- #error-block-slot {
445
- padding: 16px 20px;
446
- background: rgba(255, 255, 255, 0.45);
447
- backdrop-filter: blur(16px);
448
- border: 1px solid rgba(255, 255, 255, 0.85);
449
- border-radius: 16px;
450
- box-shadow: 0 8px 24px -6px rgba(31, 35, 48, 0.08), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
451
- max-height: 400px;
452
- opacity: 1;
453
- margin-top: 0;
454
- overflow: hidden;
455
- transition: max-height 480ms var(--ease-standard), padding 480ms var(--ease-standard), margin 480ms var(--ease-standard), opacity 480ms var(--ease-standard), border-color 320ms var(--ease-standard), box-shadow 320ms var(--ease-standard);
456
- }
457
-
458
- .error-title {
459
- color: var(--c-gray-900);
410
+ font-weight: 600;
411
+ letter-spacing: 0;
412
+ text-transform: capitalize;
413
+ margin: 0 0 4px;
414
+ }
415
+
416
+ .error-msg {
417
+ color: var(--c-gray-900);
460
418
  font-size: 13px;
461
- font-weight: 600;
462
- letter-spacing: 0;
463
- text-transform: capitalize;
464
- margin: 0 0 4px;
465
- }
466
-
467
- .error-msg {
468
- color: var(--c-gray-900);
469
- font-size: 13.5px;
470
- font-weight: 600;
471
- margin: 0 0 6px;
472
- line-height: 1.45;
473
- }
474
-
475
- .error-hint {
476
- color: var(--c-gray-500);
477
- font-size: 12.5px;
478
- margin: 0;
479
- line-height: 1.5;
480
- }
481
-
482
- .error-hint a {
483
- color: var(--accent);
484
- text-decoration: none;
485
- font-weight: 600;
486
- border-bottom: 1px dashed color-mix(in srgb, var(--accent) 50%, transparent);
487
- padding-bottom: 1px;
488
- transition: all 0.2s ease;
489
- }
490
-
491
- .error-hint a:hover {
492
- color: var(--accent);
493
- border-bottom-style: solid;
494
- }
495
-
496
- .error-hint code {
497
- font-family: var(--font-mono);
498
- font-size: 11px;
499
- padding: 2px 6px;
500
- background: rgba(15, 17, 23, 0.06);
501
- border-radius: 4px;
502
- color: var(--c-gray-700);
503
- }
504
-
505
- .footer {
506
- flex: none;
507
- padding: 14px 22px 16px;
508
- border-top: 1px solid rgba(255, 255, 255, 0.5);
509
- background: linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.5));
510
- display: flex;
511
- align-items: center;
512
- justify-content: space-between;
419
+ font-weight: 600;
420
+ margin: 0 0 6px;
421
+ line-height: 1.45;
422
+ }
423
+
424
+ .error-hint {
425
+ color: var(--c-gray-500);
426
+ font-size: 11.5px;
427
+ margin: 0;
428
+ line-height: 1.5;
429
+ }
430
+
431
+ .error-hint a {
432
+ color: var(--accent);
433
+ text-decoration: none;
434
+ font-weight: 600;
435
+ border-bottom: 1px dashed color-mix(in srgb, var(--accent) 50%, transparent);
436
+ padding-bottom: 1px;
437
+ transition: all 0.2s ease;
438
+ }
439
+
440
+ .error-hint a:hover {
441
+ color: var(--accent);
442
+ border-bottom-style: solid;
443
+ }
444
+
445
+ .error-hint code {
446
+ font-family: var(--font-mono);
447
+ font-size: 11px;
448
+ padding: 2px 6px;
449
+ background: rgba(15, 17, 23, 0.06);
450
+ border-radius: 4px;
451
+ color: var(--c-gray-700);
452
+ }
453
+
454
+ .footer {
455
+ flex: none;
456
+ padding: 15px 22px 17px;
457
+ border-top: 1px solid rgba(255, 255, 255, 0.5);
458
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.5));
459
+ display: flex;
460
+ align-items: center;
461
+ justify-content: space-between;
513
462
  gap: 12px;
514
- }
515
-
516
- .net-pill {
517
- display: inline-flex;
518
- align-items: center;
519
- gap: 6px;
520
- font-family: var(--font-mono);
463
+ }
464
+
465
+ .net-pill {
466
+ display: inline-flex;
467
+ align-items: center;
468
+ gap: 6px;
469
+ font-family: var(--font-mono);
521
470
  font-size: 11px;
522
- color: var(--c-gray-500);
471
+ color: var(--c-gray-500);
523
472
  padding: 5px 10px;
524
- background: rgba(255, 255, 255, 0.6);
525
- border: 1px solid rgba(255, 255, 255, 0.8);
526
- border-radius: 999px;
527
- box-shadow: 0 1px 2px rgba(31, 35, 48, 0.04);
473
+ background: rgba(255, 255, 255, 0.6);
474
+ border: 1px solid rgba(255, 255, 255, 0.8);
475
+ border-radius: 999px;
476
+ box-shadow: 0 1px 2px rgba(31, 35, 48, 0.04);
477
+ }
478
+
479
+ .net-pill .dot {
480
+ width: 6px;
481
+ height: 6px;
482
+ border-radius: 999px;
483
+ background: var(--c-success);
484
+ box-shadow: 0 0 0 2px rgba(26, 168, 107, 0.18);
485
+ }
486
+
487
+ .net-pill[hidden] {
488
+ display: none;
489
+ }
490
+
491
+ .actions {
492
+ display: inline-flex;
493
+ align-items: center;
494
+ gap: 10px;
495
+ }
496
+
497
+ .shortcut {
498
+ font: inherit;
499
+ font-family: var(--font-ui);
500
+
501
+ font-weight: 500;
502
+ background: rgba(255, 255, 255, 0.7);
503
+ border: 1px solid rgba(255, 255, 255, 0.85);
504
+ padding: 8px 14px;
505
+ cursor: pointer;
506
+ color: var(--c-gray-700);
507
+ display: inline-flex;
508
+ align-items: center;
509
+ gap: 6px;
510
+ border-radius: 999px;
511
+ transition: all 0.15s var(--ease-standard);
512
+ box-shadow: 0 1px 2px rgba(31, 35, 48, 0.03), 0 1px 0 rgba(255, 255, 255, 0.5) inset;
513
+ }
514
+
515
+ .shortcut:focus {
516
+ outline: none;
517
+ }
518
+
519
+ .shortcut:focus-visible {
520
+ outline: 2px solid var(--c-blue-400);
521
+ outline-offset: 2px;
522
+ }
523
+
524
+ .shortcut:hover:not(:disabled) {
525
+ background: rgba(255, 255, 255, 0.95);
526
+ color: var(--c-gray-900);
527
+ transform: translateY(-0.5px);
528
+ }
529
+
530
+ .shortcut:active:not(:disabled) {
531
+ transform: translateY(0);
532
+ }
533
+
534
+ .shortcut:disabled {
535
+ opacity: 0.4;
536
+ cursor: not-allowed;
537
+ }
538
+
539
+ .shortcut[hidden] {
540
+ display: none;
541
+ }
542
+
543
+ .shortcut.primary {
544
+ background: var(--c-gray-900);
545
+ border-color: var(--c-gray-900);
546
+ color: #fff;
547
+ box-shadow: 0 2px 8px -2px rgba(15, 17, 23, 0.2), 0 1px 0 rgba(255, 255, 255, 0.1) inset;
548
+ }
549
+
550
+ .shortcut.primary:hover:not(:disabled) {
551
+ background: #1a1d27;
552
+ color: #fff;
553
+ }
554
+
555
+ .shortcut.primary .key {
556
+ background: rgba(255, 255, 255, 0.14);
557
+ border-color: rgba(255, 255, 255, 0.18);
558
+ color: rgba(255, 255, 255, 0.9);
559
+ }
560
+
561
+ .key {
562
+ display: inline-flex;
563
+ align-items: center;
564
+ justify-content: center;
565
+ gap: 4px;
566
+ padding: 2px 6px;
567
+ font-family: var(--font-mono);
568
+ font-size: 10.5px;
569
+ font-weight: 500;
570
+ color: var(--c-gray-500);
571
+ background: rgba(15, 17, 23, 0.04);
572
+ border: 1px solid rgba(15, 17, 23, 0.07);
573
+ border-radius: 5px;
574
+ letter-spacing: 0.02em;
575
+ }
576
+
577
+ .shortcut .pix {
578
+ color: currentColor;
579
+ opacity: 0.85;
580
+ display: inline-flex;
581
+ }
582
+
583
+ .head .net {
584
+ display: none;
528
585
  }
529
586
 
530
- .net-pill .dot {
531
- width: 6px;
532
- height: 6px;
533
- border-radius: 999px;
534
- background: var(--c-success);
535
- box-shadow: 0 0 0 2px rgba(26, 168, 107, 0.18);
536
- }
587
+ @media (max-width: 560px), (max-height: 680px) {
588
+ body {
589
+ padding: 10px;
590
+ }
537
591
 
538
- .net-pill[hidden] {
539
- display: none;
540
- }
592
+ main {
593
+ max-height: calc(100dvh - 20px);
594
+ border-radius: 18px;
595
+ }
541
596
 
542
- .actions {
543
- display: inline-flex;
544
- align-items: center;
545
- gap: 10px;
546
- }
597
+ .chrome {
598
+ padding: 9px 12px;
599
+ }
547
600
 
548
- .shortcut {
549
- font: inherit;
550
- font-family: var(--font-ui);
551
- font-size: 13px;
552
- font-weight: 500;
553
- background: rgba(255, 255, 255, 0.7);
554
- border: 1px solid rgba(255, 255, 255, 0.85);
555
- padding: 7px 14px;
556
- cursor: pointer;
557
- color: var(--c-gray-700);
558
- display: inline-flex;
559
- align-items: center;
560
- gap: 8px;
561
- border-radius: 999px;
562
- transition: all 0.15s var(--ease-standard);
563
- box-shadow: 0 1px 2px rgba(31, 35, 48, 0.03), 0 1px 0 rgba(255, 255, 255, 0.5) inset;
564
- }
601
+ .body {
602
+ padding: 12px 13px 14px;
603
+ gap: 9px;
604
+ }
565
605
 
566
- .shortcut:focus {
567
- outline: none;
568
- }
606
+ .splash-wrap {
607
+ margin: 2px 0 0;
608
+ padding: 2px 0;
609
+ }
569
610
 
570
- .shortcut:focus-visible {
571
- outline: 2px solid var(--c-blue-400);
572
- outline-offset: 2px;
573
- }
611
+ .splash {
612
+ font-size: 8px;
613
+ }
574
614
 
575
- .shortcut:hover:not(:disabled) {
576
- background: rgba(255, 255, 255, 0.95);
577
- color: var(--c-gray-900);
578
- transform: translateY(-0.5px);
579
- }
615
+ .flow-title {
616
+ font-size: 16px;
617
+ }
580
618
 
581
- .shortcut:active:not(:disabled) {
582
- transform: translateY(0);
583
- }
619
+ .details,
620
+ .status {
621
+ padding: 10px 11px;
622
+ border-radius: 12px;
623
+ }
584
624
 
585
- .shortcut:disabled {
586
- opacity: 0.4;
587
- cursor: not-allowed;
588
- }
625
+ .flow-detail {
626
+ grid-template-columns: 74px 1fr;
627
+ gap: 8px;
628
+ font-size: 12px;
629
+ }
589
630
 
590
- .shortcut[hidden] {
591
- display: none;
592
- }
631
+ .status-line {
632
+ gap: 8px;
633
+ font-size: 12px;
634
+ }
593
635
 
594
- .shortcut.primary {
595
- background: var(--c-gray-900);
596
- border-color: var(--c-gray-900);
597
- color: #fff;
598
- box-shadow: 0 2px 8px -2px rgba(15, 17, 23, 0.2), 0 1px 0 rgba(255, 255, 255, 0.1) inset;
599
- }
636
+ .status-line .marker {
637
+ width: 24px;
638
+ height: 24px;
639
+ }
600
640
 
601
- .shortcut.primary:hover:not(:disabled) {
602
- background: #1a1d27;
603
- color: #fff;
604
- }
641
+ .status-hint {
642
+ margin-left: 32px;
643
+ font-size: 11.5px;
644
+ }
605
645
 
606
- .shortcut.primary .key {
607
- background: rgba(255, 255, 255, 0.14);
608
- border-color: rgba(255, 255, 255, 0.18);
609
- color: rgba(255, 255, 255, 0.9);
610
- }
646
+ .footer {
647
+ padding: 10px 12px 12px;
648
+ gap: 8px;
649
+ }
611
650
 
612
- .key {
613
- display: inline-flex;
614
- align-items: center;
615
- justify-content: center;
616
- gap: 4px;
617
- padding: 2px 7px;
618
- font-family: var(--font-mono);
619
- font-size: 10.5px;
620
- font-weight: 500;
621
- color: var(--c-gray-500);
622
- background: rgba(15, 17, 23, 0.04);
623
- border: 1px solid rgba(15, 17, 23, 0.07);
624
- border-radius: 5px;
625
- letter-spacing: 0.02em;
626
- }
651
+ .actions {
652
+ gap: 7px;
653
+ }
627
654
 
628
- .shortcut .pix {
629
- color: currentColor;
630
- opacity: 0.85;
631
- display: inline-flex;
632
- }
655
+ .shortcut {
656
+ font-size: 12px;
657
+ padding: 6px 10px;
658
+ }
633
659
 
634
- .head .net {
635
- display: none;
660
+ .net-pill {
661
+ font-size: 10px;
662
+ padding: 4px 8px;
663
+ }
636
664
  }
637
- </style>
638
- </head>
639
665
 
640
- <body>
641
- <main data-flow="sign" id="card">
642
- <div class="chrome">
643
- <span class="light r"></span>
644
- <span class="light y"></span>
645
- <span class="light g"></span>
646
- <span class="chrome-title" id="chrome-title">ethagent</span>
647
- </div>
648
- <div class="body">
649
- <div class="splash-wrap">
650
- <pre class="splash" id="splash"></pre>
651
- </div>
652
- <div class="head"><span class="label" id="prompt-text">signature request</span></div>
653
- <h2 class="flow-title" id="flow-title">Sign a message to prove ownership</h2>
654
- <div class="details" id="details-block">
655
- <p class="flow-detail" id="flow-detail"><span class="key" id="detail-key">message</span><span
656
- id="detail-val">—</span></p>
657
- </div>
658
- <div class="status" id="status-block">
659
- <p class="status-line"><span class="marker" id="status-marker">·</span><span id="status-text">connecting to your
660
- wallet…</span></p>
661
- <p class="status-hint" id="status-hint">open your wallet extension if it doesn't pop up automatically.</p>
662
- </div>
663
- <div class="tx-summary" id="tx-summary" hidden></div>
664
- <div id="error-block-slot"></div>
665
- </div>
666
- <div class="footer">
667
- <span class="net-pill" id="network-row"><span class="dot"></span><span id="net-val">Sepolia</span></span>
668
- <div class="actions">
669
- <button id="cancel" class="shortcut"><span class="key">esc</span><span>cancel</span></button>
670
- <button id="approve" class="shortcut primary" hidden><span class="key"><svg width="10" height="10"
671
- viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"
672
- stroke-linejoin="round">
673
- <polyline points="9 10 4 15 9 20"></polyline>
674
- <path d="M20 4v7a4 4 0 0 1-4 4H4"></path>
675
- </svg></span><span>retry</span></button>
676
- </div>
677
- </div>
678
- </main>
679
- <script>
680
- const glyphs = {
681
- eyes: [
682
- " .-+: ",
683
- " :=- -%@@@%. ",
684
- " *@@@@@#- *@@- ",
685
- " +@@. +@ ",
686
- " @@= -#=-+++=+: ",
687
- " #% .:===-: -@* +@@@@% ",
688
- " *@-+@@@@@: %@@+ @@@=#@ ",
689
- " *@= @@@@@@@- .@.@@@@@@@ : ",
690
- " @@+=@@@@@@@@@@@@: .% *@@@@@*-= ",
691
- " #:-@ -@@@@@@@@@-+% @ -@@@- # ",
692
- " : #+ @@@@@@@- -% =# = ",
693
- " -@: *@ .+%% ",
694
- " :%#: -- ",
695
- " .-: "
696
- ].join("\n"),
697
- ellipsis: "…",
698
- emDash: "—"
699
- };
700
- function escapeHtml(value) {
701
- return String(value == null ? "" : value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
666
+ /* dark mode: terminal-style window. */
667
+ main {
668
+ background: #0b0f14;
669
+ color: #f1f1f1;
670
+ border-color: rgba(255, 255, 255, 0.08);
671
+ box-shadow:
672
+ 0 1px 0 rgba(255, 255, 255, 0.05) inset,
673
+ 0 -1px 0 rgba(0, 0, 0, 0.4) inset,
674
+ 0 30px 60px -20px rgba(0, 0, 0, 0.55),
675
+ 0 12px 24px -8px rgba(0, 0, 0, 0.35);
676
+ --c-gray-900: #f1f1f1;
677
+ --c-gray-800: #d8d8d8;
678
+ --c-gray-700: #b0b0b0;
679
+ --c-gray-500: #8a8a8a;
680
+ --c-gray-400: #6f6f6f;
702
681
  }
703
- function renderEyes() { return escapeHtml(glyphs.eyes); }
704
- document.getElementById("splash").innerHTML = renderEyes();
705
682
 
706
- const config = window.__WALLET_CONFIG__ || { sessionToken: "preview", kind: "sign", chainIdHex: "0xaa36a7", message: "identity proof for 0x9F2a…BC4e" };
707
- const CLOSE_DELAY_MS = 1800;
708
- const TX_CLOSE_DELAY_MS = 8500;
709
- const CANCEL_CLOSE_DELAY_MS = 3200;
710
- const WALLET_PROVIDER_WAIT_MS = 3000;
711
- const WALLET_PROVIDER_POLL_MS = 100;
712
- const CHAINS = { "0x1": { name: "Ethereum Mainnet" }, "0xaa36a7": { name: "Sepolia" }, "0x2105": { name: "Base" }, "0x14a34": { name: "Base Sepolia" }, "0xa": { name: "OP Mainnet" }, "0xa4b1": { name: "Arbitrum One" } };
713
- const FLOW_COPY = {
714
- account: { accent: "sign", tabTitle: "connect wallet", label: "wallet request", title: "Connect wallet to find your agent", detail: null },
715
- sign: { accent: "sign", tabTitle: "sign message", label: "signature request", title: "Sign a message to prove ownership", detail: "message" },
716
- "sign-transaction": { accent: "transaction", tabTitle: "approve identity update", label: "identity approval", title: "Sign and submit in one wallet flow", detail: "message" },
717
- transaction: { accent: "transaction", tabTitle: "submit transaction", label: "transaction request", title: "Submit transaction to mint your ERC-8004 agent", detail: "registry" }
718
- };
719
- const STATE_TITLES = { connecting: "connecting wallet", approveSign: "approve signature", preparingTransaction: "preparing transaction", approveTransaction: "approve transaction", error: "wallet error", default: "wallet request" };
683
+ .splash {
684
+ color: #b0b0b0;
685
+ }
686
+
687
+ main::before {
688
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.18), transparent);
689
+ }
690
+
691
+ .chrome {
692
+ border-bottom-color: rgba(255, 255, 255, 0.06);
693
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.01));
694
+ }
695
+
696
+ .chrome-title {
697
+ color: rgba(241, 241, 241, 0.55);
698
+ }
699
+
700
+ .head .label {
701
+ background: rgba(255, 255, 255, 0.06);
702
+ border-color: rgba(255, 255, 255, 0.1);
703
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.05) inset;
704
+ }
705
+
706
+ .details,
707
+ .status,
708
+ #error-block-slot {
709
+ background: rgba(255, 255, 255, 0.04);
710
+ border-color: rgba(255, 255, 255, 0.08);
711
+ box-shadow: 0 4px 16px -4px rgba(0, 0, 0, 0.35), 0 1px 0 rgba(255, 255, 255, 0.04) inset;
712
+ }
713
+
714
+ .flow-detail .key {
715
+ background: rgba(255, 255, 255, 0.05);
716
+ border-color: rgba(255, 255, 255, 0.08);
717
+ }
718
+
719
+ .error-hint code {
720
+ background: rgba(255, 255, 255, 0.06);
721
+ }
722
+
723
+ .footer {
724
+ border-top-color: rgba(255, 255, 255, 0.06);
725
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.01), rgba(255, 255, 255, 0.04));
726
+ }
727
+
728
+ .net-pill {
729
+ background: rgba(255, 255, 255, 0.04);
730
+ border-color: rgba(255, 255, 255, 0.08);
731
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
732
+ }
733
+
734
+ /* esc key: secondary black */
735
+ #cancel.shortcut {
736
+ background: #1f2330;
737
+ border-color: rgba(255, 255, 255, 0.08);
738
+ color: #b0b0b0;
739
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.04) inset;
740
+ }
741
+
742
+ #cancel.shortcut:hover:not(:disabled) {
743
+ background: #2a2e3c;
744
+ color: #f1f1f1;
745
+ }
746
+
747
+ /* primary action: pin literal so the var override does not bleed through */
748
+ .shortcut.primary {
749
+ background: #1f2330;
750
+ border-color: #1f2330;
751
+ color: #fff;
752
+ box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.5), 0 1px 0 rgba(255, 255, 255, 0.1) inset;
753
+ }
754
+
755
+ .shortcut.primary:hover:not(:disabled) {
756
+ background: #2a2e3c;
757
+ color: #fff;
758
+ }
759
+
760
+ .shortcut.primary .key {
761
+ background: rgba(255, 255, 255, 0.12);
762
+ border-color: rgba(255, 255, 255, 0.2);
763
+ color: rgba(255, 255, 255, 0.9);
764
+ }
765
+
766
+ .key {
767
+ background: rgba(255, 255, 255, 0.06);
768
+ border-color: rgba(255, 255, 255, 0.1);
769
+ }
770
+ </style>
771
+ </head>
772
+
773
+ <body>
774
+ <main data-flow="sign" id="card">
775
+ <div class="chrome">
776
+ <span class="light r"></span>
777
+ <span class="light y"></span>
778
+ <span class="light g"></span>
779
+ <span class="chrome-title" id="chrome-title">ethagent</span>
780
+ </div>
781
+ <div class="body">
782
+ <div class="splash-wrap">
783
+ <pre class="splash" id="splash"></pre>
784
+ </div>
785
+ <div class="head"><span class="label" id="prompt-text">signature request</span></div>
786
+ <h2 class="flow-title" id="flow-title">Sign a message to prove ownership</h2>
787
+ <div class="details" id="details-block">
788
+ <p class="flow-detail" id="flow-detail"><span class="key" id="detail-key">message</span><span
789
+ id="detail-val">—</span></p>
790
+ </div>
791
+ <div class="status" id="status-block">
792
+ <p class="status-line"><span class="marker" id="status-marker">·</span><span id="status-text">connecting to your
793
+ wallet…</span></p>
794
+ <p class="status-hint" id="status-hint">open your wallet extension if it doesn't pop up automatically.</p>
795
+ </div>
796
+ <div id="error-block-slot"></div>
797
+ </div>
798
+ <div class="footer">
799
+ <span class="net-pill" id="network-row"><span class="dot"></span><span id="net-val">Sepolia</span></span>
800
+ <div class="actions">
801
+ <button id="cancel" class="shortcut"><span class="key">esc</span><span>cancel</span></button>
802
+ <button id="approve" class="shortcut primary" hidden><span class="key"><svg width="10" height="10"
803
+ viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"
804
+ stroke-linejoin="round">
805
+ <polyline points="9 10 4 15 9 20"></polyline>
806
+ <path d="M20 4v7a4 4 0 0 1-4 4H4"></path>
807
+ </svg></span><span>retry</span></button>
808
+ </div>
809
+ </div>
810
+ </main>
811
+ <script>
812
+ const glyphs = {
813
+ eyes: [
814
+ " .-+: ",
815
+ " :=- -%@@@%. ",
816
+ " *@@@@@#- *@@- ",
817
+ " +@@. +@ ",
818
+ " @@= -#=-+++=+: ",
819
+ " #% .:===-: -@* +@@@@% ",
820
+ " *@-+@@@@@: %@@+ @@@=#@ ",
821
+ " *@= @@@@@@@- .@.@@@@@@@ : ",
822
+ " @@+=@@@@@@@@@@@@: .% *@@@@@*-= ",
823
+ " #:-@ -@@@@@@@@@-+% @ -@@@- # ",
824
+ " : #+ @@@@@@@- -% =# = ",
825
+ " -@: *@ .+%% ",
826
+ " :%#: -- ",
827
+ " .-: "
828
+ ].join("\n"),
829
+ ellipsis: "…",
830
+ emDash: "—"
831
+ };
832
+ function escapeHtml(value) {
833
+ return String(value == null ? "" : value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
834
+ }
835
+ function renderEyes() { return escapeHtml(glyphs.eyes); }
836
+ document.getElementById("splash").innerHTML = renderEyes();
837
+
838
+ const config = window.__WALLET_CONFIG__ || { sessionToken: "preview", kind: "sign", chainIdHex: "0xaa36a7", message: "identity proof for 0x9F2a…BC4e" };
839
+ const CLOSE_DELAY_MS = 10000;
840
+ const TX_CLOSE_DELAY_MS = 10000;
841
+ const CANCEL_CLOSE_DELAY_MS = 3200;
842
+ const WALLET_PROVIDER_WAIT_MS = 3000;
843
+ const WALLET_PROVIDER_POLL_MS = 100;
844
+ const CHAINS = { "0x1": { name: "Ethereum Mainnet" }, "0xaa36a7": { name: "Sepolia" }, "0x2105": { name: "Base" }, "0x14a34": { name: "Base Sepolia" }, "0xa": { name: "OP Mainnet" }, "0xa4b1": { name: "Arbitrum One" } };
845
+ const FLOW_COPY = {
846
+ account: { accent: "sign", tabTitle: "connect wallet", label: "wallet request", title: "Connect wallet to find your agent", detail: null },
847
+ sign: { accent: "sign", tabTitle: "sign message", label: "signature request", title: "Sign a message to prove ownership", detail: "message" },
848
+ "sign-transaction": { accent: "transaction", tabTitle: "approve identity update", label: "identity approval", title: "Sign, then save identity files", detail: "message" },
849
+ transaction: { accent: "transaction", tabTitle: "submit transaction", label: "transaction request", title: "Submit transaction to mint your ERC-8004 agent", detail: "registry" }
850
+ };
851
+ const STATE_TITLES = { connecting: "connecting wallet", approveSign: "approve signature", preparingTransaction: "preparing transaction", approveTransaction: "approve transaction", error: "wallet error", default: "wallet request" };
720
852
  function chainLabel(hex) { const k = String(hex || "").toLowerCase(); return (CHAINS[k] && CHAINS[k].name) || ("chain " + k); }
721
853
  function shortAddr(addr) { if (!addr || typeof addr !== "string") return glyphs.emDash; if (addr.length <= 14) return addr; return addr.slice(0, 6) + glyphs.ellipsis + addr.slice(-4); }
722
854
  function isTransactionFlow() { return config.kind === "transaction" || config.kind === "sign-transaction"; }
723
- function transactionActionLabel() { if (config.kind === "sign-transaction") return "publish identity snapshot"; if (config.kind === "transaction") return "submit transaction"; return "wallet request"; }
724
855
 
725
856
  const card = document.getElementById("card");
726
- const promptText = document.getElementById("prompt-text");
727
- const flowTitle = document.getElementById("flow-title");
728
- const networkRow = document.getElementById("network-row");
729
- const flowDetail = document.getElementById("flow-detail");
730
- const detailsBlock = document.getElementById("details-block");
731
- const detailKey = document.getElementById("detail-key");
732
- const detailVal = document.getElementById("detail-val");
733
- const netVal = document.getElementById("net-val");
734
- const statusBlock = document.getElementById("status-block");
735
- const statusMarker = document.getElementById("status-marker");
857
+ const promptText = document.getElementById("prompt-text");
858
+ const flowTitle = document.getElementById("flow-title");
859
+ const networkRow = document.getElementById("network-row");
860
+ const flowDetail = document.getElementById("flow-detail");
861
+ const detailsBlock = document.getElementById("details-block");
862
+ const detailKey = document.getElementById("detail-key");
863
+ const detailVal = document.getElementById("detail-val");
864
+ const netVal = document.getElementById("net-val");
865
+ const statusBlock = document.getElementById("status-block");
866
+ const statusMarker = document.getElementById("status-marker");
736
867
  const statusText = document.getElementById("status-text");
737
868
  const statusHint = document.getElementById("status-hint");
738
869
  const errorSlot = document.getElementById("error-block-slot");
739
- const txSummary = document.getElementById("tx-summary");
740
870
  const approve = document.getElementById("approve");
741
- const cancel = document.getElementById("cancel");
871
+ const cancel = document.getElementById("cancel");
742
872
  let announcedEthereum = null;
743
873
  let activeEthereum = null;
744
-
745
- function rememberAnnouncedProvider(event) { const provider = event && event.detail && event.detail.provider; if (provider && !announcedEthereum) announcedEthereum = provider; }
746
- window.addEventListener("eip6963:announceProvider", rememberAnnouncedProvider);
747
- try { window.dispatchEvent(new Event("eip6963:requestProvider")); } catch (_) { }
748
- function ethereumProvider() { return activeEthereum || window.ethereum || announcedEthereum; }
749
- function noWalletError() { return new Error("No browser wallet detected. Install MetaMask, Rabby, or use Brave."); }
750
-
751
- function waitForEthereumProvider(timeoutMs) {
752
- const existing = ethereumProvider();
753
- if (existing) return Promise.resolve(existing);
754
- return new Promise((resolve, reject) => {
755
- let settled = false;
756
- const cleanup = () => {
757
- clearTimeout(timer);
758
- clearInterval(interval);
759
- window.removeEventListener("ethereum#initialized", check);
760
- window.removeEventListener("eip6963:announceProvider", onAnnounce);
761
- };
762
- const finish = (fn) => {
763
- if (settled) return;
764
- settled = true;
765
- cleanup();
766
- fn();
767
- };
768
- const check = () => { const provider = ethereumProvider(); if (provider) finish(() => resolve(provider)); };
769
- const onAnnounce = (event) => { rememberAnnouncedProvider(event); check(); };
770
- const timer = setTimeout(() => finish(() => reject(noWalletError())), timeoutMs || WALLET_PROVIDER_WAIT_MS);
771
- const interval = setInterval(check, WALLET_PROVIDER_POLL_MS);
772
- window.addEventListener("ethereum#initialized", check);
773
- window.addEventListener("eip6963:announceProvider", onAnnounce);
774
- try { window.dispatchEvent(new Event("eip6963:requestProvider")); } catch (_) { }
775
- check();
776
- });
777
- }
778
- async function walletRequest(method, params) {
779
- const provider = ethereumProvider();
780
- if (!provider) throw noWalletError();
781
- return await provider.request(params === undefined ? { method } : { method, params });
782
- }
783
-
784
- let spinning = false;
785
- function startSpinner() {
786
- spinning = true;
787
- statusMarker.innerHTML = '<span class="spinner" aria-hidden="true"></span>';
788
- statusMarker.style.background = "transparent";
789
- }
790
- function stopSpinner() {
791
- if (!spinning) return;
792
- spinning = false;
793
- statusMarker.innerHTML = "";
794
- statusMarker.style.background = "";
795
- }
796
- function setMarker(text) { stopSpinner(); statusMarker.textContent = text; }
797
- function flowCopy() { return FLOW_COPY[config.kind] || FLOW_COPY.sign; }
798
-
799
- function tabTitleForState(state) {
800
- if (state === "connecting") return STATE_TITLES.connecting;
801
- if (state === "approve-sign") return STATE_TITLES.approveSign;
802
- if (state === "preparing-transaction") return STATE_TITLES.preparingTransaction;
803
- if (state === "approve-transaction") return STATE_TITLES.approveTransaction;
804
- if (state === "error") return STATE_TITLES.error;
805
- if (state === "approve") {
806
- if (config.kind === "account") return "approve connection";
807
- if (config.kind === "sign") return STATE_TITLES.approveSign;
808
- return STATE_TITLES.approveTransaction;
809
- }
810
- if (state === "submitting") {
811
- if (config.kind === "account") return STATE_TITLES.connecting;
812
- if (config.kind === "sign") return "verifying signature";
813
- return "confirming transaction";
814
- }
815
- if (state === "done") {
816
- if (config.kind === "account") return "wallet connected";
817
- if (config.kind === "sign") return "message signed";
818
- return "transaction submitted";
819
- }
820
- return flowCopy().tabTitle || STATE_TITLES.default;
821
- }
822
- function setTabTitle(title) {
823
- const t = title || flowCopy().tabTitle || STATE_TITLES.default;
824
- document.title = t;
825
- const chromeTitle = document.getElementById("chrome-title");
826
- if (chromeTitle) chromeTitle.textContent = "ethagent · " + t;
827
- }
828
- function messagePreview(message) { const preview = String(message || "").split("\n")[0]; return preview.length > 64 ? preview.slice(0, 64) + glyphs.ellipsis : preview; }
829
- function detailPreview(copy) {
830
- if (copy.detail === "message") return messagePreview(config.message);
831
- if (copy.detail === "registry" && config.tx) return shortAddr(config.tx.to);
832
- return "";
833
- }
834
- function showPreparedMessage(message) {
835
- const copy = flowCopy();
836
- if (copy.detail !== "message") return;
837
- const preview = messagePreview(message);
838
- detailKey.textContent = copy.detail;
839
- detailVal.textContent = preview;
840
- flowDetail.hidden = preview.length === 0;
841
- detailsBlock.hidden = flowDetail.hidden;
842
- }
843
- function applyFlowChrome() {
844
- const copy = flowCopy();
845
- card.dataset.flow = copy.accent;
846
- promptText.textContent = copy.label;
847
- flowTitle.textContent = copy.title;
848
- setTabTitle(copy.tabTitle);
849
- if (!copy.detail) {
850
- networkRow.hidden = true;
851
- flowDetail.hidden = true;
852
- detailsBlock.hidden = true;
853
- } else {
854
- networkRow.hidden = false;
855
- flowDetail.hidden = false;
856
- detailsBlock.hidden = false;
857
- netVal.textContent = chainLabel(config.chainIdHex);
858
- detailKey.textContent = copy.detail;
859
- detailVal.textContent = detailPreview(copy);
860
- flowDetail.hidden = detailVal.textContent.length === 0;
861
- if (flowDetail.hidden) detailsBlock.hidden = true;
862
- }
863
- }
864
-
865
- function setStatus(marker, text, hint, spin) {
866
- const lineEl = statusText.parentElement;
867
- const hintEl = statusHint;
868
- const apply = () => {
869
- if (spin) startSpinner();
870
- else setMarker(marker);
871
- statusText.textContent = text;
872
- hintEl.textContent = hint;
873
- requestAnimationFrame(() => {
874
- lineEl.classList.remove("is-changing");
875
- hintEl.classList.remove("is-changing");
876
- });
877
- };
878
- if (statusText.textContent === text && hintEl.textContent === hint) {
879
- if (spin) startSpinner();
880
- else setMarker(marker);
881
- return;
882
- }
883
- lineEl.classList.add("is-changing");
884
- hintEl.classList.add("is-changing");
885
- setTimeout(apply, 220);
886
- }
887
-
888
- function setState(state, payload) {
889
- payload = payload || {};
874
+ let closeCountdown = null;
875
+
876
+ function rememberAnnouncedProvider(event) { const provider = event && event.detail && event.detail.provider; if (provider && !announcedEthereum) announcedEthereum = provider; }
877
+ window.addEventListener("eip6963:announceProvider", rememberAnnouncedProvider);
878
+ try { window.dispatchEvent(new Event("eip6963:requestProvider")); } catch (_) { }
879
+ function ethereumProvider() { return activeEthereum || window.ethereum || announcedEthereum; }
880
+ function noWalletError() { return new Error("No browser wallet detected. Install MetaMask, Rabby, or use Brave."); }
881
+
882
+ function waitForEthereumProvider(timeoutMs) {
883
+ const existing = ethereumProvider();
884
+ if (existing) return Promise.resolve(existing);
885
+ return new Promise((resolve, reject) => {
886
+ let settled = false;
887
+ const cleanup = () => {
888
+ clearTimeout(timer);
889
+ clearInterval(interval);
890
+ window.removeEventListener("ethereum#initialized", check);
891
+ window.removeEventListener("eip6963:announceProvider", onAnnounce);
892
+ };
893
+ const finish = (fn) => {
894
+ if (settled) return;
895
+ settled = true;
896
+ cleanup();
897
+ fn();
898
+ };
899
+ const check = () => { const provider = ethereumProvider(); if (provider) finish(() => resolve(provider)); };
900
+ const onAnnounce = (event) => { rememberAnnouncedProvider(event); check(); };
901
+ const timer = setTimeout(() => finish(() => reject(noWalletError())), timeoutMs || WALLET_PROVIDER_WAIT_MS);
902
+ const interval = setInterval(check, WALLET_PROVIDER_POLL_MS);
903
+ window.addEventListener("ethereum#initialized", check);
904
+ window.addEventListener("eip6963:announceProvider", onAnnounce);
905
+ try { window.dispatchEvent(new Event("eip6963:requestProvider")); } catch (_) { }
906
+ check();
907
+ });
908
+ }
909
+ async function walletRequest(method, params) {
910
+ const provider = ethereumProvider();
911
+ if (!provider) throw noWalletError();
912
+ return await provider.request(params === undefined ? { method } : { method, params });
913
+ }
914
+
915
+ let spinning = false;
916
+ function startSpinner() {
917
+ spinning = true;
918
+ statusMarker.innerHTML = '<span class="spinner" aria-hidden="true"></span>';
919
+ statusMarker.style.background = "transparent";
920
+ }
921
+ function stopSpinner() {
922
+ if (!spinning) return;
923
+ spinning = false;
924
+ statusMarker.innerHTML = "";
925
+ statusMarker.style.background = "";
926
+ }
927
+ function setMarker(text) { stopSpinner(); statusMarker.textContent = text; }
928
+ function flowCopy() { return FLOW_COPY[config.kind] || FLOW_COPY.sign; }
929
+
930
+ function tabTitleForState(state) {
931
+ if (state === "connecting") return STATE_TITLES.connecting;
932
+ if (state === "approve-sign") return STATE_TITLES.approveSign;
933
+ if (state === "preparing-transaction") return STATE_TITLES.preparingTransaction;
934
+ if (state === "approve-transaction") return STATE_TITLES.approveTransaction;
935
+ if (state === "error") return STATE_TITLES.error;
936
+ if (state === "approve") {
937
+ if (config.kind === "account") return "approve connection";
938
+ if (config.kind === "sign") return STATE_TITLES.approveSign;
939
+ return STATE_TITLES.approveTransaction;
940
+ }
941
+ if (state === "submitting") {
942
+ if (config.kind === "account") return STATE_TITLES.connecting;
943
+ if (config.kind === "sign") return "verifying signature";
944
+ return "confirming transaction";
945
+ }
946
+ if (state === "done") {
947
+ if (config.kind === "account") return "wallet connected";
948
+ if (config.kind === "sign") return "message signed";
949
+ return "transaction submitted";
950
+ }
951
+ return flowCopy().tabTitle || STATE_TITLES.default;
952
+ }
953
+ function setTabTitle(title) {
954
+ const t = title || flowCopy().tabTitle || STATE_TITLES.default;
955
+ document.title = t;
956
+ const chromeTitle = document.getElementById("chrome-title");
957
+ if (chromeTitle) chromeTitle.textContent = "ethagent · " + t;
958
+ }
959
+ function messagePreview(message) { const preview = String(message || "").split("\n")[0]; return preview.length > 64 ? preview.slice(0, 64) + glyphs.ellipsis : preview; }
960
+ function detailPreview(copy) {
961
+ if (copy.detail === "message") return messagePreview(config.message);
962
+ if (copy.detail === "registry" && config.tx) return shortAddr(config.tx.to);
963
+ return "";
964
+ }
965
+ function showPreparedMessage(message) {
966
+ const copy = flowCopy();
967
+ if (copy.detail !== "message") return;
968
+ const preview = messagePreview(message);
969
+ detailKey.textContent = copy.detail;
970
+ detailVal.textContent = preview;
971
+ flowDetail.hidden = preview.length === 0;
972
+ detailsBlock.hidden = flowDetail.hidden;
973
+ }
974
+ function applyFlowChrome() {
975
+ const copy = flowCopy();
976
+ card.dataset.flow = copy.accent;
977
+ promptText.textContent = copy.label;
978
+ flowTitle.textContent = copy.title;
979
+ setTabTitle(copy.tabTitle);
980
+ if (!copy.detail) {
981
+ networkRow.hidden = true;
982
+ flowDetail.hidden = true;
983
+ detailsBlock.hidden = true;
984
+ } else {
985
+ networkRow.hidden = false;
986
+ flowDetail.hidden = false;
987
+ detailsBlock.hidden = false;
988
+ netVal.textContent = chainLabel(config.chainIdHex);
989
+ detailKey.textContent = copy.detail;
990
+ detailVal.textContent = detailPreview(copy);
991
+ flowDetail.hidden = detailVal.textContent.length === 0;
992
+ if (flowDetail.hidden) detailsBlock.hidden = true;
993
+ }
994
+ }
995
+
996
+ function setStatus(marker, text, hint, spin) {
997
+ const lineEl = statusText.parentElement;
998
+ const hintEl = statusHint;
999
+ const apply = () => {
1000
+ if (spin) startSpinner();
1001
+ else setMarker(marker);
1002
+ statusText.textContent = text;
1003
+ hintEl.textContent = hint;
1004
+ requestAnimationFrame(() => {
1005
+ lineEl.classList.remove("is-changing");
1006
+ hintEl.classList.remove("is-changing");
1007
+ });
1008
+ };
1009
+ if (statusText.textContent === text && hintEl.textContent === hint) {
1010
+ if (spin) startSpinner();
1011
+ else setMarker(marker);
1012
+ return;
1013
+ }
1014
+ lineEl.classList.add("is-changing");
1015
+ hintEl.classList.add("is-changing");
1016
+ setTimeout(apply, 220);
1017
+ }
1018
+
1019
+ function setState(state, payload) {
1020
+ payload = payload || {};
890
1021
  errorSlot.innerHTML = "";
891
1022
  statusBlock.style.display = "flex";
892
1023
  setTabTitle(tabTitleForState(state));
893
- renderTransactionSummary(payload);
894
1024
  switch (state) {
895
- case "connecting":
896
- setStatus("·", "connecting to your wallet…", "open your wallet extension if it doesn't pop up automatically.", true);
897
- break;
898
- case "approve":
899
- if (config.kind === "account") setStatus("·", "approve wallet connection", "the selected address will be used to search for owned agents.", true);
900
- else if (config.kind === "sign") setStatus("·", "approve the signature in your wallet", "a prompt should be open. waiting for your signature.", true);
901
- else setStatus("·", "review and submit the transaction in your wallet", "check the network and contract address before approving.", true);
902
- break;
1025
+ case "connecting":
1026
+ setStatus("·", "connecting to your wallet…", "open your wallet extension if it doesn't pop up automatically.", true);
1027
+ break;
1028
+ case "approve":
1029
+ if (config.kind === "account") setStatus("·", "approve wallet connection", "the selected address will be used to search for owned agents.", true);
1030
+ else if (config.kind === "sign") setStatus("·", "approve the signature in your wallet", "a prompt should be open. waiting for your signature.", true);
1031
+ else setStatus("·", "review and submit the transaction in your wallet", "check the network and contract address before approving.", true);
1032
+ break;
903
1033
  case "approve-sign":
904
- setStatus("·", "approve the signature in your wallet", "used to encrypt or unlock the agent backup.", true);
1034
+ setStatus("·", "approve the signature in your wallet", "this encrypts or decrypts SOUL.md and MEMORY.md.", true);
905
1035
  break;
906
1036
  case "preparing-transaction":
907
- setStatus("·", "saving encrypted IPFS backup and preparing transaction...", "keep this page open. the transaction prompt will appear here.", true);
908
- break;
909
- case "approve-transaction":
910
- setStatus("·", "review and submit the transaction in your wallet", "check the network and contract address before approving.", true);
911
- break;
912
- case "submitting":
913
- if (config.kind === "account") setStatus("·", "connecting wallet…", "sending the selected address back.", true);
914
- else if (config.kind === "sign") setStatus("·", "verifying signature…", "recovering the signing address.", true);
915
- else setStatus("·", "submitted · waiting for confirmation…", "your wallet has accepted the transaction.", true);
916
- break;
917
- case "done":
918
- setMarker("✓");
919
- statusText.textContent = config.kind === "account" ? "connected · returning" : config.kind === "sign" ? "signed · returning" : "submitted · returning";
920
- statusHint.textContent = payload.txHash ? (isTransactionFlow() ? "transaction submitted. returning." : "this tab will close shortly.") : "this tab will close shortly.";
921
- break;
922
- case "error":
923
- stopSpinner();
924
- statusBlock.style.display = "none";
925
- renderError(payload);
926
- break;
927
- }
928
- }
929
-
930
- function renderTransactionSummary() {
931
- if (!isTransactionFlow()) {
932
- txSummary.hidden = true;
933
- txSummary.innerHTML = "";
934
- return;
935
- }
936
- const rows = [["action", transactionActionLabel()], ["network", chainLabel(config.chainIdHex)]];
937
- txSummary.innerHTML = rows.map(([key, value]) =>
938
- '<span class="tx-key">' + escapeHtml(key) + "</span>" +
939
- '<span class="tx-value' + (value === glyphs.emDash ? " muted" : "") + '">' + escapeHtml(value) + "</span>"
940
- ).join("");
941
- txSummary.hidden = false;
942
- }
943
-
944
- function renderError(payload) {
945
- const msg = payload.message || "Something went wrong.";
946
- const isNoWallet = /no wallet|window\.ethereum|metamask|rabby|brave|extension/i.test(msg);
947
- const isUserReject = /user rejected|user denied|cancelled|canceled/i.test(msg);
948
- const isWrongChain = /chain|network/i.test(msg) && !isNoWallet;
949
- let title = "wallet error";
950
- let body = msg;
951
- let hint = "press enter to retry, or esc to abort.";
952
- if (isNoWallet) {
953
- title = "no wallet";
954
- body = "install a browser wallet.";
955
- hint = 'Get <a href="https://phantom.com/download" target="_blank" rel="noopener">Phantom</a>, then press <code>enter</code>.';
956
- } else if (isUserReject) {
957
- title = "rejected";
958
- body = "request declined in wallet.";
959
- hint = "press <code>enter</code> to retry, or <code>esc</code> to abort.";
960
- } else if (isWrongChain) {
961
- title = "wrong network";
962
- hint = "expected <code>" + escapeHtml(chainLabel(config.chainIdHex)) + "</code>. switch networks, then retry.";
963
- }
964
- errorSlot.innerHTML = '<p class="error-title">' + escapeHtml(title) + "</p>" + '<p class="error-msg">' + escapeHtml(body) + "</p>" + '<p class="error-hint">' + hint + "</p>";
965
- }
966
-
967
- async function post(path, body) {
968
- const r = await fetch(path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ ...(body || {}), sessionToken: config.sessionToken }) });
969
- const d = await r.json();
970
- if (!r.ok || !d.ok) throw new Error(d.error || "wallet request failed");
971
- return d;
1037
+ setStatus("·", "encrypting SOUL.md and MEMORY.md, then preparing the update...", "keep this page open. the transaction prompt will appear here.", true);
1038
+ break;
1039
+ case "approve-transaction":
1040
+ setStatus("·", "review and submit the transaction in your wallet", "check the network and contract address before approving.", true);
1041
+ break;
1042
+ case "submitting":
1043
+ if (config.kind === "account") setStatus("·", "connecting wallet…", "sending the selected address back.", true);
1044
+ else if (config.kind === "sign") setStatus("·", "verifying signature…", "recovering the signing address.", true);
1045
+ else setStatus("·", "submitted · waiting for confirmation…", "your wallet has accepted the transaction.", true);
1046
+ break;
1047
+ case "done":
1048
+ setMarker("✓");
1049
+ statusText.textContent = config.kind === "account" ? "connected · returning" : config.kind === "sign" ? "signed · returning" : "submitted · returning";
1050
+ statusHint.textContent = payload.txHash ? (isTransactionFlow() ? "transaction submitted. returning." : "this tab will close shortly.") : "this tab will close shortly.";
1051
+ break;
1052
+ case "error":
1053
+ stopSpinner();
1054
+ statusBlock.style.display = "none";
1055
+ renderError(payload);
1056
+ break;
1057
+ }
1058
+ }
1059
+
1060
+ function renderError(payload) {
1061
+ const msg = payload.message || "Something went wrong.";
1062
+ const isNoWallet = /no wallet|window\.ethereum|metamask|rabby|brave|extension/i.test(msg);
1063
+ const isUserReject = /user rejected|user denied|cancelled|canceled/i.test(msg);
1064
+ const isWrongChain = /chain|network/i.test(msg) && !isNoWallet;
1065
+ let title = "wallet error";
1066
+ let body = msg;
1067
+ let hint = "press enter to retry, or esc to abort.";
1068
+ if (isNoWallet) {
1069
+ title = "no wallet";
1070
+ body = "install a browser wallet.";
1071
+ hint = 'Get <a href="https://phantom.com/download" target="_blank" rel="noopener">Phantom</a>, then press <code>enter</code>.';
1072
+ } else if (isUserReject) {
1073
+ title = "rejected";
1074
+ body = "request declined in wallet.";
1075
+ hint = "press <code>enter</code> to retry, or <code>esc</code> to abort.";
1076
+ } else if (isWrongChain) {
1077
+ title = "wrong network";
1078
+ hint = "expected <code>" + escapeHtml(chainLabel(config.chainIdHex)) + "</code>. switch networks, then retry.";
1079
+ }
1080
+ errorSlot.innerHTML = '<p class="error-title">' + escapeHtml(title) + "</p>" + '<p class="error-msg">' + escapeHtml(body) + "</p>" + '<p class="error-hint">' + hint + "</p>";
1081
+ }
1082
+
1083
+ async function post(path, body) {
1084
+ const r = await fetch(path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ ...(body || {}), sessionToken: config.sessionToken }) });
1085
+ const d = await r.json();
1086
+ if (!r.ok || !d.ok) throw new Error(d.error || "wallet request failed");
1087
+ return d;
1088
+ }
1089
+ function showCloseCountdown(delayMs) {
1090
+ if (closeCountdown) clearInterval(closeCountdown);
1091
+ const deadline = Date.now() + delayMs;
1092
+ const update = () => {
1093
+ const seconds = Math.max(1, Math.ceil((deadline - Date.now()) / 1000));
1094
+ statusHint.textContent = "return to your terminal. closing in " + seconds + "s.";
1095
+ };
1096
+ update();
1097
+ closeCountdown = setInterval(update, 250);
972
1098
  }
973
1099
  function closeSoon(delayMs) {
1100
+ const ms = delayMs == null ? CLOSE_DELAY_MS : delayMs;
1101
+ showCloseCountdown(ms);
974
1102
  setTimeout(() => {
1103
+ if (closeCountdown) clearInterval(closeCountdown);
975
1104
  try { if (window.opener && !window.opener.closed) window.opener.focus(); } catch (_) { }
976
1105
  window.close();
977
1106
  window.open("", "_self");
978
1107
  window.close();
979
- }, delayMs == null ? CLOSE_DELAY_MS : delayMs);
980
- }
981
-
982
- async function ensureWallet() {
983
- setState("connecting");
984
- const provider = await waitForEthereumProvider();
985
- activeEthereum = provider;
986
- const accounts = await provider.request({ method: "eth_requestAccounts" });
987
- const account = accounts && accounts[0];
988
- if (!account) throw new Error("No wallet account was selected.");
989
- if (!config.chainIdHex) return account;
990
- try { await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: config.chainIdHex }] }); }
991
- catch (err) {
992
- const cur = await provider.request({ method: "eth_chainId" });
993
- if (String(cur).toLowerCase() !== String(config.chainIdHex).toLowerCase()) throw err;
994
- }
995
- return account;
996
- }
997
-
998
- async function runWalletFlow() {
999
- approve.disabled = true;
1000
- approve.hidden = true;
1001
- cancel.disabled = false;
1002
- errorSlot.innerHTML = "";
1003
- try {
1004
- const account = await ensureWallet();
1005
- if (config.kind === "account") {
1006
- setState("submitting");
1007
- await post("/complete", { account });
1008
- setState("done");
1009
- closeSoon();
1010
- return;
1011
- }
1012
- if (config.kind === "sign") {
1013
- const prepared = config.message ? { message: config.message } : await post("/prepare", { account });
1014
- showPreparedMessage(prepared.message);
1015
- setState("approve");
1016
- const signature = await walletRequest("personal_sign", [prepared.message, account]);
1017
- setState("submitting");
1018
- await post("/complete", { account, message: prepared.message, signature });
1019
- setState("done");
1020
- closeSoon();
1021
- return;
1022
- }
1023
- if (config.kind === "sign-transaction") {
1024
- const prepared = config.message ? { message: config.message } : await post("/prepare", { account });
1025
- showPreparedMessage(prepared.message);
1026
- setState("approve-sign", { account });
1027
- const signature = await walletRequest("personal_sign", [prepared.message, account]);
1028
- setState("preparing-transaction", { account });
1029
- const txPayload = await post("/prepare-transaction", { account, message: prepared.message, signature });
1030
- setState("approve-transaction", { account, tx: txPayload.tx });
1031
- const tx = txPayload.tx || {};
1032
- const txHash = await walletRequest("eth_sendTransaction", [{ from: account, to: tx.to, data: tx.data, value: tx.value }]);
1033
- setState("submitting", { account, tx, txHash });
1034
- await post("/complete", { account, txHash });
1035
- setState("done", { account, tx, txHash });
1036
- closeSoon(TX_CLOSE_DELAY_MS);
1037
- return;
1038
- }
1039
- if (config.kind === "transaction") {
1040
- setState("approve", { account, tx: config.tx });
1041
- const txHash = await walletRequest("eth_sendTransaction", [{ from: account, to: config.tx.to, data: config.tx.data, value: config.tx.value }]);
1042
- setState("submitting", { account, tx: config.tx, txHash });
1043
- await post("/complete", { account, txHash });
1044
- setState("done", { account, tx: config.tx, txHash });
1045
- closeSoon(TX_CLOSE_DELAY_MS);
1046
- return;
1047
- }
1048
- throw new Error("Unknown wallet request type.");
1049
- } catch (err) {
1050
- approve.disabled = false;
1051
- approve.hidden = false;
1052
- cancel.disabled = false;
1053
- setState("error", { message: (err && err.message) || String(err) });
1054
- }
1055
- }
1056
-
1057
- async function cancelFlow() {
1058
- approve.disabled = true;
1059
- cancel.disabled = true;
1060
- await post("/cancel", {}).catch(() => { });
1061
- setState("done");
1062
- statusText.textContent = "cancelled · returning";
1063
- closeSoon(CANCEL_CLOSE_DELAY_MS);
1064
- }
1065
- approve.onclick = runWalletFlow;
1066
- cancel.onclick = cancelFlow;
1067
- window.addEventListener("keydown", (e) => {
1068
- if (e.key === "Escape") { e.preventDefault(); cancelFlow(); }
1069
- else if (e.key === "Enter" && !approve.hidden && !approve.disabled) { e.preventDefault(); runWalletFlow(); }
1070
- });
1071
-
1072
- applyFlowChrome();
1073
- if (!window.__WALLET_PREVIEW__) {
1074
- window.addEventListener("load", () => setTimeout(runWalletFlow, 150));
1075
- } else {
1076
- window.__walletPreview = { setState, setConfig: (c) => { Object.assign(config, c); applyFlowChrome(); } };
1077
- setState("connecting");
1078
- }
1079
- </script>
1080
- </body>
1081
-
1082
- </html>
1108
+ }, ms);
1109
+ }
1110
+
1111
+ async function ensureWallet() {
1112
+ setState("connecting");
1113
+ const provider = await waitForEthereumProvider();
1114
+ activeEthereum = provider;
1115
+ const accounts = await provider.request({ method: "eth_requestAccounts" });
1116
+ const account = accounts && accounts[0];
1117
+ if (!account) throw new Error("No wallet account was selected.");
1118
+ if (!config.chainIdHex) return account;
1119
+ try { await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: config.chainIdHex }] }); }
1120
+ catch (err) {
1121
+ const cur = await provider.request({ method: "eth_chainId" });
1122
+ if (String(cur).toLowerCase() !== String(config.chainIdHex).toLowerCase()) throw err;
1123
+ }
1124
+ return account;
1125
+ }
1126
+
1127
+ async function runWalletFlow() {
1128
+ approve.disabled = true;
1129
+ approve.hidden = true;
1130
+ cancel.disabled = false;
1131
+ errorSlot.innerHTML = "";
1132
+ try {
1133
+ const account = await ensureWallet();
1134
+ if (config.kind === "account") {
1135
+ setState("submitting");
1136
+ await post("/complete", { account });
1137
+ setState("done");
1138
+ closeSoon();
1139
+ return;
1140
+ }
1141
+ if (config.kind === "sign") {
1142
+ const prepared = config.message ? { message: config.message } : await post("/prepare", { account });
1143
+ showPreparedMessage(prepared.message);
1144
+ setState("approve");
1145
+ const signature = await walletRequest("personal_sign", [prepared.message, account]);
1146
+ setState("submitting");
1147
+ await post("/complete", { account, message: prepared.message, signature });
1148
+ setState("done");
1149
+ closeSoon();
1150
+ return;
1151
+ }
1152
+ if (config.kind === "sign-transaction") {
1153
+ const prepared = config.message ? { message: config.message } : await post("/prepare", { account });
1154
+ showPreparedMessage(prepared.message);
1155
+ setState("approve-sign", { account });
1156
+ const signature = await walletRequest("personal_sign", [prepared.message, account]);
1157
+ setState("preparing-transaction", { account });
1158
+ const txPayload = await post("/prepare-transaction", { account, message: prepared.message, signature });
1159
+ setState("approve-transaction", { account, tx: txPayload.tx });
1160
+ const tx = txPayload.tx || {};
1161
+ const txHash = await walletRequest("eth_sendTransaction", [{ from: account, to: tx.to, data: tx.data, value: tx.value }]);
1162
+ setState("submitting", { account, tx, txHash });
1163
+ await post("/complete", { account, txHash });
1164
+ setState("done", { account, tx, txHash });
1165
+ closeSoon(TX_CLOSE_DELAY_MS);
1166
+ return;
1167
+ }
1168
+ if (config.kind === "transaction") {
1169
+ setState("approve", { account, tx: config.tx });
1170
+ const txHash = await walletRequest("eth_sendTransaction", [{ from: account, to: config.tx.to, data: config.tx.data, value: config.tx.value }]);
1171
+ setState("submitting", { account, tx: config.tx, txHash });
1172
+ await post("/complete", { account, txHash });
1173
+ setState("done", { account, tx: config.tx, txHash });
1174
+ closeSoon(TX_CLOSE_DELAY_MS);
1175
+ return;
1176
+ }
1177
+ throw new Error("Unknown wallet request type.");
1178
+ } catch (err) {
1179
+ approve.disabled = false;
1180
+ approve.hidden = false;
1181
+ cancel.disabled = false;
1182
+ setState("error", { message: (err && err.message) || String(err) });
1183
+ }
1184
+ }
1185
+
1186
+ async function cancelFlow() {
1187
+ approve.disabled = true;
1188
+ cancel.disabled = true;
1189
+ await post("/cancel", {}).catch(() => { });
1190
+ setState("done");
1191
+ statusText.textContent = "cancelled · returning";
1192
+ closeSoon(CANCEL_CLOSE_DELAY_MS);
1193
+ }
1194
+ approve.onclick = runWalletFlow;
1195
+ cancel.onclick = cancelFlow;
1196
+ window.addEventListener("keydown", (e) => {
1197
+ if (e.key === "Escape") { e.preventDefault(); cancelFlow(); }
1198
+ else if (e.key === "Enter" && !approve.hidden && !approve.disabled) { e.preventDefault(); runWalletFlow(); }
1199
+ });
1200
+
1201
+ applyFlowChrome();
1202
+ if (!window.__WALLET_PREVIEW__) {
1203
+ window.addEventListener("load", () => setTimeout(runWalletFlow, 150));
1204
+ } else {
1205
+ window.__walletPreview = { setState, setConfig: (c) => { Object.assign(config, c); applyFlowChrome(); } };
1206
+ setState("connecting");
1207
+ }
1208
+ </script>
1209
+ </body>
1210
+
1211
+ </html>