@zoreal/oauth2-js 0.1.11 → 0.1.12

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.11";
292
+ declare const SDK_VERSION = "0.1.12";
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.11";
292
+ declare const SDK_VERSION = "0.1.12";
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.11";
17
+ var SDK_VERSION = "0.1.12";
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,109 +967,121 @@ 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
-
989
970
  .${PREFIX}-qr-well {
990
971
  position: relative;
991
- z-index: 1;
992
972
  display: grid;
993
973
  place-items: center;
994
974
  box-sizing: border-box;
995
975
  width: 204px;
996
976
  height: 204px;
997
- margin: 0;
977
+ margin: 20px auto 0;
998
978
  padding: 12px;
999
979
  border: 1px solid var(--zrl-line);
1000
- border-radius: 16px;
980
+ border-radius: var(--zrl-radius);
1001
981
  background: var(--zrl-qr-bg);
982
+ /* The light on the edge takes its shape and colour from here. The colour
983
+ is fixed, not themed: the brand blue on both grounds, a lighter tint of
984
+ it at the head, no other hue. */
985
+ --zrl-radius: 16px;
986
+ --zrl-beam: #00b4d9;
987
+ --zrl-beam-head: #7fe0f4;
988
+ --zrl-beam-size: 100px;
989
+ --zrl-beam-time: 4s;
990
+ --zrl-glow-reach: 20px;
991
+ --zrl-glow-core: 3px;
1002
992
  }
1003
993
 
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 {
994
+ /* The light on the well's edge: a short comet running along the border, with
995
+ a soft glow outside it. Three overlays inside the well, each masked so the
996
+ comet can only ever paint where its mask allows, and the white interior lies
997
+ outside every mask: nothing here can reach the quiet zone a camera needs,
998
+ whatever the comet is doing. The mask is the padding box cut out of the
999
+ border box, a transparent layer clipped to the padding box intersected
1000
+ with a solid one clipped to the border box. The prefixed form is for Chrome
1001
+ before 120 and Safari before 15.4; the unprefixed one, declared after it,
1002
+ wins everywhere else.
1003
+
1004
+ qr-beam keeps a 1px ring on the border line: the comet itself.
1005
+ qr-beam-glow is the glow: a wide band outside the well that blurs whatever
1006
+ is inside it, and inside it qr-beam-glow-band keeps a 3px ring with a
1007
+ second copy of the comet. The blur has to sit on the parent because a
1008
+ filter is applied before a mask: blurred on the band itself, the glow
1009
+ would be cut back to the band's own edge. On the parent it runs after the
1010
+ band has clipped the comet thin and before the parent's mask cuts away the
1011
+ inward half, which is what makes it fade outward and never over the QR.
1012
+ All three share one containing block, the well's padding box, so the two
1013
+ comets ride the same path; the spent badge is a later sibling and paints
1014
+ above them. */
1015
+ .${PREFIX}-qr-beam,
1016
+ .${PREFIX}-qr-beam-glow,
1017
+ .${PREFIX}-qr-beam-glow-band {
1011
1018
  position: absolute;
1012
- inset: -2px;
1019
+ inset: -1px;
1020
+ border: 1px solid transparent;
1021
+ border-radius: var(--zrl-radius);
1013
1022
  pointer-events: none;
1014
- filter: drop-shadow(0 0 3px var(--zrl-accent)) drop-shadow(0 0 10px var(--zrl-accent));
1023
+ -webkit-mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);
1024
+ -webkit-mask-clip: padding-box, border-box;
1025
+ -webkit-mask-composite: source-in;
1026
+ mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);
1027
+ mask-clip: padding-box, border-box;
1028
+ mask-composite: intersect;
1015
1029
  }
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;
1030
+ .${PREFIX}-qr-beam-glow {
1031
+ inset: calc(0px - var(--zrl-glow-reach));
1032
+ border-width: var(--zrl-glow-reach);
1033
+ border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-reach));
1034
+ filter: blur(6px);
1035
+ opacity: 0.7;
1036
+ }
1037
+ .${PREFIX}-qr-beam-glow-band {
1038
+ inset: calc(0px - var(--zrl-glow-core));
1039
+ border-width: var(--zrl-glow-core);
1040
+ border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-core));
1032
1041
  }
1033
- .${PREFIX}-qr-arc::before,
1034
- .${PREFIX}-qr-arc::after {
1042
+
1043
+ /* At rest the edge holds a dim, even blue: a 1px line on the border and, from
1044
+ the glow band, a soft halo outside it. Hidden while the comet runs, so the
1045
+ border reads as the well's own line with a light passing over it; shown
1046
+ once the light has stopped. Every path below ends here, which is what
1047
+ makes them look the same at rest. */
1048
+ .${PREFIX}-qr-beam::before,
1049
+ .${PREFIX}-qr-beam-glow-band::before {
1035
1050
  content: '';
1036
1051
  position: absolute;
1052
+ inset: -50%;
1053
+ background: var(--zrl-beam);
1054
+ opacity: 0;
1037
1055
  transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);
1038
1056
  }
1039
1057
 
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 {
1058
+ /* The moving light. Declared bottom-up: the transform fallback here, and each
1059
+ @supports block further down replaces it where the browser allows. */
1060
+ .${PREFIX}-qr-beam::after,
1061
+ .${PREFIX}-qr-beam-glow-band::after {
1062
+ content: '';
1063
+ position: absolute;
1047
1064
  inset: -50%;
1048
1065
  background: conic-gradient(
1049
1066
  from var(--zrl-angle, 0deg),
1050
1067
  transparent 0deg 245deg,
1051
- var(--zrl-accent) 332deg 346deg,
1068
+ var(--zrl-beam) 332deg 346deg,
1052
1069
  transparent 356deg 360deg
1053
1070
  );
1054
1071
  animation: ${PREFIX}-orbit 3s linear infinite;
1072
+ transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);
1055
1073
  }
1056
1074
 
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 {
1075
+ /* Spent: the light stops where it is and fades, and the edge settles to the
1076
+ dim glow. Paused rather than removed, so it does not jump back to its start
1077
+ on the way out. */
1078
+ .${PREFIX}-qr-well[data-spent="true"] .${PREFIX}-qr-beam::after,
1079
+ .${PREFIX}-qr-well[data-spent="true"] .${PREFIX}-qr-beam-glow-band::after {
1069
1080
  animation-play-state: paused;
1070
1081
  opacity: 0;
1071
1082
  }
1072
- .${PREFIX}-qr-halo[data-spent="true"] .${PREFIX}-qr-arc::after { opacity: 0.34; }
1083
+ .${PREFIX}-qr-well[data-spent="true"] .${PREFIX}-qr-beam::before { opacity: 0.55; }
1084
+ .${PREFIX}-qr-well[data-spent="true"] .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7; }
1073
1085
 
1074
1086
  .${PREFIX}-qr {
1075
1087
  display: block;
@@ -1213,17 +1225,16 @@ var CSS = `
1213
1225
  70%, 100% { transform: scale(2.6); opacity: 0 }
1214
1226
  }
1215
1227
 
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.
1228
+ /* Three ways to move the light, one look at rest.
1229
+ Where offset-path takes a rect() (Chrome 116, Firefox 122, Safari 18) the
1230
+ comet rides the edge. Before that, a conic gradient turns inside the same
1231
+ masks instead: on a registered custom property where @property exists, and
1232
+ by rotating an oversized square where it does not, since an untyped custom
1233
+ property cannot interpolate and the light would snap once a cycle.
1223
1234
  There is no direct query for @property support. transition-behavior
1224
1235
  shipped after it in every engine (Chrome 117 vs 85, Firefox 129 vs 128,
1225
1236
  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.
1237
+ sends a capable browser down the transform tier, which looks the same.
1227
1238
  inherits: false so the angle stays on the element that animates it and
1228
1239
  never reaches the host page or the well. */
1229
1240
  @property --zrl-angle {
@@ -1233,12 +1244,34 @@ var CSS = `
1233
1244
  }
1234
1245
  @keyframes ${PREFIX}-orbit { to { transform: rotate(360deg) } }
1235
1246
  @keyframes ${PREFIX}-orbit-angle { to { --zrl-angle: 360deg } }
1247
+ @keyframes ${PREFIX}-beam { to { offset-distance: 100% } }
1236
1248
  @supports (transition-behavior: allow-discrete) {
1237
- .${PREFIX}-qr-arc::before {
1238
- inset: 0;
1249
+ .${PREFIX}-qr-beam::after {
1250
+ inset: -1px;
1251
+ animation-name: ${PREFIX}-orbit-angle;
1252
+ }
1253
+ .${PREFIX}-qr-beam-glow-band::after {
1254
+ inset: calc(0px - var(--zrl-glow-core));
1239
1255
  animation-name: ${PREFIX}-orbit-angle;
1240
1256
  }
1241
1257
  }
1258
+ /* The comet: a square that rides the well's edge on a rounded-rect path, its
1259
+ anchor a little ahead of centre, turned to face the way it travels, and
1260
+ masked down to what its overlay allows. The gradient runs against the
1261
+ direction of travel: a light head, the blue body, nothing behind. One lap
1262
+ in 4s, linear: continuous motion has no ease. */
1263
+ @supports (offset-path: rect(0 auto auto 0)) {
1264
+ .${PREFIX}-qr-beam::after,
1265
+ .${PREFIX}-qr-beam-glow-band::after {
1266
+ inset: auto;
1267
+ width: var(--zrl-beam-size);
1268
+ height: var(--zrl-beam-size);
1269
+ background: linear-gradient(to left, var(--zrl-beam-head), var(--zrl-beam), transparent);
1270
+ offset-path: rect(0 auto auto 0 round calc(var(--zrl-radius) - 1px));
1271
+ offset-anchor: 60% 50%;
1272
+ animation: ${PREFIX}-beam var(--zrl-beam-time) linear infinite;
1273
+ }
1274
+ }
1242
1275
 
1243
1276
  @media (prefers-reduced-motion: reduce) {
1244
1277
  .${PREFIX}-scrim,
@@ -1249,10 +1282,11 @@ var CSS = `
1249
1282
  .${PREFIX}-cancel,
1250
1283
  .${PREFIX}-close,
1251
1284
  .${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 }
1285
+ /* No travelling light; the edge keeps its dim static glow instead. */
1286
+ .${PREFIX}-qr-beam::after,
1287
+ .${PREFIX}-qr-beam-glow-band::after { animation: none; opacity: 0 }
1288
+ .${PREFIX}-qr-beam::before { opacity: 0.55 }
1289
+ .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7 }
1256
1290
  }
1257
1291
  `;
1258
1292
  function ensureStyles() {
@@ -1357,11 +1391,13 @@ function mountPairingModal(state, options) {
1357
1391
  const title = el("h2", cx("title"));
1358
1392
  title.id = titleId;
1359
1393
  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")));
1364
1394
  const well = el("div", cx("qr-well"));
1395
+ const glow = el("span", cx("qr-beam-glow"));
1396
+ glow.setAttribute("aria-hidden", "true");
1397
+ glow.appendChild(el("span", cx("qr-beam-glow-band")));
1398
+ const beam = el("span", cx("qr-beam"));
1399
+ beam.setAttribute("aria-hidden", "true");
1400
+ well.append(glow, beam);
1365
1401
  const qr = el("img", cx("qr"));
1366
1402
  qr.alt = t.qrAlt;
1367
1403
  qr.width = 180;
@@ -1372,14 +1408,13 @@ function mountPairingModal(state, options) {
1372
1408
  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"));
1373
1409
  overlay.appendChild(badge);
1374
1410
  well.append(qr, overlay);
1375
- halo.append(glow, well);
1376
1411
  const status = el("div", cx("status"));
1377
1412
  const dot = el("span", cx("dot"));
1378
1413
  dot.append(el("i"), el("i"));
1379
1414
  const statusLabel = el("span");
1380
1415
  status.append(dot, statusLabel);
1381
1416
  const timer = el("p", cx("timer"));
1382
- body.append(mark, title, bodyText, halo, status, timer);
1417
+ body.append(mark, title, bodyText, well, status, timer);
1383
1418
  const help = el("div", cx("help"));
1384
1419
  help.append(el("p", cx("help-title"), t.noIdTitle), el("p", cx("help-body"), t.noIdBody));
1385
1420
  const footer = el("div", cx("footer"));
@@ -1421,7 +1456,7 @@ function mountPairingModal(state, options) {
1421
1456
  bodyText.textContent = s.status === "enrolling" ? t.bodyEnrolling : settled ? t.bodyApprove : t.bodyScan;
1422
1457
  statusLabel.textContent = settled ? t.waitingApproval : t.waiting;
1423
1458
  qr.dataset.spent = String(settled);
1424
- halo.dataset.spent = String(settled);
1459
+ well.dataset.spent = String(settled);
1425
1460
  overlay.style.display = settled ? "" : "none";
1426
1461
  if (s.qrUrl) showFrame(s.qrUrl);
1427
1462
  };