smartcomply-web-sdk 1.0.61 → 1.0.63

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 (89) hide show
  1. package/dist/camera/CameraManager.d.ts +8 -0
  2. package/dist/camera/CameraManager.d.ts.map +1 -1
  3. package/dist/camera/CameraManager.js +13 -2
  4. package/dist/camera/CameraManager.js.map +1 -1
  5. package/dist/camera/Permission.d.ts +11 -0
  6. package/dist/camera/Permission.d.ts.map +1 -1
  7. package/dist/camera/Permission.js +23 -0
  8. package/dist/camera/Permission.js.map +1 -1
  9. package/dist/esm/camera/CameraManager.d.ts +8 -0
  10. package/dist/esm/camera/CameraManager.d.ts.map +1 -1
  11. package/dist/esm/camera/CameraManager.js +11 -1
  12. package/dist/esm/camera/CameraManager.js.map +1 -1
  13. package/dist/esm/camera/Permission.d.ts +11 -0
  14. package/dist/esm/camera/Permission.d.ts.map +1 -1
  15. package/dist/esm/camera/Permission.js +22 -0
  16. package/dist/esm/camera/Permission.js.map +1 -1
  17. package/dist/esm/flow/DocumentCapture.d.ts +20 -5
  18. package/dist/esm/flow/DocumentCapture.d.ts.map +1 -1
  19. package/dist/esm/flow/DocumentCapture.js +191 -207
  20. package/dist/esm/flow/DocumentCapture.js.map +1 -1
  21. package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
  22. package/dist/esm/flow/SmartComplyFlow.js +84 -116
  23. package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
  24. package/dist/esm/flow/icons.d.ts +14 -0
  25. package/dist/esm/flow/icons.d.ts.map +1 -0
  26. package/dist/esm/flow/icons.js +34 -0
  27. package/dist/esm/flow/icons.js.map +1 -0
  28. package/dist/esm/flow/spinner.d.ts +15 -0
  29. package/dist/esm/flow/spinner.d.ts.map +1 -0
  30. package/dist/esm/flow/spinner.js +55 -0
  31. package/dist/esm/flow/spinner.js.map +1 -0
  32. package/dist/esm/flow/theme.d.ts +19 -0
  33. package/dist/esm/flow/theme.d.ts.map +1 -1
  34. package/dist/esm/flow/theme.js +19 -0
  35. package/dist/esm/flow/theme.js.map +1 -1
  36. package/dist/esm/modules/liveness/LivenessUI.d.ts +10 -0
  37. package/dist/esm/modules/liveness/LivenessUI.d.ts.map +1 -1
  38. package/dist/esm/modules/liveness/LivenessUI.js +17 -16
  39. package/dist/esm/modules/liveness/LivenessUI.js.map +1 -1
  40. package/dist/esm/modules/liveness/liveness.d.ts +2 -0
  41. package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
  42. package/dist/esm/modules/liveness/liveness.js +1 -1
  43. package/dist/esm/modules/liveness/liveness.js.map +1 -1
  44. package/dist/esm/utils/constants.d.ts +11 -3
  45. package/dist/esm/utils/constants.d.ts.map +1 -1
  46. package/dist/esm/utils/constants.js +11 -3
  47. package/dist/esm/utils/constants.js.map +1 -1
  48. package/dist/esm/utils/documentRules.d.ts +15 -0
  49. package/dist/esm/utils/documentRules.d.ts.map +1 -0
  50. package/dist/esm/utils/documentRules.js +27 -0
  51. package/dist/esm/utils/documentRules.js.map +1 -0
  52. package/dist/flow/DocumentCapture.d.ts +20 -5
  53. package/dist/flow/DocumentCapture.d.ts.map +1 -1
  54. package/dist/flow/DocumentCapture.js +191 -207
  55. package/dist/flow/DocumentCapture.js.map +1 -1
  56. package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
  57. package/dist/flow/SmartComplyFlow.js +86 -118
  58. package/dist/flow/SmartComplyFlow.js.map +1 -1
  59. package/dist/flow/icons.d.ts +14 -0
  60. package/dist/flow/icons.d.ts.map +1 -0
  61. package/dist/flow/icons.js +37 -0
  62. package/dist/flow/icons.js.map +1 -0
  63. package/dist/flow/spinner.d.ts +15 -0
  64. package/dist/flow/spinner.d.ts.map +1 -0
  65. package/dist/flow/spinner.js +59 -0
  66. package/dist/flow/spinner.js.map +1 -0
  67. package/dist/flow/theme.d.ts +19 -0
  68. package/dist/flow/theme.d.ts.map +1 -1
  69. package/dist/flow/theme.js +20 -1
  70. package/dist/flow/theme.js.map +1 -1
  71. package/dist/modules/liveness/LivenessUI.d.ts +10 -0
  72. package/dist/modules/liveness/LivenessUI.d.ts.map +1 -1
  73. package/dist/modules/liveness/LivenessUI.js +17 -16
  74. package/dist/modules/liveness/LivenessUI.js.map +1 -1
  75. package/dist/modules/liveness/liveness.d.ts +2 -0
  76. package/dist/modules/liveness/liveness.d.ts.map +1 -1
  77. package/dist/modules/liveness/liveness.js +1 -1
  78. package/dist/modules/liveness/liveness.js.map +1 -1
  79. package/dist/smartcomply.browser.js +376 -313
  80. package/dist/smartcomply.browser.js.map +4 -4
  81. package/dist/utils/constants.d.ts +11 -3
  82. package/dist/utils/constants.d.ts.map +1 -1
  83. package/dist/utils/constants.js +11 -3
  84. package/dist/utils/constants.js.map +1 -1
  85. package/dist/utils/documentRules.d.ts +15 -0
  86. package/dist/utils/documentRules.d.ts.map +1 -0
  87. package/dist/utils/documentRules.js +31 -0
  88. package/dist/utils/documentRules.js.map +1 -0
  89. package/package.json +6 -3
@@ -5,6 +5,9 @@ const Smartcomply_1 = require("../client/Smartcomply");
5
5
  const theme_1 = require("./theme");
6
6
  const DocumentCapture_1 = require("./DocumentCapture");
7
7
  const device_1 = require("../utils/device");
8
+ const documentRules_1 = require("../utils/documentRules");
9
+ const icons_1 = require("./icons");
10
+ const spinner_1 = require("./spinner");
8
11
  const STEP_ORDER = [
9
12
  "loading",
10
13
  "welcome",
@@ -44,18 +47,6 @@ function resolveIdTypeInfo(typeName, isDocumentFlow) {
44
47
  .replace(/\s*\(BVN\)\s*/gi, ' ')
45
48
  .replace(/\s{2,}/g, ' ')
46
49
  .trim();
47
- // Helper function to check if document requires back side
48
- const requiresBackSide = (type) => {
49
- const t = type.toLowerCase();
50
- return t.includes("voter") ||
51
- t.includes("driver") ||
52
- t.includes("license") ||
53
- t.includes("national id") ||
54
- t.includes("national id card");
55
- // Note: NIN document (nin_with_face, nin_slip) does NOT require back side —
56
- // the NIN card is single-sided. Only the physical NIN slip has a back but
57
- // the OCR pipeline only needs the front.
58
- };
59
50
  // BVN — always 11 digits
60
51
  if (u.includes("BVN") || u.includes("BANK VERIFICATION")) {
61
52
  return { icon: "🏦", desc: "Enter your 11-digit Bank Verification Number (BVN)", requiresBack: false };
@@ -67,7 +58,7 @@ function resolveIdTypeInfo(typeName, isDocumentFlow) {
67
58
  // Ghana Card / Ghana National ID
68
59
  if (u.includes("GHANA")) {
69
60
  if (isDocumentFlow)
70
- return { icon: "🪪", desc: "Capture or upload your Ghana Card", requiresBack: true };
61
+ return { icon: "🪪", desc: "Capture or upload your Ghana Card", requiresBack: (0, documentRules_1.requiresBackSide)(typeName) };
71
62
  return { icon: "🪪", desc: "Enter your Ghana Card number", requiresBack: false };
72
63
  }
73
64
  // SSNIT (Ghana social security)
@@ -84,7 +75,7 @@ function resolveIdTypeInfo(typeName, isDocumentFlow) {
84
75
  return { icon: "🏢", desc: advanced ? "Enter RC number, company name & type" : "Enter your company registration number", requiresBack: false };
85
76
  // Voter / PVC
86
77
  if (u.includes("VOTER") || u.includes("PVC")) {
87
- const requiresBack = requiresBackSide(typeName);
78
+ const requiresBack = (0, documentRules_1.requiresBackSide)(typeName);
88
79
  return {
89
80
  icon: "🗳️",
90
81
  desc: isDocumentFlow
@@ -98,7 +89,7 @@ function resolveIdTypeInfo(typeName, isDocumentFlow) {
98
89
  return { icon: "📘", desc: isDocumentFlow ? "Capture or upload your passport" : "Enter your passport number", requiresBack: false };
99
90
  // Driver's license
100
91
  if (u.includes("DRIVER") || u.includes("LICENSE") || u.includes("LICENCE")) {
101
- const requiresBack = requiresBackSide(typeName);
92
+ const requiresBack = (0, documentRules_1.requiresBackSide)(typeName);
102
93
  return {
103
94
  icon: "🚗",
104
95
  desc: isDocumentFlow
@@ -109,7 +100,7 @@ function resolveIdTypeInfo(typeName, isDocumentFlow) {
109
100
  }
110
101
  // National ID card (generic)
111
102
  if (u.includes("NATIONAL ID") || (u.includes("NATIONAL") && u.includes("CARD"))) {
112
- const requiresBack = requiresBackSide(typeName);
103
+ const requiresBack = (0, documentRules_1.requiresBackSide)(typeName);
113
104
  return {
114
105
  icon: "🪪",
115
106
  desc: isDocumentFlow
@@ -240,18 +231,6 @@ function _formatDocType(code) {
240
231
  }
241
232
  // SmartComplyFlow
242
233
  // ─────────────────────────────────────────────────────────────────────
243
- // Add CSS animation keyframes for spin animations
244
- const style = document.createElement('style');
245
- style.textContent = `
246
- @keyframes sc-spin {
247
- 0% { transform: rotate(0deg); }
248
- 100% { transform: rotate(360deg); }
249
- }
250
- `;
251
- if (!document.head.querySelector('style[data-sc-animations]')) {
252
- style.setAttribute('data-sc-animations', 'true');
253
- document.head.appendChild(style);
254
- }
255
234
  class SmartComplyFlow {
256
235
  constructor(options) {
257
236
  this.theme = (0, theme_1.getTheme)("default");
@@ -623,27 +602,9 @@ class SmartComplyFlow {
623
602
  // ── Loading ─────────────────────────────────────────────────────
624
603
  renderLoading(container) {
625
604
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;gap:16px;";
626
- const spinnerWrap = document.createElement("div");
627
- spinnerWrap.style.cssText = "position:relative;width:60px;height:60px;flex-shrink:0;";
628
- const ring = document.createElement("div");
629
- ring.style.cssText = `
630
- position:absolute;inset:0;border-radius:50%;
631
- border:3px solid ${this.theme.border};
632
- border-top-color:${this.theme.primary};
633
- animation:sc-spin 0.9s linear infinite;
634
- `;
635
- const centre = document.createElement("div");
636
- centre.style.cssText = `
637
- position:absolute;inset:0;
638
- display:flex;align-items:center;justify-content:center;
639
- font-size:22px;
640
- `;
641
- centre.textContent = "\uD83D\uDEE1\uFE0F";
642
- spinnerWrap.appendChild(ring);
643
- spinnerWrap.appendChild(centre);
644
- container.appendChild(spinnerWrap);
605
+ container.appendChild((0, spinner_1.renderSpinner)(this.theme, { size: 60, icon: (0, icons_1.icon)("shield", 22) }));
645
606
  const label = document.createElement("div");
646
- label.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
607
+ label.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.base}px;font-weight:600;`;
647
608
  label.textContent = "Setting up secure session";
648
609
  container.appendChild(label);
649
610
  const sub = document.createElement("div");
@@ -662,11 +623,11 @@ class SmartComplyFlow {
662
623
  iconWrap.style.cssText = `
663
624
  width:50px;height:50px;border-radius:14px;
664
625
  display:flex;align-items:center;justify-content:center;
665
- font-size:22px;
626
+ color:${this.theme.primary};
666
627
  background:linear-gradient(135deg,${this.theme.primary}22,${this.theme.primaryGlow});
667
628
  border:1.5px solid ${this.theme.primary}33;
668
629
  `;
669
- iconWrap.textContent = isDocFlow ? "🪪" : "🛡️";
630
+ iconWrap.innerHTML = (0, icons_1.icon)(isDocFlow ? "id-card" : "shield", 24);
670
631
  topRow.appendChild(iconWrap);
671
632
  const badge = document.createElement("div");
672
633
  badge.style.cssText = `
@@ -685,7 +646,7 @@ class SmartComplyFlow {
685
646
  titleWrap.style.cssText = "display:flex;flex-direction:column;gap:5px;";
686
647
  const title = document.createElement("h2");
687
648
  title.style.cssText = `
688
- color:${this.theme.text};font-size:20px;font-weight:700;
649
+ color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;
689
650
  margin:0;line-height:1.2;
690
651
  `;
691
652
  // W2: Use client brand name in title
@@ -719,7 +680,7 @@ class SmartComplyFlow {
719
680
  const stepsWrap = document.createElement("div");
720
681
  stepsWrap.style.cssText = `
721
682
  width:100%;display:flex;flex-direction:column;gap:0;
722
- border-radius:12px;overflow:hidden;
683
+ border-radius:${theme_1.RADIUS.md}px;overflow:hidden;
723
684
  border:1.5px solid ${this.theme.border};
724
685
  `;
725
686
  steps.forEach((step, i) => {
@@ -732,7 +693,7 @@ class SmartComplyFlow {
732
693
  `;
733
694
  const iconEl = document.createElement("div");
734
695
  iconEl.style.cssText = `
735
- width:28px;height:28px;border-radius:8px;flex-shrink:0;
696
+ width:28px;height:28px;border-radius:${theme_1.RADIUS.sm}px;flex-shrink:0;
736
697
  display:flex;align-items:center;justify-content:center;
737
698
  font-size:13px;
738
699
  background:${this.theme.primary}12;
@@ -960,7 +921,7 @@ class SmartComplyFlow {
960
921
  // Dropdown field
961
922
  const select = document.createElement("select");
962
923
  select.style.cssText = `
963
- width:100%;padding:13px 16px;border-radius:12px;
924
+ width:100%;padding:13px 16px;border-radius:${theme_1.RADIUS.md}px;
964
925
  font-size:15px;
965
926
  background:${this.theme.inputBg};
966
927
  color:${this.theme.text};
@@ -995,7 +956,7 @@ class SmartComplyFlow {
995
956
  fileInput.type = "file";
996
957
  fileInput.accept = "image/*";
997
958
  fileInput.style.cssText = `
998
- width:100%;padding:12px 16px;border-radius:12px;
959
+ width:100%;padding:12px 16px;border-radius:${theme_1.RADIUS.md}px;
999
960
  font-size:14px;font-weight:500;
1000
961
  background:${this.theme.inputBg};
1001
962
  color:${this.theme.text};
@@ -1039,7 +1000,7 @@ class SmartComplyFlow {
1039
1000
  if (rule?.numeric)
1040
1001
  input.inputMode = "numeric";
1041
1002
  input.style.cssText = `
1042
- width:100%;padding:14px 16px;border-radius:12px;
1003
+ width:100%;padding:14px 16px;border-radius:${theme_1.RADIUS.md}px;
1043
1004
  font-size:16px;font-weight:500;
1044
1005
  background:${this.theme.inputBg};
1045
1006
  color:${this.theme.text};
@@ -1101,7 +1062,7 @@ class SmartComplyFlow {
1101
1062
  errorEl.id = "sc-id-error";
1102
1063
  errorEl.style.cssText = `
1103
1064
  color:${this.theme.error};font-size:13px;display:none;
1104
- padding:8px 12px;border-radius:8px;background:${this.theme.errorBg};
1065
+ padding:8px 12px;border-radius:${theme_1.RADIUS.sm}px;background:${this.theme.errorBg};
1105
1066
  `;
1106
1067
  container.appendChild(errorEl);
1107
1068
  // Submit
@@ -1122,7 +1083,7 @@ class SmartComplyFlow {
1122
1083
  };
1123
1084
  submitBtn.addEventListener("click", async () => {
1124
1085
  submitBtn.disabled = true;
1125
- submitBtn.innerHTML = `<span style="display:inline-flex;align-items:center;justify-content:center;gap:8px;"><span style="width:13px;height:13px;border-radius:50%;border:2px solid rgba(255,255,255,0.3);border-top-color:#fff;animation:sc-spin 0.6s linear infinite;display:inline-block;flex-shrink:0;"></span>Verifying...</span>`;
1086
+ submitBtn.innerHTML = `<span style="display:inline-flex;align-items:center;justify-content:center;gap:8px;">${(0, spinner_1.renderInlineSpinner)("#fff", 13)}Verifying...</span>`;
1126
1087
  errorEl.style.display = "none";
1127
1088
  try {
1128
1089
  this.verificationResult = await this.sdk.onboarding.verify({
@@ -1249,11 +1210,11 @@ class SmartComplyFlow {
1249
1210
  // W13: Pre-warm camera silently while user reads this screen
1250
1211
  this._prewarmCamera();
1251
1212
  const iconWrap = document.createElement("div");
1252
- iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;font-size:28px;`;
1253
- iconWrap.textContent = "🛡️";
1213
+ iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;color:${this.theme.primary};`;
1214
+ iconWrap.innerHTML = (0, icons_1.icon)("shield", 28);
1254
1215
  container.appendChild(iconWrap);
1255
1216
  const title = document.createElement("h2");
1256
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1217
+ title.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1257
1218
  title.textContent = this._ivsName ? `Is this you?` : "Identity Confirmed";
1258
1219
  container.appendChild(title);
1259
1220
  if (this._ivsName) {
@@ -1317,12 +1278,25 @@ class SmartComplyFlow {
1317
1278
  }
1318
1279
  // W13: Pre-warm camera while user reads
1319
1280
  this._prewarmCamera();
1281
+ // Step badge — pairs with DocumentCapture.showPreview's "Step 1 of 2"
1282
+ // badge shown right after capture, so this doesn't read as unexplained
1283
+ // repetition: that screen checked photo clarity, this one checks the
1284
+ // OCR-extracted data.
1285
+ const stepBadge = document.createElement("div");
1286
+ stepBadge.style.cssText = `
1287
+ display:inline-flex;align-items:center;gap:5px;
1288
+ padding:3px 11px;border-radius:${theme_1.RADIUS.pill}px;
1289
+ font-size:10.5px;font-weight:600;letter-spacing:0.2px;
1290
+ color:${this.theme.primary};background:${this.theme.primary}15;border:1px solid ${this.theme.primary}30;
1291
+ `;
1292
+ stepBadge.textContent = "STEP 2 OF 2 · CHECK WHAT WE READ";
1293
+ container.appendChild(stepBadge);
1320
1294
  const iconWrap = document.createElement("div");
1321
- iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;font-size:28px;`;
1322
- iconWrap.textContent = "📄";
1295
+ iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;color:${this.theme.primary};`;
1296
+ iconWrap.innerHTML = (0, icons_1.icon)("document", 28);
1323
1297
  container.appendChild(iconWrap);
1324
1298
  const title = document.createElement("h2");
1325
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1299
+ title.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1326
1300
  title.textContent = "Are these your details?";
1327
1301
  container.appendChild(title);
1328
1302
  const sub = document.createElement("p");
@@ -1391,6 +1365,12 @@ class SmartComplyFlow {
1391
1365
  container.appendChild(yesBtn);
1392
1366
  const noBtn = this.createSecondaryButton("No, retake document");
1393
1367
  this._onceClick(noBtn, () => {
1368
+ // Destroy the old DocumentCapture instance before replacing it —
1369
+ // matches the header back-button handler for this same step (see
1370
+ // backTargets.document_confirm) so retaking via either path releases
1371
+ // any still-open camera stream the same way, instead of silently
1372
+ // overwriting this.docCapture without cleanup.
1373
+ this.docCapture?.destroy();
1394
1374
  this._confirmRetries++;
1395
1375
  this._ocrName = "";
1396
1376
  this._ocrDocNumber = "";
@@ -1408,19 +1388,10 @@ class SmartComplyFlow {
1408
1388
  // ── W6+W7+W8: OCR Gate (document verification) ───────────────────
1409
1389
  renderOcrGate(container) {
1410
1390
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:20px 0;text-align:center;";
1411
- const spinnerWrap = document.createElement("div");
1412
- spinnerWrap.style.cssText = "position:relative;width:52px;height:52px;flex-shrink:0;";
1413
- const ring = document.createElement("div");
1414
- ring.style.cssText = `position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.9s linear infinite;`;
1415
- const centre = document.createElement("div");
1416
- centre.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;";
1417
- centre.textContent = "📄";
1418
- spinnerWrap.appendChild(ring);
1419
- spinnerWrap.appendChild(centre);
1420
- container.appendChild(spinnerWrap);
1391
+ container.appendChild((0, spinner_1.renderSpinner)(this.theme, { size: 52, icon: (0, icons_1.icon)("document", 20) }));
1421
1392
  const statusEl = document.createElement("div");
1422
1393
  statusEl.setAttribute("aria-live", "polite");
1423
- statusEl.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
1394
+ statusEl.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.base}px;font-weight:600;`;
1424
1395
  statusEl.textContent = "Reading your document…";
1425
1396
  container.appendChild(statusEl);
1426
1397
  const subEl = document.createElement("div");
@@ -1521,7 +1492,7 @@ class SmartComplyFlow {
1521
1492
  icon.textContent = "📅";
1522
1493
  container.appendChild(icon);
1523
1494
  const t = document.createElement("h2");
1524
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1495
+ t.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1525
1496
  t.textContent = "Document Expired";
1526
1497
  container.appendChild(t);
1527
1498
  const d = document.createElement("p");
@@ -1547,7 +1518,7 @@ class SmartComplyFlow {
1547
1518
  icon.textContent = "🔍";
1548
1519
  container.appendChild(icon);
1549
1520
  const t = document.createElement("h2");
1550
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1521
+ t.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1551
1522
  t.textContent = "Document Photo Unclear";
1552
1523
  container.appendChild(t);
1553
1524
  const msg = reason === "image_too_blurry" ? "Your document photo is too blurry. Please retake it in good lighting with a steady hand."
@@ -1584,11 +1555,11 @@ class SmartComplyFlow {
1584
1555
  verificationResult: this.verificationResult || undefined,
1585
1556
  });
1586
1557
  const iconWrap = document.createElement("div");
1587
- iconWrap.style.cssText = `width:80px;height:80px;border-radius:50%;background:${this.theme.successBg};border:3px solid ${this.theme.success};display:flex;align-items:center;justify-content:center;font-size:36px;font-weight:700;color:${this.theme.success};animation:sc-pop 0.4s ease;`;
1588
- iconWrap.textContent = "✓";
1558
+ iconWrap.style.cssText = `width:80px;height:80px;border-radius:50%;background:${this.theme.successBg};border:3px solid ${this.theme.success};display:flex;align-items:center;justify-content:center;color:${this.theme.success};animation:sc-pop 0.4s ease;`;
1559
+ iconWrap.innerHTML = (0, icons_1.icon)("check", 36);
1589
1560
  container.appendChild(iconWrap);
1590
1561
  const title = document.createElement("h2");
1591
- title.style.cssText = `color:${this.theme.text};font-size:22px;font-weight:700;margin:0;`;
1562
+ title.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.headingLg}px;font-weight:700;margin:0;`;
1592
1563
  title.textContent = "You're all done!";
1593
1564
  container.appendChild(title);
1594
1565
  const desc = document.createElement("p");
@@ -1615,7 +1586,7 @@ class SmartComplyFlow {
1615
1586
  icon.textContent = "⚠️";
1616
1587
  container.appendChild(icon);
1617
1588
  const t = document.createElement("h2");
1618
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1589
+ t.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1619
1590
  t.textContent = "Unable to Complete Verification";
1620
1591
  container.appendChild(t);
1621
1592
  const d = document.createElement("p");
@@ -1670,15 +1641,12 @@ class SmartComplyFlow {
1670
1641
  }
1671
1642
  this.documentBlob = blob;
1672
1643
  // Show scanning state
1673
- captureContainer.innerHTML = `
1674
- <div style="display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;">
1675
- <div style="position:relative;width:52px;height:52px;">
1676
- <div style="position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.8s linear infinite;"></div>
1677
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔍</div>
1678
- </div>
1644
+ captureContainer.innerHTML = "";
1645
+ captureContainer.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;";
1646
+ captureContainer.appendChild((0, spinner_1.renderSpinner)(this.theme, { size: 52, icon: (0, icons_1.icon)("search", 20) }));
1647
+ captureContainer.insertAdjacentHTML("beforeend", `
1679
1648
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Scanning document...</div>
1680
- <div style="color:${this.theme.textSecondary};font-size:12px;text-align:center;max-width:200px;line-height:1.5;">Running OCR and verifying your identity</div>
1681
- </div>`;
1649
+ <div style="color:${this.theme.textSecondary};font-size:12px;text-align:center;max-width:200px;line-height:1.5;">Running OCR and verifying your identity</div>`);
1682
1650
  // Create liveness entry with document.
1683
1651
  // Use the first collected field value as identifier when available
1684
1652
  // (document flow has no data-verification step, so collectedFields is
@@ -1742,15 +1710,12 @@ class SmartComplyFlow {
1742
1710
  console.log(`Retrying document processing (attempt ${retryCount}/${maxRetries})`);
1743
1711
  // Show retry message
1744
1712
  if (captureContainer) {
1745
- captureContainer.innerHTML = `
1746
- <div style="display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;">
1747
- <div style="position:relative;width:52px;height:52px;">
1748
- <div style="position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.8s linear infinite;"></div>
1749
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔄</div>
1750
- </div>
1713
+ captureContainer.innerHTML = "";
1714
+ captureContainer.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;";
1715
+ captureContainer.appendChild((0, spinner_1.renderSpinner)(this.theme, { size: 52, icon: (0, icons_1.icon)("refresh", 20) }));
1716
+ captureContainer.insertAdjacentHTML("beforeend", `
1751
1717
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Retrying...</div>
1752
- <div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>
1753
- </div>`;
1718
+ <div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>`);
1754
1719
  }
1755
1720
  // Wait before retry
1756
1721
  await new Promise(resolve => setTimeout(resolve, 2000));
@@ -1812,7 +1777,7 @@ class SmartComplyFlow {
1812
1777
  const retryBtn = document.createElement("button");
1813
1778
  retryBtn.textContent = "Try Again";
1814
1779
  retryBtn.setAttribute("aria-label", "Retake document photo");
1815
- retryBtn.style.cssText = `margin-top:8px;padding:12px 24px;border-radius:8px;background:${this.theme.primary};color:#fff;border:none;font-size:14px;font-weight:600;cursor:pointer;`;
1780
+ retryBtn.style.cssText = `margin-top:8px;padding:12px 24px;border-radius:${theme_1.RADIUS.sm}px;background:${this.theme.primary};color:#fff;border:none;font-size:14px;font-weight:600;cursor:pointer;`;
1816
1781
  retryBtn.addEventListener("click", () => this.showStep("document_capture"));
1817
1782
  wrap.appendChild(retryBtn);
1818
1783
  }
@@ -1873,6 +1838,12 @@ class SmartComplyFlow {
1873
1838
  : "liveness_only",
1874
1839
  // B4: pass pre-warmed stream so camera.open() is skipped
1875
1840
  prewarmedStream: this._prewarmedStream || undefined,
1841
+ colors: {
1842
+ primary: this.theme.primary,
1843
+ success: this.theme.success,
1844
+ error: this.theme.error,
1845
+ warning: this.theme.warning,
1846
+ },
1876
1847
  }, ["BLINK"], this.livenessEntryId || undefined)
1877
1848
  .then((result) => {
1878
1849
  // W11: After submit, end user is done — show "You're all done" screen.
@@ -1914,14 +1885,14 @@ class SmartComplyFlow {
1914
1885
  // /v1/sdk/liveness/submit/) are an operator-facing problem, not something
1915
1886
  // the end user can act on — never show "wallet"/"balance" language to them.
1916
1887
  const isBillingErr = /wallet|balance|insufficient.*fund|payment.*required/i.test(message);
1917
- const errorIcon = isCameraErr ? "📷"
1918
- : isNetworkErr ? "📡"
1919
- : isAuthErr ? "⏱"
1920
- : isDocQuality ? "🔍"
1921
- : isExpiredDoc ? "📅"
1922
- : isFaceMatch ? "🤳"
1923
- : isBillingErr ? "⚠️"
1924
- : "✕";
1888
+ const errorIconName = isCameraErr ? "camera"
1889
+ : isNetworkErr ? "network"
1890
+ : isAuthErr ? "clock"
1891
+ : isDocQuality ? "search"
1892
+ : isExpiredDoc ? "calendar"
1893
+ : isFaceMatch ? "face"
1894
+ : isBillingErr ? "warning"
1895
+ : "close";
1925
1896
  const errorTitle = isCameraErr ? "Camera Access Required"
1926
1897
  : isNetworkErr ? "Connection Problem"
1927
1898
  : isAuthErr ? "Session Expired"
@@ -1950,14 +1921,14 @@ class SmartComplyFlow {
1950
1921
  iconWrap.style.cssText = `
1951
1922
  width:72px;height:72px;border-radius:50%;
1952
1923
  display:flex;align-items:center;justify-content:center;
1953
- font-size:28px;
1924
+ color:${this.theme.error};
1954
1925
  background:${this.theme.errorBg};
1955
1926
  border:3px solid ${this.theme.error};
1956
1927
  `;
1957
- iconWrap.textContent = errorIcon;
1928
+ iconWrap.innerHTML = (0, icons_1.icon)(errorIconName, 28);
1958
1929
  wrapper.appendChild(iconWrap);
1959
1930
  const title = document.createElement("h2");
1960
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1931
+ title.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1961
1932
  title.textContent = errorTitle;
1962
1933
  wrapper.appendChild(title);
1963
1934
  const desc = document.createElement("p");
@@ -1996,7 +1967,7 @@ class SmartComplyFlow {
1996
1967
  this.headerBackBtn.innerHTML = "&#8592;";
1997
1968
  this.headerBackBtn.setAttribute("aria-label", "Go back to previous step");
1998
1969
  this.headerBackBtn.style.cssText = `
1999
- width:30px;height:30px;border-radius:8px;flex-shrink:0;
1970
+ width:30px;height:30px;border-radius:${theme_1.RADIUS.sm}px;flex-shrink:0;
2000
1971
  display:none;align-items:center;justify-content:center;
2001
1972
  border:1.5px solid ${this.theme.border};cursor:pointer;
2002
1973
  font-size:16px;line-height:1;
@@ -2054,7 +2025,7 @@ class SmartComplyFlow {
2054
2025
  closeBtn.innerHTML = "✕";
2055
2026
  closeBtn.setAttribute("aria-label", "Close identity verification");
2056
2027
  closeBtn.style.cssText = `
2057
- width:32px;height:32px;border-radius:8px;flex-shrink:0;
2028
+ width:32px;height:32px;border-radius:${theme_1.RADIUS.sm}px;flex-shrink:0;
2058
2029
  display:flex;align-items:center;justify-content:center;
2059
2030
  border:none;cursor:pointer;font-size:16px;
2060
2031
  background:${this.theme.shimmer};color:${this.theme.textMuted};
@@ -2091,7 +2062,7 @@ class SmartComplyFlow {
2091
2062
  createStepTitle(text) {
2092
2063
  const el = document.createElement("h3");
2093
2064
  el.style.cssText = `
2094
- color:${this.theme.text};font-size:18px;font-weight:700;
2065
+ color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;
2095
2066
  margin:0;
2096
2067
  `;
2097
2068
  el.textContent = text;
@@ -2101,7 +2072,7 @@ class SmartComplyFlow {
2101
2072
  const card = document.createElement("button");
2102
2073
  card.setAttribute("aria-label", subtitle ? `${title}: ${subtitle}` : title);
2103
2074
  card.style.cssText = `
2104
- width:100%;padding:13px 16px;border-radius:12px;
2075
+ width:100%;padding:13px 16px;border-radius:${theme_1.RADIUS.md}px;
2105
2076
  background:${this.theme.cardBg};
2106
2077
  border:2px solid ${this.theme.border};
2107
2078
  cursor:pointer;text-align:left;
@@ -2141,7 +2112,7 @@ class SmartComplyFlow {
2141
2112
  const btn = document.createElement("button");
2142
2113
  btn.textContent = text;
2143
2114
  btn.style.cssText = `
2144
- width:100%;padding:14px;border-radius:12px;
2115
+ width:100%;padding:14px;border-radius:${theme_1.RADIUS.md}px;
2145
2116
  font-size:15px;font-weight:600;
2146
2117
  background:${this.theme.primary};color:#fff;
2147
2118
  border:none;cursor:pointer;
@@ -2162,7 +2133,7 @@ class SmartComplyFlow {
2162
2133
  const btn = document.createElement("button");
2163
2134
  btn.textContent = text;
2164
2135
  btn.style.cssText = `
2165
- width:100%;padding:12px;border-radius:12px;
2136
+ width:100%;padding:12px;border-radius:${theme_1.RADIUS.md}px;
2166
2137
  font-size:14px;font-weight:500;
2167
2138
  background:transparent;color:${this.theme.textSecondary};
2168
2139
  border:none;cursor:pointer;
@@ -2300,9 +2271,6 @@ class SmartComplyFlow {
2300
2271
  const style = document.createElement("style");
2301
2272
  style.id = "sc-flow-styles";
2302
2273
  style.textContent = `
2303
- @keyframes sc-spin {
2304
- to { transform: rotate(360deg); }
2305
- }
2306
2274
  @keyframes sc-pop {
2307
2275
  0% { transform: scale(0.8); opacity: 0; }
2308
2276
  50% { transform: scale(1.05); }