@genex-ai/embed-sdk 0.9.0 → 0.11.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.
@@ -25,6 +25,7 @@ var authWaiters = [];
25
25
  var playerWaiters = [];
26
26
  var overlayEl = null;
27
27
  var popoverEl = null;
28
+ var popoverMinimizeTimer;
28
29
  var pendingPlayerSave;
29
30
  var hasPendingPlayerSave = false;
30
31
  var pendingScores = /* @__PURE__ */ new Map();
@@ -621,19 +622,91 @@ function enterBlocked() {
621
622
  }
622
623
  var OVERLAY_TEXT = {
623
624
  // Delayed (text grace period): most connects finish before the text ever
624
- // shows — a sub-second text flash reads as a glitch.
625
- connecting: { title: "Connecting\u2026" },
626
- // Same copy as `connecting` so the whole standalone bounce (gate ->
625
+ // shows — a sub-second text flash reads as a glitch. `wordmark` states show
626
+ // GENEX in the breathing decrypt loop instead of an instruction.
627
+ connecting: { title: "GENEX", wordmark: true },
628
+ // Same treatment as `connecting` so the whole standalone bounce (gate ->
627
629
  // /play/authorize -> gate) reads as ONE continuous screen. Still neutral:
628
630
  // the automatic bounce precedes GUEST play as often as sign-in.
629
- redirecting: { title: "Connecting\u2026" },
631
+ redirecting: { title: "GENEX", wordmark: true },
630
632
  // Terminal states need the visitor to act — text shows immediately.
631
633
  "blocked-standalone": { title: "Sign in to play", button: "Sign in", instant: true },
632
634
  "blocked-embedded": { title: "Sign in on the Genex dashboard to continue", instant: true }
633
635
  };
634
- var OVERLAY_FONT_URL = "https://cdn.genex.technology/fonts/Geist-variable.woff2";
635
- var OVERLAY_FONT_STACK = "'Geist',system-ui,-apple-system,sans-serif";
636
+ var OVERLAY_FONT_URL = "https://cdn.genex.technology/fonts/GeistMono-variable.woff2";
637
+ var OVERLAY_FONT_STACK = "'Geist Mono',ui-monospace,SFMono-Regular,Menlo,monospace";
636
638
  var overlayFontRequested = false;
639
+ var SCRAMBLE_NOISE = "!<>-_/[]{}=+*^?#$%&@";
640
+ var SCRAMBLE_TICK_MS = 50;
641
+ var SCRAMBLE_SETTLE_TICKS = 10;
642
+ var WORDMARK_LOOP_PHASES = [
643
+ { name: "churn", ticks: 5 },
644
+ { name: "settle", ticks: 10 },
645
+ { name: "hold", ticks: 12 },
646
+ { name: "dissolve", ticks: 8 }
647
+ ];
648
+ function scrambleNoise(length) {
649
+ let out = "";
650
+ for (let i = 0; i < length; i++) {
651
+ out += SCRAMBLE_NOISE[Math.floor(Math.random() * SCRAMBLE_NOISE.length)];
652
+ }
653
+ return out;
654
+ }
655
+ function scrambleStatic() {
656
+ try {
657
+ const w = win();
658
+ if (!w || typeof w.matchMedia !== "function") return true;
659
+ return w.matchMedia("(prefers-reduced-motion: reduce)").matches;
660
+ } catch {
661
+ return true;
662
+ }
663
+ }
664
+ function scrambleIn(el, text) {
665
+ if (scrambleStatic()) {
666
+ el.textContent = text;
667
+ return () => {
668
+ };
669
+ }
670
+ let i = 0;
671
+ el.textContent = scrambleNoise(text.length);
672
+ const id = setInterval(() => {
673
+ i++;
674
+ if (i >= SCRAMBLE_SETTLE_TICKS) {
675
+ el.textContent = text;
676
+ clearInterval(id);
677
+ return;
678
+ }
679
+ const lock = Math.floor(i / SCRAMBLE_SETTLE_TICKS * text.length);
680
+ el.textContent = text.slice(0, lock) + scrambleNoise(text.length - lock);
681
+ }, SCRAMBLE_TICK_MS);
682
+ return () => clearInterval(id);
683
+ }
684
+ function wordmarkLoop(el, text) {
685
+ if (scrambleStatic()) {
686
+ el.textContent = text;
687
+ return () => {
688
+ };
689
+ }
690
+ let phase = 0;
691
+ let i = 0;
692
+ const frame = () => {
693
+ const p = WORDMARK_LOOP_PHASES[phase];
694
+ let kept;
695
+ if (p.name === "churn") kept = 0;
696
+ else if (p.name === "settle") kept = Math.floor(i / p.ticks * text.length);
697
+ else if (p.name === "hold") kept = text.length;
698
+ else kept = text.length - Math.floor(i / p.ticks * text.length);
699
+ el.textContent = text.slice(0, kept) + scrambleNoise(text.length - kept);
700
+ i++;
701
+ if (i >= p.ticks) {
702
+ i = 0;
703
+ phase = (phase + 1) % WORDMARK_LOOP_PHASES.length;
704
+ }
705
+ };
706
+ frame();
707
+ const id = setInterval(frame, SCRAMBLE_TICK_MS);
708
+ return () => clearInterval(id);
709
+ }
637
710
  function ensureOverlayFont(d) {
638
711
  if (overlayFontRequested) return;
639
712
  overlayFontRequested = true;
@@ -641,7 +714,7 @@ function ensureOverlayFont(d) {
641
714
  const FF = win()?.FontFace;
642
715
  const fonts = d.fonts;
643
716
  if (!FF || !fonts?.add) return;
644
- const face = new FF("Geist", `url(${OVERLAY_FONT_URL}) format('woff2')`, {
717
+ const face = new FF("Geist Mono", `url(${OVERLAY_FONT_URL}) format('woff2')`, {
645
718
  weight: "100 900",
646
719
  display: "swap"
647
720
  });
@@ -664,7 +737,7 @@ function removeOverlay(instant = false) {
664
737
  const el = overlayEl;
665
738
  overlayEl = null;
666
739
  if (!el) return;
667
- el.clearTextTimer();
740
+ el.stopText();
668
741
  const root = el.root;
669
742
  if (instant || !root.style) {
670
743
  root.remove?.();
@@ -686,12 +759,12 @@ function buildOverlay(d) {
686
759
  ensureOverlayFont(d);
687
760
  const root = d.createElement("div");
688
761
  root.setAttribute("data-genex-embed-overlay", "");
689
- root.style.cssText = `position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:16px;background:#000;color:#fff;font-family:${OVERLAY_FONT_STACK};text-align:center;pointer-events:auto;user-select:none`;
762
+ root.style.cssText = `position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:14px;background:#000;color:#fff;font-family:${OVERLAY_FONT_STACK};text-align:center;pointer-events:auto;user-select:none`;
690
763
  const title = d.createElement("div");
691
- title.style.cssText = "font-size:18px;font-weight:600;padding:0 24px;opacity:0";
764
+ title.style.cssText = "font-size:13px;font-weight:400;line-height:1.3;padding:0 24px;opacity:0;min-height:1.3em;font-variant-ligatures:none";
692
765
  root.appendChild(title);
693
766
  const button = d.createElement("button");
694
- button.style.cssText = "font-size:15px;font-weight:600;padding:10px 28px;border-radius:9px;border:none;cursor:pointer;background:#4b60bf;color:#fff;display:none";
767
+ button.style.cssText = "font-family:inherit;font-size:13px;font-weight:500;line-height:1.3;padding:8px 22px;border-radius:9px;border:none;cursor:pointer;background:#4b60bf;color:#fff;display:none";
695
768
  button.addEventListener("mouseenter", () => {
696
769
  button.style.background = "#3f51a8";
697
770
  });
@@ -712,29 +785,37 @@ function buildOverlay(d) {
712
785
  if (d.body) mount();
713
786
  else d.addEventListener?.("DOMContentLoaded", mount);
714
787
  let textTimer;
715
- const clearTextTimer = () => {
788
+ let cancelScramble;
789
+ const stopText = () => {
716
790
  if (textTimer !== void 0) {
717
791
  clearTimeout(textTimer);
718
792
  textTimer = void 0;
719
793
  }
794
+ if (cancelScramble) {
795
+ cancelScramble();
796
+ cancelScramble = void 0;
797
+ }
720
798
  };
721
799
  return {
722
800
  root,
723
- clearTextTimer,
801
+ stopText,
724
802
  setContent(kind) {
725
803
  const text = OVERLAY_TEXT[kind];
726
- clearTextTimer();
804
+ stopText();
727
805
  title.textContent = text.title;
728
- if (text.instant || overlayTextDelayMs <= 0) {
729
- title.style.transition = "";
806
+ title.style.letterSpacing = text.wordmark ? "0.22em" : "";
807
+ title.style.marginRight = text.wordmark ? "-0.22em" : "";
808
+ const reveal = () => {
730
809
  title.style.opacity = "1";
810
+ cancelScramble = text.wordmark ? wordmarkLoop(title, text.title) : scrambleIn(title, text.title);
811
+ };
812
+ if (text.instant || overlayTextDelayMs <= 0) {
813
+ reveal();
731
814
  } else {
732
- title.style.transition = "";
733
815
  title.style.opacity = "0";
734
816
  textTimer = setTimeout(() => {
735
817
  textTimer = void 0;
736
- title.style.transition = "opacity 200ms ease";
737
- title.style.opacity = "1";
818
+ reveal();
738
819
  }, overlayTextDelayMs);
739
820
  }
740
821
  button.style.display = text.button ? "inline-block" : "none";
@@ -752,6 +833,10 @@ function showGuestPopover() {
752
833
  }
753
834
  }
754
835
  function removeGuestPopover() {
836
+ if (popoverMinimizeTimer !== void 0) {
837
+ clearTimeout(popoverMinimizeTimer);
838
+ popoverMinimizeTimer = void 0;
839
+ }
755
840
  try {
756
841
  const root = popoverEl?.root;
757
842
  root?.remove?.();
@@ -761,15 +846,31 @@ function removeGuestPopover() {
761
846
  }
762
847
  function buildGuestPopover(d) {
763
848
  ensureOverlayFont(d);
849
+ let mobile = false;
850
+ try {
851
+ const w = win();
852
+ const nav = w?.navigator;
853
+ if (w && nav) {
854
+ const coarse = w.matchMedia?.("(pointer: coarse)").matches ?? false;
855
+ const touch = (nav.maxTouchPoints ?? 0) > 1;
856
+ const mobileUA = nav.userAgentData ? nav.userAgentData.mobile : /Mobi|Android/i.test(nav.userAgent ?? "");
857
+ mobile = touch && coarse || mobileUA;
858
+ }
859
+ } catch {
860
+ }
764
861
  const root = d.createElement("div");
765
862
  root.setAttribute("data-genex-guest-popover", "");
766
- root.style.cssText = `position:fixed;top:12px;right:12px;z-index:2147483646;display:flex;align-items:center;gap:12px;padding:10px 12px 10px 16px;border-radius:12px;background:rgba(0,0,0,0.85);color:#fff;font-family:${OVERLAY_FONT_STACK};font-size:13px;box-shadow:0 4px 24px rgba(0,0,0,0.4);pointer-events:auto;user-select:none`;
863
+ root.style.cssText = "position:fixed;top:calc(12px + env(safe-area-inset-top));right:calc(12px + env(safe-area-inset-right));z-index:2147483646;display:flex;justify-content:flex-end;pointer-events:none";
864
+ const card = d.createElement("div");
865
+ card.style.cssText = `display:flex;align-items:center;border-radius:12px;background:rgba(0,0,0,0.85);color:#fff;font-family:${OVERLAY_FONT_STACK};box-shadow:0 4px 24px rgba(0,0,0,0.4);pointer-events:auto;user-select:none;` + (mobile ? "gap:8px;padding:8px 8px 8px 12px;font-size:12px;max-width:calc(100vw - 88px)" : "gap:12px;padding:10px 12px 10px 16px;font-size:13px");
866
+ root.appendChild(card);
767
867
  const text = d.createElement("span");
768
- text.textContent = user?.name ? `Playing as ${user.name} \u2014 sign in to save progress` : "Sign in to save progress";
769
- root.appendChild(text);
868
+ text.textContent = !mobile && user?.name ? `Playing as ${user.name} \u2014 sign in to save progress` : "Sign in to save progress";
869
+ if (mobile) text.style.cssText = "overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
870
+ card.appendChild(text);
770
871
  const signIn = d.createElement("button");
771
872
  signIn.textContent = "Sign in";
772
- signIn.style.cssText = "font-size:13px;font-weight:600;padding:6px 16px;border-radius:9px;border:none;cursor:pointer;background:#4b60bf;color:#fff";
873
+ signIn.style.cssText = "font-family:inherit;font-weight:500;border-radius:9px;border:none;cursor:pointer;background:#4b60bf;color:#fff;" + (mobile ? "font-size:12px;padding:5px 12px" : "font-size:13px;padding:6px 16px");
773
874
  signIn.addEventListener("mouseenter", () => {
774
875
  signIn.style.background = "#3f51a8";
775
876
  });
@@ -783,7 +884,7 @@ function buildGuestPopover(d) {
783
884
  clearRetryFlag();
784
885
  void redirectToAuthorize(w);
785
886
  });
786
- root.appendChild(signIn);
887
+ card.appendChild(signIn);
787
888
  const dismiss = d.createElement("button");
788
889
  dismiss.textContent = "\xD7";
789
890
  dismiss.setAttribute("aria-label", "Dismiss");
@@ -792,7 +893,24 @@ function buildGuestPopover(d) {
792
893
  writePopoverDismissed();
793
894
  removeGuestPopover();
794
895
  });
795
- root.appendChild(dismiss);
896
+ card.appendChild(dismiss);
897
+ if (mobile) {
898
+ const chip = d.createElement("button");
899
+ chip.setAttribute("aria-label", "Sign in to save progress");
900
+ chip.style.cssText = "display:none;width:44px;height:44px;border-radius:50%;border:none;cursor:pointer;background:rgba(0,0,0,0.55);color:#fff;align-items:center;justify-content:center;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);pointer-events:auto";
901
+ chip.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" y1="12" x2="3" y2="12"/></svg>';
902
+ root.appendChild(chip);
903
+ const setMinimized = (v) => {
904
+ card.style.display = v ? "none" : "flex";
905
+ chip.style.display = v ? "flex" : "none";
906
+ };
907
+ popoverMinimizeTimer = setTimeout(() => setMinimized(true), 5e3);
908
+ card.addEventListener("pointerdown", () => {
909
+ if (popoverMinimizeTimer !== void 0) clearTimeout(popoverMinimizeTimer);
910
+ popoverMinimizeTimer = void 0;
911
+ });
912
+ chip.addEventListener("click", () => setMinimized(false));
913
+ }
796
914
  const mount = () => {
797
915
  if (d.body && !root.isConnected) d.body.appendChild(root);
798
916
  };
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  submitScore,
18
18
  waitForAuth,
19
19
  waitForPlayer
20
- } from "./chunk-BWJ2FVCH.js";
20
+ } from "./chunk-EEOKYBXG.js";
21
21
  export {
22
22
  __resetForTests,
23
23
  _stashTicketFromUrl,
package/dist/sentry.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  _stashTicketFromUrl,
3
3
  getUser,
4
4
  on
5
- } from "./chunk-BWJ2FVCH.js";
5
+ } from "./chunk-EEOKYBXG.js";
6
6
 
7
7
  // src/sentry.ts
8
8
  import * as Sentry from "@sentry/browser";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/embed-sdk",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Player identity + durable game state for genex games \u2014 signed-in or guest play, per-player save slots, shared world state, and soft-trust leaderboards.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",