@tatchi-xyz/sdk 0.45.0 → 0.46.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/dist/cjs/react/components/AccountMenuButton/LinkedDevicesModal2.js +169 -172
  2. package/dist/cjs/react/components/AccountMenuButton/LinkedDevicesModal2.js.map +1 -1
  3. package/dist/cjs/react/components/QRCodeScanner.js +90 -96
  4. package/dist/cjs/react/components/QRCodeScanner.js.map +1 -1
  5. package/dist/cjs/react/components/theme/ThemeProvider.js +3 -17
  6. package/dist/cjs/react/components/theme/ThemeProvider.js.map +1 -1
  7. package/dist/cjs/react/context/TatchiPasskeyProvider.js +0 -1
  8. package/dist/cjs/react/context/TatchiPasskeyProvider.js.map +1 -1
  9. package/dist/esm/react/components/AccountMenuButton/LinkedDevicesModal2.js +169 -172
  10. package/dist/esm/react/components/AccountMenuButton/LinkedDevicesModal2.js.map +1 -1
  11. package/dist/esm/react/components/QRCodeScanner.js +90 -96
  12. package/dist/esm/react/components/QRCodeScanner.js.map +1 -1
  13. package/dist/esm/react/components/theme/ThemeProvider.js +4 -18
  14. package/dist/esm/react/components/theme/ThemeProvider.js.map +1 -1
  15. package/dist/esm/react/context/TatchiPasskeyProvider.js +0 -1
  16. package/dist/esm/react/context/TatchiPasskeyProvider.js.map +1 -1
  17. package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
  18. package/dist/types/src/react/components/theme/ThemeProvider.d.ts +0 -8
  19. package/dist/types/src/react/components/theme/ThemeProvider.d.ts.map +1 -1
  20. package/dist/types/src/react/components/theme/index.d.ts +1 -1
  21. package/dist/types/src/react/components/theme/index.d.ts.map +1 -1
  22. package/dist/types/src/react/context/TatchiPasskeyProvider.d.ts +4 -4
  23. package/dist/types/src/react/context/TatchiPasskeyProvider.d.ts.map +1 -1
  24. package/dist/types/src/react/index.d.ts +1 -1
  25. package/dist/types/src/react/index.d.ts.map +1 -1
  26. package/dist/workers/offline-export-sw.js +156 -1
  27. package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
  28. package/dist/workers/web3authn-signer.worker.js +1360 -2
  29. package/dist/workers/web3authn-vrf.worker.js +2857 -2
  30. package/package.json +1 -1
@@ -150,196 +150,193 @@ const LinkedDevicesModal = ({ nearAccountId, isOpen, onClose }) => {
150
150
  e.preventDefault();
151
151
  e.stopPropagation();
152
152
  };
153
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ThemeProvider.Theme, {
154
- mode: "scope-only",
155
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
156
- className: `w3a-access-keys-modal-backdrop theme-${theme}`,
157
- onClick: handleBackdropClick,
153
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ThemeProvider.Theme, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
154
+ className: `w3a-access-keys-modal-backdrop theme-${theme}`,
155
+ onClick: handleBackdropClick,
156
+ onMouseDown: (e) => e.stopPropagation(),
157
+ onMouseUp: (e) => e.stopPropagation(),
158
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
159
+ className: "w3a-access-keys-modal-content",
160
+ onClick: handleModalContentClick,
158
161
  onMouseDown: (e) => e.stopPropagation(),
159
162
  onMouseUp: (e) => e.stopPropagation(),
160
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
161
- className: "w3a-access-keys-modal-content",
162
- onClick: handleModalContentClick,
163
- onMouseDown: (e) => e.stopPropagation(),
164
- onMouseUp: (e) => e.stopPropagation(),
165
- children: [
166
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
167
- className: "w3a-access-keys-modal-header",
168
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
169
- className: "w3a-access-keys-modal-title",
170
- children: "Linked Devices"
171
- })
172
- }),
173
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
174
- className: "w3a-access-keys-modal-close",
163
+ children: [
164
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
165
+ className: "w3a-access-keys-modal-header",
166
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
167
+ className: "w3a-access-keys-modal-title",
168
+ children: "Linked Devices"
169
+ })
170
+ }),
171
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
172
+ className: "w3a-access-keys-modal-close",
173
+ onClick: (e) => {
174
+ e.preventDefault();
175
+ e.stopPropagation();
176
+ onClose();
177
+ },
178
+ children: "✕"
179
+ }),
180
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
181
+ className: "w3a-access-keys-error",
182
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: error }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
175
183
  onClick: (e) => {
176
184
  e.preventDefault();
177
185
  e.stopPropagation();
178
- onClose();
186
+ loadAuthenticators();
179
187
  },
180
- children: ""
181
- }),
182
- error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
183
- className: "w3a-access-keys-error",
184
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: error }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
185
- onClick: (e) => {
186
- e.preventDefault();
187
- e.stopPropagation();
188
- loadAuthenticators();
189
- },
190
- className: "w3a-btn w3a-btn-primary",
191
- children: "Try Again"
192
- })]
193
- }),
194
- !isLoading && !error && authRows.filter((r) => r.credentialId !== "placeholder").length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
195
- className: "w3a-access-keys-empty",
196
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "No authenticators found." })
197
- }),
198
- !error && authRows.filter((r) => r.credentialId !== "placeholder").length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
199
- className: "w3a-keys-list",
200
- children: (() => {
201
- console.log("authRows", authRows);
202
- const rows = authRows.filter((r) => r.credentialId !== "placeholder");
203
- const current = currentDeviceNumber != null ? rows.find((r) => r.deviceNumber === currentDeviceNumber) : null;
204
- const others = currentDeviceNumber != null ? rows.filter((r) => r.deviceNumber !== currentDeviceNumber) : rows;
205
- const items = [];
206
- if (current) {
207
- const index = 0;
208
- const currentKey = current.nearPublicKey || loginState?.nearPublicKey || null;
209
- const isCurrentKey = !!currentKey && loginState?.nearPublicKey === currentKey;
210
- const canDelete = !!accessKeyList && accessKeyList.keys.length > 1;
211
- const isDeletingThisKey = !!currentKey && deletingKeyPublicKey === currentKey;
212
- items.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
213
- className: "w3a-key-item",
214
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
215
- className: "w3a-key-content",
216
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
217
- className: "w3a-key-details",
218
- children: [
219
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
220
- className: "w3a-key-header",
221
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
222
- className: "mono w3a-device-row",
223
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
224
- className: "w3a-device-badge",
225
- children: ["Device ", current.deviceNumber]
226
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
227
- className: "w3a-current-device-text",
228
- children: "(current device)"
229
- })]
230
- })
231
- }),
232
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
233
- className: "mono w3a-registered",
234
- children: ["Registered: ", formatDateTime(current.registered)]
235
- }),
236
- currentKey && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
237
- className: "mono w3a-copyable-key w3a-access-key-current",
238
- onClick: (e) => {
239
- e.stopPropagation();
240
- copyToClipboard(currentKey, index);
241
- },
242
- onMouseEnter: () => setTooltipVisible(index),
243
- onMouseLeave: () => setTooltipVisible(null),
244
- title: "Click to copy",
245
- children: [
246
- "Access Key: ",
247
- currentKey,
248
- tooltipVisible === index && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
249
- className: "w3a-copy-tooltip",
250
- children: "Click to copy"
251
- })
252
- ]
188
+ className: "w3a-btn w3a-btn-primary",
189
+ children: "Try Again"
190
+ })]
191
+ }),
192
+ !isLoading && !error && authRows.filter((r) => r.credentialId !== "placeholder").length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
193
+ className: "w3a-access-keys-empty",
194
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "No authenticators found." })
195
+ }),
196
+ !error && authRows.filter((r) => r.credentialId !== "placeholder").length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
197
+ className: "w3a-keys-list",
198
+ children: (() => {
199
+ console.log("authRows", authRows);
200
+ const rows = authRows.filter((r) => r.credentialId !== "placeholder");
201
+ const current = currentDeviceNumber != null ? rows.find((r) => r.deviceNumber === currentDeviceNumber) : null;
202
+ const others = currentDeviceNumber != null ? rows.filter((r) => r.deviceNumber !== currentDeviceNumber) : rows;
203
+ const items = [];
204
+ if (current) {
205
+ const index = 0;
206
+ const currentKey = current.nearPublicKey || loginState?.nearPublicKey || null;
207
+ const isCurrentKey = !!currentKey && loginState?.nearPublicKey === currentKey;
208
+ const canDelete = !!accessKeyList && accessKeyList.keys.length > 1;
209
+ const isDeletingThisKey = !!currentKey && deletingKeyPublicKey === currentKey;
210
+ items.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
211
+ className: "w3a-key-item",
212
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
213
+ className: "w3a-key-content",
214
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
215
+ className: "w3a-key-details",
216
+ children: [
217
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
218
+ className: "w3a-key-header",
219
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
220
+ className: "mono w3a-device-row",
221
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
222
+ className: "w3a-device-badge",
223
+ children: ["Device ", current.deviceNumber]
224
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
225
+ className: "w3a-current-device-text",
226
+ children: "(current device)"
227
+ })]
253
228
  })
254
- ]
255
- }), currentKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
256
- className: "w3a-key-status",
257
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
258
- className: `w3a-btn ${isCurrentKey ? "w3a-btn-primary" : "w3a-btn-danger"}`,
259
- style: { width: "64px" },
260
- disabled: !canDelete || isDeletingThisKey,
229
+ }),
230
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
231
+ className: "mono w3a-registered",
232
+ children: ["Registered: ", formatDateTime(current.registered)]
233
+ }),
234
+ currentKey && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
235
+ className: "mono w3a-copyable-key w3a-access-key-current",
261
236
  onClick: (e) => {
262
- e.preventDefault();
263
237
  e.stopPropagation();
264
- if (canDelete && !isDeletingThisKey) handleDeleteKey(currentKey);
238
+ copyToClipboard(currentKey, index);
265
239
  },
266
- children: isDeletingThisKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "w3a-spinner" }) : "Delete"
240
+ onMouseEnter: () => setTooltipVisible(index),
241
+ onMouseLeave: () => setTooltipVisible(null),
242
+ title: "Click to copy",
243
+ children: [
244
+ "Access Key: ",
245
+ currentKey,
246
+ tooltipVisible === index && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
247
+ className: "w3a-copy-tooltip",
248
+ children: "Click to copy"
249
+ })
250
+ ]
267
251
  })
268
- })]
269
- })
270
- }, `current-${current.deviceNumber}`));
271
- }
272
- others.forEach((item, i) => {
273
- const canDelete = !!accessKeyList && accessKeyList.keys.length > 1;
274
- const isDeletingThisKey = !!item.nearPublicKey && deletingKeyPublicKey === item.nearPublicKey;
275
- items.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
276
- className: "w3a-key-item",
277
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
278
- className: "w3a-key-content",
279
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
280
- className: "w3a-key-details",
281
- children: [
282
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
283
- className: "w3a-key-header",
284
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
285
- className: "mono w3a-device-row",
286
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
287
- className: "w3a-device-badge",
288
- children: ["Device ", item.deviceNumber]
289
- })
252
+ ]
253
+ }), currentKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
254
+ className: "w3a-key-status",
255
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
256
+ className: `w3a-btn ${isCurrentKey ? "w3a-btn-primary" : "w3a-btn-danger"}`,
257
+ style: { width: "64px" },
258
+ disabled: !canDelete || isDeletingThisKey,
259
+ onClick: (e) => {
260
+ e.preventDefault();
261
+ e.stopPropagation();
262
+ if (canDelete && !isDeletingThisKey) handleDeleteKey(currentKey);
263
+ },
264
+ children: isDeletingThisKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "w3a-spinner" }) : "Delete"
265
+ })
266
+ })]
267
+ })
268
+ }, `current-${current.deviceNumber}`));
269
+ }
270
+ others.forEach((item, i) => {
271
+ const canDelete = !!accessKeyList && accessKeyList.keys.length > 1;
272
+ const isDeletingThisKey = !!item.nearPublicKey && deletingKeyPublicKey === item.nearPublicKey;
273
+ items.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
274
+ className: "w3a-key-item",
275
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
276
+ className: "w3a-key-content",
277
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
278
+ className: "w3a-key-details",
279
+ children: [
280
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
281
+ className: "w3a-key-header",
282
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
283
+ className: "mono w3a-device-row",
284
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
285
+ className: "w3a-device-badge",
286
+ children: ["Device ", item.deviceNumber]
290
287
  })
291
- }),
292
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
293
- className: "mono w3a-registered",
294
- children: ["Registered: ", formatDateTime(item.registered)]
295
- }),
296
- item.nearPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
297
- className: "mono w3a-copyable-key",
298
- onClick: (e) => {
299
- e.stopPropagation();
300
- copyToClipboard(item.nearPublicKey, 10 + i);
301
- },
302
- onMouseEnter: () => setTooltipVisible(10 + i),
303
- onMouseLeave: () => setTooltipVisible(null),
304
- title: "Click to copy",
305
- children: [
306
- "Access Key: ",
307
- item.nearPublicKey,
308
- tooltipVisible === 10 + i && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
309
- className: "w3a-copy-tooltip",
310
- children: "Click to copy"
311
- })
312
- ]
313
288
  })
314
- ]
315
- }), item.nearPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
316
- className: "w3a-key-status",
317
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
318
- className: "w3a-btn w3a-btn-danger",
319
- style: { width: "64px" },
320
- disabled: !canDelete || isDeletingThisKey,
289
+ }),
290
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
291
+ className: "mono w3a-registered",
292
+ children: ["Registered: ", formatDateTime(item.registered)]
293
+ }),
294
+ item.nearPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
295
+ className: "mono w3a-copyable-key",
321
296
  onClick: (e) => {
322
- e.preventDefault();
323
297
  e.stopPropagation();
324
- if (canDelete && !isDeletingThisKey) handleDeleteKey(item.nearPublicKey);
298
+ copyToClipboard(item.nearPublicKey, 10 + i);
325
299
  },
326
- children: isDeletingThisKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "w3a-spinner" }) : "Delete"
300
+ onMouseEnter: () => setTooltipVisible(10 + i),
301
+ onMouseLeave: () => setTooltipVisible(null),
302
+ title: "Click to copy",
303
+ children: [
304
+ "Access Key: ",
305
+ item.nearPublicKey,
306
+ tooltipVisible === 10 + i && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
307
+ className: "w3a-copy-tooltip",
308
+ children: "Click to copy"
309
+ })
310
+ ]
327
311
  })
328
- })]
329
- })
330
- }, `other-${item.deviceNumber}-${i}`));
331
- });
332
- return items;
333
- })()
334
- }),
335
- deleteError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
336
- className: "w3a-access-keys-error",
337
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: deleteError })
338
- })
339
- ]
340
- })
312
+ ]
313
+ }), item.nearPublicKey && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
314
+ className: "w3a-key-status",
315
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
316
+ className: "w3a-btn w3a-btn-danger",
317
+ style: { width: "64px" },
318
+ disabled: !canDelete || isDeletingThisKey,
319
+ onClick: (e) => {
320
+ e.preventDefault();
321
+ e.stopPropagation();
322
+ if (canDelete && !isDeletingThisKey) handleDeleteKey(item.nearPublicKey);
323
+ },
324
+ children: isDeletingThisKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "w3a-spinner" }) : "Delete"
325
+ })
326
+ })]
327
+ })
328
+ }, `other-${item.deviceNumber}-${i}`));
329
+ });
330
+ return items;
331
+ })()
332
+ }),
333
+ deleteError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
334
+ className: "w3a-access-keys-error",
335
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: deleteError })
336
+ })
337
+ ]
341
338
  })
342
- });
339
+ }) });
343
340
  };
344
341
 
345
342
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"LinkedDevicesModal2.js","names":["LinkedDevicesModal: React.FC<LinkedDevicesModalProps>","useTatchi","useTheme","getAuthenticatorsByUser","toAccountId","rows: Array<{ credentialId: string; registered: string; deviceNumber: number; nearPublicKey: string | null }>","err: any","Theme","items: React.ReactNode[]"],"sources":["../../../../../src/react/components/AccountMenuButton/LinkedDevicesModal.tsx"],"sourcesContent":["import React, { useState, useEffect } from 'react';\nimport { useTatchi } from '../../context';\nimport './LinkedDevicesModal.css';\nimport { useTheme, Theme } from '../theme';\n\nimport { getAuthenticatorsByUser } from '@/core/rpcCalls';\nimport type { ContractStoredAuthenticator } from '@/core/TatchiPasskey/syncAccount';\nimport type { AccessKeyList } from '@/core/NearClient';\nimport { toAccountId } from '@/core/types/accountIds';\n\ninterface LinkedDevicesModalProps {\n nearAccountId: string;\n isOpen: boolean;\n onClose: () => void;\n}\n\nexport const LinkedDevicesModal: React.FC<LinkedDevicesModalProps> = ({\n nearAccountId,\n isOpen,\n onClose\n}) => {\n const { tatchi, loginState, viewAccessKeyList } = useTatchi();\n const { theme } = useTheme();\n // Authenticators list: credentialId + registered timestamp + device number\n const [authRows, setAuthRows] = useState<Array<{\n credentialId: string;\n registered: string;\n deviceNumber: number;\n nearPublicKey: string | null;\n }>>([{ credentialId: 'placeholder', registered: '', deviceNumber: 0, nearPublicKey: null }]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [accessKeyList, setAccessKeyList] = useState<AccessKeyList | null>(null);\n const [tooltipVisible, setTooltipVisible] = useState<number | null>(null);\n const [copiedKeys, setCopiedKeys] = useState<Set<number>>(new Set());\n const [currentDeviceNumber, setCurrentDeviceNumber] = useState<number | null>(null);\n const [deletingKeyPublicKey, setDeletingKeyPublicKey] = useState<string | null>(null);\n const [deleteError, setDeleteError] = useState<string | null>(null);\n\n const formatDateTime = (iso: string) => {\n if (!iso) return '—';\n try { return new Date(iso).toLocaleString(); } catch { return iso; }\n };\n\n useEffect(() => {\n if (isOpen) {\n loadAuthenticators();\n // Also resolve current device number for highlighting\n (async () => {\n try {\n if (!tatchi) return;\n const { login } = await tatchi.getLoginSession(nearAccountId);\n const dn = (login as any)?.userData?.deviceNumber;\n if (typeof dn === 'number' && Number.isFinite(dn)) {\n setCurrentDeviceNumber(dn);\n } else {\n setCurrentDeviceNumber(null);\n }\n } catch {\n setCurrentDeviceNumber(null);\n }\n })();\n }\n }, [isOpen]);\n\n // Close on ESC press while modal is open\n useEffect(() => {\n if (!isOpen) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' || e.key === 'Esc') {\n e.preventDefault();\n onClose();\n }\n };\n window.addEventListener('keydown', onKeyDown);\n return () => window.removeEventListener('keydown', onKeyDown);\n }, [isOpen, onClose]);\n\n const loadAuthenticators = async () => {\n if (!tatchi) return;\n\n setIsLoading(true);\n setError(null);\n setDeleteError(null);\n\n try {\n const nearClient = tatchi.getNearClient();\n const contractId = tatchi.configs.contractId;\n const [tuples, keys] = await Promise.all([\n getAuthenticatorsByUser(nearClient, contractId, toAccountId(nearAccountId)),\n viewAccessKeyList(nearAccountId)\n ]);\n\n // Map each authenticator to a single row with credentialId and registered timestamp\n const rows: Array<{ credentialId: string; registered: string; deviceNumber: number; nearPublicKey: string | null }> = [];\n for (const [credentialId, auth] of (tuples || []) as Array<[string, ContractStoredAuthenticator]>) {\n const dn = Number(auth.device_number);\n const rawRegistered = String((auth as any).registered ?? '');\n const registered = (() => {\n if (!rawRegistered) return '';\n // Legacy contracts may return a numeric timestamp string.\n if (/^\\d+$/.test(rawRegistered)) {\n const ts = Number(rawRegistered);\n return Number.isFinite(ts) ? new Date(ts).toISOString() : rawRegistered;\n }\n return rawRegistered;\n })();\n\n const nearPublicKey = (auth as any).near_public_key ?? null;\n rows.push({ credentialId, registered, deviceNumber: dn, nearPublicKey });\n }\n setAuthRows(rows.length > 0 ? rows : []);\n setAccessKeyList(keys);\n } catch (err: any) {\n setError(err.message || 'Failed to load linked devices or access keys');\n setAccessKeyList(null);\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleDeleteKey = async (publicKey: string) => {\n if (!tatchi || !publicKey) return;\n if (!nearAccountId) return;\n\n setDeletingKeyPublicKey(publicKey);\n setDeleteError(null);\n\n try {\n await tatchi.deleteDeviceKey(nearAccountId, publicKey, {\n signerMode: { mode: 'threshold-signer', behavior: 'fallback' },\n });\n await loadAuthenticators();\n } catch (err: any) {\n setDeleteError(err.message || 'Failed to delete access key');\n } finally {\n setDeletingKeyPublicKey(null);\n }\n };\n\n const copyToClipboard = async (text: string, keyIndex: number) => {\n try {\n await navigator.clipboard.writeText(text);\n\n // Fire custom event for copy action\n const copyEvent = new CustomEvent('accessKeyCopied', {\n detail: {\n publicKey: text,\n keyIndex: keyIndex,\n timestamp: Date.now()\n }\n });\n window.dispatchEvent(copyEvent);\n\n // Show brief tooltip feedback\n setTooltipVisible(keyIndex);\n setTimeout(() => setTooltipVisible(null), 2000);\n\n // Set copied state for status badge\n setCopiedKeys(prev => new Set(prev).add(keyIndex));\n setTimeout(() => {\n setCopiedKeys(prev => {\n const newSet = new Set(prev);\n newSet.delete(keyIndex);\n return newSet;\n });\n }, 3000);\n } catch (err) {\n console.error('Failed to copy to clipboard:', err);\n }\n };\n\n if (!isOpen) return null;\n\n // Prevent any events from bubbling up to parent components\n const handleBackdropClick = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onClose();\n };\n\n const handleModalContentClick = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n // Don't call onClose here - we want to keep the modal open\n };\n\n return (\n <Theme mode=\"scope-only\">\n <div className={`w3a-access-keys-modal-backdrop theme-${theme}`}\n onClick={handleBackdropClick}\n onMouseDown={(e) => e.stopPropagation()}\n onMouseUp={(e) => e.stopPropagation()}\n >\n <div className=\"w3a-access-keys-modal-content\"\n onClick={handleModalContentClick}\n onMouseDown={(e) => e.stopPropagation()}\n onMouseUp={(e) => e.stopPropagation()}\n >\n <div className=\"w3a-access-keys-modal-header\">\n <h2 className=\"w3a-access-keys-modal-title\">Linked Devices</h2>\n </div>\n <button className=\"w3a-access-keys-modal-close\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onClose();\n }}\n >\n ✕\n </button>\n\n {error && (\n <div className=\"w3a-access-keys-error\">\n <p>{error}</p>\n <button onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n loadAuthenticators();\n }} className=\"w3a-btn w3a-btn-primary\">\n Try Again\n </button>\n </div>\n )}\n\n {!isLoading && !error && authRows.filter(r => r.credentialId !== 'placeholder').length === 0 && (\n <div className=\"w3a-access-keys-empty\">\n <p>No authenticators found.</p>\n </div>\n )}\n\n {!error && authRows.filter(r => r.credentialId !== 'placeholder').length > 0 && (\n <div className=\"w3a-keys-list\">\n {(() => {\n console.log(\"authRows\", authRows)\n const rows = authRows.filter(r => r.credentialId !== 'placeholder');\n const current = (currentDeviceNumber != null)\n ? rows.find(r => r.deviceNumber === currentDeviceNumber)\n : null;\n const others = (currentDeviceNumber != null)\n ? rows.filter(r => r.deviceNumber !== currentDeviceNumber)\n : rows;\n\n const items: React.ReactNode[] = [];\n\n if (current) {\n const index = 0;\n const currentKey = current.nearPublicKey || loginState?.nearPublicKey || null;\n const isCurrentKey = !!currentKey && loginState?.nearPublicKey === currentKey;\n const canDelete = !!accessKeyList && accessKeyList.keys.length > 1;\n const isDeletingThisKey = !!currentKey && deletingKeyPublicKey === currentKey;\n items.push(\n <div key={`current-${current.deviceNumber}`} className=\"w3a-key-item\">\n <div className=\"w3a-key-content\">\n <div className=\"w3a-key-details\">\n <div className=\"w3a-key-header\">\n <div className=\"mono w3a-device-row\">\n <span className=\"w3a-device-badge\">Device {current.deviceNumber}</span>\n <span className=\"w3a-current-device-text\">(current device)</span>\n </div>\n </div>\n <div className=\"mono w3a-registered\">Registered: {formatDateTime(current.registered)}</div>\n {currentKey && (\n <div\n className=\"mono w3a-copyable-key w3a-access-key-current\"\n onClick={(e) => {\n e.stopPropagation();\n copyToClipboard(currentKey, index);\n }}\n onMouseEnter={() => setTooltipVisible(index)}\n onMouseLeave={() => setTooltipVisible(null)}\n title=\"Click to copy\"\n >\n Access Key: {currentKey}\n {tooltipVisible === index && (\n <div className=\"w3a-copy-tooltip\">Click to copy</div>\n )}\n </div>\n )}\n </div>\n {currentKey && (\n <div className=\"w3a-key-status\">\n <button\n className={`w3a-btn ${isCurrentKey ? 'w3a-btn-primary' : 'w3a-btn-danger'}`}\n style={{ width: '64px' }}\n disabled={!canDelete || isDeletingThisKey}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (canDelete && !isDeletingThisKey) {\n void handleDeleteKey(currentKey);\n }\n }}\n >\n {isDeletingThisKey ? (\n <span className=\"w3a-spinner\"/>\n ) : (\n 'Delete'\n )}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n }\n\n others.forEach((item, i) => {\n const canDelete = !!accessKeyList && accessKeyList.keys.length > 1;\n const isDeletingThisKey = !!item.nearPublicKey && deletingKeyPublicKey === item.nearPublicKey;\n items.push(\n <div key={`other-${item.deviceNumber}-${i}`} className=\"w3a-key-item\">\n <div className=\"w3a-key-content\">\n <div className=\"w3a-key-details\">\n <div className=\"w3a-key-header\">\n <div className=\"mono w3a-device-row\">\n <span className=\"w3a-device-badge\">Device {item.deviceNumber}</span>\n </div>\n </div>\n <div className=\"mono w3a-registered\">Registered: {formatDateTime(item.registered)}</div>\n {item.nearPublicKey && (\n <div\n className=\"mono w3a-copyable-key\"\n onClick={(e) => {\n e.stopPropagation();\n copyToClipboard(item.nearPublicKey!, 10 + i);\n }}\n onMouseEnter={() => setTooltipVisible(10 + i)}\n onMouseLeave={() => setTooltipVisible(null)}\n title=\"Click to copy\"\n >\n Access Key: {item.nearPublicKey}\n {tooltipVisible === 10 + i && (\n <div className=\"w3a-copy-tooltip\">Click to copy</div>\n )}\n </div>\n )}\n </div>\n {item.nearPublicKey && (\n <div className=\"w3a-key-status\">\n <button\n className=\"w3a-btn w3a-btn-danger\"\n style={{ width: '64px' }}\n disabled={!canDelete || isDeletingThisKey}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (canDelete && !isDeletingThisKey) {\n void handleDeleteKey(item.nearPublicKey!);\n }\n }}\n >\n {isDeletingThisKey ? (\n <span className=\"w3a-spinner\"/>\n ) : (\n 'Delete'\n )}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n });\n\n return items;\n })()}\n </div>\n )}\n\n {deleteError && (\n <div className=\"w3a-access-keys-error\">\n <p>{deleteError}</p>\n </div>\n )}\n </div>\n </div>\n </Theme>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"LinkedDevicesModal2.js","names":["LinkedDevicesModal: React.FC<LinkedDevicesModalProps>","useTatchi","useTheme","getAuthenticatorsByUser","toAccountId","rows: Array<{ credentialId: string; registered: string; deviceNumber: number; nearPublicKey: string | null }>","err: any","Theme","items: React.ReactNode[]"],"sources":["../../../../../src/react/components/AccountMenuButton/LinkedDevicesModal.tsx"],"sourcesContent":["import React, { useState, useEffect } from 'react';\nimport { useTatchi } from '../../context';\nimport './LinkedDevicesModal.css';\nimport { useTheme, Theme } from '../theme';\n\nimport { getAuthenticatorsByUser } from '@/core/rpcCalls';\nimport type { ContractStoredAuthenticator } from '@/core/TatchiPasskey/syncAccount';\nimport type { AccessKeyList } from '@/core/NearClient';\nimport { toAccountId } from '@/core/types/accountIds';\n\ninterface LinkedDevicesModalProps {\n nearAccountId: string;\n isOpen: boolean;\n onClose: () => void;\n}\n\nexport const LinkedDevicesModal: React.FC<LinkedDevicesModalProps> = ({\n nearAccountId,\n isOpen,\n onClose\n}) => {\n const { tatchi, loginState, viewAccessKeyList } = useTatchi();\n const { theme } = useTheme();\n // Authenticators list: credentialId + registered timestamp + device number\n const [authRows, setAuthRows] = useState<Array<{\n credentialId: string;\n registered: string;\n deviceNumber: number;\n nearPublicKey: string | null;\n }>>([{ credentialId: 'placeholder', registered: '', deviceNumber: 0, nearPublicKey: null }]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [accessKeyList, setAccessKeyList] = useState<AccessKeyList | null>(null);\n const [tooltipVisible, setTooltipVisible] = useState<number | null>(null);\n const [copiedKeys, setCopiedKeys] = useState<Set<number>>(new Set());\n const [currentDeviceNumber, setCurrentDeviceNumber] = useState<number | null>(null);\n const [deletingKeyPublicKey, setDeletingKeyPublicKey] = useState<string | null>(null);\n const [deleteError, setDeleteError] = useState<string | null>(null);\n\n const formatDateTime = (iso: string) => {\n if (!iso) return '—';\n try { return new Date(iso).toLocaleString(); } catch { return iso; }\n };\n\n useEffect(() => {\n if (isOpen) {\n loadAuthenticators();\n // Also resolve current device number for highlighting\n (async () => {\n try {\n if (!tatchi) return;\n const { login } = await tatchi.getLoginSession(nearAccountId);\n const dn = (login as any)?.userData?.deviceNumber;\n if (typeof dn === 'number' && Number.isFinite(dn)) {\n setCurrentDeviceNumber(dn);\n } else {\n setCurrentDeviceNumber(null);\n }\n } catch {\n setCurrentDeviceNumber(null);\n }\n })();\n }\n }, [isOpen]);\n\n // Close on ESC press while modal is open\n useEffect(() => {\n if (!isOpen) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' || e.key === 'Esc') {\n e.preventDefault();\n onClose();\n }\n };\n window.addEventListener('keydown', onKeyDown);\n return () => window.removeEventListener('keydown', onKeyDown);\n }, [isOpen, onClose]);\n\n const loadAuthenticators = async () => {\n if (!tatchi) return;\n\n setIsLoading(true);\n setError(null);\n setDeleteError(null);\n\n try {\n const nearClient = tatchi.getNearClient();\n const contractId = tatchi.configs.contractId;\n const [tuples, keys] = await Promise.all([\n getAuthenticatorsByUser(nearClient, contractId, toAccountId(nearAccountId)),\n viewAccessKeyList(nearAccountId)\n ]);\n\n // Map each authenticator to a single row with credentialId and registered timestamp\n const rows: Array<{ credentialId: string; registered: string; deviceNumber: number; nearPublicKey: string | null }> = [];\n for (const [credentialId, auth] of (tuples || []) as Array<[string, ContractStoredAuthenticator]>) {\n const dn = Number(auth.device_number);\n const rawRegistered = String((auth as any).registered ?? '');\n const registered = (() => {\n if (!rawRegistered) return '';\n // Legacy contracts may return a numeric timestamp string.\n if (/^\\d+$/.test(rawRegistered)) {\n const ts = Number(rawRegistered);\n return Number.isFinite(ts) ? new Date(ts).toISOString() : rawRegistered;\n }\n return rawRegistered;\n })();\n\n const nearPublicKey = (auth as any).near_public_key ?? null;\n rows.push({ credentialId, registered, deviceNumber: dn, nearPublicKey });\n }\n setAuthRows(rows.length > 0 ? rows : []);\n setAccessKeyList(keys);\n } catch (err: any) {\n setError(err.message || 'Failed to load linked devices or access keys');\n setAccessKeyList(null);\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleDeleteKey = async (publicKey: string) => {\n if (!tatchi || !publicKey) return;\n if (!nearAccountId) return;\n\n setDeletingKeyPublicKey(publicKey);\n setDeleteError(null);\n\n try {\n await tatchi.deleteDeviceKey(nearAccountId, publicKey, {\n signerMode: { mode: 'threshold-signer', behavior: 'fallback' },\n });\n await loadAuthenticators();\n } catch (err: any) {\n setDeleteError(err.message || 'Failed to delete access key');\n } finally {\n setDeletingKeyPublicKey(null);\n }\n };\n\n const copyToClipboard = async (text: string, keyIndex: number) => {\n try {\n await navigator.clipboard.writeText(text);\n\n // Fire custom event for copy action\n const copyEvent = new CustomEvent('accessKeyCopied', {\n detail: {\n publicKey: text,\n keyIndex: keyIndex,\n timestamp: Date.now()\n }\n });\n window.dispatchEvent(copyEvent);\n\n // Show brief tooltip feedback\n setTooltipVisible(keyIndex);\n setTimeout(() => setTooltipVisible(null), 2000);\n\n // Set copied state for status badge\n setCopiedKeys(prev => new Set(prev).add(keyIndex));\n setTimeout(() => {\n setCopiedKeys(prev => {\n const newSet = new Set(prev);\n newSet.delete(keyIndex);\n return newSet;\n });\n }, 3000);\n } catch (err) {\n console.error('Failed to copy to clipboard:', err);\n }\n };\n\n if (!isOpen) return null;\n\n // Prevent any events from bubbling up to parent components\n const handleBackdropClick = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onClose();\n };\n\n const handleModalContentClick = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n // Don't call onClose here - we want to keep the modal open\n };\n\n return (\n <Theme>\n <div className={`w3a-access-keys-modal-backdrop theme-${theme}`}\n onClick={handleBackdropClick}\n onMouseDown={(e) => e.stopPropagation()}\n onMouseUp={(e) => e.stopPropagation()}\n >\n <div className=\"w3a-access-keys-modal-content\"\n onClick={handleModalContentClick}\n onMouseDown={(e) => e.stopPropagation()}\n onMouseUp={(e) => e.stopPropagation()}\n >\n <div className=\"w3a-access-keys-modal-header\">\n <h2 className=\"w3a-access-keys-modal-title\">Linked Devices</h2>\n </div>\n <button className=\"w3a-access-keys-modal-close\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onClose();\n }}\n >\n ✕\n </button>\n\n {error && (\n <div className=\"w3a-access-keys-error\">\n <p>{error}</p>\n <button onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n loadAuthenticators();\n }} className=\"w3a-btn w3a-btn-primary\">\n Try Again\n </button>\n </div>\n )}\n\n {!isLoading && !error && authRows.filter(r => r.credentialId !== 'placeholder').length === 0 && (\n <div className=\"w3a-access-keys-empty\">\n <p>No authenticators found.</p>\n </div>\n )}\n\n {!error && authRows.filter(r => r.credentialId !== 'placeholder').length > 0 && (\n <div className=\"w3a-keys-list\">\n {(() => {\n console.log(\"authRows\", authRows)\n const rows = authRows.filter(r => r.credentialId !== 'placeholder');\n const current = (currentDeviceNumber != null)\n ? rows.find(r => r.deviceNumber === currentDeviceNumber)\n : null;\n const others = (currentDeviceNumber != null)\n ? rows.filter(r => r.deviceNumber !== currentDeviceNumber)\n : rows;\n\n const items: React.ReactNode[] = [];\n\n if (current) {\n const index = 0;\n const currentKey = current.nearPublicKey || loginState?.nearPublicKey || null;\n const isCurrentKey = !!currentKey && loginState?.nearPublicKey === currentKey;\n const canDelete = !!accessKeyList && accessKeyList.keys.length > 1;\n const isDeletingThisKey = !!currentKey && deletingKeyPublicKey === currentKey;\n items.push(\n <div key={`current-${current.deviceNumber}`} className=\"w3a-key-item\">\n <div className=\"w3a-key-content\">\n <div className=\"w3a-key-details\">\n <div className=\"w3a-key-header\">\n <div className=\"mono w3a-device-row\">\n <span className=\"w3a-device-badge\">Device {current.deviceNumber}</span>\n <span className=\"w3a-current-device-text\">(current device)</span>\n </div>\n </div>\n <div className=\"mono w3a-registered\">Registered: {formatDateTime(current.registered)}</div>\n {currentKey && (\n <div\n className=\"mono w3a-copyable-key w3a-access-key-current\"\n onClick={(e) => {\n e.stopPropagation();\n copyToClipboard(currentKey, index);\n }}\n onMouseEnter={() => setTooltipVisible(index)}\n onMouseLeave={() => setTooltipVisible(null)}\n title=\"Click to copy\"\n >\n Access Key: {currentKey}\n {tooltipVisible === index && (\n <div className=\"w3a-copy-tooltip\">Click to copy</div>\n )}\n </div>\n )}\n </div>\n {currentKey && (\n <div className=\"w3a-key-status\">\n <button\n className={`w3a-btn ${isCurrentKey ? 'w3a-btn-primary' : 'w3a-btn-danger'}`}\n style={{ width: '64px' }}\n disabled={!canDelete || isDeletingThisKey}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (canDelete && !isDeletingThisKey) {\n void handleDeleteKey(currentKey);\n }\n }}\n >\n {isDeletingThisKey ? (\n <span className=\"w3a-spinner\"/>\n ) : (\n 'Delete'\n )}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n }\n\n others.forEach((item, i) => {\n const canDelete = !!accessKeyList && accessKeyList.keys.length > 1;\n const isDeletingThisKey = !!item.nearPublicKey && deletingKeyPublicKey === item.nearPublicKey;\n items.push(\n <div key={`other-${item.deviceNumber}-${i}`} className=\"w3a-key-item\">\n <div className=\"w3a-key-content\">\n <div className=\"w3a-key-details\">\n <div className=\"w3a-key-header\">\n <div className=\"mono w3a-device-row\">\n <span className=\"w3a-device-badge\">Device {item.deviceNumber}</span>\n </div>\n </div>\n <div className=\"mono w3a-registered\">Registered: {formatDateTime(item.registered)}</div>\n {item.nearPublicKey && (\n <div\n className=\"mono w3a-copyable-key\"\n onClick={(e) => {\n e.stopPropagation();\n copyToClipboard(item.nearPublicKey!, 10 + i);\n }}\n onMouseEnter={() => setTooltipVisible(10 + i)}\n onMouseLeave={() => setTooltipVisible(null)}\n title=\"Click to copy\"\n >\n Access Key: {item.nearPublicKey}\n {tooltipVisible === 10 + i && (\n <div className=\"w3a-copy-tooltip\">Click to copy</div>\n )}\n </div>\n )}\n </div>\n {item.nearPublicKey && (\n <div className=\"w3a-key-status\">\n <button\n className=\"w3a-btn w3a-btn-danger\"\n style={{ width: '64px' }}\n disabled={!canDelete || isDeletingThisKey}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (canDelete && !isDeletingThisKey) {\n void handleDeleteKey(item.nearPublicKey!);\n }\n }}\n >\n {isDeletingThisKey ? (\n <span className=\"w3a-spinner\"/>\n ) : (\n 'Delete'\n )}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n });\n\n return items;\n })()}\n </div>\n )}\n\n {deleteError && (\n <div className=\"w3a-access-keys-error\">\n <p>{deleteError}</p>\n </div>\n )}\n </div>\n </div>\n </Theme>\n );\n};\n"],"mappings":"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"}