smartcomply-web-sdk 1.0.62 → 1.0.64

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 (59) hide show
  1. package/dist/esm/flow/DocumentCapture.d.ts +21 -1
  2. package/dist/esm/flow/DocumentCapture.d.ts.map +1 -1
  3. package/dist/esm/flow/DocumentCapture.js +155 -84
  4. package/dist/esm/flow/DocumentCapture.js.map +1 -1
  5. package/dist/esm/flow/SmartComplyFlow.d.ts +1 -0
  6. package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
  7. package/dist/esm/flow/SmartComplyFlow.js +89 -105
  8. package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
  9. package/dist/esm/flow/icons.d.ts +14 -0
  10. package/dist/esm/flow/icons.d.ts.map +1 -0
  11. package/dist/esm/flow/icons.js +34 -0
  12. package/dist/esm/flow/icons.js.map +1 -0
  13. package/dist/esm/flow/spinner.d.ts +15 -0
  14. package/dist/esm/flow/spinner.d.ts.map +1 -0
  15. package/dist/esm/flow/spinner.js +55 -0
  16. package/dist/esm/flow/spinner.js.map +1 -0
  17. package/dist/esm/flow/theme.d.ts +19 -0
  18. package/dist/esm/flow/theme.d.ts.map +1 -1
  19. package/dist/esm/flow/theme.js +19 -0
  20. package/dist/esm/flow/theme.js.map +1 -1
  21. package/dist/esm/modules/liveness/LivenessUI.d.ts +10 -0
  22. package/dist/esm/modules/liveness/LivenessUI.d.ts.map +1 -1
  23. package/dist/esm/modules/liveness/LivenessUI.js +17 -16
  24. package/dist/esm/modules/liveness/LivenessUI.js.map +1 -1
  25. package/dist/esm/modules/liveness/liveness.d.ts +2 -0
  26. package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
  27. package/dist/esm/modules/liveness/liveness.js +1 -1
  28. package/dist/esm/modules/liveness/liveness.js.map +1 -1
  29. package/dist/flow/DocumentCapture.d.ts +21 -1
  30. package/dist/flow/DocumentCapture.d.ts.map +1 -1
  31. package/dist/flow/DocumentCapture.js +155 -84
  32. package/dist/flow/DocumentCapture.js.map +1 -1
  33. package/dist/flow/SmartComplyFlow.d.ts +1 -0
  34. package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
  35. package/dist/flow/SmartComplyFlow.js +88 -104
  36. package/dist/flow/SmartComplyFlow.js.map +1 -1
  37. package/dist/flow/icons.d.ts +14 -0
  38. package/dist/flow/icons.d.ts.map +1 -0
  39. package/dist/flow/icons.js +37 -0
  40. package/dist/flow/icons.js.map +1 -0
  41. package/dist/flow/spinner.d.ts +15 -0
  42. package/dist/flow/spinner.d.ts.map +1 -0
  43. package/dist/flow/spinner.js +59 -0
  44. package/dist/flow/spinner.js.map +1 -0
  45. package/dist/flow/theme.d.ts +19 -0
  46. package/dist/flow/theme.d.ts.map +1 -1
  47. package/dist/flow/theme.js +20 -1
  48. package/dist/flow/theme.js.map +1 -1
  49. package/dist/modules/liveness/LivenessUI.d.ts +10 -0
  50. package/dist/modules/liveness/LivenessUI.d.ts.map +1 -1
  51. package/dist/modules/liveness/LivenessUI.js +17 -16
  52. package/dist/modules/liveness/LivenessUI.js.map +1 -1
  53. package/dist/modules/liveness/liveness.d.ts +2 -0
  54. package/dist/modules/liveness/liveness.d.ts.map +1 -1
  55. package/dist/modules/liveness/liveness.js +1 -1
  56. package/dist/modules/liveness/liveness.js.map +1 -1
  57. package/dist/smartcomply.browser.js +333 -209
  58. package/dist/smartcomply.browser.js.map +4 -4
  59. package/package.json +1 -1
@@ -39,6 +39,7 @@ export declare class SmartComplyFlow {
39
39
  private idNumber;
40
40
  private documentBlob;
41
41
  private documentBackBlob;
42
+ private _documentViaCamera;
42
43
  private identityCheckId;
43
44
  private verificationResult;
44
45
  private livenessEntryId;
@@ -1 +1 @@
1
- {"version":3,"file":"SmartComplyFlow.d.ts","sourceRoot":"","sources":["../../../src/flow/SmartComplyFlow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SmartComplyFlow.d.ts","sourceRoot":"","sources":["../../../src/flow/SmartComplyFlow.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
1
  import { SmartComply } from "../client/Smartcomply";
2
- import { getTheme } from "./theme";
2
+ import { getTheme, TYPE, RADIUS } from "./theme";
3
3
  import { DocumentCapture } from "./DocumentCapture";
4
4
  import { collectDeviceMetadata } from "../utils/device";
5
5
  import { requiresBackSide } from "../utils/documentRules";
6
+ import { icon } from "./icons";
7
+ import { renderSpinner, renderInlineSpinner } from "./spinner";
6
8
  const STEP_ORDER = [
7
9
  "loading",
8
10
  "welcome",
@@ -226,18 +228,6 @@ function _formatDocType(code) {
226
228
  }
227
229
  // SmartComplyFlow
228
230
  // ─────────────────────────────────────────────────────────────────────
229
- // Add CSS animation keyframes for spin animations
230
- const style = document.createElement('style');
231
- style.textContent = `
232
- @keyframes sc-spin {
233
- 0% { transform: rotate(0deg); }
234
- 100% { transform: rotate(360deg); }
235
- }
236
- `;
237
- if (!document.head.querySelector('style[data-sc-animations]')) {
238
- style.setAttribute('data-sc-animations', 'true');
239
- document.head.appendChild(style);
240
- }
241
231
  export class SmartComplyFlow {
242
232
  constructor(options) {
243
233
  this.theme = getTheme("default");
@@ -262,6 +252,11 @@ export class SmartComplyFlow {
262
252
  this.idNumber = "";
263
253
  this.documentBlob = null;
264
254
  this.documentBackBlob = null;
255
+ // Whether the document's front image came from the live camera vs. a
256
+ // gallery/file upload — gates whether pre-warming the liveness camera on
257
+ // the confirm screens makes sense (reusing an already-open camera) or
258
+ // would surprise a user who hasn't touched their camera yet this session.
259
+ this._documentViaCamera = false;
265
260
  this.identityCheckId = null;
266
261
  this.verificationResult = null;
267
262
  this.livenessEntryId = null;
@@ -609,27 +604,9 @@ export class SmartComplyFlow {
609
604
  // ── Loading ─────────────────────────────────────────────────────
610
605
  renderLoading(container) {
611
606
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;gap:16px;";
612
- const spinnerWrap = document.createElement("div");
613
- spinnerWrap.style.cssText = "position:relative;width:60px;height:60px;flex-shrink:0;";
614
- const ring = document.createElement("div");
615
- ring.style.cssText = `
616
- position:absolute;inset:0;border-radius:50%;
617
- border:3px solid ${this.theme.border};
618
- border-top-color:${this.theme.primary};
619
- animation:sc-spin 0.9s linear infinite;
620
- `;
621
- const centre = document.createElement("div");
622
- centre.style.cssText = `
623
- position:absolute;inset:0;
624
- display:flex;align-items:center;justify-content:center;
625
- font-size:22px;
626
- `;
627
- centre.textContent = "\uD83D\uDEE1\uFE0F";
628
- spinnerWrap.appendChild(ring);
629
- spinnerWrap.appendChild(centre);
630
- container.appendChild(spinnerWrap);
607
+ container.appendChild(renderSpinner(this.theme, { size: 60, icon: icon("shield", 22) }));
631
608
  const label = document.createElement("div");
632
- label.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
609
+ label.style.cssText = `color:${this.theme.text};font-size:${TYPE.base}px;font-weight:600;`;
633
610
  label.textContent = "Setting up secure session";
634
611
  container.appendChild(label);
635
612
  const sub = document.createElement("div");
@@ -648,11 +625,11 @@ export class SmartComplyFlow {
648
625
  iconWrap.style.cssText = `
649
626
  width:50px;height:50px;border-radius:14px;
650
627
  display:flex;align-items:center;justify-content:center;
651
- font-size:22px;
628
+ color:${this.theme.primary};
652
629
  background:linear-gradient(135deg,${this.theme.primary}22,${this.theme.primaryGlow});
653
630
  border:1.5px solid ${this.theme.primary}33;
654
631
  `;
655
- iconWrap.textContent = isDocFlow ? "🪪" : "🛡️";
632
+ iconWrap.innerHTML = icon(isDocFlow ? "id-card" : "shield", 24);
656
633
  topRow.appendChild(iconWrap);
657
634
  const badge = document.createElement("div");
658
635
  badge.style.cssText = `
@@ -671,7 +648,7 @@ export class SmartComplyFlow {
671
648
  titleWrap.style.cssText = "display:flex;flex-direction:column;gap:5px;";
672
649
  const title = document.createElement("h2");
673
650
  title.style.cssText = `
674
- color:${this.theme.text};font-size:20px;font-weight:700;
651
+ color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;
675
652
  margin:0;line-height:1.2;
676
653
  `;
677
654
  // W2: Use client brand name in title
@@ -705,7 +682,7 @@ export class SmartComplyFlow {
705
682
  const stepsWrap = document.createElement("div");
706
683
  stepsWrap.style.cssText = `
707
684
  width:100%;display:flex;flex-direction:column;gap:0;
708
- border-radius:12px;overflow:hidden;
685
+ border-radius:${RADIUS.md}px;overflow:hidden;
709
686
  border:1.5px solid ${this.theme.border};
710
687
  `;
711
688
  steps.forEach((step, i) => {
@@ -718,7 +695,7 @@ export class SmartComplyFlow {
718
695
  `;
719
696
  const iconEl = document.createElement("div");
720
697
  iconEl.style.cssText = `
721
- width:28px;height:28px;border-radius:8px;flex-shrink:0;
698
+ width:28px;height:28px;border-radius:${RADIUS.sm}px;flex-shrink:0;
722
699
  display:flex;align-items:center;justify-content:center;
723
700
  font-size:13px;
724
701
  background:${this.theme.primary}12;
@@ -946,7 +923,7 @@ export class SmartComplyFlow {
946
923
  // Dropdown field
947
924
  const select = document.createElement("select");
948
925
  select.style.cssText = `
949
- width:100%;padding:13px 16px;border-radius:12px;
926
+ width:100%;padding:13px 16px;border-radius:${RADIUS.md}px;
950
927
  font-size:15px;
951
928
  background:${this.theme.inputBg};
952
929
  color:${this.theme.text};
@@ -981,7 +958,7 @@ export class SmartComplyFlow {
981
958
  fileInput.type = "file";
982
959
  fileInput.accept = "image/*";
983
960
  fileInput.style.cssText = `
984
- width:100%;padding:12px 16px;border-radius:12px;
961
+ width:100%;padding:12px 16px;border-radius:${RADIUS.md}px;
985
962
  font-size:14px;font-weight:500;
986
963
  background:${this.theme.inputBg};
987
964
  color:${this.theme.text};
@@ -1025,7 +1002,7 @@ export class SmartComplyFlow {
1025
1002
  if (rule?.numeric)
1026
1003
  input.inputMode = "numeric";
1027
1004
  input.style.cssText = `
1028
- width:100%;padding:14px 16px;border-radius:12px;
1005
+ width:100%;padding:14px 16px;border-radius:${RADIUS.md}px;
1029
1006
  font-size:16px;font-weight:500;
1030
1007
  background:${this.theme.inputBg};
1031
1008
  color:${this.theme.text};
@@ -1087,7 +1064,7 @@ export class SmartComplyFlow {
1087
1064
  errorEl.id = "sc-id-error";
1088
1065
  errorEl.style.cssText = `
1089
1066
  color:${this.theme.error};font-size:13px;display:none;
1090
- padding:8px 12px;border-radius:8px;background:${this.theme.errorBg};
1067
+ padding:8px 12px;border-radius:${RADIUS.sm}px;background:${this.theme.errorBg};
1091
1068
  `;
1092
1069
  container.appendChild(errorEl);
1093
1070
  // Submit
@@ -1108,7 +1085,7 @@ export class SmartComplyFlow {
1108
1085
  };
1109
1086
  submitBtn.addEventListener("click", async () => {
1110
1087
  submitBtn.disabled = true;
1111
- 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>`;
1088
+ submitBtn.innerHTML = `<span style="display:inline-flex;align-items:center;justify-content:center;gap:8px;">${renderInlineSpinner("#fff", 13)}Verifying...</span>`;
1112
1089
  errorEl.style.display = "none";
1113
1090
  try {
1114
1091
  this.verificationResult = await this.sdk.onboarding.verify({
@@ -1235,11 +1212,11 @@ export class SmartComplyFlow {
1235
1212
  // W13: Pre-warm camera silently while user reads this screen
1236
1213
  this._prewarmCamera();
1237
1214
  const iconWrap = document.createElement("div");
1238
- 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;`;
1239
- iconWrap.textContent = "🛡️";
1215
+ 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};`;
1216
+ iconWrap.innerHTML = icon("shield", 28);
1240
1217
  container.appendChild(iconWrap);
1241
1218
  const title = document.createElement("h2");
1242
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1219
+ title.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1243
1220
  title.textContent = this._ivsName ? `Is this you?` : "Identity Confirmed";
1244
1221
  container.appendChild(title);
1245
1222
  if (this._ivsName) {
@@ -1301,14 +1278,32 @@ export class SmartComplyFlow {
1301
1278
  this._renderRetryLimitReached(container);
1302
1279
  return;
1303
1280
  }
1304
- // W13: Pre-warm camera while user reads
1305
- this._prewarmCamera();
1281
+ // W13: Pre-warm camera while user reads — but only if the document photo
1282
+ // itself came from the camera. Pre-warming here when the user uploaded
1283
+ // from gallery means requesting camera access before they've touched
1284
+ // their camera at all this session, which reads as an unexplained/
1285
+ // unexpected prompt rather than a seamless continuation.
1286
+ if (this._documentViaCamera)
1287
+ this._prewarmCamera();
1288
+ // Step badge — pairs with DocumentCapture.showPreview's "Step 1 of 2"
1289
+ // badge shown right after capture, so this doesn't read as unexplained
1290
+ // repetition: that screen checked photo clarity, this one checks the
1291
+ // OCR-extracted data.
1292
+ const stepBadge = document.createElement("div");
1293
+ stepBadge.style.cssText = `
1294
+ display:inline-flex;align-items:center;gap:5px;
1295
+ padding:3px 11px;border-radius:${RADIUS.pill}px;
1296
+ font-size:10.5px;font-weight:600;letter-spacing:0.2px;
1297
+ color:${this.theme.primary};background:${this.theme.primary}15;border:1px solid ${this.theme.primary}30;
1298
+ `;
1299
+ stepBadge.textContent = "STEP 2 OF 2 · CHECK WHAT WE READ";
1300
+ container.appendChild(stepBadge);
1306
1301
  const iconWrap = document.createElement("div");
1307
- 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;`;
1308
- iconWrap.textContent = "📄";
1302
+ 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};`;
1303
+ iconWrap.innerHTML = icon("document", 28);
1309
1304
  container.appendChild(iconWrap);
1310
1305
  const title = document.createElement("h2");
1311
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1306
+ title.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1312
1307
  title.textContent = "Are these your details?";
1313
1308
  container.appendChild(title);
1314
1309
  const sub = document.createElement("p");
@@ -1400,19 +1395,10 @@ export class SmartComplyFlow {
1400
1395
  // ── W6+W7+W8: OCR Gate (document verification) ───────────────────
1401
1396
  renderOcrGate(container) {
1402
1397
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:20px 0;text-align:center;";
1403
- const spinnerWrap = document.createElement("div");
1404
- spinnerWrap.style.cssText = "position:relative;width:52px;height:52px;flex-shrink:0;";
1405
- const ring = document.createElement("div");
1406
- 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;`;
1407
- const centre = document.createElement("div");
1408
- centre.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;";
1409
- centre.textContent = "📄";
1410
- spinnerWrap.appendChild(ring);
1411
- spinnerWrap.appendChild(centre);
1412
- container.appendChild(spinnerWrap);
1398
+ container.appendChild(renderSpinner(this.theme, { size: 52, icon: icon("document", 20) }));
1413
1399
  const statusEl = document.createElement("div");
1414
1400
  statusEl.setAttribute("aria-live", "polite");
1415
- statusEl.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
1401
+ statusEl.style.cssText = `color:${this.theme.text};font-size:${TYPE.base}px;font-weight:600;`;
1416
1402
  statusEl.textContent = "Reading your document…";
1417
1403
  container.appendChild(statusEl);
1418
1404
  const subEl = document.createElement("div");
@@ -1513,7 +1499,7 @@ export class SmartComplyFlow {
1513
1499
  icon.textContent = "📅";
1514
1500
  container.appendChild(icon);
1515
1501
  const t = document.createElement("h2");
1516
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1502
+ t.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1517
1503
  t.textContent = "Document Expired";
1518
1504
  container.appendChild(t);
1519
1505
  const d = document.createElement("p");
@@ -1539,7 +1525,7 @@ export class SmartComplyFlow {
1539
1525
  icon.textContent = "🔍";
1540
1526
  container.appendChild(icon);
1541
1527
  const t = document.createElement("h2");
1542
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1528
+ t.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1543
1529
  t.textContent = "Document Photo Unclear";
1544
1530
  container.appendChild(t);
1545
1531
  const msg = reason === "image_too_blurry" ? "Your document photo is too blurry. Please retake it in good lighting with a steady hand."
@@ -1576,11 +1562,11 @@ export class SmartComplyFlow {
1576
1562
  verificationResult: this.verificationResult || undefined,
1577
1563
  });
1578
1564
  const iconWrap = document.createElement("div");
1579
- 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;`;
1580
- iconWrap.textContent = "✓";
1565
+ 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;`;
1566
+ iconWrap.innerHTML = icon("check", 36);
1581
1567
  container.appendChild(iconWrap);
1582
1568
  const title = document.createElement("h2");
1583
- title.style.cssText = `color:${this.theme.text};font-size:22px;font-weight:700;margin:0;`;
1569
+ title.style.cssText = `color:${this.theme.text};font-size:${TYPE.headingLg}px;font-weight:700;margin:0;`;
1584
1570
  title.textContent = "You're all done!";
1585
1571
  container.appendChild(title);
1586
1572
  const desc = document.createElement("p");
@@ -1607,7 +1593,7 @@ export class SmartComplyFlow {
1607
1593
  icon.textContent = "⚠️";
1608
1594
  container.appendChild(icon);
1609
1595
  const t = document.createElement("h2");
1610
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1596
+ t.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1611
1597
  t.textContent = "Unable to Complete Verification";
1612
1598
  container.appendChild(t);
1613
1599
  const d = document.createElement("p");
@@ -1642,6 +1628,7 @@ export class SmartComplyFlow {
1642
1628
  // below so compression only happens once.
1643
1629
  this.documentBlob = await _compressImage(result.front, 1600, 0.85);
1644
1630
  this.documentBackBlob = result.back ? await _compressImage(result.back, 1600, 0.85) : null;
1631
+ this._documentViaCamera = result.viaCamera;
1645
1632
  // Process the front image — pass the already-compressed blob
1646
1633
  await this.processDocumentBlob(this.documentBlob, captureContainer, container);
1647
1634
  }
@@ -1662,15 +1649,12 @@ export class SmartComplyFlow {
1662
1649
  }
1663
1650
  this.documentBlob = blob;
1664
1651
  // Show scanning state
1665
- captureContainer.innerHTML = `
1666
- <div style="display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;">
1667
- <div style="position:relative;width:52px;height:52px;">
1668
- <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>
1669
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔍</div>
1670
- </div>
1652
+ captureContainer.innerHTML = "";
1653
+ captureContainer.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;";
1654
+ captureContainer.appendChild(renderSpinner(this.theme, { size: 52, icon: icon("search", 20) }));
1655
+ captureContainer.insertAdjacentHTML("beforeend", `
1671
1656
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Scanning document...</div>
1672
- <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>
1673
- </div>`;
1657
+ <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>`);
1674
1658
  // Create liveness entry with document.
1675
1659
  // Use the first collected field value as identifier when available
1676
1660
  // (document flow has no data-verification step, so collectedFields is
@@ -1734,15 +1718,12 @@ export class SmartComplyFlow {
1734
1718
  console.log(`Retrying document processing (attempt ${retryCount}/${maxRetries})`);
1735
1719
  // Show retry message
1736
1720
  if (captureContainer) {
1737
- captureContainer.innerHTML = `
1738
- <div style="display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;">
1739
- <div style="position:relative;width:52px;height:52px;">
1740
- <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>
1741
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔄</div>
1742
- </div>
1721
+ captureContainer.innerHTML = "";
1722
+ captureContainer.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;";
1723
+ captureContainer.appendChild(renderSpinner(this.theme, { size: 52, icon: icon("refresh", 20) }));
1724
+ captureContainer.insertAdjacentHTML("beforeend", `
1743
1725
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Retrying...</div>
1744
- <div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>
1745
- </div>`;
1726
+ <div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>`);
1746
1727
  }
1747
1728
  // Wait before retry
1748
1729
  await new Promise(resolve => setTimeout(resolve, 2000));
@@ -1804,7 +1785,7 @@ export class SmartComplyFlow {
1804
1785
  const retryBtn = document.createElement("button");
1805
1786
  retryBtn.textContent = "Try Again";
1806
1787
  retryBtn.setAttribute("aria-label", "Retake document photo");
1807
- 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;`;
1788
+ retryBtn.style.cssText = `margin-top:8px;padding:12px 24px;border-radius:${RADIUS.sm}px;background:${this.theme.primary};color:#fff;border:none;font-size:14px;font-weight:600;cursor:pointer;`;
1808
1789
  retryBtn.addEventListener("click", () => this.showStep("document_capture"));
1809
1790
  wrap.appendChild(retryBtn);
1810
1791
  }
@@ -1865,6 +1846,12 @@ export class SmartComplyFlow {
1865
1846
  : "liveness_only",
1866
1847
  // B4: pass pre-warmed stream so camera.open() is skipped
1867
1848
  prewarmedStream: this._prewarmedStream || undefined,
1849
+ colors: {
1850
+ primary: this.theme.primary,
1851
+ success: this.theme.success,
1852
+ error: this.theme.error,
1853
+ warning: this.theme.warning,
1854
+ },
1868
1855
  }, ["BLINK"], this.livenessEntryId || undefined)
1869
1856
  .then((result) => {
1870
1857
  // W11: After submit, end user is done — show "You're all done" screen.
@@ -1906,14 +1893,14 @@ export class SmartComplyFlow {
1906
1893
  // /v1/sdk/liveness/submit/) are an operator-facing problem, not something
1907
1894
  // the end user can act on — never show "wallet"/"balance" language to them.
1908
1895
  const isBillingErr = /wallet|balance|insufficient.*fund|payment.*required/i.test(message);
1909
- const errorIcon = isCameraErr ? "📷"
1910
- : isNetworkErr ? "📡"
1911
- : isAuthErr ? "⏱"
1912
- : isDocQuality ? "🔍"
1913
- : isExpiredDoc ? "📅"
1914
- : isFaceMatch ? "🤳"
1915
- : isBillingErr ? "⚠️"
1916
- : "✕";
1896
+ const errorIconName = isCameraErr ? "camera"
1897
+ : isNetworkErr ? "network"
1898
+ : isAuthErr ? "clock"
1899
+ : isDocQuality ? "search"
1900
+ : isExpiredDoc ? "calendar"
1901
+ : isFaceMatch ? "face"
1902
+ : isBillingErr ? "warning"
1903
+ : "close";
1917
1904
  const errorTitle = isCameraErr ? "Camera Access Required"
1918
1905
  : isNetworkErr ? "Connection Problem"
1919
1906
  : isAuthErr ? "Session Expired"
@@ -1942,14 +1929,14 @@ export class SmartComplyFlow {
1942
1929
  iconWrap.style.cssText = `
1943
1930
  width:72px;height:72px;border-radius:50%;
1944
1931
  display:flex;align-items:center;justify-content:center;
1945
- font-size:28px;
1932
+ color:${this.theme.error};
1946
1933
  background:${this.theme.errorBg};
1947
1934
  border:3px solid ${this.theme.error};
1948
1935
  `;
1949
- iconWrap.textContent = errorIcon;
1936
+ iconWrap.innerHTML = icon(errorIconName, 28);
1950
1937
  wrapper.appendChild(iconWrap);
1951
1938
  const title = document.createElement("h2");
1952
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1939
+ title.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1953
1940
  title.textContent = errorTitle;
1954
1941
  wrapper.appendChild(title);
1955
1942
  const desc = document.createElement("p");
@@ -1988,7 +1975,7 @@ export class SmartComplyFlow {
1988
1975
  this.headerBackBtn.innerHTML = "&#8592;";
1989
1976
  this.headerBackBtn.setAttribute("aria-label", "Go back to previous step");
1990
1977
  this.headerBackBtn.style.cssText = `
1991
- width:30px;height:30px;border-radius:8px;flex-shrink:0;
1978
+ width:30px;height:30px;border-radius:${RADIUS.sm}px;flex-shrink:0;
1992
1979
  display:none;align-items:center;justify-content:center;
1993
1980
  border:1.5px solid ${this.theme.border};cursor:pointer;
1994
1981
  font-size:16px;line-height:1;
@@ -2046,7 +2033,7 @@ export class SmartComplyFlow {
2046
2033
  closeBtn.innerHTML = "✕";
2047
2034
  closeBtn.setAttribute("aria-label", "Close identity verification");
2048
2035
  closeBtn.style.cssText = `
2049
- width:32px;height:32px;border-radius:8px;flex-shrink:0;
2036
+ width:32px;height:32px;border-radius:${RADIUS.sm}px;flex-shrink:0;
2050
2037
  display:flex;align-items:center;justify-content:center;
2051
2038
  border:none;cursor:pointer;font-size:16px;
2052
2039
  background:${this.theme.shimmer};color:${this.theme.textMuted};
@@ -2083,7 +2070,7 @@ export class SmartComplyFlow {
2083
2070
  createStepTitle(text) {
2084
2071
  const el = document.createElement("h3");
2085
2072
  el.style.cssText = `
2086
- color:${this.theme.text};font-size:18px;font-weight:700;
2073
+ color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;
2087
2074
  margin:0;
2088
2075
  `;
2089
2076
  el.textContent = text;
@@ -2093,7 +2080,7 @@ export class SmartComplyFlow {
2093
2080
  const card = document.createElement("button");
2094
2081
  card.setAttribute("aria-label", subtitle ? `${title}: ${subtitle}` : title);
2095
2082
  card.style.cssText = `
2096
- width:100%;padding:13px 16px;border-radius:12px;
2083
+ width:100%;padding:13px 16px;border-radius:${RADIUS.md}px;
2097
2084
  background:${this.theme.cardBg};
2098
2085
  border:2px solid ${this.theme.border};
2099
2086
  cursor:pointer;text-align:left;
@@ -2133,7 +2120,7 @@ export class SmartComplyFlow {
2133
2120
  const btn = document.createElement("button");
2134
2121
  btn.textContent = text;
2135
2122
  btn.style.cssText = `
2136
- width:100%;padding:14px;border-radius:12px;
2123
+ width:100%;padding:14px;border-radius:${RADIUS.md}px;
2137
2124
  font-size:15px;font-weight:600;
2138
2125
  background:${this.theme.primary};color:#fff;
2139
2126
  border:none;cursor:pointer;
@@ -2154,7 +2141,7 @@ export class SmartComplyFlow {
2154
2141
  const btn = document.createElement("button");
2155
2142
  btn.textContent = text;
2156
2143
  btn.style.cssText = `
2157
- width:100%;padding:12px;border-radius:12px;
2144
+ width:100%;padding:12px;border-radius:${RADIUS.md}px;
2158
2145
  font-size:14px;font-weight:500;
2159
2146
  background:transparent;color:${this.theme.textSecondary};
2160
2147
  border:none;cursor:pointer;
@@ -2292,9 +2279,6 @@ export class SmartComplyFlow {
2292
2279
  const style = document.createElement("style");
2293
2280
  style.id = "sc-flow-styles";
2294
2281
  style.textContent = `
2295
- @keyframes sc-spin {
2296
- to { transform: rotate(360deg); }
2297
- }
2298
2282
  @keyframes sc-pop {
2299
2283
  0% { transform: scale(0.8); opacity: 0; }
2300
2284
  50% { transform: scale(1.05); }