@zoreal/oauth2-react 0.2.10 → 0.2.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.js CHANGED
@@ -5,7 +5,7 @@ import { createContext, useContext, useMemo, useState as useState2 } from "react
5
5
 
6
6
  // src/wire.ts
7
7
  var WIRE_VERSION = 1;
8
- var SDK_VERSION = "0.2.10";
8
+ var SDK_VERSION = "0.2.12";
9
9
  var DEFAULT_ISSUER = "https://id.zoreal.com";
10
10
  var POLL_INTERVAL_MS = 2e3;
11
11
  var POLL_INTERVAL_ENROLLING_MS = 5e3;
@@ -825,8 +825,20 @@ var LIGHT = `
825
825
  --zrl-accent-ink: #04698a;
826
826
  --zrl-urgent: #b4761a;
827
827
  --zrl-qr-bg: #ffffff;
828
+ --zrl-qr-filter: none;
829
+ --zrl-qr-spent-filter: blur(3px);
830
+ --zrl-qr-blend: normal;
828
831
  --zrl-shadow: 0 1px 2px rgba(16, 18, 27, 0.06), 0 20px 50px -12px rgba(16, 18, 27, 0.3);
829
832
  --zrl-ring: rgba(16, 18, 27, 0.07);
833
+ /* The light on the QR well's edge. Brand blue on both grounds, a lighter
834
+ tint at the head; only its strength is themed, see the dark block. */
835
+ --zrl-beam: #00b4d9;
836
+ --zrl-beam-head: #7fe0f4;
837
+ --zrl-beam-line: 2px;
838
+ --zrl-glow-core: 4px;
839
+ --zrl-glow-reach: 24px;
840
+ --zrl-glow-blur: 8px;
841
+ --zrl-glow-opacity: 0.6;
830
842
  `;
831
843
  var DARK = `
832
844
  --zrl-scrim: rgba(0, 0, 0, 0.62);
@@ -841,12 +853,24 @@ var DARK = `
841
853
  --zrl-accent-soft: #0d3b47;
842
854
  --zrl-accent-ink: #7fdcf0;
843
855
  --zrl-urgent: #e0a952;
844
- /* The QR well stays white in dark mode on purpose: a scanner needs the
845
- quiet-zone contrast, and an inverted QR fails on a good number of phone
846
- cameras. It reads as a deliberate light panel, not a theming miss. */
847
- --zrl-qr-bg: #ffffff;
856
+ /* The code is drawn light on the dark surface: the panel is transparent
857
+ and the image is inverted and screened, so only the modules and the
858
+ mark show. */
859
+ --zrl-qr-bg: transparent;
860
+ --zrl-qr-filter: invert(1);
861
+ --zrl-qr-spent-filter: invert(1) blur(3px);
862
+ --zrl-qr-blend: screen;
848
863
  --zrl-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 50px -12px rgba(0, 0, 0, 0.65);
849
864
  --zrl-ring: rgba(255, 255, 255, 0.1);
865
+ /* A glow that reads on a white card disappears on a dark one: the light
866
+ here is brighter and wider, and its halo reaches further out. */
867
+ --zrl-beam: #22c8ec;
868
+ --zrl-beam-head: #c2f3fc;
869
+ --zrl-beam-line: 3px;
870
+ --zrl-glow-core: 6px;
871
+ --zrl-glow-reach: 32px;
872
+ --zrl-glow-blur: 10px;
873
+ --zrl-glow-opacity: 0.85;
850
874
  `;
851
875
  var CSS = `
852
876
  .${PREFIX}-root { ${LIGHT} }
@@ -906,115 +930,128 @@ var CSS = `
906
930
  color: var(--zrl-ink-soft);
907
931
  }
908
932
 
909
- /* The light around the well: a thin ring in the accent, one bright arc
910
- travelling round it, and a soft glow of the same colour bleeding a few
911
- pixels outward. It says the pairing is live without a word.
912
-
913
- It sits BELOW the well, which is why the well is wrapped instead of given a
914
- pseudo-element. Anything drawn inside the well paints over the white quiet
915
- zone the camera needs, and a negative z-index from inside the well lands
916
- wherever the card's stacking context happens to be that frame (the card's
917
- entrance animation makes and unmakes one). The wrapper is the well's old
918
- box exactly, same size, same margin, so nothing moves; the well keeps its
919
- own stacking order on top, and the ring cannot touch the QR. */
920
- .${PREFIX}-qr-halo {
921
- position: relative;
922
- isolation: isolate;
923
- width: 204px;
924
- height: 204px;
925
- margin: 20px auto 0;
926
- }
927
-
928
933
  .${PREFIX}-qr-well {
929
934
  position: relative;
930
- z-index: 1;
931
935
  display: grid;
932
936
  place-items: center;
933
937
  box-sizing: border-box;
934
938
  width: 204px;
935
939
  height: 204px;
936
- margin: 0;
940
+ margin: 20px auto 0;
937
941
  padding: 12px;
938
942
  border: 1px solid var(--zrl-line);
939
- border-radius: 16px;
943
+ border-radius: var(--zrl-radius);
940
944
  background: var(--zrl-qr-bg);
945
+ /* The light on the edge takes its shape from here and its colour and
946
+ strength from the theme tokens above. One lap in 4s on every tier. */
947
+ --zrl-radius: 16px;
948
+ --zrl-beam-time: 4s;
941
949
  }
942
950
 
943
- /* The glow is the ring's own alpha, blurred and drawn beneath it, so it
944
- follows the arc round and fades with it. It has to be a parent of the
945
- masked band: a filter is applied before a mask, so a shadow cast from the
946
- band itself would be cut off at the band's edge. Two shadows because one
947
- 2px source blurred over 10px is too faint to read as light; the second
948
- blurs the first. */
949
- .${PREFIX}-qr-glow {
951
+ /* The light on the well's edge: a short comet running along the border, with
952
+ a soft glow outside it. Three overlays inside the well, each masked so the
953
+ comet can only ever paint where its mask allows, and the white interior lies
954
+ outside every mask: nothing here can reach the quiet zone a camera needs,
955
+ whatever the comet is doing. The mask is the padding box cut out of the
956
+ border box, a transparent layer clipped to the padding box intersected
957
+ with a solid one clipped to the border box. The prefixed form is for Chrome
958
+ before 120 and Safari before 15.4; the unprefixed one, declared after it,
959
+ wins everywhere else.
960
+
961
+ qr-beam keeps a thin ring on the border line: the comet itself.
962
+ qr-beam-glow is the glow: a wide band outside the well that blurs whatever
963
+ is inside it, and inside it qr-beam-glow-band keeps a 3px ring with a
964
+ second copy of the comet. The blur has to sit on the parent because a
965
+ filter is applied before a mask: blurred on the band itself, the glow
966
+ would be cut back to the band's own edge. On the parent it runs after the
967
+ band has clipped the comet thin and before the parent's mask cuts away the
968
+ inward half, which is what makes it fade outward and never over the QR.
969
+ All three share one containing block, the well's padding box, so the two
970
+ comets ride the same path; the spent badge is a later sibling and paints
971
+ above them. */
972
+ .${PREFIX}-qr-beam,
973
+ .${PREFIX}-qr-beam-glow,
974
+ .${PREFIX}-qr-beam-glow-band {
950
975
  position: absolute;
951
- inset: -2px;
976
+ inset: calc(0px - var(--zrl-beam-line));
977
+ border: var(--zrl-beam-line) solid transparent;
978
+ border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-beam-line));
952
979
  pointer-events: none;
953
- filter: drop-shadow(0 0 3px var(--zrl-accent)) drop-shadow(0 0 10px var(--zrl-accent));
980
+ -webkit-mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);
981
+ -webkit-mask-clip: padding-box, border-box;
982
+ -webkit-mask-composite: source-in;
983
+ mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);
984
+ mask-clip: padding-box, border-box;
985
+ mask-composite: intersect;
954
986
  }
955
-
956
- /* The band: a 2px ring just outside the well, cut with a mask that keeps the
957
- padding and drops the content box. The gradient is on a pseudo-element
958
- rather than on the band, because in the fallback it has to rotate as a
959
- whole while the band stays put. The prefixed mask is for Chrome before 120;
960
- the unprefixed one, declared after it, wins everywhere else. */
961
- .${PREFIX}-qr-arc {
962
- position: absolute;
963
- inset: 0;
964
- padding: 2px;
965
- border-radius: 18px;
966
- overflow: hidden;
967
- -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
968
- -webkit-mask-composite: xor;
969
- mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
970
- mask-composite: exclude;
987
+ .${PREFIX}-qr-beam-glow {
988
+ inset: calc(0px - var(--zrl-glow-reach));
989
+ border-width: var(--zrl-glow-reach);
990
+ border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-reach));
991
+ filter: blur(var(--zrl-glow-blur));
992
+ opacity: var(--zrl-glow-opacity);
993
+ will-change: filter;
971
994
  }
972
- .${PREFIX}-qr-arc::before,
973
- .${PREFIX}-qr-arc::after {
995
+ .${PREFIX}-qr-beam-glow-band {
996
+ inset: calc(0px - var(--zrl-glow-core));
997
+ border-width: var(--zrl-glow-core);
998
+ border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-core));
999
+ }
1000
+
1001
+ /* At rest the edge holds a dim, even blue: a 1px line on the border and, from
1002
+ the glow band, a soft halo outside it. Hidden while the comet runs, so the
1003
+ border reads as the well's own line with a light passing over it; shown
1004
+ once the light has stopped. Every path below ends here, which is what
1005
+ makes them look the same at rest. */
1006
+ .${PREFIX}-qr-beam::before,
1007
+ .${PREFIX}-qr-beam-glow-band::before {
974
1008
  content: '';
975
1009
  position: absolute;
1010
+ inset: -50%;
1011
+ background: var(--zrl-beam);
1012
+ opacity: 0;
976
1013
  transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);
977
1014
  }
978
1015
 
979
- /* The light. Mostly transparent, a tail that builds over a quarter turn, a
980
- short full-strength head and a drop-off within a few degrees, so it reads
981
- as travelling rather than blinking. One revolution every 3s, linear:
982
- continuous motion has no ease. Oversized so its corners still cover the
983
- band mid-rotation in the transform fallback; the property path shrinks it
984
- back to the band. */
985
- .${PREFIX}-qr-arc::before {
1016
+ /* The moving light: an oversized square carrying a conic sweep, rotated
1017
+ whole. A transform animation runs on the compositor, so the light keeps
1018
+ moving while the page is busy; animating the gradient angle instead
1019
+ repaints every frame on the main thread and stutters. */
1020
+ .${PREFIX}-qr-beam::after,
1021
+ .${PREFIX}-qr-beam-glow-band::after {
1022
+ content: '';
1023
+ position: absolute;
986
1024
  inset: -50%;
987
1025
  background: conic-gradient(
988
- from var(--zrl-angle, 0deg),
989
- transparent 0deg 245deg,
990
- var(--zrl-accent) 332deg 346deg,
1026
+ from 0deg,
1027
+ transparent 0deg 220deg,
1028
+ var(--zrl-beam) 330deg,
1029
+ var(--zrl-beam-head) 348deg,
991
1030
  transparent 356deg 360deg
992
1031
  );
993
- animation: ${PREFIX}-orbit 3s linear infinite;
994
- }
995
-
996
- /* The rest state: the same ring, evenly lit. Faint under the moving light so
997
- the ring reads as a ring; stronger once the light has stopped. */
998
- .${PREFIX}-qr-arc::after {
999
- inset: 0;
1000
- background: var(--zrl-accent);
1001
- opacity: 0.16;
1032
+ animation: ${PREFIX}-orbit var(--zrl-beam-time) linear infinite;
1033
+ will-change: transform;
1034
+ transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);
1002
1035
  }
1003
1036
 
1004
- /* Spent: the light stops where it is and fades, and the ring settles to a
1005
- dim, even glow. Paused rather than removed, so it does not jump back to
1006
- its start on the way out. */
1007
- .${PREFIX}-qr-halo[data-spent="true"] .${PREFIX}-qr-arc::before {
1037
+ /* Spent: the light stops where it is and fades, and the edge settles to the
1038
+ dim glow. Paused rather than removed, so it does not jump back to its start
1039
+ on the way out. */
1040
+ .${PREFIX}-qr-well[data-spent="true"] .${PREFIX}-qr-beam::after,
1041
+ .${PREFIX}-qr-well[data-spent="true"] .${PREFIX}-qr-beam-glow-band::after {
1008
1042
  animation-play-state: paused;
1009
1043
  opacity: 0;
1010
1044
  }
1011
- .${PREFIX}-qr-halo[data-spent="true"] .${PREFIX}-qr-arc::after { opacity: 0.34; }
1045
+ .${PREFIX}-qr-well[data-spent="true"] .${PREFIX}-qr-beam::before { opacity: 0.55; }
1046
+ .${PREFIX}-qr-well[data-spent="true"] .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7; }
1012
1047
 
1013
1048
  .${PREFIX}-qr {
1014
1049
  display: block;
1015
1050
  width: 100%;
1016
1051
  height: 100%;
1017
1052
  border-radius: 8px;
1053
+ filter: var(--zrl-qr-filter);
1054
+ mix-blend-mode: var(--zrl-qr-blend);
1018
1055
  transition: filter 300ms cubic-bezier(0.23, 1, 0.32, 1),
1019
1056
  opacity 300ms cubic-bezier(0.23, 1, 0.32, 1),
1020
1057
  transform 300ms cubic-bezier(0.23, 1, 0.32, 1);
@@ -1023,7 +1060,7 @@ var CSS = `
1023
1060
  /* Once the code is claimed the QR is spent. Blurring it out rather than
1024
1061
  swapping it keeps one object on screen through the state change, so the eye
1025
1062
  reads a transformation instead of two things trading places. */
1026
- .${PREFIX}-qr[data-spent="true"] { opacity: 0.2; filter: blur(3px); transform: scale(0.96); }
1063
+ .${PREFIX}-qr[data-spent="true"] { opacity: 0.2; filter: var(--zrl-qr-spent-filter); transform: scale(0.96); }
1027
1064
 
1028
1065
  .${PREFIX}-qr-overlay {
1029
1066
  position: absolute;
@@ -1152,32 +1189,7 @@ var CSS = `
1152
1189
  70%, 100% { transform: scale(2.6); opacity: 0 }
1153
1190
  }
1154
1191
 
1155
- /* Two ways to turn the light, one look.
1156
- Where the browser can register a typed custom property, the gradient's own
1157
- angle animates and the element never moves. Where it cannot (Firefox before
1158
- 128), an untyped custom property cannot interpolate and the light would
1159
- snap once a cycle, so the oversized pseudo-element rotates instead and the
1160
- band's mask keeps the ring in place. At rest the two are the same pixels:
1161
- same gradient, same centre, angle zero.
1162
- There is no direct query for @property support. transition-behavior
1163
- shipped after it in every engine (Chrome 117 vs 85, Firefox 129 vs 128,
1164
- Safari 17.4 vs 16.4), so it stands in: a yes is never wrong, and a no only
1165
- sends a capable browser down the fallback, which looks the same.
1166
- inherits: false so the angle stays on the element that animates it and
1167
- never reaches the host page or the well. */
1168
- @property --zrl-angle {
1169
- syntax: '<angle>';
1170
- inherits: false;
1171
- initial-value: 0deg;
1172
- }
1173
1192
  @keyframes ${PREFIX}-orbit { to { transform: rotate(360deg) } }
1174
- @keyframes ${PREFIX}-orbit-angle { to { --zrl-angle: 360deg } }
1175
- @supports (transition-behavior: allow-discrete) {
1176
- .${PREFIX}-qr-arc::before {
1177
- inset: 0;
1178
- animation-name: ${PREFIX}-orbit-angle;
1179
- }
1180
- }
1181
1193
 
1182
1194
  @media (prefers-reduced-motion: reduce) {
1183
1195
  .${PREFIX}-scrim,
@@ -1188,10 +1200,11 @@ var CSS = `
1188
1200
  .${PREFIX}-cancel,
1189
1201
  .${PREFIX}-close,
1190
1202
  .${PREFIX}-timer { transition: none }
1191
- /* No travelling light. The ring keeps a still, soft glow instead, a step
1192
- brighter than the spent state so pending and spent still read apart. */
1193
- .${PREFIX}-qr-arc::before { animation: none; opacity: 0 }
1194
- .${PREFIX}-qr-arc::after { opacity: 0.5 }
1203
+ /* No travelling light; the edge keeps its dim static glow instead. */
1204
+ .${PREFIX}-qr-beam::after,
1205
+ .${PREFIX}-qr-beam-glow-band::after { animation: none; opacity: 0 }
1206
+ .${PREFIX}-qr-beam::before { opacity: 0.55 }
1207
+ .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7 }
1195
1208
  }
1196
1209
  `;
1197
1210
  function ensureStyles() {
@@ -1302,12 +1315,11 @@ function PairingModal({
1302
1315
  /* @__PURE__ */ jsx3(ZorealLockup, { height: 44, className: cx("lockup") }),
1303
1316
  /* @__PURE__ */ jsx3("h2", { id: titleId, className: cx("title"), children: settled ? t.titleApprove : t.title }),
1304
1317
  /* @__PURE__ */ jsx3("p", { className: cx("body-text"), children: body }),
1305
- /* @__PURE__ */ jsxs3("div", { className: cx("qr-halo"), "data-spent": settled, children: [
1306
- /* @__PURE__ */ jsx3("span", { className: cx("qr-glow"), "aria-hidden": true, children: /* @__PURE__ */ jsx3("span", { className: cx("qr-arc") }) }),
1307
- /* @__PURE__ */ jsxs3("div", { className: cx("qr-well"), children: [
1308
- /* @__PURE__ */ jsx3("img", { className: cx("qr"), "data-spent": settled, src: frameUrl, alt: t.qrAlt, width: 180, height: 180 }),
1309
- settled && /* @__PURE__ */ jsx3("span", { className: cx("qr-overlay"), children: /* @__PURE__ */ jsx3("span", { className: cx("qr-badge"), children: /* @__PURE__ */ jsx3(IconPhone, {}) }) })
1310
- ] })
1318
+ /* @__PURE__ */ jsxs3("div", { className: cx("qr-well"), "data-spent": settled, children: [
1319
+ /* @__PURE__ */ jsx3("span", { className: cx("qr-beam-glow"), "aria-hidden": true, children: /* @__PURE__ */ jsx3("span", { className: cx("qr-beam-glow-band") }) }),
1320
+ /* @__PURE__ */ jsx3("span", { className: cx("qr-beam"), "aria-hidden": true }),
1321
+ /* @__PURE__ */ jsx3("img", { className: cx("qr"), "data-spent": settled, src: frameUrl, alt: t.qrAlt, width: 180, height: 180 }),
1322
+ settled && /* @__PURE__ */ jsx3("span", { className: cx("qr-overlay"), children: /* @__PURE__ */ jsx3("span", { className: cx("qr-badge"), children: /* @__PURE__ */ jsx3(IconPhone, {}) }) })
1311
1323
  ] }),
1312
1324
  /* @__PURE__ */ jsxs3("div", { className: cx("status"), children: [
1313
1325
  /* @__PURE__ */ jsxs3("span", { className: cx("dot"), children: [