@le-space/ui 0.3.31 → 0.4.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.
@@ -49,6 +49,12 @@ export let apiHost = undefined
49
49
  $: pollingDetail = state.busy.refreshing
50
50
  ? (state.statusText || 'Refreshing relay sponsor data from Aleph and the selected CRN.')
51
51
  : (state.deploymentProgress.detail || state.statusText || 'Waiting for the next confirmed relay state from Aleph.')
52
+ $: confirmedRegistrationByInstanceHash = new Map(
53
+ (state.bootstrapRegistrations ?? [])
54
+ .filter((entry) => entry.confirmed && entry.instanceItemHash)
55
+ .map((entry) => [entry.instanceItemHash, entry]),
56
+ )
57
+ $: orphanRegistrations = state.orphanBootstrapRegistrations ?? []
52
58
 
53
59
  onMount(async () => {
54
60
  const unsubscribe = controller.subscribe((next) => {
@@ -220,12 +226,19 @@ export let apiHost = undefined
220
226
 
221
227
  {#each state.instances as entry}
222
228
  <AccordionSection title={`${entry.instance.content?.metadata?.name ?? 'relay'} · ${shortHash(entry.instance.item_hash)}`} open={true}>
229
+ {@const confirmedRegistration = confirmedRegistrationByInstanceHash.get(entry.instance.item_hash)}
223
230
  <div class="instance-topline">
224
231
  <div class="chip-row">
225
232
  <span class="chip">{entry.details.messageStatus}</span>
226
233
  {#if entry.details.crnUrl}
227
234
  <span class="chip">{entry.details.crnUrl.replace(/^https?:\/\//, '')}</span>
228
235
  {/if}
236
+ {#if confirmedRegistration}
237
+ <span class="chip chip-confirmed">
238
+ <span class="chip-dot-confirmed"></span>
239
+ Registration confirmed
240
+ </span>
241
+ {/if}
229
242
  </div>
230
243
  <button
231
244
  class="delete"
@@ -267,6 +280,13 @@ export let apiHost = undefined
267
280
  <strong>{joinMappedPorts(entry.details.mappedPorts)}</strong>
268
281
  </div>
269
282
 
283
+ {#if confirmedRegistration}
284
+ <div class="mono-block">
285
+ <span>Bootstrap Registration</span>
286
+ <strong>{shortHash(confirmedRegistration.messageHash ?? confirmedRegistration.content?.registrationId ?? 'confirmed', 14, 8)}</strong>
287
+ </div>
288
+ {/if}
289
+
270
290
  <div class="link-row">
271
291
  <CopyButton text={entry.instance.item_hash} label="Copy hash" />
272
292
  {#if entry.details.webUrl}
@@ -281,6 +301,34 @@ export let apiHost = undefined
281
301
  {/if}
282
302
  </AccordionSection>
283
303
  {/each}
304
+
305
+ {#if orphanRegistrations.length > 0}
306
+ <div class="orphan-box">
307
+ <div class="orphan-head">
308
+ <strong>Orphan bootstrap registrations</strong>
309
+ <small>Current-wallet registrations without a matching instance. Forget them directly from here.</small>
310
+ </div>
311
+
312
+ {#each orphanRegistrations as entry}
313
+ {@const registrationHash = entry.messageHash ?? entry.hash}
314
+ <div class="orphan-card">
315
+ <div class="orphan-title">{entry.content?.registrationId ?? 'registration'} · {shortHash(registrationHash ?? 'unknown')}</div>
316
+ <div>Peer: {entry.content?.peerId ?? '-'}</div>
317
+ <div>Linked instance: {entry.instanceItemHash ? shortHash(entry.instanceItemHash) : 'missing'}</div>
318
+ <div>Browser multiaddrs: {String(entry.content?.browserMultiaddrs?.length ?? 0)}</div>
319
+ <div>Updated: {formatDateTime(entry.content?.updatedAt ?? entry.time)}</div>
320
+ <button
321
+ class="delete"
322
+ type="button"
323
+ disabled={!registrationHash || state.busy.deletingRegistrationHash === registrationHash}
324
+ on:click={() => registrationHash && controller.deleteBootstrapRegistration(registrationHash)}
325
+ >
326
+ {state.busy.deletingRegistrationHash === registrationHash ? 'Forgetting…' : 'Forget registration'}
327
+ </button>
328
+ </div>
329
+ {/each}
330
+ </div>
331
+ {/if}
284
332
  </section>
285
333
  {/if}
286
334
  </aside>
@@ -401,6 +449,22 @@ export let apiHost = undefined
401
449
  color: #ffd4d4;
402
450
  }
403
451
 
452
+ .chip-confirmed {
453
+ background: rgba(34, 197, 94, 0.16);
454
+ color: #86efac;
455
+ display: inline-flex;
456
+ align-items: center;
457
+ gap: 0.32rem;
458
+ }
459
+
460
+ .chip-dot-confirmed {
461
+ width: 0.45rem;
462
+ height: 0.45rem;
463
+ border-radius: 999px;
464
+ background: #22c55e;
465
+ box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);
466
+ }
467
+
404
468
  .status-strip,
405
469
  .metrics {
406
470
  display: grid;
@@ -425,6 +489,38 @@ export let apiHost = undefined
425
489
  gap: 0.7rem;
426
490
  }
427
491
 
492
+ .orphan-box {
493
+ display: grid;
494
+ gap: 0.7rem;
495
+ padding: 0.85rem;
496
+ border-radius: 1rem;
497
+ border: 1px solid rgba(248, 113, 113, 0.22);
498
+ background: linear-gradient(180deg, rgba(127, 29, 29, 0.18), rgba(69, 10, 10, 0.12));
499
+ }
500
+
501
+ .orphan-head {
502
+ display: grid;
503
+ gap: 0.2rem;
504
+ }
505
+
506
+ .orphan-head small {
507
+ color: #fecaca;
508
+ line-height: 1.35;
509
+ }
510
+
511
+ .orphan-card {
512
+ display: grid;
513
+ gap: 0.35rem;
514
+ padding: 0.75rem;
515
+ border-radius: 0.9rem;
516
+ background: rgba(15, 23, 42, 0.22);
517
+ border: 1px solid rgba(255, 255, 255, 0.08);
518
+ }
519
+
520
+ .orphan-title {
521
+ font-weight: 700;
522
+ }
523
+
428
524
  .alert {
429
525
  margin: 0.8rem 0 0;
430
526
  padding: 0.75rem 0.85rem;