@bkazemi/verily 0.9.4 → 0.10.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.
package/dist/verily.js CHANGED
@@ -9577,6 +9577,7 @@ var Verily = (() => {
9577
9577
  Refused: () => Refused,
9578
9578
  accountsOf: () => accountsOf,
9579
9579
  attestationLabel: () => attestationLabel,
9580
+ authenticatedEarlier: () => authenticatedEarlier,
9580
9581
  dialogPath: () => dialogPath,
9581
9582
  dialogRoute: () => dialogRoute,
9582
9583
  expired: () => expired,
@@ -9593,17 +9594,20 @@ var Verily = (() => {
9593
9594
  isRedirectProvider: () => isRedirectProvider,
9594
9595
  lastProved: () => lastProved,
9595
9596
  localSide: () => localSide,
9597
+ parseMaxAge: () => parseMaxAge,
9596
9598
  place: () => place,
9597
9599
  presentConnections: () => presentConnections,
9598
9600
  proofTitle: () => proofTitle,
9599
9601
  providerMethod: () => providerMethod,
9602
+ recent: () => recent,
9600
9603
  routeOf: () => routeOf,
9601
9604
  routePath: () => routePath,
9602
9605
  routes: () => routes,
9603
9606
  signedBy: () => signedBy,
9604
9607
  status: () => status,
9605
9608
  statusLabel: () => statusLabel,
9606
- verifySigned: () => verifySigned
9609
+ verifySigned: () => verifySigned,
9610
+ withMaxAge: () => withMaxAge
9607
9611
  });
9608
9612
 
9609
9613
  // src/core/signed.ts
@@ -9753,6 +9757,21 @@ var Verily = (() => {
9753
9757
  const main = evidence.attestations.external[0];
9754
9758
  return main.artifactUrl ? main.confirmedAt : evidence.authenticatedAt;
9755
9759
  }
9760
+ function authenticatedEarlier(evidence) {
9761
+ return evidence.approvedAt - evidence.authenticatedAt > 864e5;
9762
+ }
9763
+ function recent(evidence, maxAgeMs, now) {
9764
+ return now - lastProved(evidence) <= maxAgeMs;
9765
+ }
9766
+ function withMaxAge(evidence, maxAgeMs, now) {
9767
+ return maxAgeMs !== void 0 && evidence.status === "verified" && !recent(evidence, maxAgeMs, now) ? { ...evidence, status: "unconfirmed" } : evidence;
9768
+ }
9769
+ function parseMaxAge(text) {
9770
+ const match = /^\s*(\d+)\s*([smhd]?)\s*$/.exec(text ?? "");
9771
+ if (!match) return void 0;
9772
+ const unit = { "": 1e3, s: 1e3, m: 6e4, h: 36e5, d: 864e5 }[match[2]];
9773
+ return Number(match[1]) * unit;
9774
+ }
9756
9775
  function statusLabel(evidence, now) {
9757
9776
  if (evidence.status === "revoked") return "Revoked";
9758
9777
  if (evidence.status === "retired") return "Retired";
@@ -9825,6 +9844,9 @@ var Verily = (() => {
9825
9844
  return attestation.hosted ? where : `${where} | Last checked ${when(attestation.confirmedAt)}`;
9826
9845
  }
9827
9846
 
9847
+ // src/browser/badge.css.ts
9848
+ var badge_css_default = ":host {\n display: inline-block;\n max-width: 100%;\n vertical-align: middle;\n}\n* {\n box-sizing: border-box;\n}\n.badge,\n.peek {\n border: 1px solid var(--verily-border, #dce2e0);\n border-radius: 6px;\n background: var(--verily-surface, #fff);\n color: var(--verily-text, #202c29);\n font: var(--verily-font-size, 13px)/1.35\n var(--verily-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n}\n.badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n max-width: 100%;\n padding: 3px 5px;\n text-decoration: none;\n}\nbutton.badge {\n margin: 0;\n cursor: pointer;\n}\na[href]:hover,\nbutton.badge:hover {\n background: var(--verily-hover, #f3f6f4);\n border-color: var(--verily-border, #dce2e0);\n}\n.badge:focus-visible {\n outline: 2px solid #357ce5;\n outline-offset: 2px;\n}\n.name {\n font-weight: 600;\n overflow-wrap: anywhere;\n min-width: 0;\n}\n.more {\n flex-shrink: 0;\n font-size: 11px;\n font-weight: 600;\n color: var(--verily-muted, #65726c);\n}\n.label {\n display: none;\n font-size: 11px;\n color: var(--verily-muted, #65726c);\n}\n.badge:hover .label,\n.badge:focus-within .label {\n display: inline;\n}\n.icon {\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n font-size: 12px;\n font-weight: 750;\n}\n.icon .glyph {\n display: block;\n width: 12px;\n height: 12px;\n}\n.mark {\n display: block;\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n}\n.mark.pending {\n opacity: 0.45;\n}\n/* A dim ring where the account will go; it only spins where motion is welcome. */\n.spinner {\n flex-shrink: 0;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n border: 2px solid currentColor;\n opacity: 0.3;\n}\n.divider {\n flex-shrink: 0;\n width: 1px;\n height: 12px;\n background: var(--verily-border, #dce2e0);\n}\n/* Several accounts, one to a row. The mark stays beside them and the rule runs their height. */\n.badge.stacked {\n align-items: stretch;\n padding: 5px 8px 5px 6px;\n text-align: left;\n}\n.stacked .mark {\n align-self: center;\n}\n.stacked .divider {\n height: auto;\n}\n.rows {\n display: grid;\n gap: 4px;\n min-width: 0;\n}\n.row {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n/*\n * The accounts behind a short pill's count, opened beside it. Shut unless the browser\n * has it open as a popover, so one without popovers never shows it at all.\n */\n.peek {\n display: none;\n position: fixed;\n inset: auto;\n margin: 0;\n padding: 8px 10px;\n width: max-content;\n max-width: min(320px, calc(100vw - 16px));\n border-radius: 8px;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.14);\n pointer-events: none;\n overflow: hidden;\n}\n/* A row taken out to fit the window: its own display would otherwise keep it showing. */\n.row[hidden] {\n display: none;\n}\n.peek:popover-open {\n display: block;\n}\n.peek .via {\n display: block;\n margin-top: 6px;\n font-size: 11px;\n color: var(--verily-muted, #65726c);\n}\n.provider {\n display: block;\n flex-shrink: 0;\n width: 14px;\n height: 14px;\n}\n.expired .icon {\n color: #815a12;\n background: #fbefce;\n}\n.revoked .icon,\n.retired .icon,\n.message .icon {\n color: #626d69;\n background: #edf0ee;\n}\n/* Nothing ran out and nothing wants attention: the holder chose this, so it is only quiet. */\n.retired .name {\n color: var(--verily-muted, #65726c);\n}\n.message {\n color: var(--verily-muted, #65726c);\n}\n.add {\n font-weight: 600;\n}\n@media (prefers-reduced-motion: no-preference) {\n a,\n button {\n transition:\n background 0.12s,\n border-color 0.12s;\n }\n /* The mark is drawn before the answer arrives, so it resolves rather than swaps. */\n .mark,\n .mark path {\n transition:\n opacity 0.18s ease,\n stroke 0.18s ease;\n }\n .spinner {\n border-top-color: transparent;\n opacity: 0.4;\n animation: verily-spin 0.7s linear infinite;\n }\n}\n@keyframes verily-spin {\n to {\n transform: rotate(360deg);\n }\n}\n";
9849
+
9828
9850
  // src/marks.ts
9829
9851
  var github = {
9830
9852
  viewBox: "0 0 16 16",
@@ -9893,13 +9915,11 @@ var Verily = (() => {
9893
9915
  check: { pathLength: "176", "stroke-dasharray": "108 176", "stroke-dashoffset": "-54" }
9894
9916
  };
9895
9917
 
9918
+ // src/browser/mark.css.ts
9919
+ var mark_css_default = "@media (prefers-reduced-motion: no-preference) {\n .mark.current path:last-child {\n animation: verily-draw 0.8s cubic-bezier(0.65, 0, 0.35, 1) 0.1s backwards;\n }\n}\n@keyframes verily-draw {\n from {\n stroke-dasharray: 0 176;\n }\n to {\n stroke-dasharray: 108 176;\n }\n}\n";
9920
+
9896
9921
  // src/browser/mark.ts
9897
- var markStyles = `
9898
- @media (prefers-reduced-motion: no-preference) {
9899
- .mark.current path:last-child { animation: verily-draw .8s cubic-bezier(.65, 0, .35, 1) .1s backwards; }
9900
- }
9901
- @keyframes verily-draw { from { stroke-dasharray: 0 176; } to { stroke-dasharray: 108 176; } }
9902
- `;
9922
+ var markStyles = mark_css_default;
9903
9923
  function paintMark(svg, tone) {
9904
9924
  const colors = tones[tone];
9905
9925
  svg.setAttribute("class", `mark ${tone}`);
@@ -9976,72 +9996,7 @@ var Verily = (() => {
9976
9996
  }
9977
9997
 
9978
9998
  // src/browser/badge.ts
9979
- var styles = `
9980
- :host { display: inline-block; max-width: 100%; vertical-align: middle; }
9981
- * { box-sizing: border-box; }
9982
- .badge, .peek {
9983
- border: 1px solid var(--verily-border, #dce2e0); border-radius: 6px;
9984
- background: var(--verily-surface, #fff); color: var(--verily-text, #202c29);
9985
- font: var(--verily-font-size, 13px)/1.35
9986
- var(--verily-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
9987
- }
9988
- .badge {
9989
- display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 3px 5px;
9990
- text-decoration: none;
9991
- }
9992
- button.badge { margin: 0; cursor: pointer; }
9993
- a[href]:hover, button.badge:hover { background: var(--verily-hover, #f3f6f4); border-color: var(--verily-border, #dce2e0); }
9994
- .badge:focus-visible { outline: 2px solid #357ce5; outline-offset: 2px; }
9995
- .name { font-weight: 600; overflow-wrap: anywhere; min-width: 0; }
9996
- .more { flex-shrink: 0; font-size: 11px; font-weight: 600; color: var(--verily-muted, #65726c); }
9997
- .label { display: none; font-size: 11px; color: var(--verily-muted, #65726c); }
9998
- .badge:hover .label, .badge:focus-within .label { display: inline; }
9999
- .icon { display: grid; place-items: center; flex-shrink: 0; width: 20px; height: 20px; border-radius: 50%; font-size: 12px; font-weight: 750; }
10000
- .icon .glyph { display: block; width: 12px; height: 12px; }
10001
- .mark { display: block; flex-shrink: 0; width: 20px; height: 20px; }
10002
- .mark.pending { opacity: .45; }
10003
- /* A dim ring where the account will go; it only spins where motion is welcome. */
10004
- .spinner {
10005
- flex-shrink: 0; width: 14px; height: 14px; border-radius: 50%;
10006
- border: 2px solid currentColor; opacity: .3;
10007
- }
10008
- .divider { flex-shrink: 0; width: 1px; height: 12px; background: var(--verily-border, #dce2e0); }
10009
- /* Several accounts, one to a row. The mark stays beside them and the rule runs their height. */
10010
- .badge.stacked { align-items: stretch; padding: 5px 8px 5px 6px; text-align: left; }
10011
- .stacked .mark { align-self: center; }
10012
- .stacked .divider { height: auto; }
10013
- .rows { display: grid; gap: 4px; min-width: 0; }
10014
- .row { display: flex; align-items: center; gap: 6px; min-width: 0; }
10015
- /*
10016
- * The accounts behind a short pill's count, opened beside it. Shut unless the browser
10017
- * has it open as a popover, so one without popovers never shows it at all.
10018
- */
10019
- .peek {
10020
- display: none; position: fixed; inset: auto; margin: 0; padding: 8px 10px;
10021
- width: max-content; max-width: min(320px, calc(100vw - 16px));
10022
- border-radius: 8px; box-shadow: 0 6px 20px rgba(0, 0, 0, .14); pointer-events: none;
10023
- overflow: hidden;
10024
- }
10025
- /* A row taken out to fit the window: its own display would otherwise keep it showing. */
10026
- .row[hidden] { display: none; }
10027
- .peek:popover-open { display: block; }
10028
- .peek .via { display: block; margin-top: 6px; font-size: 11px; color: var(--verily-muted, #65726c); }
10029
- .provider { display: block; flex-shrink: 0; width: 14px; height: 14px; }
10030
- .expired .icon { color: #815a12; background: #fbefce; }
10031
- .revoked .icon, .retired .icon, .message .icon { color: #626d69; background: #edf0ee; }
10032
- /* Nothing ran out and nothing wants attention: the holder chose this, so it is only quiet. */
10033
- .retired .name { color: var(--verily-muted, #65726c); }
10034
- .message { color: var(--verily-muted, #65726c); }
10035
- .add { font-weight: 600; }
10036
- @media (prefers-reduced-motion: no-preference) {
10037
- a, button { transition: background .12s, border-color .12s; }
10038
- /* The mark is drawn before the answer arrives, so it resolves rather than swaps. */
10039
- .mark, .mark path { transition: opacity .18s ease, stroke .18s ease; }
10040
- .spinner { border-top-color: transparent; opacity: .4; animation: verily-spin .7s linear infinite; }
10041
- }
10042
- @keyframes verily-spin { to { transform: rotate(360deg); } }
10043
- ${markStyles}
10044
- `;
9999
+ var styles = badge_css_default + markStyles;
10045
10000
  var sheet;
10046
10001
  var frames = /* @__PURE__ */ new WeakMap();
10047
10002
  var pendingKey = "\0pending";
@@ -10319,6 +10274,537 @@ var Verily = (() => {
10319
10274
  return button2;
10320
10275
  }
10321
10276
 
10277
+ // src/browser/evidence-dialog.css.ts
10278
+ var evidence_dialog_css_default = `* {
10279
+ box-sizing: border-box;
10280
+ }
10281
+ dialog {
10282
+ width: min(460px, calc(100vw - 32px));
10283
+ max-height: calc(100dvh - 40px);
10284
+ margin: auto;
10285
+ padding: 0;
10286
+ overflow: hidden;
10287
+ border: 1px solid #dce2de;
10288
+ border-radius: 16px;
10289
+ background: #fff;
10290
+ color: #23312b;
10291
+ box-shadow: 0 24px 90px #10201935;
10292
+ font:
10293
+ 13px/1.6 system-ui,
10294
+ sans-serif;
10295
+ }
10296
+ /* The pane scrolls inside the dialog, which clips its scrollbar to the rounded corners.
10297
+ Contain keeps a scroll past its end from scrolling the page behind. */
10298
+ dialog[open] {
10299
+ display: flex;
10300
+ flex-direction: column;
10301
+ }
10302
+ .pane {
10303
+ min-height: 0;
10304
+ padding: 24px;
10305
+ overflow-y: auto;
10306
+ overscroll-behavior: contain;
10307
+ }
10308
+ dialog::backdrop {
10309
+ background: #15271f66;
10310
+ }
10311
+ header {
10312
+ display: flex;
10313
+ align-items: center;
10314
+ justify-content: space-between;
10315
+ gap: 16px;
10316
+ margin-bottom: 20px;
10317
+ }
10318
+ h2 {
10319
+ margin: 0;
10320
+ font-size: 18px;
10321
+ font-weight: 650;
10322
+ letter-spacing: -0.3px;
10323
+ }
10324
+ button {
10325
+ width: 32px;
10326
+ height: 32px;
10327
+ border: 1px solid #dce2de;
10328
+ border-radius: 8px;
10329
+ background: #fff;
10330
+ color: #52645a;
10331
+ font:
10332
+ 20px system-ui,
10333
+ sans-serif;
10334
+ cursor: pointer;
10335
+ }
10336
+ button:hover {
10337
+ background: #f2f5f1;
10338
+ }
10339
+ .icon {
10340
+ display: grid;
10341
+ place-items: center;
10342
+ padding: 0;
10343
+ }
10344
+ .icon svg {
10345
+ width: 16px;
10346
+ height: 16px;
10347
+ }
10348
+ .title {
10349
+ display: flex;
10350
+ align-items: center;
10351
+ gap: 6px;
10352
+ min-width: 0;
10353
+ }
10354
+ .all {
10355
+ display: grid;
10356
+ place-items: center;
10357
+ width: 28px;
10358
+ height: 28px;
10359
+ border-radius: 8px;
10360
+ color: #52645a;
10361
+ }
10362
+ .all:hover {
10363
+ background: #f2f5f1;
10364
+ }
10365
+ .all svg {
10366
+ width: 16px;
10367
+ height: 16px;
10368
+ }
10369
+ a {
10370
+ color: #245f43;
10371
+ text-underline-offset: 3px;
10372
+ overflow-wrap: anywhere;
10373
+ }
10374
+ a:focus-visible,
10375
+ button:focus-visible,
10376
+ summary:focus-visible {
10377
+ outline: 2px solid #357ce5;
10378
+ outline-offset: 3px;
10379
+ }
10380
+ /* An element given its own display would otherwise go on showing while hidden. */
10381
+ [hidden] {
10382
+ display: none !important;
10383
+ }
10384
+ .summary {
10385
+ display: flex;
10386
+ align-items: center;
10387
+ gap: 8px;
10388
+ margin-top: 16px;
10389
+ }
10390
+ .mark {
10391
+ width: 36px;
10392
+ height: 36px;
10393
+ flex-shrink: 0;
10394
+ }
10395
+ .provider {
10396
+ width: 14px;
10397
+ height: 14px;
10398
+ }
10399
+ .state {
10400
+ font-size: 14px;
10401
+ font-weight: 650;
10402
+ line-height: 1.4;
10403
+ }
10404
+ .muted {
10405
+ color: #6b786f;
10406
+ font-size: 12px;
10407
+ }
10408
+ .account {
10409
+ position: relative;
10410
+ }
10411
+ .veiled > :not(.checking) {
10412
+ filter: blur(4px);
10413
+ opacity: 0.55;
10414
+ user-select: none;
10415
+ }
10416
+ .checking {
10417
+ position: absolute;
10418
+ inset: 0;
10419
+ display: flex;
10420
+ align-items: center;
10421
+ justify-content: center;
10422
+ gap: 8px;
10423
+ color: #23312b;
10424
+ font-size: 13px;
10425
+ font-weight: 600;
10426
+ }
10427
+ .spinner {
10428
+ width: 14px;
10429
+ height: 14px;
10430
+ border: 2px solid currentColor;
10431
+ border-right-color: transparent;
10432
+ border-radius: 50%;
10433
+ animation: spin 0.7s linear infinite;
10434
+ }
10435
+ @keyframes spin {
10436
+ to {
10437
+ transform: rotate(360deg);
10438
+ }
10439
+ }
10440
+ @media (prefers-reduced-motion: reduce) {
10441
+ .spinner {
10442
+ animation: none;
10443
+ }
10444
+ }
10445
+ /* The "signed" pill after the verifier's name, one size smaller than the line it sits on. */
10446
+ .summary .signed {
10447
+ display: inline-block;
10448
+ margin-left: 5px;
10449
+ padding: 0 6px;
10450
+ border: 1px solid #cfdcd3;
10451
+ border-radius: 999px;
10452
+ font-size: 10px;
10453
+ font-weight: 550;
10454
+ line-height: 1.3;
10455
+ text-decoration: none;
10456
+ vertical-align: 1px;
10457
+ }
10458
+ .summary a.signed:hover {
10459
+ background: #f2f5f1;
10460
+ }
10461
+ .summary .signed.bad {
10462
+ border-color: #b3261e;
10463
+ color: #b3261e;
10464
+ font-weight: 650;
10465
+ cursor: help;
10466
+ }
10467
+ .caution {
10468
+ display: inline-block;
10469
+ margin-left: 3px;
10470
+ color: #a15c00;
10471
+ vertical-align: -1.5px;
10472
+ }
10473
+ .caution svg {
10474
+ display: block;
10475
+ width: 10px;
10476
+ height: 10px;
10477
+ }
10478
+ .account {
10479
+ padding: 14px 16px;
10480
+ border: 1px solid #e0e6df;
10481
+ border-radius: 10px;
10482
+ margin-top: 12px;
10483
+ }
10484
+ .account h3 {
10485
+ display: flex;
10486
+ align-items: center;
10487
+ gap: 6px;
10488
+ margin: 0 0 3px;
10489
+ color: #6b786f;
10490
+ font-size: 11px;
10491
+ font-weight: 550;
10492
+ }
10493
+ .account a,
10494
+ .account strong {
10495
+ font-weight: 650;
10496
+ font-size: 14px;
10497
+ }
10498
+ /* The account's own name carries that weight; a link inside a line of prose does not. */
10499
+ .summary a,
10500
+ .method a,
10501
+ .additional a {
10502
+ font: inherit;
10503
+ }
10504
+ .reference {
10505
+ margin-top: 2px;
10506
+ }
10507
+ /* A fingerprint in fours: it breaks between groups, and copies as one unbroken word. */
10508
+ .fingerprint {
10509
+ font-family: ui-monospace, monospace;
10510
+ text-wrap: balance;
10511
+ }
10512
+ .fingerprint span {
10513
+ display: inline-block;
10514
+ margin-right: 0.5ch;
10515
+ }
10516
+ .method {
10517
+ margin-top: 8px;
10518
+ }
10519
+ /* In the corner the heading leaves empty, apart from everything that was checked. */
10520
+ .listing {
10521
+ position: absolute;
10522
+ top: 12px;
10523
+ right: 14px;
10524
+ padding: 1px 8px;
10525
+ border: 1px solid #e0e6df;
10526
+ border-radius: 999px;
10527
+ color: #6b786f;
10528
+ font-size: 11px;
10529
+ font-weight: 550;
10530
+ }
10531
+ .listing.preferred {
10532
+ border-color: #245f43;
10533
+ color: #245f43;
10534
+ }
10535
+ /* Quiet, as its pill is: the surface and the name step back, and the controls do not. */
10536
+ .account.retired {
10537
+ background: #f6f8f5;
10538
+ }
10539
+ .account.retired .identity > a,
10540
+ .account.retired .identity > strong,
10541
+ .account.retired .named > a,
10542
+ .account.retired .named > strong {
10543
+ color: #6b786f;
10544
+ }
10545
+ /* The name, then a dot and the account's creation date. The date wraps under a long name. */
10546
+ /* Clipped 4px left of the text, which hides the dot when the date wraps to a new line. */
10547
+ .named {
10548
+ display: flex;
10549
+ flex-wrap: wrap;
10550
+ align-items: baseline;
10551
+ gap: 0 14px;
10552
+ margin-left: -4px;
10553
+ padding-left: 4px;
10554
+ overflow-x: clip;
10555
+ }
10556
+ .since {
10557
+ position: relative;
10558
+ white-space: nowrap;
10559
+ }
10560
+ /* The dot sits in the gap before the date. */
10561
+ .since::before {
10562
+ content: '\xB7';
10563
+ position: absolute;
10564
+ left: -7px;
10565
+ transform: translateX(-50%);
10566
+ }
10567
+ .account.retired .mark {
10568
+ opacity: 0.45;
10569
+ }
10570
+ .group {
10571
+ margin: 20px 0 0;
10572
+ color: #6b786f;
10573
+ font-size: 11px;
10574
+ font-weight: 550;
10575
+ }
10576
+ summary.group {
10577
+ display: flex;
10578
+ align-items: center;
10579
+ gap: 4px;
10580
+ width: fit-content;
10581
+ border-radius: 4px;
10582
+ list-style: none;
10583
+ cursor: pointer;
10584
+ }
10585
+ summary::-webkit-details-marker {
10586
+ display: none;
10587
+ }
10588
+ .fold {
10589
+ display: flex;
10590
+ align-items: center;
10591
+ gap: 4px;
10592
+ width: auto;
10593
+ height: auto;
10594
+ padding: 2px 8px 2px 4px;
10595
+ margin-left: -4px;
10596
+ border: 0;
10597
+ background: none;
10598
+ font:
10599
+ 550 12px/1.6 system-ui,
10600
+ sans-serif;
10601
+ }
10602
+ .fold svg,
10603
+ summary svg {
10604
+ width: 12px;
10605
+ height: 12px;
10606
+ }
10607
+ .fold[aria-expanded='true'] svg,
10608
+ details[open] > summary svg {
10609
+ transform: rotate(90deg);
10610
+ }
10611
+ @media (prefers-reduced-motion: no-preference) {
10612
+ .fold svg,
10613
+ summary svg {
10614
+ transition: transform 0.15s;
10615
+ }
10616
+ }
10617
+ /* The fold and what the holder may do share a line, until a question needs the whole of it. */
10618
+ .bar {
10619
+ display: flex;
10620
+ align-items: center;
10621
+ flex-wrap: wrap;
10622
+ gap: 8px;
10623
+ margin-top: 10px;
10624
+ }
10625
+ .account .bar .row {
10626
+ margin: 0 0 0 auto;
10627
+ }
10628
+ .additional {
10629
+ margin-top: 2px;
10630
+ padding-left: 12px;
10631
+ }
10632
+ .joiner {
10633
+ display: block;
10634
+ width: 20px;
10635
+ height: 20px;
10636
+ margin: 8px auto -4px;
10637
+ color: #90a096;
10638
+ }
10639
+ dl {
10640
+ margin: 16px 0 0;
10641
+ padding-top: 12px;
10642
+ border-top: 1px solid #e5e9e3;
10643
+ display: grid;
10644
+ grid-template-columns: auto 1fr;
10645
+ gap: 5px 16px;
10646
+ font-size: 11px;
10647
+ }
10648
+ dt {
10649
+ color: #6b786f;
10650
+ }
10651
+ dd {
10652
+ margin: 0;
10653
+ text-align: right;
10654
+ overflow-wrap: anywhere;
10655
+ }
10656
+ .explanation {
10657
+ margin-top: 16px;
10658
+ }
10659
+ .action {
10660
+ width: auto;
10661
+ height: auto;
10662
+ padding: 9px 14px;
10663
+ font:
10664
+ 600 13px/1.3 system-ui,
10665
+ sans-serif;
10666
+ color: #23312b;
10667
+ text-align: left;
10668
+ }
10669
+ .action.primary {
10670
+ border-color: #245f43;
10671
+ background: #245f43;
10672
+ color: #fff;
10673
+ }
10674
+ .action.primary:hover {
10675
+ background: #1d4f37;
10676
+ }
10677
+ .action:disabled {
10678
+ opacity: 0.6;
10679
+ cursor: progress;
10680
+ }
10681
+ .action.danger:hover {
10682
+ border-color: #b3261e;
10683
+ background: #fdecea;
10684
+ color: #b3261e;
10685
+ }
10686
+ .row {
10687
+ display: flex;
10688
+ justify-content: end;
10689
+ gap: 8px;
10690
+ margin-top: 16px;
10691
+ }
10692
+ .account .row {
10693
+ align-items: center;
10694
+ justify-content: start;
10695
+ flex-wrap: wrap;
10696
+ margin-top: 12px;
10697
+ }
10698
+ .account .action {
10699
+ padding: 5px 10px;
10700
+ font-size: 12px;
10701
+ }
10702
+ .account .action.dots {
10703
+ display: grid;
10704
+ place-items: center;
10705
+ align-self: stretch;
10706
+ width: 30px;
10707
+ padding: 0;
10708
+ }
10709
+ .dots svg {
10710
+ width: 16px;
10711
+ height: 16px;
10712
+ }
10713
+ /* Placed under its button each time it opens, above the dialog and clipped by none of it. */
10714
+ .menu {
10715
+ position: fixed;
10716
+ inset: auto;
10717
+ margin: 0;
10718
+ min-width: 170px;
10719
+ padding: 6px;
10720
+ border: 1px solid #dce2de;
10721
+ border-radius: 10px;
10722
+ background: #fff;
10723
+ color: #23312b;
10724
+ box-shadow: 0 12px 40px #10201930;
10725
+ }
10726
+ .menu:popover-open {
10727
+ display: grid;
10728
+ }
10729
+ .account .menu .action {
10730
+ border: 0;
10731
+ border-radius: 6px;
10732
+ padding: 6px 10px;
10733
+ font-weight: 550;
10734
+ }
10735
+ .menu .danger {
10736
+ color: #b3261e;
10737
+ }
10738
+ .menu hr {
10739
+ width: 100%;
10740
+ margin: 4px 0;
10741
+ border: 0;
10742
+ border-top: 1px solid #e5e9e3;
10743
+ }
10744
+ footer {
10745
+ display: flex;
10746
+ align-items: center;
10747
+ justify-content: start;
10748
+ gap: 6px;
10749
+ margin: 20px 0 -8px;
10750
+ color: #9aa9a0;
10751
+ font-size: 11px;
10752
+ }
10753
+ footer a {
10754
+ display: flex;
10755
+ color: inherit;
10756
+ }
10757
+ .logo {
10758
+ display: block;
10759
+ height: 13px;
10760
+ }
10761
+ /* Four or more accounts on a wide screen: two equal cards to a row. */
10762
+ @media (min-width: 960px) {
10763
+ dialog.many {
10764
+ width: min(900px, calc(100vw - 32px));
10765
+ }
10766
+ dialog.many .tiles {
10767
+ display: grid;
10768
+ grid-template-columns: repeat(2, minmax(0, 1fr));
10769
+ gap: 12px;
10770
+ margin-top: 12px;
10771
+ }
10772
+ /* Each card is three rows of the grid: who, its verdict, and the rest. A row is as tall as
10773
+ the taller card's part, so the verdicts and details of neighbouring cards line up. */
10774
+ dialog.many .tiles > .account {
10775
+ display: grid;
10776
+ grid-row: span 3;
10777
+ grid-template-rows: subgrid;
10778
+ row-gap: 0;
10779
+ margin-top: 0;
10780
+ }
10781
+ /* The dates sit at the foot of the third part, so they line up too. */
10782
+ dialog.many .tiles .rest {
10783
+ display: flex;
10784
+ flex-direction: column;
10785
+ }
10786
+ dialog.many .tiles .rest > dl {
10787
+ margin-top: auto;
10788
+ }
10789
+ dialog.many .tiles .rest > :has(+ dl) {
10790
+ margin-bottom: 16px;
10791
+ }
10792
+ /* The site's card is one tile wide, centred over the two columns. */
10793
+ dialog.many .local {
10794
+ width: calc(50% - 6px);
10795
+ margin-inline: auto;
10796
+ text-align: center;
10797
+ }
10798
+ dialog.many .local h3,
10799
+ dialog.many .local .named {
10800
+ justify-content: center;
10801
+ }
10802
+ dialog.many .local .additional {
10803
+ padding-left: 0;
10804
+ }
10805
+ }
10806
+ `;
10807
+
10322
10808
  // src/browser/signed.ts
10323
10809
  var checks = /* @__PURE__ */ new Map();
10324
10810
  var published = /* @__PURE__ */ new Map();
@@ -10520,7 +11006,7 @@ ${claims(evidence)}`;
10520
11006
  }
10521
11007
 
10522
11008
  // src/version.ts
10523
- var version = "v0.9.4";
11009
+ var version = "v0.10.0";
10524
11010
 
10525
11011
  // src/browser/evidence-dialog.ts
10526
11012
  function stampLink() {
@@ -10529,120 +11015,29 @@ ${claims(evidence)}`;
10529
11015
  return link;
10530
11016
  }
10531
11017
  var openDialogs = /* @__PURE__ */ new WeakMap();
10532
- var styles2 = `
10533
- * { box-sizing: border-box; }
10534
- dialog { width: min(460px, calc(100vw - 32px)); max-height: calc(100dvh - 40px); margin: auto; padding: 0; overflow: hidden; border: 1px solid #dce2de; border-radius: 16px; background: #fff; color: #23312b; box-shadow: 0 24px 90px #10201935; font: 13px/1.6 system-ui, sans-serif; }
10535
- /* The pane scrolls inside the dialog, which clips its scrollbar to the rounded corners. */
10536
- dialog[open] { display: flex; flex-direction: column; }
10537
- .pane { min-height: 0; padding: 24px; overflow-y: auto; }
10538
- dialog::backdrop { background: #15271f66; }
10539
- header { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
10540
- h2 { margin: 0; font-size: 18px; font-weight: 650; letter-spacing: -.3px; }
10541
- button { width: 32px; height: 32px; border: 1px solid #dce2de; border-radius: 8px; background: #fff; color: #52645a; font: 20px system-ui, sans-serif; cursor: pointer; }
10542
- button:hover { background: #f2f5f1; }
10543
- .icon { display: grid; place-items: center; padding: 0; }
10544
- .icon svg { width: 16px; height: 16px; }
10545
- .title { display: flex; align-items: center; gap: 6px; min-width: 0; }
10546
- .all { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; color: #52645a; }
10547
- .all:hover { background: #f2f5f1; }
10548
- .all svg { width: 16px; height: 16px; }
10549
- a { color: #245f43; text-underline-offset: 3px; overflow-wrap: anywhere; }
10550
- a:focus-visible, button:focus-visible, summary:focus-visible { outline: 2px solid #357ce5; outline-offset: 3px; }
10551
- /* An element given its own display would otherwise go on showing while hidden. */
10552
- [hidden] { display: none !important; }
10553
- .summary { display: flex; align-items: center; gap: 8px; margin-top: 16px; }
10554
- .mark { width: 36px; height: 36px; flex-shrink: 0; }
10555
- .provider { width: 14px; height: 14px; }
10556
- .state { font-size: 14px; font-weight: 650; line-height: 1.4; }
10557
- .muted { color: #6b786f; font-size: 12px; }
10558
- .account { position: relative; }
10559
- .veiled > :not(.checking) { filter: blur(4px); opacity: .55; user-select: none; }
10560
- .checking { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 8px; color: #23312b; font-size: 13px; font-weight: 600; }
10561
- .spinner { width: 14px; height: 14px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin .7s linear infinite; }
10562
- @keyframes spin { to { transform: rotate(360deg); } }
10563
- @media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
10564
- /* The "signed" pill after the verifier's name, one size smaller than the line it sits on. */
10565
- .summary .signed { display: inline-block; margin-left: 5px; padding: 0 6px; border: 1px solid #cfdcd3; border-radius: 999px; font-size: 10px; font-weight: 550; line-height: 1.3; text-decoration: none; vertical-align: 1px; }
10566
- .summary a.signed:hover { background: #f2f5f1; }
10567
- .summary .signed.bad { border-color: #b3261e; color: #b3261e; font-weight: 650; cursor: help; }
10568
- .caution { display: inline-block; margin-left: 3px; color: #a15c00; vertical-align: -1.5px; }
10569
- .caution svg { display: block; width: 10px; height: 10px; }
10570
- .account { padding: 14px 16px; border: 1px solid #e0e6df; border-radius: 10px; margin-top: 12px; }
10571
- .account h3 { display: flex; align-items: center; gap: 6px; margin: 0 0 3px; color: #6b786f; font-size: 11px; font-weight: 550; }
10572
- .account a, .account strong { font-weight: 650; font-size: 14px; }
10573
- /* The account's own name carries that weight; a link inside a line of prose does not. */
10574
- .summary a, .method a, .additional a { font: inherit; }
10575
- .reference { margin-top: 2px; }
10576
- /* A fingerprint in fours: it breaks between groups, and copies as one unbroken word. */
10577
- .fingerprint { font-family: ui-monospace, monospace; text-wrap: balance; }
10578
- .fingerprint span { display: inline-block; margin-right: .5ch; }
10579
- .method { margin-top: 8px; }
10580
- /* In the corner the heading leaves empty, apart from everything that was checked. */
10581
- .listing { position: absolute; top: 12px; right: 14px; padding: 1px 8px; border: 1px solid #e0e6df; border-radius: 999px; color: #6b786f; font-size: 11px; font-weight: 550; }
10582
- .listing.preferred { border-color: #245f43; color: #245f43; }
10583
- /* Quiet, as its pill is: the surface and the name step back, and the controls do not. */
10584
- .account.retired { background: #f6f8f5; }
10585
- .account.retired .identity > a, .account.retired .identity > strong, .account.retired .named > a, .account.retired .named > strong { color: #6b786f; }
10586
- /* The name, then a dot and the account's creation date. The date wraps under a long name. */
10587
- /* Clipped 4px left of the text, which hides the dot when the date wraps to a new line. */
10588
- .named { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 14px; margin-left: -4px; padding-left: 4px; overflow-x: clip; }
10589
- .since { position: relative; white-space: nowrap; }
10590
- /* The dot sits in the gap before the date. */
10591
- .since::before { content: "\xB7"; position: absolute; left: -7px; transform: translateX(-50%); }
10592
- .account.retired .mark { opacity: .45; }
10593
- .group { margin: 20px 0 0; color: #6b786f; font-size: 11px; font-weight: 550; }
10594
- summary.group { display: flex; align-items: center; gap: 4px; width: fit-content; border-radius: 4px; list-style: none; cursor: pointer; }
10595
- summary::-webkit-details-marker { display: none; }
10596
- .fold { display: flex; align-items: center; gap: 4px; width: auto; height: auto; padding: 2px 8px 2px 4px; margin-left: -4px; border: 0; background: none; font: 550 12px/1.6 system-ui, sans-serif; }
10597
- .fold svg, summary svg { width: 12px; height: 12px; }
10598
- .fold[aria-expanded="true"] svg, details[open] > summary svg { transform: rotate(90deg); }
10599
- @media (prefers-reduced-motion: no-preference) { .fold svg, summary svg { transition: transform .15s; } }
10600
- /* The fold and what the holder may do share a line, until a question needs the whole of it. */
10601
- .bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
10602
- .account .bar .row { margin: 0 0 0 auto; }
10603
- .additional { margin-top: 2px; padding-left: 12px; }
10604
- .joiner { display: block; width: 20px; height: 20px; margin: 8px auto -4px; color: #90a096; }
10605
- dl { margin: 16px 0 0; padding-top: 12px; border-top: 1px solid #e5e9e3; display: grid; grid-template-columns: auto 1fr; gap: 5px 16px; font-size: 11px; }
10606
- dt { color: #6b786f; }
10607
- dd { margin: 0; text-align: right; overflow-wrap: anywhere; }
10608
- .explanation { margin-top: 16px; }
10609
- .action { width: auto; height: auto; padding: 9px 14px; font: 600 13px/1.3 system-ui, sans-serif; color: #23312b; text-align: left; }
10610
- .action.primary { border-color: #245f43; background: #245f43; color: #fff; }
10611
- .action.primary:hover { background: #1d4f37; }
10612
- .action:disabled { opacity: .6; cursor: progress; }
10613
- .action.danger:hover { border-color: #b3261e; background: #fdecea; color: #b3261e; }
10614
- .row { display: flex; justify-content: end; gap: 8px; margin-top: 16px; }
10615
- .account .row { align-items: center; justify-content: start; flex-wrap: wrap; margin-top: 12px; }
10616
- .account .action { padding: 5px 10px; font-size: 12px; }
10617
- .account .action.dots { display: grid; place-items: center; align-self: stretch; width: 30px; padding: 0; }
10618
- .dots svg { width: 16px; height: 16px; }
10619
- /* Placed under its button each time it opens, above the dialog and clipped by none of it. */
10620
- .menu { position: fixed; inset: auto; margin: 0; min-width: 170px; padding: 6px; border: 1px solid #dce2de; border-radius: 10px; background: #fff; color: #23312b; box-shadow: 0 12px 40px #10201930; }
10621
- .menu:popover-open { display: grid; }
10622
- .account .menu .action { border: 0; border-radius: 6px; padding: 6px 10px; font-weight: 550; }
10623
- .menu .danger { color: #b3261e; }
10624
- .menu hr { width: 100%; margin: 4px 0; border: 0; border-top: 1px solid #e5e9e3; }
10625
- footer { display: flex; align-items: center; justify-content: start; gap: 6px; margin: 20px 0 -8px; color: #9aa9a0; font-size: 11px; }
10626
- footer a { display: flex; color: inherit; }
10627
- .logo { display: block; height: 13px; }
10628
- /* Four or more accounts on a wide screen: two equal cards to a row. */
10629
- @media (min-width: 960px) {
10630
- dialog.many { width: min(900px, calc(100vw - 32px)); }
10631
- dialog.many .tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 12px; }
10632
- /* Each card is three rows of the grid: who, its verdict, and the rest. A row is as tall as
10633
- the taller card's part, so the verdicts and details of neighbouring cards line up. */
10634
- dialog.many .tiles > .account { display: grid; grid-row: span 3; grid-template-rows: subgrid; row-gap: 0; margin-top: 0; }
10635
- /* The dates sit at the foot of the third part, so they line up too. */
10636
- dialog.many .tiles .rest { display: flex; flex-direction: column; }
10637
- dialog.many .tiles .rest > dl { margin-top: auto; }
10638
- dialog.many .tiles .rest > :has(+ dl) { margin-bottom: 16px; }
10639
- /* The site's card is one tile wide, centred over the two columns. */
10640
- dialog.many .local { width: calc(50% - 6px); margin-inline: auto; text-align: center; }
10641
- dialog.many .local h3, dialog.many .local .named { justify-content: center; }
10642
- dialog.many .local .additional { padding-left: 0; }
10643
- }
10644
- ${markStyles}
10645
- `;
11018
+ var holds = 0;
11019
+ var release = () => {
11020
+ };
11021
+ function holdPage(dialog) {
11022
+ if (holds++ === 0) {
11023
+ const { style, clientWidth } = document.documentElement;
11024
+ const held = ["overflow-x", "overflow-y", "scrollbar-gutter"];
11025
+ const saved = held.map((name) => [
11026
+ style.getPropertyValue(name),
11027
+ style.getPropertyPriority(name)
11028
+ ]);
11029
+ if (innerWidth > clientWidth) style.setProperty("scrollbar-gutter", "stable", "important");
11030
+ style.setProperty("overflow-x", "hidden", "important");
11031
+ style.setProperty("overflow-y", "hidden", "important");
11032
+ release = () => held.forEach((name, i2) => {
11033
+ const [value, priority] = saved[i2];
11034
+ if (value) style.setProperty(name, value, priority);
11035
+ else style.removeProperty(name);
11036
+ });
11037
+ }
11038
+ dialog.addEventListener("close", () => --holds === 0 && release(), { once: true });
11039
+ }
11040
+ var styles2 = evidence_dialog_css_default + markStyles;
10646
11041
  function node(tag, text = "", className = "") {
10647
11042
  const element = document.createElement(tag);
10648
11043
  element.textContent = text;
@@ -11036,7 +11431,11 @@ ${claims(evidence)}`;
11036
11431
  ]);
11037
11432
  }
11038
11433
  const main = evidence.attestations.external[0];
11039
- if (main.artifactUrl && !retired) dateRows.push(["Last checked", main.confirmedAt]);
11434
+ if (!retired) {
11435
+ if (main.artifactUrl) dateRows.push(["Last checked", main.confirmedAt]);
11436
+ else if (authenticatedEarlier(evidence))
11437
+ dateRows.push(["Authenticated", evidence.authenticatedAt]);
11438
+ }
11040
11439
  for (const [label, time] of dateRows) {
11041
11440
  dates.append(node("dt", label), node("dd", moment(time)));
11042
11441
  }
@@ -11336,34 +11735,16 @@ ${record2.external.id}`;
11336
11735
  { once: true }
11337
11736
  );
11338
11737
  if (opened && [opened].flat().length) draw(opened);
11738
+ holdPage(dialog);
11339
11739
  dialog.showModal();
11340
11740
  void refresh();
11341
11741
  }
11342
11742
 
11743
+ // src/browser/connect-dialog.css.ts
11744
+ var connect_dialog_css_default = ".steps > p:first-child {\n margin-top: 0;\n}\n.choices {\n display: grid;\n gap: 8px;\n margin-top: 16px;\n}\n/* With a way back beside it, the heading sits by that and not in the middle. */\nheader.backed h2 {\n margin-right: auto;\n}\n.choices .action {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.choices .more {\n margin-left: auto;\n color: #6b786f;\n font-weight: 500;\n font-size: 12px;\n}\n.row.start {\n justify-content: start;\n}\n/* The block scrolls and its frame does not, so the copy button stays in its corner. */\n.code {\n position: relative;\n margin: 10px 0;\n}\npre {\n margin: 0;\n padding: 10px 64px 10px 12px;\n border: 1px solid #e0e6df;\n border-radius: 8px;\n background: #f6f8f5;\n font:\n 12px/1.5 ui-monospace,\n monospace;\n white-space: pre;\n overflow-x: auto;\n}\n.code .action {\n position: absolute;\n top: 9px;\n right: 9px;\n padding: 3px 8px;\n font-size: 11px;\n}\nlabel {\n display: grid;\n gap: 4px;\n margin-top: 12px;\n font-weight: 600;\n}\ninput[type='url'],\ninput[type='email'],\ninput[type='tel'],\ninput[type='text'],\ntextarea {\n width: 100%;\n padding: 8px 10px;\n border: 1px solid #cfd8d2;\n border-radius: 8px;\n font:\n 13px/1.4 system-ui,\n sans-serif;\n color: inherit;\n background: #fff;\n}\ntextarea {\n font-family: ui-monospace, monospace;\n font-size: 12px;\n resize: vertical;\n}\nfieldset {\n margin: 16px 0 0;\n padding: 10px 14px;\n border: 1px solid #e0e6df;\n border-radius: 10px;\n}\nlegend {\n padding: 0 4px;\n color: #6b786f;\n font-size: 11px;\n font-weight: 550;\n}\nfieldset label {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin-top: 4px;\n font-weight: 500;\n}\n/* A radio has no baseline to speak of, so it is set in the middle of the label's first line. */\nfieldset input {\n flex-shrink: 0;\n align-self: flex-start;\n width: 13px;\n height: 13px;\n font: inherit;\n margin-top: 4px;\n margin-top: calc((1lh - 13px) / 2);\n margin-bottom: 0;\n}\n.error {\n color: #9b2c33;\n}\n";
11745
+
11343
11746
  // src/browser/connect-dialog.ts
11344
- var connectStyles = `
11345
- ${styles2}
11346
- .steps > p:first-child { margin-top: 0; }
11347
- .choices { display: grid; gap: 8px; margin-top: 16px; }
11348
- /* With a way back beside it, the heading sits by that and not in the middle. */
11349
- header.backed h2 { margin-right: auto; }
11350
- .choices .action { display: flex; align-items: center; gap: 8px; }
11351
- .choices .more { margin-left: auto; color: #6b786f; font-weight: 500; font-size: 12px; }
11352
- .row.start { justify-content: start; }
11353
- /* The block scrolls and its frame does not, so the copy button stays in its corner. */
11354
- .code { position: relative; margin: 10px 0; }
11355
- pre { margin: 0; padding: 10px 64px 10px 12px; border: 1px solid #e0e6df; border-radius: 8px; background: #f6f8f5; font: 12px/1.5 ui-monospace, monospace; white-space: pre; overflow-x: auto; }
11356
- .code .action { position: absolute; top: 9px; right: 9px; padding: 3px 8px; font-size: 11px; }
11357
- label { display: grid; gap: 4px; margin-top: 12px; font-weight: 600; }
11358
- input[type=url], input[type=email], input[type=tel], input[type=text], textarea { width: 100%; padding: 8px 10px; border: 1px solid #cfd8d2; border-radius: 8px; font: 13px/1.4 system-ui, sans-serif; color: inherit; background: #fff; }
11359
- textarea { font-family: ui-monospace, monospace; font-size: 12px; resize: vertical; }
11360
- fieldset { margin: 16px 0 0; padding: 10px 14px; border: 1px solid #e0e6df; border-radius: 10px; }
11361
- legend { padding: 0 4px; color: #6b786f; font-size: 11px; font-weight: 550; }
11362
- fieldset label { display: flex; align-items: baseline; gap: 8px; margin-top: 4px; font-weight: 500; }
11363
- /* A radio has no baseline to speak of, so it is set in the middle of the label's first line. */
11364
- fieldset input { flex-shrink: 0; align-self: flex-start; width: 13px; height: 13px; font: inherit; margin-top: 4px; margin-top: calc((1lh - 13px) / 2); margin-bottom: 0; }
11365
- .error { color: #9b2c33; }
11366
- `;
11747
+ var connectStyles = styles2 + connect_dialog_css_default;
11367
11748
  function codeBlock(code) {
11368
11749
  const block = node("div", "", "code");
11369
11750
  const scroller = node("pre");
@@ -11814,6 +12195,7 @@ ${record2.external.id}`;
11814
12195
  { once: true }
11815
12196
  )
11816
12197
  );
12198
+ holdPage(dialog);
11817
12199
  dialog.showModal();
11818
12200
  void choose();
11819
12201
  return closed;
@@ -11869,7 +12251,7 @@ ${record2.external.id}`;
11869
12251
  }
11870
12252
  },
11871
12253
  latestGroup.get(element),
11872
- accounts,
12254
+ (records) => accounts(aged(element, records)),
11873
12255
  managers.get(element),
11874
12256
  openHosts.has(element)
11875
12257
  );
@@ -11878,9 +12260,20 @@ ${record2.external.id}`;
11878
12260
  var stackedHosts = /* @__PURE__ */ new WeakSet();
11879
12261
  var quietHosts = /* @__PURE__ */ new WeakSet();
11880
12262
  var openHosts = /* @__PURE__ */ new WeakSet();
12263
+ var maxAges = /* @__PURE__ */ new WeakMap();
12264
+ function bound(element, maxAgeMs) {
12265
+ if (maxAgeMs === void 0) maxAges.delete(element);
12266
+ else maxAges.set(element, maxAgeMs);
12267
+ }
12268
+ function aged(element, records) {
12269
+ const maxAgeMs = maxAges.get(element);
12270
+ const now = Date.now();
12271
+ return records.map((e2) => withMaxAge(e2, maxAgeMs, now));
12272
+ }
11881
12273
  var stackedAccounts = 4;
11882
12274
  var peekedAccounts = 8;
11883
- function arrange(element, { stacked, peek, detailsOpen }) {
12275
+ function arrange(element, { stacked, peek, detailsOpen, maxAgeMs }) {
12276
+ bound(element, maxAgeMs);
11884
12277
  if (detailsOpen) openHosts.add(element);
11885
12278
  else openHosts.delete(element);
11886
12279
  if (stacked) stackedHosts.add(element);
@@ -11900,7 +12293,7 @@ ${record2.external.id}`;
11900
12293
  }));
11901
12294
  }
11902
12295
  function presentGroup(element, records, load2) {
11903
- const ordered = accounts(records.map(standing));
12296
+ const ordered = accounts(aged(element, records.map(standing)));
11904
12297
  const verified = ordered.filter(live);
11905
12298
  const lead = verified[0] ?? ordered[0];
11906
12299
  latestGroup.set(element, records);
@@ -11982,8 +12375,8 @@ ${record2.external.id}`;
11982
12375
  });
11983
12376
  if (!response.ok) throw new Error("Verily request unavailable");
11984
12377
  const answer = await response.json();
11985
- const bound = response.headers.get("X-Verily-Flow");
11986
- if (bound && record(answer) && typeof answer.id === "string") bindings.set(answer.id, bound);
12378
+ const bound2 = response.headers.get("X-Verily-Flow");
12379
+ if (bound2 && record(answer) && typeof answer.id === "string") bindings.set(answer.id, bound2);
11987
12380
  return answer;
11988
12381
  }
11989
12382
  async function request(path, data) {
@@ -12132,7 +12525,12 @@ ${record2.external.id}`;
12132
12525
  if (!validEvidence(data)) throw new Error("Invalid evidence response");
12133
12526
  return data;
12134
12527
  },
12135
- async mountBadge(element, { connectionId, evidence }) {
12528
+ async mountBadge(element, {
12529
+ connectionId,
12530
+ evidence,
12531
+ maxAgeMs
12532
+ }) {
12533
+ bound(element, maxAgeMs);
12136
12534
  if (!evidence && !badgeShown(element)) renderBadgePending(element);
12137
12535
  const current = begin(element);
12138
12536
  try {
@@ -12149,7 +12547,7 @@ ${record2.external.id}`;
12149
12547
  return [fresh2];
12150
12548
  });
12151
12549
  const draw = () => {
12152
- const badge = renderBadge(element, standing(e2));
12550
+ const badge = renderBadge(element, aged(element, [standing(e2)])[0]);
12153
12551
  if (badge) opens(element, badge);
12154
12552
  };
12155
12553
  draw();
@@ -12168,7 +12566,11 @@ ${record2.external.id}`;
12168
12566
  async mountBadges(element, { connectionIds, ...arrangement }) {
12169
12567
  const ids = [...new Set(connectionIds)];
12170
12568
  arrange(element, arrangement);
12171
- if (ids.length === 1) return client.mountBadge(element, { connectionId: ids[0] });
12569
+ if (ids.length === 1)
12570
+ return client.mountBadge(element, {
12571
+ connectionId: ids[0],
12572
+ maxAgeMs: arrangement.maxAgeMs
12573
+ });
12172
12574
  if (!badgeShown(element)) renderBadgePending(element);
12173
12575
  const read2 = async () => {
12174
12576
  const answers = await Promise.allSettled(ids.map((id) => client.getConnection(id)));
@@ -12313,7 +12715,8 @@ ${record2.external.id}`;
12313
12715
  "connect",
12314
12716
  "stacked",
12315
12717
  "peek",
12316
- "details-open"
12718
+ "details-open",
12719
+ "max-age"
12317
12720
  ];
12318
12721
  handed;
12319
12722
  /**
@@ -12368,7 +12771,8 @@ ${record2.external.id}`;
12368
12771
  const arrangement = {
12369
12772
  stacked: this.getAttribute("stacked") !== null,
12370
12773
  peek: this.getAttribute("peek") !== "off",
12371
- detailsOpen: this.getAttribute("details-open") !== null
12774
+ detailsOpen: this.getAttribute("details-open") !== null,
12775
+ maxAgeMs: parseMaxAge(this.getAttribute("max-age"))
12372
12776
  };
12373
12777
  if (backendUrl && (handoffUrl !== null || this.getAttribute("connect") !== null)) {
12374
12778
  const client = init({ backendUrl, handoffUrl: handoffUrl ?? void 0 });
@@ -12422,7 +12826,12 @@ ${record2.external.id}`;
12422
12826
  renderBadgePending(this);
12423
12827
  return;
12424
12828
  }
12425
- if (backendUrl) void init({ backendUrl }).mountBadge(this, { connectionId, evidence });
12829
+ if (backendUrl)
12830
+ void init({ backendUrl }).mountBadge(this, {
12831
+ connectionId,
12832
+ evidence,
12833
+ maxAgeMs: arrangement.maxAgeMs
12834
+ });
12426
12835
  }
12427
12836
  }
12428
12837
  );