@zoreal/oauth2-js 0.1.10 → 0.1.11

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/index.d.cts CHANGED
@@ -289,7 +289,7 @@ declare function mountPairingModal(state: PairingState, options: PairingModalOpt
289
289
  * auth-code mode leaves it to the RP backend.
290
290
  */
291
291
  declare const WIRE_VERSION = 1;
292
- declare const SDK_VERSION = "0.1.10";
292
+ declare const SDK_VERSION = "0.1.11";
293
293
  declare const SDK_NAME = "@zoreal/oauth2-js";
294
294
  declare const DEFAULT_ISSUER = "https://id.zoreal.com";
295
295
  /** Pending TTL is short. Poll gently; over-polling cancels the request. */
package/dist/index.d.ts CHANGED
@@ -289,7 +289,7 @@ declare function mountPairingModal(state: PairingState, options: PairingModalOpt
289
289
  * auth-code mode leaves it to the RP backend.
290
290
  */
291
291
  declare const WIRE_VERSION = 1;
292
- declare const SDK_VERSION = "0.1.10";
292
+ declare const SDK_VERSION = "0.1.11";
293
293
  declare const SDK_NAME = "@zoreal/oauth2-js";
294
294
  declare const DEFAULT_ISSUER = "https://id.zoreal.com";
295
295
  /** Pending TTL is short. Poll gently; over-polling cancels the request. */
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ function unsafeClaims(idToken) {
14
14
 
15
15
  // src/wire.ts
16
16
  var WIRE_VERSION = 1;
17
- var SDK_VERSION = "0.1.10";
17
+ var SDK_VERSION = "0.1.11";
18
18
  var SDK_NAME = "@zoreal/oauth2-js";
19
19
  var DEFAULT_ISSUER = "https://id.zoreal.com";
20
20
  var POLL_INTERVAL_MS = 2e3;
@@ -967,20 +967,110 @@ var CSS = `
967
967
  color: var(--zrl-ink-soft);
968
968
  }
969
969
 
970
+ /* The light around the well: a thin ring in the accent, one bright arc
971
+ travelling round it, and a soft glow of the same colour bleeding a few
972
+ pixels outward. It says the pairing is live without a word.
973
+
974
+ It sits BELOW the well, which is why the well is wrapped instead of given a
975
+ pseudo-element. Anything drawn inside the well paints over the white quiet
976
+ zone the camera needs, and a negative z-index from inside the well lands
977
+ wherever the card's stacking context happens to be that frame (the card's
978
+ entrance animation makes and unmakes one). The wrapper is the well's old
979
+ box exactly, same size, same margin, so nothing moves; the well keeps its
980
+ own stacking order on top, and the ring cannot touch the QR. */
981
+ .${PREFIX}-qr-halo {
982
+ position: relative;
983
+ isolation: isolate;
984
+ width: 204px;
985
+ height: 204px;
986
+ margin: 20px auto 0;
987
+ }
988
+
970
989
  .${PREFIX}-qr-well {
971
990
  position: relative;
991
+ z-index: 1;
972
992
  display: grid;
973
993
  place-items: center;
974
994
  box-sizing: border-box;
975
995
  width: 204px;
976
996
  height: 204px;
977
- margin: 20px auto 0;
997
+ margin: 0;
978
998
  padding: 12px;
979
999
  border: 1px solid var(--zrl-line);
980
1000
  border-radius: 16px;
981
1001
  background: var(--zrl-qr-bg);
982
1002
  }
983
1003
 
1004
+ /* The glow is the ring's own alpha, blurred and drawn beneath it, so it
1005
+ follows the arc round and fades with it. It has to be a parent of the
1006
+ masked band: a filter is applied before a mask, so a shadow cast from the
1007
+ band itself would be cut off at the band's edge. Two shadows because one
1008
+ 2px source blurred over 10px is too faint to read as light; the second
1009
+ blurs the first. */
1010
+ .${PREFIX}-qr-glow {
1011
+ position: absolute;
1012
+ inset: -2px;
1013
+ pointer-events: none;
1014
+ filter: drop-shadow(0 0 3px var(--zrl-accent)) drop-shadow(0 0 10px var(--zrl-accent));
1015
+ }
1016
+
1017
+ /* The band: a 2px ring just outside the well, cut with a mask that keeps the
1018
+ padding and drops the content box. The gradient is on a pseudo-element
1019
+ rather than on the band, because in the fallback it has to rotate as a
1020
+ whole while the band stays put. The prefixed mask is for Chrome before 120;
1021
+ the unprefixed one, declared after it, wins everywhere else. */
1022
+ .${PREFIX}-qr-arc {
1023
+ position: absolute;
1024
+ inset: 0;
1025
+ padding: 2px;
1026
+ border-radius: 18px;
1027
+ overflow: hidden;
1028
+ -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
1029
+ -webkit-mask-composite: xor;
1030
+ mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
1031
+ mask-composite: exclude;
1032
+ }
1033
+ .${PREFIX}-qr-arc::before,
1034
+ .${PREFIX}-qr-arc::after {
1035
+ content: '';
1036
+ position: absolute;
1037
+ transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);
1038
+ }
1039
+
1040
+ /* The light. Mostly transparent, a tail that builds over a quarter turn, a
1041
+ short full-strength head and a drop-off within a few degrees, so it reads
1042
+ as travelling rather than blinking. One revolution every 3s, linear:
1043
+ continuous motion has no ease. Oversized so its corners still cover the
1044
+ band mid-rotation in the transform fallback; the property path shrinks it
1045
+ back to the band. */
1046
+ .${PREFIX}-qr-arc::before {
1047
+ inset: -50%;
1048
+ background: conic-gradient(
1049
+ from var(--zrl-angle, 0deg),
1050
+ transparent 0deg 245deg,
1051
+ var(--zrl-accent) 332deg 346deg,
1052
+ transparent 356deg 360deg
1053
+ );
1054
+ animation: ${PREFIX}-orbit 3s linear infinite;
1055
+ }
1056
+
1057
+ /* The rest state: the same ring, evenly lit. Faint under the moving light so
1058
+ the ring reads as a ring; stronger once the light has stopped. */
1059
+ .${PREFIX}-qr-arc::after {
1060
+ inset: 0;
1061
+ background: var(--zrl-accent);
1062
+ opacity: 0.16;
1063
+ }
1064
+
1065
+ /* Spent: the light stops where it is and fades, and the ring settles to a
1066
+ dim, even glow. Paused rather than removed, so it does not jump back to
1067
+ its start on the way out. */
1068
+ .${PREFIX}-qr-halo[data-spent="true"] .${PREFIX}-qr-arc::before {
1069
+ animation-play-state: paused;
1070
+ opacity: 0;
1071
+ }
1072
+ .${PREFIX}-qr-halo[data-spent="true"] .${PREFIX}-qr-arc::after { opacity: 0.34; }
1073
+
984
1074
  .${PREFIX}-qr {
985
1075
  display: block;
986
1076
  width: 100%;
@@ -1123,6 +1213,33 @@ var CSS = `
1123
1213
  70%, 100% { transform: scale(2.6); opacity: 0 }
1124
1214
  }
1125
1215
 
1216
+ /* Two ways to turn the light, one look.
1217
+ Where the browser can register a typed custom property, the gradient's own
1218
+ angle animates and the element never moves. Where it cannot (Firefox before
1219
+ 128), an untyped custom property cannot interpolate and the light would
1220
+ snap once a cycle, so the oversized pseudo-element rotates instead and the
1221
+ band's mask keeps the ring in place. At rest the two are the same pixels:
1222
+ same gradient, same centre, angle zero.
1223
+ There is no direct query for @property support. transition-behavior
1224
+ shipped after it in every engine (Chrome 117 vs 85, Firefox 129 vs 128,
1225
+ Safari 17.4 vs 16.4), so it stands in: a yes is never wrong, and a no only
1226
+ sends a capable browser down the fallback, which looks the same.
1227
+ inherits: false so the angle stays on the element that animates it and
1228
+ never reaches the host page or the well. */
1229
+ @property --zrl-angle {
1230
+ syntax: '<angle>';
1231
+ inherits: false;
1232
+ initial-value: 0deg;
1233
+ }
1234
+ @keyframes ${PREFIX}-orbit { to { transform: rotate(360deg) } }
1235
+ @keyframes ${PREFIX}-orbit-angle { to { --zrl-angle: 360deg } }
1236
+ @supports (transition-behavior: allow-discrete) {
1237
+ .${PREFIX}-qr-arc::before {
1238
+ inset: 0;
1239
+ animation-name: ${PREFIX}-orbit-angle;
1240
+ }
1241
+ }
1242
+
1126
1243
  @media (prefers-reduced-motion: reduce) {
1127
1244
  .${PREFIX}-scrim,
1128
1245
  .${PREFIX}-card,
@@ -1132,6 +1249,10 @@ var CSS = `
1132
1249
  .${PREFIX}-cancel,
1133
1250
  .${PREFIX}-close,
1134
1251
  .${PREFIX}-timer { transition: none }
1252
+ /* No travelling light. The ring keeps a still, soft glow instead, a step
1253
+ brighter than the spent state so pending and spent still read apart. */
1254
+ .${PREFIX}-qr-arc::before { animation: none; opacity: 0 }
1255
+ .${PREFIX}-qr-arc::after { opacity: 0.5 }
1135
1256
  }
1136
1257
  `;
1137
1258
  function ensureStyles() {
@@ -1236,6 +1357,10 @@ function mountPairingModal(state, options) {
1236
1357
  const title = el("h2", cx("title"));
1237
1358
  title.id = titleId;
1238
1359
  const bodyText = el("p", cx("body-text"));
1360
+ const halo = el("div", cx("qr-halo"));
1361
+ const glow = el("span", cx("qr-glow"));
1362
+ glow.setAttribute("aria-hidden", "true");
1363
+ glow.appendChild(el("span", cx("qr-arc")));
1239
1364
  const well = el("div", cx("qr-well"));
1240
1365
  const qr = el("img", cx("qr"));
1241
1366
  qr.alt = t.qrAlt;
@@ -1247,13 +1372,14 @@ function mountPairingModal(state, options) {
1247
1372
  badge.appendChild(strokeIcon(24, ["M8.5 2h7a2.5 2.5 0 0 1 2.5 2.5v15a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 6 19.5v-15A2.5 2.5 0 0 1 8.5 2Z", "M11 18.5h2"], "1.8"));
1248
1373
  overlay.appendChild(badge);
1249
1374
  well.append(qr, overlay);
1375
+ halo.append(glow, well);
1250
1376
  const status = el("div", cx("status"));
1251
1377
  const dot = el("span", cx("dot"));
1252
1378
  dot.append(el("i"), el("i"));
1253
1379
  const statusLabel = el("span");
1254
1380
  status.append(dot, statusLabel);
1255
1381
  const timer = el("p", cx("timer"));
1256
- body.append(mark, title, bodyText, well, status, timer);
1382
+ body.append(mark, title, bodyText, halo, status, timer);
1257
1383
  const help = el("div", cx("help"));
1258
1384
  help.append(el("p", cx("help-title"), t.noIdTitle), el("p", cx("help-body"), t.noIdBody));
1259
1385
  const footer = el("div", cx("footer"));
@@ -1295,6 +1421,7 @@ function mountPairingModal(state, options) {
1295
1421
  bodyText.textContent = s.status === "enrolling" ? t.bodyEnrolling : settled ? t.bodyApprove : t.bodyScan;
1296
1422
  statusLabel.textContent = settled ? t.waitingApproval : t.waiting;
1297
1423
  qr.dataset.spent = String(settled);
1424
+ halo.dataset.spent = String(settled);
1298
1425
  overlay.style.display = settled ? "" : "none";
1299
1426
  if (s.qrUrl) showFrame(s.qrUrl);
1300
1427
  };