@rehers/rehers-roleplay-sdk 2.5.3 → 2.5.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/roleplay-sdk.js +85 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rehers/rehers-roleplay-sdk",
3
- "version": "2.5.3",
3
+ "version": "2.5.4",
4
4
  "description": "Seamless Roleplay SDK — embed roleplay call sessions via a modal + iframe",
5
5
  "main": "roleplay-sdk.js",
6
6
  "types": "index.d.ts",
package/roleplay-sdk.js CHANGED
@@ -50,6 +50,17 @@
50
50
  var dialogAddToScenarioPendingContacts = null;
51
51
  var dialogListener = null;
52
52
  var dialogCloseTeardownTimer = null;
53
+ var dialogRevealTimer = null;
54
+ var mountRevealTimer = null;
55
+
56
+ // Max wait before we reveal the iframe even if it never sends ROLEPLAY_READY.
57
+ // Prevents users from staring at an empty container if the hosted app is slow
58
+ // or fails to signal. 3s is conservative; the handshake normally lands in < 500ms.
59
+ var IFRAME_REVEAL_FALLBACK_MS = 3000;
60
+
61
+ function revealIframe(iframeEl) {
62
+ if (iframeEl) iframeEl.style.opacity = "1";
63
+ }
53
64
 
54
65
  // ── Safe logging ──────────────────────────────────────────────────
55
66
 
@@ -227,6 +238,10 @@
227
238
  clearTimeout(dialogCloseTeardownTimer);
228
239
  dialogCloseTeardownTimer = null;
229
240
  }
241
+ if (dialogRevealTimer) {
242
+ clearTimeout(dialogRevealTimer);
243
+ dialogRevealTimer = null;
244
+ }
230
245
 
231
246
  if (dialogOverlay && dialogOverlay.parentNode) {
232
247
  dialogOverlay.parentNode.removeChild(dialogOverlay);
@@ -251,6 +266,10 @@
251
266
 
252
267
  function teardownMount() {
253
268
  try {
269
+ if (mountRevealTimer) {
270
+ clearTimeout(mountRevealTimer);
271
+ mountRevealTimer = null;
272
+ }
254
273
  if (mountIframe && mountIframe.parentNode) {
255
274
  mountIframe.parentNode.removeChild(mountIframe);
256
275
  }
@@ -321,6 +340,11 @@
321
340
 
322
341
  switch (data.type) {
323
342
  case "ROLEPLAY_READY":
343
+ if (mountRevealTimer) {
344
+ clearTimeout(mountRevealTimer);
345
+ mountRevealTimer = null;
346
+ }
347
+ revealIframe(mountIframe);
324
348
  getSessionToken()
325
349
  .then(function (token) {
326
350
  dispatchInitToTarget(mountIframe, null, null);
@@ -359,6 +383,11 @@
359
383
 
360
384
  switch (data.type) {
361
385
  case "ROLEPLAY_READY":
386
+ if (dialogRevealTimer) {
387
+ clearTimeout(dialogRevealTimer);
388
+ dialogRevealTimer = null;
389
+ }
390
+ revealIframe(dialogIframe);
362
391
  getSessionToken()
363
392
  .then(function (token) {
364
393
  dispatchInitToTarget(dialogIframe, dialogContactData, dialogAddToScenarioPendingContacts);
@@ -369,9 +398,9 @@
369
398
  break;
370
399
 
371
400
  case "ROLEPLAY_ERROR":
372
- if (dialogCallbacks.onError) {
373
- dialogCallbacks.onError({ code: data.code, message: data.message });
374
- }
401
+ var regularOnError = dialogCallbacks.onError;
402
+ teardownDialog();
403
+ if (regularOnError) regularOnError({ code: data.code, message: data.message });
375
404
  break;
376
405
 
377
406
  case "ROLEPLAY_CLOSED":
@@ -394,9 +423,9 @@
394
423
  break;
395
424
 
396
425
  case "ADD_TO_SCENARIO_ERROR":
397
- if (dialogAddToScenarioCallbacks.onError) {
398
- dialogAddToScenarioCallbacks.onError({ code: data.code, message: data.message });
399
- }
426
+ var atsOnError = dialogAddToScenarioCallbacks.onError;
427
+ teardownDialog();
428
+ if (atsOnError) atsOnError({ code: data.code, message: data.message });
400
429
  break;
401
430
 
402
431
  case "ADD_TO_SCENARIO_CLOSED":
@@ -416,10 +445,30 @@
416
445
  try {
417
446
  sendMsg(dialogIframe, { type: "roleplay-close" });
418
447
  if (dialogCloseTeardownTimer) clearTimeout(dialogCloseTeardownTimer);
419
- dialogCloseTeardownTimer = setTimeout(teardownDialog, 300);
448
+ dialogCloseTeardownTimer = setTimeout(function () {
449
+ // Fallback path: iframe didn't respond with ROLEPLAY_CLOSED or
450
+ // ADD_TO_SCENARIO_CLOSED. Fire the host's onClose ourselves so
451
+ // React state (or any controlled caller) can re-render cleanly
452
+ // and reopen the dialog later.
453
+ var mode = dialogMode;
454
+ var atsClose = dialogAddToScenarioCallbacks.onClose;
455
+ var regularClose = dialogCallbacks.onClose;
456
+ teardownDialog();
457
+ try {
458
+ if (mode === "add-to-scenario" && atsClose) atsClose();
459
+ else if (regularClose) regularClose();
460
+ } catch (_) {}
461
+ }, 300);
420
462
  } catch (e) {
421
463
  logError("close", e);
464
+ var mode2 = dialogMode;
465
+ var atsClose2 = dialogAddToScenarioCallbacks.onClose;
466
+ var regularClose2 = dialogCallbacks.onClose;
422
467
  teardownDialog();
468
+ try {
469
+ if (mode2 === "add-to-scenario" && atsClose2) atsClose2();
470
+ else if (regularClose2) regularClose2();
471
+ } catch (_) {}
423
472
  }
424
473
  }
425
474
 
@@ -433,6 +482,11 @@
433
482
  iframeEl.style.height = "100%";
434
483
  iframeEl.style.border = "none";
435
484
  iframeEl.style.display = "block";
485
+ // Start invisible. The host app flashes a brief unstyled loader before it's
486
+ // ready; we reveal the iframe once ROLEPLAY_READY arrives (or after a
487
+ // fallback timeout). See revealIframe() callers.
488
+ iframeEl.style.opacity = "0";
489
+ iframeEl.style.transition = "opacity 120ms ease-out";
436
490
  return iframeEl;
437
491
  }
438
492
 
@@ -595,6 +649,13 @@
595
649
  dialogOverlay = el;
596
650
  dialogIframe = iframeEl;
597
651
  document.body.appendChild(dialogOverlay);
652
+
653
+ // Fallback: reveal the iframe even if ROLEPLAY_READY never arrives.
654
+ if (dialogRevealTimer) clearTimeout(dialogRevealTimer);
655
+ dialogRevealTimer = setTimeout(function () {
656
+ revealIframe(dialogIframe);
657
+ dialogRevealTimer = null;
658
+ }, IFRAME_REVEAL_FALLBACK_MS);
598
659
  } catch (e) {
599
660
  logError("open", e);
600
661
  teardownDialog();
@@ -631,6 +692,13 @@
631
692
  var iframeEl = createIframe("/");
632
693
  mountIframe = iframeEl;
633
694
  container.appendChild(iframeEl);
695
+
696
+ // Fallback: reveal the iframe even if ROLEPLAY_READY never arrives.
697
+ if (mountRevealTimer) clearTimeout(mountRevealTimer);
698
+ mountRevealTimer = setTimeout(function () {
699
+ revealIframe(mountIframe);
700
+ mountRevealTimer = null;
701
+ }, IFRAME_REVEAL_FALLBACK_MS);
634
702
  } catch (e) {
635
703
  logError("mount", e);
636
704
  teardownMount();
@@ -721,6 +789,9 @@
721
789
  iframeEl.style.top = "0";
722
790
  iframeEl.style.left = "0";
723
791
  iframeEl.style.zIndex = "1";
792
+ // Hide until ROLEPLAY_READY so we don't flash the host app's pre-init state.
793
+ iframeEl.style.opacity = "0";
794
+ iframeEl.style.transition = "opacity 120ms ease-out";
724
795
 
725
796
  var closeBtn = document.createElement("button");
726
797
  closeBtn.type = "button";
@@ -758,6 +829,13 @@
758
829
  dialogOverlay = el;
759
830
  dialogIframe = iframeEl;
760
831
  document.body.appendChild(dialogOverlay);
832
+
833
+ // Fallback: reveal the iframe even if ROLEPLAY_READY never arrives.
834
+ if (dialogRevealTimer) clearTimeout(dialogRevealTimer);
835
+ dialogRevealTimer = setTimeout(function () {
836
+ revealIframe(dialogIframe);
837
+ dialogRevealTimer = null;
838
+ }, IFRAME_REVEAL_FALLBACK_MS);
761
839
  } catch (e) {
762
840
  logError("addToScenario", e);
763
841
  teardownDialog();