@glideidentity/web-client-sdk 5.0.0 → 5.0.1-beta.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.
Files changed (89) hide show
  1. package/README.md +108 -8
  2. package/dist/adapters/angular/index.js +0 -1
  3. package/dist/adapters/angular/phone-auth.service.d.ts +0 -18
  4. package/dist/adapters/angular/phone-auth.service.js +0 -26
  5. package/dist/adapters/react/index.js +0 -3
  6. package/dist/adapters/react/useClient.js +0 -1
  7. package/dist/adapters/react/usePhoneAuth.js +1 -16
  8. package/dist/adapters/vanilla/client.js +0 -1
  9. package/dist/adapters/vanilla/index.js +0 -1
  10. package/dist/adapters/vanilla/phone-auth.js +0 -31
  11. package/dist/adapters/vue/index.js +0 -4
  12. package/dist/adapters/vue/useClient.js +0 -5
  13. package/dist/adapters/vue/usePhoneAuth.js +1 -20
  14. package/dist/browser/web-client-sdk.min.js +1 -1
  15. package/dist/browser.js +0 -6
  16. package/dist/core/client.js +0 -12
  17. package/dist/core/logger.js +1 -81
  18. package/dist/core/phone-auth/api-types.js +0 -83
  19. package/dist/core/phone-auth/client.js +27 -366
  20. package/dist/core/phone-auth/error-utils.js +1 -83
  21. package/dist/core/phone-auth/index.js +0 -1
  22. package/dist/core/phone-auth/status-types.d.ts +0 -78
  23. package/dist/core/phone-auth/status-types.js +0 -17
  24. package/dist/core/phone-auth/strategies/desktop.js +8 -126
  25. package/dist/core/phone-auth/strategies/index.d.ts +0 -4
  26. package/dist/core/phone-auth/strategies/index.js +0 -4
  27. package/dist/core/phone-auth/strategies/link.js +10 -88
  28. package/dist/core/phone-auth/strategies/ts43.d.ts +0 -19
  29. package/dist/core/phone-auth/strategies/ts43.js +2 -33
  30. package/dist/core/phone-auth/strategies/types.js +0 -4
  31. package/dist/core/phone-auth/type-guards.js +0 -131
  32. package/dist/core/phone-auth/types.js +0 -32
  33. package/dist/core/phone-auth/ui/mobile-debug-console.js +2 -28
  34. package/dist/core/phone-auth/ui/modal.d.ts +33 -55
  35. package/dist/core/phone-auth/ui/modal.js +889 -422
  36. package/dist/core/phone-auth/validation-utils.js +2 -40
  37. package/dist/core/version.js +1 -2
  38. package/dist/esm/adapters/angular/index.js +0 -1
  39. package/dist/esm/adapters/angular/phone-auth.service.d.ts +0 -18
  40. package/dist/esm/adapters/angular/phone-auth.service.js +0 -26
  41. package/dist/esm/adapters/react/index.js +0 -3
  42. package/dist/esm/adapters/react/useClient.js +0 -1
  43. package/dist/esm/adapters/react/usePhoneAuth.js +1 -16
  44. package/dist/esm/adapters/vanilla/client.js +0 -1
  45. package/dist/esm/adapters/vanilla/index.js +0 -1
  46. package/dist/esm/adapters/vanilla/phone-auth.d.ts +0 -24
  47. package/dist/esm/adapters/vanilla/phone-auth.js +0 -31
  48. package/dist/esm/adapters/vue/index.js +0 -4
  49. package/dist/esm/adapters/vue/useClient.js +0 -5
  50. package/dist/esm/adapters/vue/usePhoneAuth.js +1 -20
  51. package/dist/esm/browser.js +0 -6
  52. package/dist/esm/core/client.d.ts +0 -10
  53. package/dist/esm/core/client.js +0 -12
  54. package/dist/esm/core/logger.d.ts +0 -53
  55. package/dist/esm/core/logger.js +1 -81
  56. package/dist/esm/core/phone-auth/api-types.d.ts +0 -315
  57. package/dist/esm/core/phone-auth/api-types.js +0 -83
  58. package/dist/esm/core/phone-auth/client.d.ts +0 -144
  59. package/dist/esm/core/phone-auth/client.js +27 -366
  60. package/dist/esm/core/phone-auth/error-utils.d.ts +0 -29
  61. package/dist/esm/core/phone-auth/error-utils.js +1 -83
  62. package/dist/esm/core/phone-auth/index.js +1 -3
  63. package/dist/esm/core/phone-auth/status-types.d.ts +0 -78
  64. package/dist/esm/core/phone-auth/status-types.js +0 -17
  65. package/dist/esm/core/phone-auth/strategies/desktop.d.ts +0 -63
  66. package/dist/esm/core/phone-auth/strategies/desktop.js +8 -126
  67. package/dist/esm/core/phone-auth/strategies/index.d.ts +0 -4
  68. package/dist/esm/core/phone-auth/strategies/index.js +0 -4
  69. package/dist/esm/core/phone-auth/strategies/link.d.ts +0 -48
  70. package/dist/esm/core/phone-auth/strategies/link.js +10 -88
  71. package/dist/esm/core/phone-auth/strategies/ts43.d.ts +0 -19
  72. package/dist/esm/core/phone-auth/strategies/ts43.js +2 -33
  73. package/dist/esm/core/phone-auth/strategies/types.d.ts +0 -13
  74. package/dist/esm/core/phone-auth/strategies/types.js +0 -4
  75. package/dist/esm/core/phone-auth/type-guards.d.ts +0 -128
  76. package/dist/esm/core/phone-auth/type-guards.js +0 -131
  77. package/dist/esm/core/phone-auth/types.d.ts +0 -108
  78. package/dist/esm/core/phone-auth/types.js +0 -32
  79. package/dist/esm/core/phone-auth/ui/mobile-debug-console.d.ts +0 -4
  80. package/dist/esm/core/phone-auth/ui/mobile-debug-console.js +2 -28
  81. package/dist/esm/core/phone-auth/ui/modal.d.ts +27 -68
  82. package/dist/esm/core/phone-auth/ui/modal.js +889 -422
  83. package/dist/esm/core/phone-auth/validation-utils.d.ts +0 -31
  84. package/dist/esm/core/phone-auth/validation-utils.js +2 -40
  85. package/dist/esm/core/types.d.ts +0 -35
  86. package/dist/esm/core/version.js +1 -2
  87. package/dist/esm/index.js +1 -9
  88. package/dist/index.js +0 -7
  89. package/package.json +1 -1
@@ -1,55 +1,40 @@
1
1
  "use strict";
2
- /**
3
- * Modal UI Component for Phone Authentication
4
- *
5
- * This file creates the UI components (modals, buttons) that are shown
6
- * when the SDK is NOT in headless mode. Think of it like a popup window
7
- * that handles the authentication flow for you.
8
- */
9
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
10
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
11
- return new (P || (P = Promise))(function (resolve, reject) {
12
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
13
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
14
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
15
- step((generator = generator.apply(thisArg, _arguments || [])).next());
16
- });
17
- };
18
2
  Object.defineProperty(exports, "__esModule", { value: true });
19
3
  exports.AuthModal = void 0;
20
- /**
21
- * Creates and manages a modal dialog for authentication
22
- *
23
- * @example
24
- * const modal = new AuthModal({
25
- * title: "Verify Your Phone",
26
- * description: "Complete authentication to continue"
27
- * });
28
- * modal.show();
29
- */
30
4
  class AuthModal {
31
5
  constructor(options, callbacks) {
32
6
  this.container = null;
33
7
  this.backdrop = null;
34
8
  this.isOpen = false;
9
+ this.currentStep = 'os-choice';
10
+ this.qrCodeData = null;
11
+ this.statusMessage = '';
12
+ this.originalBodyOverflow = '';
13
+ this.isClosing = false;
14
+ this.iconApple = `<svg class="glide-icon glide-icon-os" viewBox="0 0 384 512" fill="currentColor"><path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 52.3-11.4 69.5-34.3z"/></svg>`;
15
+ this.iconAndroid = `<svg class="glide-icon glide-icon-os" viewBox="0 0 576 512" fill="currentColor"><path d="M420.55,301.93a24,24,0,1,1,24-24,24,24,0,0,1-24,24m-265.1,0a24,24,0,1,1,24-24,24,24,0,0,1-24,24m273.7-144.48,47.94-83a10,10,0,1,0-17.27-10h0l-48.54,84.07a301.25,301.25,0,0,0-246.56,0L116.18,64.45a10,10,0,1,0-17.27,10h0l47.94,83C64.53,202.22,8.24,285.55,0,384H576c-8.24-98.45-64.54-181.78-146.85-226.55"/></svg>`;
16
+ this.iconBack = `<svg class="glide-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>`;
35
17
  this.options = options || {};
36
18
  this.callbacks = callbacks || {};
37
- // Bind escape key handler
19
+ this.theme = this.options.theme || 'auto';
38
20
  this.handleEscapeKey = this.handleEscapeKey.bind(this);
39
21
  }
22
+ shouldUseDarkMode() {
23
+ if (this.theme === 'dark')
24
+ return true;
25
+ if (this.theme === 'light')
26
+ return false;
27
+ return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
28
+ }
40
29
  handleEscapeKey(event) {
41
30
  var _a, _b;
42
31
  if (event.key === 'Escape' && this.isOpen) {
43
- // Check if escape closing is enabled (default true)
44
32
  if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.closeOnEscape) !== false) {
45
- (_b = this.closeCallback) === null || _b === void 0 ? void 0 : _b.call(this); // Call the cancellation callback if set
33
+ (_b = this.closeCallback) === null || _b === void 0 ? void 0 : _b.call(this);
46
34
  this.close();
47
35
  }
48
36
  }
49
37
  }
50
- /**
51
- * Escape HTML to prevent XSS attacks
52
- */
53
38
  escapeHtml(unsafe) {
54
39
  return unsafe
55
40
  .replace(/&/g, "&amp;")
@@ -58,541 +43,1023 @@ class AuthModal {
58
43
  .replace(/"/g, "&quot;")
59
44
  .replace(/'/g, "&#039;");
60
45
  }
61
- /**
62
- * Shows the modal with a QR code for desktop authentication
63
- * Supports both single QR code (legacy) and dual-platform QR codes (iOS + Android)
64
- */
65
- showQRCode(qrCodeData, statusMessage = 'Scan QR code with your phone') {
66
- console.log('[Modal] showQRCode called with:', qrCodeData);
67
- // If modal is already open, don't recreate it
68
- if (this.isOpen) {
69
- console.log('[Modal] Modal already open, skipping recreation');
70
- return;
46
+ showQRCode(qrCodeData, statusMessage = 'Scan with your phone camera') {
47
+ this.qrCodeData = qrCodeData;
48
+ this.statusMessage = statusMessage;
49
+ const mode = this.options.viewMode || 'toggle';
50
+ if (typeof qrCodeData === 'string') {
51
+ this.renderToggleMode(qrCodeData, statusMessage);
71
52
  }
72
- // Check if it's the new dual-platform format with VALID Android QR code
73
- if (typeof qrCodeData === 'object' && qrCodeData.iosQRCode) {
74
- const hasValidAndroidQR = qrCodeData.androidQRCode &&
75
- qrCodeData.androidQRCode.length > 0 &&
76
- qrCodeData.androidQRCode !== qrCodeData.iosQRCode;
77
- console.log('[Modal] Has valid Android QR?', hasValidAndroidQR);
78
- if (hasValidAndroidQR) {
79
- console.log('[Modal] Using dual-platform modal');
80
- this.createDualPlatformQRModal(qrCodeData, statusMessage);
81
- // Note: createDualPlatformQRModal calls show() internally
82
- return;
53
+ else {
54
+ switch (mode) {
55
+ case 'dual':
56
+ this.renderDualMode(qrCodeData, statusMessage);
57
+ break;
58
+ case 'pre-step':
59
+ this.renderPreStepMode(qrCodeData, statusMessage);
60
+ break;
61
+ case 'toggle':
62
+ default:
63
+ this.renderToggleMode(qrCodeData, statusMessage);
64
+ break;
83
65
  }
84
- else {
85
- console.log('[Modal] Android QR missing/empty, using single iOS QR');
86
- // Only iOS QR code available - show single QR
87
- this.createModal(`
88
- <div class="glide-auth-qr-container">
89
- <img src="${this.escapeHtml(qrCodeData.iosQRCode)}" alt="QR Code" class="glide-auth-qr-code" />
90
- <p class="glide-auth-status">Scan with your iPhone to authenticate</p>
91
- </div>
92
- `);
66
+ }
67
+ this.show();
68
+ }
69
+ updateStatus(status, isError = false) {
70
+ this.statusMessage = status;
71
+ if (this.container) {
72
+ const el = this.container.querySelector('#glide-status');
73
+ if (el) {
74
+ el.textContent = status;
75
+ if (isError) {
76
+ el.style.color = '#ff3b30';
77
+ }
78
+ else {
79
+ el.style.color = '';
80
+ }
93
81
  }
94
82
  }
83
+ }
84
+ setCloseCallback(callback) {
85
+ this.closeCallback = callback;
86
+ }
87
+ renderToggleMode(data, msg) {
88
+ let iosQR = '';
89
+ let androidQR = '';
90
+ if (typeof data === 'string') {
91
+ iosQR = data;
92
+ androidQR = data;
93
+ }
95
94
  else {
96
- console.log('[Modal] Using legacy single QR code modal');
97
- // Legacy single QR code
98
- this.createModal(`
99
- <div class="glide-auth-qr-container">
100
- <img src="${this.escapeHtml(qrCodeData)}" alt="QR Code" class="glide-auth-qr-code" />
101
- <p class="glide-auth-status">${this.escapeHtml(statusMessage)}</p>
102
- </div>
103
- `);
95
+ iosQR = data.iosQRCode;
96
+ androidQR = data.androidQRCode || data.iosQRCode;
104
97
  }
105
- this.show();
98
+ const content = `
99
+ <div class="glide-content">
100
+ <div class="glide-toggle" id="glide-toggle" data-active="ios">
101
+ <div class="glide-toggle-slider"></div>
102
+ <button class="glide-btn glide-toggle-btn active" data-platform="ios">${this.iconApple}<span>iOS</span></button>
103
+ <button class="glide-btn glide-toggle-btn" data-platform="android">${this.iconAndroid}<span>Android</span></button>
104
+ </div>
105
+
106
+ <div class="glide-qr-area">
107
+ <img
108
+ class="glide-qr-img"
109
+ id="glide-qr-img"
110
+ src="${this.escapeHtml(iosQR)}"
111
+ alt="QR Code"
112
+ class="glide-qr-img"
113
+ data-ios="${this.escapeHtml(iosQR)}"
114
+ data-android="${this.escapeHtml(androidQR)}"
115
+ />
116
+
117
+ <!-- Animation Overlay (Inside QR Area for centering) -->
118
+ <div id="glide-phone-overlay" class="glide-phone-animation-overlay">
119
+ <div class="glide-outlined-phone">
120
+ <div class="glide-scan-line"></div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ `;
126
+ this.createModal(content, '', true);
127
+ this.setupPlatformToggles();
128
+ this.setupHelpInteraction();
106
129
  }
107
- /**
108
- * Creates a modal with iOS/Android platform toggle
109
- */
110
- createDualPlatformQRModal(qrCodeData, statusMessage) {
130
+ renderDualMode(data, msg) {
131
+ const qrOverlayHtml = `
132
+ <div class="glide-phone-animation-overlay">
133
+ <div class="glide-outlined-phone">
134
+ <div class="glide-scan-line"></div>
135
+ </div>
136
+ </div>
137
+ `;
111
138
  this.createModal(`
112
- <div class="glide-auth-qr-container">
113
- <!-- Platform Switcher -->
114
- <div class="glide-platform-switcher">
115
- <button class="glide-platform-btn glide-platform-ios active" data-platform="ios">
116
- <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
117
- <path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
118
- </svg>
119
- <span>iOS</span>
120
- </button>
121
- <button class="glide-platform-btn glide-platform-android" data-platform="android">
122
- <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
123
- <path d="M17.6 9.48l1.84-3.18c.16-.31.04-.69-.26-.85-.29-.15-.65-.06-.83.22l-1.88 3.24c-2.86-1.21-6.08-1.21-8.94 0L5.65 5.67c-.19-.28-.54-.38-.83-.22-.3.16-.42.54-.26.85l1.84 3.18C4.08 11.08 2.4 13.97 2.4 17.2h19.2c0-3.23-1.68-6.12-4.0-7.72zM7.0 14.8c-.66 0-1.2-.54-1.2-1.2s.54-1.2 1.2-1.2 1.2.54 1.2 1.2-.54 1.2-1.2 1.2zm10 0c-.66 0-1.2-.54-1.2-1.2s.54-1.2 1.2-1.2 1.2.54 1.2 1.2-.54 1.2-1.2 1.2z"/>
124
- </svg>
125
- <span>Android</span>
126
- </button>
139
+ <div class="glide-content glide-dual-mode">
140
+ <div class="glide-dual-container">
141
+ <div class="glide-dual-item">
142
+ <div class="glide-os-logo">
143
+ ${this.iconApple}
144
+ <span>iOS</span>
145
+ </div>
146
+ <div class="glide-qr-area">
147
+ <img src="${this.escapeHtml(data.iosQRCode)}" alt="iOS QR" class="glide-qr-img" />
148
+ ${qrOverlayHtml}
149
+ </div>
150
+ </div>
151
+ <div class="glide-dual-separator">
152
+ <div class="glide-separator-line"></div>
153
+ <span class="glide-separator-text">or</span>
154
+ <div class="glide-separator-line"></div>
155
+ </div>
156
+ <div class="glide-dual-item">
157
+ <div class="glide-os-logo">
158
+ ${this.iconAndroid}
159
+ <span>Android</span>
160
+ </div>
161
+ <div class="glide-qr-area">
162
+ <img src="${this.escapeHtml(data.androidQRCode || data.iosQRCode)}" alt="Android QR" class="glide-qr-img" />
163
+ ${qrOverlayHtml}
164
+ </div>
165
+ </div>
127
166
  </div>
128
-
129
- <!-- QR Code Image -->
130
- <img
131
- id="glide-qr-code-img"
132
- src="${this.escapeHtml(qrCodeData.iosQRCode)}"
133
- alt="QR Code"
134
- class="glide-auth-qr-code"
135
- data-ios="${this.escapeHtml(qrCodeData.iosQRCode)}"
136
- data-android="${this.escapeHtml(qrCodeData.androidQRCode || '')}"
137
- />
138
-
139
- <!-- Status Message -->
140
- <p class="glide-auth-status" id="glide-platform-message">Scan with your iPhone to authenticate</p>
141
167
  </div>
142
- `);
143
- // IMPORTANT: Call show() to actually display the modal!
168
+ `, 'glide-modal-wide', true);
169
+ this.setupHelpInteraction();
170
+ }
171
+ renderPreStepMode(data, msg) {
172
+ this.currentStep = 'os-choice';
173
+ const contentHtml = `
174
+ <div class="glide-pre-step-container">
175
+ <button class="glide-os-choice-btn" id="glide-btn-ios">
176
+ ${this.iconApple}
177
+ <span>iOS</span>
178
+ </button>
179
+ <button class="glide-os-choice-btn" id="glide-btn-android">
180
+ ${this.iconAndroid}
181
+ <span>Android</span>
182
+ </button>
183
+ </div>
184
+ `;
185
+ this.createModal(contentHtml, '', false);
186
+ this.setupPreStepListeners();
144
187
  this.show();
145
188
  }
146
- /**
147
- * Sets a callback to be called when the modal is cancelled/closed
148
- */
149
- setCloseCallback(callback) {
150
- this.closeCallback = callback;
189
+ setupPreStepListeners() {
190
+ var _a, _b;
191
+ if (!this.container)
192
+ return;
193
+ (_a = this.container.querySelector('#glide-btn-ios')) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => {
194
+ this.currentStep = 'ios-qr';
195
+ this.updatePreStepUI();
196
+ });
197
+ (_b = this.container.querySelector('#glide-btn-android')) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => {
198
+ this.currentStep = 'android-qr';
199
+ this.updatePreStepUI();
200
+ });
151
201
  }
152
- /**
153
- * Shows the modal with a button for Link authentication (App Clips)
154
- * IMPORTANT: The button click is required for iOS to recognize the app link
155
- */
156
- showLinkButton(url, buttonText) {
157
- return new Promise((resolve) => {
158
- var _a, _b, _c;
159
- const text = buttonText || ((_a = this.options) === null || _a === void 0 ? void 0 : _a.buttonText) || 'Open Verification App';
160
- this.createModal(`
161
- <div class="glide-auth-link-container">
162
- <p class="glide-auth-description">
163
- ${((_b = this.options) === null || _b === void 0 ? void 0 : _b.description) || 'Click below to verify your phone number'}
164
- </p>
165
- <button class="glide-auth-button" id="glide-auth-link-button">
166
- ${text}
202
+ updatePreStepUI() {
203
+ var _a, _b, _c;
204
+ if (!this.container)
205
+ return;
206
+ let contentHtml = '';
207
+ if (this.currentStep === 'os-choice') {
208
+ contentHtml = `
209
+ <div class="glide-pre-step-container">
210
+ <button class="glide-os-choice-btn" id="glide-btn-ios">
211
+ ${this.iconApple}
212
+ <span>iOS</span>
213
+ </button>
214
+ <button class="glide-os-choice-btn" id="glide-btn-android">
215
+ ${this.iconAndroid}
216
+ <span>Android</span>
167
217
  </button>
168
- <p class="glide-auth-helper-text">
169
- You'll be redirected to complete verification
170
- </p>
171
218
  </div>
172
- `);
173
- // Show modal first so elements are in the DOM
174
- this.show();
175
- // Add click handler for the button AFTER modal is in DOM
176
- // CRITICAL: This click event is required for iOS to recognize the app link
177
- // Do NOT call window.open automatically - it must be triggered by user interaction
178
- const button = (_c = this.container) === null || _c === void 0 ? void 0 : _c.querySelector('#glide-auth-link-button');
179
- if (button) {
180
- button.addEventListener('click', (event) => {
181
- var _a, _b;
182
- (_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onAuthStart) === null || _b === void 0 ? void 0 : _b.call(_a);
183
- // User-initiated click is required for app links to work properly on iOS
184
- // This ensures the OS recognizes it should open an app, not just a browser tab
185
- window.open(url, '_blank');
186
- resolve();
187
- });
188
- }
189
- else {
190
- console.error('[Modal] Link button not found in modal');
191
- }
219
+ `;
220
+ this.createModal(contentHtml, '', false);
221
+ this.setupPreStepListeners();
222
+ }
223
+ else if (this.currentStep === 'ios-qr') {
224
+ const qr = (typeof this.qrCodeData === 'object' && ((_a = this.qrCodeData) === null || _a === void 0 ? void 0 : _a.iosQRCode))
225
+ ? this.qrCodeData.iosQRCode
226
+ : this.qrCodeData;
227
+ contentHtml = `
228
+ <div class="glide-content">
229
+ <div class="glide-qr-area">
230
+ <img src="${this.escapeHtml(qr)}" alt="QR Code" class="glide-qr-img" />
231
+
232
+ <!-- Animation Overlay -->
233
+ <div id="glide-phone-overlay" class="glide-phone-animation-overlay">
234
+ <div class="glide-outlined-phone">
235
+ <div class="glide-scan-line"></div>
236
+ </div>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ `;
241
+ this.createModal(contentHtml, '', true, true);
242
+ this.setupBackButton();
243
+ this.setupHelpInteraction();
244
+ }
245
+ else if (this.currentStep === 'android-qr') {
246
+ const qr = (typeof this.qrCodeData === 'object' && ((_b = this.qrCodeData) === null || _b === void 0 ? void 0 : _b.androidQRCode))
247
+ ? this.qrCodeData.androidQRCode
248
+ : (typeof this.qrCodeData === 'object' && ((_c = this.qrCodeData) === null || _c === void 0 ? void 0 : _c.iosQRCode))
249
+ ? this.qrCodeData.iosQRCode
250
+ : this.qrCodeData;
251
+ contentHtml = `
252
+ <div class="glide-content">
253
+ <div class="glide-qr-area">
254
+ <img src="${this.escapeHtml(qr)}" alt="QR Code" class="glide-qr-img" />
255
+
256
+ <!-- Animation Overlay -->
257
+ <div id="glide-phone-overlay" class="glide-phone-animation-overlay">
258
+ <div class="glide-outlined-phone">
259
+ <div class="glide-scan-line"></div>
260
+ </div>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ `;
265
+ this.createModal(contentHtml, '', true, true);
266
+ this.setupBackButton();
267
+ this.setupHelpInteraction();
268
+ }
269
+ }
270
+ setupBackButton() {
271
+ var _a;
272
+ if (!this.container)
273
+ return;
274
+ (_a = this.container.querySelector('#glide-back-btn')) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => {
275
+ this.currentStep = 'os-choice';
276
+ this.updatePreStepUI();
192
277
  });
193
278
  }
194
- /**
195
- * Shows the modal with a button for TS43 authentication
196
- * IMPORTANT: The button click is required for Digital Credentials API (transient activation)
197
- */
198
- showTS43Button(onAuthenticate) {
199
- return new Promise((resolve, reject) => {
200
- var _a, _b, _c;
201
- const text = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.buttonText) || 'Verify with Carrier';
202
- this.createModal(`
203
- <div class="glide-auth-ts43-container">
204
- <p class="glide-auth-description">
205
- ${((_b = this.options) === null || _b === void 0 ? void 0 : _b.description) || 'Verify using your carrier credentials'}
206
- </p>
207
- <button class="glide-auth-button" id="glide-auth-ts43-button">
208
- ${text}
209
- </button>
210
- <p class="glide-auth-helper-text">
211
- Secure verification through your mobile carrier
212
- </p>
213
- </div>
214
- `);
215
- // Show modal first so elements are in the DOM
216
- this.show();
217
- // Add click handler for the button AFTER modal is in DOM
218
- // CRITICAL: Digital Credentials API requires "transient activation" (user interaction)
219
- // Do NOT call onAuthenticate automatically - it must be triggered by user click
220
- const button = (_c = this.container) === null || _c === void 0 ? void 0 : _c.querySelector('#glide-auth-ts43-button');
221
- if (button) {
222
- button.addEventListener('click', (event) => __awaiter(this, void 0, void 0, function* () {
223
- var _a, _b, _c, _d, _e, _f;
224
- (_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onAuthStart) === null || _b === void 0 ? void 0 : _b.call(_a);
225
- button.setAttribute('disabled', 'true');
226
- button.textContent = 'Verifying...';
227
- try {
228
- const result = yield onAuthenticate();
229
- (_d = (_c = this.callbacks) === null || _c === void 0 ? void 0 : _c.onAuthComplete) === null || _d === void 0 ? void 0 : _d.call(_c, result);
230
- resolve(result);
231
- this.close();
232
- }
233
- catch (error) {
234
- (_f = (_e = this.callbacks) === null || _e === void 0 ? void 0 : _e.onError) === null || _f === void 0 ? void 0 : _f.call(_e, error);
235
- button.removeAttribute('disabled');
236
- button.textContent = text;
237
- reject(error);
238
- }
239
- }));
279
+ createModal(content, extraClass = '', includeHelpButton = false, includeBackButton = false) {
280
+ var _a, _b, _c, _d, _e;
281
+ if (this.isClosing) {
282
+ this.isClosing = false;
283
+ this.cleanup();
284
+ }
285
+ if (!this.container) {
286
+ this.backdrop = document.createElement('div');
287
+ this.backdrop.className = 'glide-backdrop';
288
+ this.backdrop.id = 'glide-backdrop';
289
+ this.container = document.createElement('div');
290
+ this.container.className = `glide-modal ${((_a = this.options) === null || _a === void 0 ? void 0 : _a.className) || ''} ${extraClass}`;
291
+ this.container.id = 'glide-modal';
292
+ if (this.shouldUseDarkMode()) {
293
+ this.container.classList.add('dark');
240
294
  }
241
- else {
242
- console.error('[Modal] TS43 button not found in modal');
295
+ if (this.isOpen) {
296
+ document.body.appendChild(this.backdrop);
297
+ document.body.appendChild(this.container);
298
+ }
299
+ }
300
+ else {
301
+ this.container.className = `glide-modal ${((_b = this.options) === null || _b === void 0 ? void 0 : _b.className) || ''} ${extraClass}`;
302
+ if (this.shouldUseDarkMode()) {
303
+ this.container.classList.add('dark');
243
304
  }
244
- });
245
- }
246
- /**
247
- * Updates the status message in the modal
248
- */
249
- updateStatus(message, isError = false) {
250
- var _a;
251
- const statusEl = (_a = this.container) === null || _a === void 0 ? void 0 : _a.querySelector('.glide-auth-status');
252
- if (statusEl) {
253
- statusEl.textContent = message;
254
- statusEl.className = isError ? 'glide-auth-status glide-auth-error' : 'glide-auth-status';
255
305
  }
256
- }
257
- /**
258
- * Creates the modal HTML structure
259
- */
260
- createModal(content) {
261
- var _a, _b, _c;
262
- // Clean up any existing modal
263
- this.cleanup();
264
- // Create backdrop (dark overlay)
265
- this.backdrop = document.createElement('div');
266
- this.backdrop.className = 'glide-auth-backdrop';
267
- this.backdrop.style.cssText = `
268
- position: fixed;
269
- top: 0;
270
- left: 0;
271
- right: 0;
272
- bottom: 0;
273
- background: rgba(0, 0, 0, 0.5);
274
- z-index: 9998;
275
- opacity: 0;
276
- transition: opacity 0.3s ease;
277
- `;
278
- // Create modal container
279
- this.container = document.createElement('div');
280
- this.container.className = `glide-auth-modal ${((_a = this.options) === null || _a === void 0 ? void 0 : _a.className) || ''}`;
281
- this.container.style.cssText = `
282
- position: fixed;
283
- top: 50%;
284
- left: 50%;
285
- transform: translate(-50%, -50%) scale(0.9);
286
- background: white;
287
- border-radius: 12px;
288
- padding: 24px;
289
- max-width: 400px;
290
- width: 90%;
291
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
292
- z-index: 9999;
293
- opacity: 0;
294
- transition: opacity 0.3s ease, transform 0.3s ease;
295
- `;
296
- // Add modal content
297
306
  this.container.innerHTML = `
298
- <div class="glide-auth-header">
299
- ${((_b = this.options) === null || _b === void 0 ? void 0 : _b.showCloseButton) !== false ? `
300
- <button class="glide-auth-close" aria-label="Close">
301
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
302
- <line x1="18" y1="6" x2="6" y2="18"></line>
303
- <line x1="6" y1="6" x2="18" y2="18"></line>
304
- </svg>
305
- </button>
306
- ` : ''}
307
- <h2 class="glide-auth-title">${((_c = this.options) === null || _c === void 0 ? void 0 : _c.title) || 'Phone Verification'}</h2>
308
- </div>
309
- <div class="glide-auth-content">
310
- ${content}
311
- </div>
307
+ ${includeBackButton ? `
308
+ <button class="glide-btn glide-btn-back" id="glide-back-btn" aria-label="Back">
309
+ ${this.iconBack}
310
+ </button>
311
+ ` : ''}
312
+ ${((_c = this.options) === null || _c === void 0 ? void 0 : _c.showCloseButton) !== false ? `
313
+ <button class="glide-btn glide-btn-close" id="glide-close-btn" aria-label="Close">
314
+ <svg class="glide-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
315
+ <line x1="18" y1="6" x2="6" y2="18"></line>
316
+ <line x1="6" y1="6" x2="18" y2="18"></line>
317
+ </svg>
318
+ </button>
319
+ ` : ''}
320
+ <h2 class="glide-title">${((_d = this.options) === null || _d === void 0 ? void 0 : _d.title) || 'Scan to Verify'}</h2>
321
+ <div id="glide-modal-content">${content}</div>
322
+ <p class="glide-status" id="glide-status">${((_e = this.options) === null || _e === void 0 ? void 0 : _e.description) || ''}</p>
323
+ ${includeHelpButton ? `
324
+ <button class="glide-btn glide-btn-help" id="glide-help-btn">?</button>
325
+ ` : ''}
312
326
  `;
313
- // Add styles
314
327
  this.injectStyles();
315
- // Add close button handler
316
- const closeBtn = this.container.querySelector('.glide-auth-close');
328
+ const closeBtn = this.container.querySelector('#glide-close-btn');
317
329
  if (closeBtn) {
318
330
  closeBtn.addEventListener('click', () => {
319
331
  var _a;
320
- (_a = this.closeCallback) === null || _a === void 0 ? void 0 : _a.call(this); // Call cancellation callback if set
332
+ (_a = this.closeCallback) === null || _a === void 0 ? void 0 : _a.call(this);
321
333
  this.close();
322
334
  });
323
335
  }
324
- // Add backdrop click handler
325
- this.backdrop.addEventListener('click', (e) => {
326
- var _a, _b;
327
- // Only close if backdrop itself was clicked and closeOnBackdrop is enabled (default true)
328
- if (e.target === this.backdrop && ((_a = this.options) === null || _a === void 0 ? void 0 : _a.closeOnBackdrop) !== false) {
329
- (_b = this.closeCallback) === null || _b === void 0 ? void 0 : _b.call(this); // Call cancellation callback if set
330
- this.close();
331
- }
332
- });
336
+ if (this.backdrop) {
337
+ this.backdrop.onclick = (e) => {
338
+ var _a, _b;
339
+ if (e.target === this.backdrop && ((_a = this.options) === null || _a === void 0 ? void 0 : _a.closeOnBackdrop) !== false) {
340
+ (_b = this.closeCallback) === null || _b === void 0 ? void 0 : _b.call(this);
341
+ this.close();
342
+ }
343
+ };
344
+ }
345
+ }
346
+ setupHelpInteraction() {
347
+ var _a;
348
+ const trigger = (_a = this.container) === null || _a === void 0 ? void 0 : _a.querySelector('#glide-help-btn');
349
+ if (trigger) {
350
+ trigger.addEventListener('click', () => {
351
+ var _a;
352
+ const overlays = (_a = this.container) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.glide-phone-animation-overlay');
353
+ if (overlays) {
354
+ overlays.forEach(overlay => {
355
+ overlay.classList.remove('playing');
356
+ void overlay.offsetWidth;
357
+ overlay.classList.add('playing');
358
+ setTimeout(() => {
359
+ overlay.classList.remove('playing');
360
+ }, 3000);
361
+ });
362
+ }
363
+ });
364
+ }
333
365
  }
334
- /**
335
- * Injects CSS styles for the modal
336
- */
337
366
  injectStyles() {
338
- if (document.getElementById('glide-auth-styles'))
367
+ if (document.getElementById('glide-modal-styles'))
339
368
  return;
340
369
  const styles = document.createElement('style');
341
- styles.id = 'glide-auth-styles';
370
+ styles.id = 'glide-modal-styles';
342
371
  styles.textContent = `
343
- .glide-auth-backdrop {
344
- backdrop-filter: blur(4px);
372
+ :root {
373
+ --glide-primary: #007AFF;
374
+ --glide-text: #1d1d1f;
375
+ --glide-bg-light: rgba(255, 255, 255, 0.6);
376
+ --glide-bg-dark: rgba(30, 30, 30, 0.6);
377
+ --glide-phone-border: #000000; /* High Contrast Black */
378
+
379
+ /* Button sizes */
380
+ --glide-btn-size: 28px;
381
+ --glide-help-btn-size: 24px;
382
+ --glide-toggle-icon-size: 14px;
383
+ --glide-dual-icon-size: 18px;
384
+ }
385
+
386
+ #glide-backdrop {
387
+ position: fixed;
388
+ top: 0;
389
+ left: 0;
390
+ right: 0;
391
+ bottom: 0;
392
+ background: rgba(0, 0, 0, 0.2);
393
+ backdrop-filter: blur(8px);
394
+ -webkit-backdrop-filter: blur(8px);
395
+ z-index: 9998;
396
+ opacity: 0;
397
+ transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1);
345
398
  }
346
399
 
347
- .glide-auth-modal {
348
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
400
+ #glide-modal {
401
+ position: fixed;
402
+ top: 20px;
403
+ left: 50%;
404
+ transform: translateX(-50%) scale(0.94);
405
+ background: var(--glide-bg-light);
406
+ backdrop-filter: blur(24px) saturate(180%);
407
+ -webkit-backdrop-filter: blur(24px) saturate(180%);
408
+ border-radius: 24px;
409
+ padding: 32px;
410
+ width: 360px;
411
+ max-width: 90%;
412
+ box-shadow:
413
+ 0 20px 40px rgba(0,0,0,0.2),
414
+ 0 0 0 1px rgba(255,255,255,0.6) inset,
415
+ 0 0 0 1px rgba(0,0,0,0.05);
416
+ z-index: 9999;
417
+ opacity: 0;
418
+ transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
419
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
420
+ text-align: center;
421
+ color: var(--glide-text);
422
+ }
423
+
424
+ #glide-modal.glide-modal-wide {
425
+ width: 600px;
426
+ max-width: 95vw;
349
427
  }
350
428
 
351
- .glide-auth-header {
352
- position: relative;
353
- margin-bottom: 20px;
429
+ #glide-modal.dark {
430
+ background: var(--glide-bg-dark);
431
+ color: white;
432
+ box-shadow:
433
+ 0 20px 40px rgba(0,0,0,0.4),
434
+ 0 0 0 1px rgba(255,255,255,0.15) inset,
435
+ 0 0 0 1px rgba(0,0,0,0.5);
436
+ --glide-phone-border: #ffffff; /* High Contrast White */
354
437
  }
355
438
 
356
- .glide-auth-close {
439
+ #glide-modal .glide-btn-close {
357
440
  position: absolute;
358
- top: 0;
359
- right: 0;
360
- background: none;
441
+ top: 16px;
442
+ right: 16px;
443
+ width: var(--glide-btn-size);
444
+ height: var(--glide-btn-size);
445
+ min-width: var(--glide-btn-size);
446
+ min-height: var(--glide-btn-size);
447
+ max-width: var(--glide-btn-size);
448
+ max-height: var(--glide-btn-size);
449
+ background: rgba(118, 118, 128, 0.12);
361
450
  border: none;
451
+ border-radius: 50%;
452
+ display: flex;
453
+ align-items: center;
454
+ justify-content: center;
362
455
  cursor: pointer;
456
+ color: rgba(0,0,0,0.5);
457
+ transition: background 0.2s;
363
458
  padding: 0;
364
- color: #666;
365
- transition: color 0.2s;
459
+ z-index: 20;
460
+ box-sizing: border-box;
461
+ flex-shrink: 0;
366
462
  }
367
463
 
368
- .glide-auth-close:hover {
369
- color: #000;
464
+ #glide-modal.dark .glide-btn-close {
465
+ background: rgba(255, 255, 255, 0.1);
466
+ color: rgba(255,255,255,0.5);
370
467
  }
371
468
 
372
- .glide-auth-title {
373
- margin: 0;
374
- font-size: 24px;
375
- font-weight: 600;
376
- color: #333;
377
- text-align: center;
469
+ #glide-modal .glide-btn-close:hover {
470
+ background: rgba(118, 118, 128, 0.2);
471
+ }
472
+
473
+ #glide-modal.dark .glide-btn-close:hover {
474
+ background: rgba(255, 255, 255, 0.2);
378
475
  }
379
476
 
380
- .glide-auth-content {
477
+ .glide-title {
478
+ margin: 0 0 8px 0;
479
+ font-size: 22px;
480
+ font-weight: 600;
481
+ letter-spacing: -0.01em;
482
+ }
483
+
484
+ .glide-status {
485
+ margin: 12px 0 0 0;
486
+ font-size: 13px;
487
+ color: rgba(0,0,0,0.5);
381
488
  text-align: center;
489
+ min-height: 18px;
490
+ }
491
+
492
+ .glide-status:empty {
493
+ display: none;
494
+ }
495
+
496
+ #glide-modal.dark .glide-status {
497
+ color: rgba(255,255,255,0.5);
498
+ }
499
+
500
+ .glide-content {
501
+ display: flex;
502
+ flex-direction: column;
503
+ align-items: center;
504
+ position: relative;
505
+ }
506
+
507
+ /* --- Sliding Toggle Switch --- */
508
+ #glide-toggle {
509
+ background: rgba(118, 118, 128, 0.12);
510
+ padding: 2px;
511
+ border-radius: 8px;
512
+ display: inline-flex;
513
+ margin-bottom: 24px;
514
+ margin-top: 16px;
515
+ position: relative;
516
+ height: 32px;
517
+ width: 200px;
518
+ box-sizing: border-box;
382
519
  }
383
520
 
384
- .glide-auth-description {
385
- color: #666;
386
- margin: 0 0 20px 0;
387
- font-size: 16px;
388
- line-height: 1.5;
521
+ #glide-modal.dark #glide-toggle {
522
+ background: rgba(118, 118, 128, 0.24);
523
+ }
524
+
525
+ /* The sliding background */
526
+ .glide-toggle-slider {
527
+ position: absolute;
528
+ top: 2px;
529
+ left: 2px;
530
+ width: calc(50% - 2px);
531
+ height: calc(100% - 4px);
532
+ background: white;
533
+ border-radius: 6px;
534
+ box-shadow: 0 3px 8px rgba(0,0,0,0.12), 0 3px 1px rgba(0,0,0,0.04);
535
+ transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
536
+ z-index: 0;
537
+ }
538
+
539
+ #glide-modal.dark .glide-toggle-slider {
540
+ background: #636366;
541
+ }
542
+
543
+ /* Move slider for Android */
544
+ #glide-toggle[data-active="android"] .glide-toggle-slider {
545
+ transform: translateX(100%);
389
546
  }
390
547
 
391
- .glide-auth-button {
392
- background: #007AFF;
393
- color: white;
548
+ .glide-toggle-btn {
549
+ flex: 1;
550
+ background: none;
394
551
  border: none;
395
- border-radius: 8px;
396
- padding: 12px 24px;
397
- font-size: 16px;
552
+ padding: 0;
553
+ margin: 0;
554
+ font-size: 13px;
398
555
  font-weight: 500;
556
+ color: inherit;
399
557
  cursor: pointer;
400
- transition: background 0.2s, transform 0.1s;
401
- min-width: 200px;
558
+ position: relative;
559
+ z-index: 1;
560
+ transition: color 0.2s;
561
+ display: flex;
562
+ align-items: center;
563
+ justify-content: center;
564
+ gap: 6px;
565
+ min-height: auto;
566
+ height: auto;
567
+ min-width: auto;
568
+ border-radius: 0;
402
569
  }
403
-
404
- .glide-auth-button:hover:not(:disabled) {
405
- background: #0051D5;
406
- transform: translateY(-1px);
570
+
571
+ #glide-modal .glide-toggle-btn .glide-icon-os,
572
+ #glide-modal .glide-toggle-btn svg {
573
+ width: var(--glide-toggle-icon-size);
574
+ height: var(--glide-toggle-icon-size);
575
+ flex-shrink: 0;
576
+ margin: 0;
577
+ padding: 0;
407
578
  }
408
-
409
- .glide-auth-button:active {
410
- transform: translateY(0);
579
+
580
+ .glide-toggle-btn span {
581
+ margin: 0;
582
+ padding: 0;
583
+ line-height: 1;
411
584
  }
412
-
413
- .glide-auth-button:disabled {
414
- opacity: 0.6;
415
- cursor: not-allowed;
585
+
586
+ #glide-modal.dark .glide-toggle-btn {
587
+ color: rgba(255,255,255,0.6);
588
+ }
589
+
590
+ #glide-toggle[data-active="ios"] .glide-toggle-btn[data-platform="ios"],
591
+ #glide-toggle[data-active="android"] .glide-toggle-btn[data-platform="android"] {
592
+ color: #1d1d1f;
593
+ }
594
+
595
+ #glide-modal.dark #glide-toggle[data-active="ios"] .glide-toggle-btn[data-platform="ios"],
596
+ #glide-modal.dark #glide-toggle[data-active="android"] .glide-toggle-btn[data-platform="android"] {
597
+ color: white;
416
598
  }
417
599
 
418
- .glide-auth-helper-text {
419
- color: #999;
420
- font-size: 14px;
421
- margin: 16px 0 0 0;
600
+ /* QR Area */
601
+ .glide-qr-area {
602
+ position: relative;
603
+ margin: 0 auto;
422
604
  }
423
605
 
424
- .glide-auth-qr-code {
425
- max-width: 256px;
426
- width: 100%;
427
- height: auto;
428
- margin: 20px auto;
606
+ .glide-qr-img {
607
+ width: 200px;
608
+ height: 200px;
609
+ object-fit: contain;
429
610
  display: block;
611
+ border-radius: 16px;
612
+ }
613
+
614
+ /* Dual Mode QR Area - no extra padding, same as single mode */
615
+ .glide-dual-mode .glide-qr-area {
616
+ background: transparent;
617
+ padding: 0;
618
+ }
619
+
620
+ .glide-dual-mode .glide-qr-img {
621
+ width: 180px;
622
+ height: 180px;
623
+ border-radius: 16px;
624
+ }
625
+
626
+ /* --- Help Icon & Interaction --- */
627
+ .glide-btn-help {
628
+ position: absolute;
629
+ bottom: 20px;
630
+ right: 20px;
631
+ width: var(--glide-help-btn-size);
632
+ height: var(--glide-help-btn-size);
633
+ min-width: var(--glide-help-btn-size);
634
+ min-height: var(--glide-help-btn-size);
635
+ max-width: var(--glide-help-btn-size);
636
+ max-height: var(--glide-help-btn-size);
637
+ border-radius: 50%;
638
+ border: 1.5px solid rgba(0,0,0,0.2);
639
+ color: rgba(0,0,0,0.4);
640
+ display: flex;
641
+ align-items: center;
642
+ justify-content: center;
643
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
644
+ font-size: 14px;
645
+ font-weight: 600;
646
+ line-height: 1;
647
+ cursor: pointer;
648
+ transition: all 0.2s;
649
+ background: transparent;
650
+ padding: 0;
651
+ margin: 0;
652
+ box-sizing: border-box;
653
+ z-index: 10;
654
+ }
655
+
656
+ #glide-modal.dark .glide-btn-help {
657
+ border-color: rgba(255,255,255,0.3);
658
+ color: rgba(255,255,255,0.5);
430
659
  }
431
660
 
432
- .glide-auth-status {
433
- color: #666;
434
- font-size: 16px;
435
- margin: 16px 0;
661
+ .glide-btn-help:hover {
662
+ border-color: var(--glide-primary);
663
+ color: var(--glide-primary);
664
+ background: rgba(0, 122, 255, 0.1);
665
+ }
666
+
667
+ /* Tooltip */
668
+ .glide-btn-help::after {
669
+ content: "Need help?";
670
+ position: absolute;
671
+ bottom: 100%;
672
+ left: 50%;
673
+ transform: translateX(-50%) translateY(-8px);
674
+ background: rgba(0,0,0,0.8);
675
+ color: white;
676
+ padding: 4px 8px;
677
+ border-radius: 4px;
678
+ font-size: 11px;
679
+ white-space: nowrap;
680
+ opacity: 0;
681
+ pointer-events: none;
682
+ transition: opacity 0.2s;
683
+ }
684
+
685
+ .glide-btn-help:hover::after {
686
+ opacity: 1;
436
687
  }
437
688
 
438
- .glide-auth-error {
439
- color: #FF3B30;
689
+ /* --- Outlined Phone Animation --- */
690
+ .glide-phone-animation-overlay {
691
+ position: absolute;
692
+ top: 0;
693
+ left: 0;
694
+ width: 100%;
695
+ height: 100%;
696
+ display: flex;
697
+ align-items: center;
698
+ justify-content: center;
699
+ pointer-events: none;
700
+ opacity: 0;
701
+ transition: opacity 0.3s;
702
+ border-radius: 16px;
703
+ background: rgba(255,255,255,0.8);
704
+ }
705
+
706
+ #glide-modal.dark .glide-phone-animation-overlay {
707
+ background: rgba(0,0,0,0.6);
708
+ }
709
+
710
+ .glide-phone-animation-overlay.playing {
711
+ opacity: 1;
440
712
  }
441
713
 
442
- .glide-auth-qr-container,
443
- .glide-auth-link-container,
444
- .glide-auth-ts43-container {
714
+ .glide-outlined-phone {
715
+ width: 100px;
716
+ height: 180px;
717
+ border: 4px solid var(--glide-phone-border);
718
+ border-radius: 16px;
719
+ position: relative;
720
+ background: transparent;
721
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
722
+ transform: translateY(20px);
723
+ }
724
+
725
+ /* Notch */
726
+ .glide-outlined-phone::before {
727
+ content: '';
728
+ position: absolute;
729
+ top: -1px;
730
+ left: 50%;
731
+ transform: translateX(-50%);
732
+ width: 40%;
733
+ height: 12px;
734
+ background: var(--glide-phone-border);
735
+ border-bottom-left-radius: 8px;
736
+ border-bottom-right-radius: 8px;
737
+ }
738
+
739
+ /* Scanning Line */
740
+ .glide-scan-line {
741
+ position: absolute;
742
+ top: 10%;
743
+ left: 5%;
744
+ width: 90%;
745
+ height: 2px;
746
+ background: var(--glide-primary);
747
+ box-shadow: 0 0 8px var(--glide-primary);
748
+ opacity: 0;
749
+ }
750
+
751
+ /* Animation Keyframes */
752
+ @keyframes glide-scan-motion {
753
+ 0% { top: 10%; opacity: 0; }
754
+ 10% { opacity: 1; }
755
+ 90% { opacity: 1; }
756
+ 100% { top: 90%; opacity: 0; }
757
+ }
758
+
759
+ .glide-phone-animation-overlay.playing .glide-outlined-phone {
760
+ animation: glide-phone-appear 3s ease-in-out forwards;
761
+ }
762
+
763
+ .glide-phone-animation-overlay.playing .glide-scan-line {
764
+ animation: glide-scan-motion 2s ease-in-out 0.5s infinite;
765
+ }
766
+
767
+ @keyframes glide-phone-appear {
768
+ 0% { transform: translateY(20px); opacity: 0; }
769
+ 10% { transform: translateY(0); opacity: 1; }
770
+ 90% { transform: translateY(0); opacity: 1; }
771
+ 100% { transform: translateY(20px); opacity: 0; }
772
+ }
773
+
774
+ /* Dual Mode */
775
+ .glide-dual-container {
776
+ display: flex;
777
+ justify-content: center;
778
+ align-items: stretch;
779
+ gap: 32px;
780
+ margin-top: 20px;
781
+ }
782
+
783
+ .glide-dual-item {
784
+ display: flex;
785
+ flex-direction: column;
786
+ align-items: center;
787
+ }
788
+
789
+ .glide-os-logo {
790
+ display: flex;
791
+ align-items: center;
792
+ gap: 8px;
793
+ font-weight: 600;
794
+ margin-bottom: 12px;
795
+ font-size: 15px;
796
+ color: inherit;
797
+ }
798
+
799
+ /* Dual mode OS logo - aligned icons with text, spacing to QR */
800
+ .glide-dual-mode .glide-os-logo {
801
+ margin-bottom: 12px; /* Space between label and QR code */
802
+ }
803
+
804
+ #glide-modal .glide-dual-mode .glide-os-logo .glide-icon-os,
805
+ #glide-modal .glide-dual-mode .glide-os-logo svg {
806
+ width: var(--glide-dual-icon-size);
807
+ height: var(--glide-dual-icon-size);
808
+ margin: 0;
809
+ padding: 0;
810
+ flex-shrink: 0;
811
+ }
812
+
813
+ .glide-dual-mode .glide-os-logo span {
814
+ margin: 0;
815
+ padding: 0;
816
+ line-height: 1;
817
+ }
818
+
819
+ .glide-os-logo svg {
820
+ width: 20px;
821
+ height: 20px;
822
+ opacity: 0.8;
823
+ }
824
+
825
+ /* Dual Mode Separator - aligned to QR codes only */
826
+ .glide-dual-separator {
827
+ display: flex;
828
+ flex-direction: column;
829
+ align-items: center;
830
+ justify-content: center;
831
+ align-self: stretch;
832
+ /* Offset for OS label (18px icon + 12px margin = ~30px) */
833
+ margin-top: 30px;
445
834
  padding: 20px 0;
446
835
  }
447
-
448
- /* Platform Switcher Styles */
449
- .glide-platform-switcher {
836
+
837
+ .glide-separator-line {
838
+ width: 1px;
839
+ flex: 1;
840
+ background: linear-gradient(to bottom, transparent, rgba(128,128,128,0.3), transparent);
841
+ }
842
+
843
+ .glide-separator-text {
844
+ padding: 8px 0;
845
+ font-size: 11px;
846
+ font-weight: 500;
847
+ color: rgba(128,128,128,0.5);
848
+ text-transform: uppercase;
849
+ letter-spacing: 0.5px;
850
+ }
851
+
852
+ #glide-modal.dark .glide-separator-line {
853
+ background: linear-gradient(to bottom, transparent, rgba(255,255,255,0.2), transparent);
854
+ }
855
+
856
+ #glide-modal.dark .glide-separator-text {
857
+ color: rgba(255,255,255,0.4);
858
+ }
859
+
860
+ /* Pre-Step */
861
+ .glide-pre-step-container {
450
862
  display: flex;
863
+ gap: 20px;
451
864
  justify-content: center;
452
- gap: 10px;
453
- margin-bottom: 20px;
865
+ margin: 24px 0;
454
866
  }
455
-
456
- .glide-platform-btn {
867
+
868
+ .glide-os-choice-btn {
457
869
  display: flex;
870
+ flex-direction: column;
458
871
  align-items: center;
459
- gap: 6px;
460
- padding: 10px 20px;
461
- border: 2px solid;
462
- background: transparent;
463
- border-radius: 8px;
872
+ justify-content: center;
873
+ width: 140px;
874
+ height: 140px;
875
+ border: 1px solid rgba(0,0,0,0.08);
876
+ border-radius: 20px;
877
+ background: rgba(255,255,255,0.4);
464
878
  cursor: pointer;
879
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
880
+ position: relative;
881
+ overflow: hidden;
465
882
  font-size: 14px;
466
- font-weight: 600;
467
- transition: all 0.2s ease;
883
+ color: inherit;
468
884
  }
469
-
470
- .glide-platform-btn svg {
885
+
886
+ #glide-modal.dark .glide-os-choice-btn {
887
+ background: rgba(255,255,255,0.05);
888
+ border-color: rgba(255,255,255,0.1);
889
+ color: white;
890
+ }
891
+
892
+ .glide-os-choice-btn:hover {
893
+ background: rgba(255,255,255,0.9);
894
+ transform: translateY(-4px);
895
+ box-shadow: 0 12px 24px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.04);
896
+ border-color: var(--glide-primary);
897
+ }
898
+
899
+ #glide-modal.dark .glide-os-choice-btn:hover {
900
+ background: rgba(255,255,255,0.15);
901
+ box-shadow: 0 12px 24px rgba(0,0,0,0.3);
902
+ border-color: var(--glide-primary);
903
+ }
904
+
905
+ .glide-icon-os {
906
+ width: 40px;
907
+ height: 40px;
908
+ margin-bottom: 12px;
909
+ transition: transform 0.3s;
910
+ fill: currentColor;
911
+ }
912
+
913
+ .glide-os-choice-btn:hover .glide-icon-os {
914
+ transform: scale(1.1);
915
+ }
916
+
917
+ #glide-modal .glide-btn-back {
918
+ position: absolute;
919
+ top: 16px;
920
+ left: 16px;
921
+ width: var(--glide-btn-size);
922
+ height: var(--glide-btn-size);
923
+ min-width: var(--glide-btn-size);
924
+ min-height: var(--glide-btn-size);
925
+ max-width: var(--glide-btn-size);
926
+ max-height: var(--glide-btn-size);
927
+ background: rgba(118, 118, 128, 0.12);
928
+ border: none;
929
+ border-radius: 50%;
930
+ display: flex;
931
+ align-items: center;
932
+ justify-content: center;
933
+ cursor: pointer;
934
+ color: rgba(0,0,0,0.5);
935
+ transition: background 0.2s;
936
+ z-index: 20;
937
+ padding: 0;
938
+ margin: 0;
939
+ box-sizing: border-box;
471
940
  flex-shrink: 0;
472
941
  }
473
-
474
- .glide-platform-ios {
475
- border-color: #007AFF;
476
- color: #007AFF;
942
+
943
+ #glide-modal.dark .glide-btn-back {
944
+ background: rgba(255, 255, 255, 0.1);
945
+ color: rgba(255,255,255,0.5);
477
946
  }
478
947
 
479
- .glide-platform-ios.active {
480
- background: #007AFF;
481
- color: white;
948
+ .glide-btn-back:hover {
949
+ background: rgba(118, 118, 128, 0.2);
482
950
  }
483
-
484
- .glide-platform-ios:hover:not(.active) {
485
- background: rgba(0, 122, 255, 0.1);
951
+
952
+ #glide-modal.dark .glide-btn-back:hover {
953
+ background: rgba(255, 255, 255, 0.2);
486
954
  }
487
-
488
- .glide-platform-android {
489
- border-color: #3DDC84;
490
- color: #3DDC84;
955
+
956
+ .glide-spinner {
957
+ width: 40px;
958
+ height: 40px;
959
+ border: 3px solid rgba(0,0,0,0.1);
960
+ border-top-color: var(--glide-primary);
961
+ border-radius: 50%;
962
+ animation: glide-spin 1s linear infinite;
963
+ margin: 40px auto;
491
964
  }
492
-
493
- .glide-platform-android.active {
494
- background: #3DDC84;
495
- color: white;
965
+
966
+ #glide-modal.dark .glide-spinner {
967
+ border-color: rgba(255,255,255,0.1);
968
+ border-top-color: var(--glide-primary);
496
969
  }
497
-
498
- .glide-platform-android:hover:not(.active) {
499
- background: rgba(61, 220, 132, 0.1);
970
+
971
+ @keyframes glide-spin {
972
+ to { transform: rotate(360deg); }
500
973
  }
501
974
  `;
502
975
  document.head.appendChild(styles);
503
976
  }
504
- /**
505
- * Shows the modal with animation
506
- */
507
977
  show() {
508
978
  var _a, _b;
509
- if (!this.container || !this.backdrop || this.isOpen)
979
+ if (!this.container || !this.backdrop || this.isOpen) {
980
+ if (this.isOpen && this.container && this.backdrop) {
981
+ document.removeEventListener('keydown', this.handleEscapeKey);
982
+ document.addEventListener('keydown', this.handleEscapeKey);
983
+ }
510
984
  return;
985
+ }
511
986
  document.body.appendChild(this.backdrop);
512
987
  document.body.appendChild(this.container);
513
- // Add escape key listener for closing modal
988
+ this.lockBodyScroll();
514
989
  document.addEventListener('keydown', this.handleEscapeKey);
515
- // Setup platform toggle handlers if they exist
516
- this.setupPlatformToggles();
517
- // Trigger animation
518
990
  requestAnimationFrame(() => {
519
991
  if (this.backdrop && this.container) {
520
992
  this.backdrop.style.opacity = '1';
521
993
  this.container.style.opacity = '1';
522
- this.container.style.transform = 'translate(-50%, -50%) scale(1)';
994
+ this.container.style.transform = 'translateX(-50%) scale(1)';
523
995
  }
524
996
  });
525
997
  this.isOpen = true;
526
998
  (_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onOpen) === null || _b === void 0 ? void 0 : _b.call(_a);
527
999
  }
528
- /**
529
- * Setup click handlers for iOS/Android platform toggle
530
- */
531
1000
  setupPlatformToggles() {
532
- var _a, _b, _c;
533
- const platformBtns = (_a = this.container) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.glide-platform-btn');
534
- const qrImg = (_b = this.container) === null || _b === void 0 ? void 0 : _b.querySelector('#glide-qr-code-img');
535
- const message = (_c = this.container) === null || _c === void 0 ? void 0 : _c.querySelector('#glide-platform-message');
1001
+ var _a, _b, _c, _d;
1002
+ const container = (_a = this.container) === null || _a === void 0 ? void 0 : _a.querySelector('#glide-toggle');
1003
+ const platformBtns = (_b = this.container) === null || _b === void 0 ? void 0 : _b.querySelectorAll('.glide-toggle-btn');
1004
+ const qrImg = (_c = this.container) === null || _c === void 0 ? void 0 : _c.querySelector('#glide-qr-img');
1005
+ const message = (_d = this.container) === null || _d === void 0 ? void 0 : _d.querySelector('#glide-platform-message');
536
1006
  if (!platformBtns || !qrImg)
537
1007
  return;
538
1008
  platformBtns.forEach((btn) => {
539
1009
  btn.addEventListener('click', (e) => {
540
1010
  const target = e.currentTarget;
541
1011
  const platform = target.getAttribute('data-platform');
542
- // Update active state
1012
+ if (container && platform) {
1013
+ container.setAttribute('data-active', platform);
1014
+ }
543
1015
  platformBtns.forEach(b => b.classList.remove('active'));
544
1016
  target.classList.add('active');
545
- // Switch QR code
546
1017
  if (platform === 'ios') {
547
1018
  qrImg.src = qrImg.getAttribute('data-ios') || '';
548
1019
  if (message)
549
- message.textContent = 'Scan with your iPhone to authenticate';
1020
+ message.textContent = 'Scan with your iPhone camera';
550
1021
  }
551
1022
  else if (platform === 'android') {
552
1023
  qrImg.src = qrImg.getAttribute('data-android') || '';
553
1024
  if (message)
554
- message.textContent = 'Scan with your Android device to authenticate';
1025
+ message.textContent = 'Scan with your Android camera';
555
1026
  }
556
1027
  });
557
1028
  });
558
1029
  }
559
- /**
560
- * Closes the modal with animation
561
- */
1030
+ lockBodyScroll() {
1031
+ this.originalBodyOverflow = document.body.style.overflow;
1032
+ document.body.style.overflow = 'hidden';
1033
+ }
1034
+ unlockBodyScroll() {
1035
+ document.body.style.overflow = this.originalBodyOverflow;
1036
+ }
562
1037
  close() {
563
1038
  if (!this.container || !this.backdrop || !this.isOpen)
564
1039
  return;
565
- // Remove escape key listener
1040
+ this.isClosing = true;
566
1041
  document.removeEventListener('keydown', this.handleEscapeKey);
567
- // Animate out
568
1042
  this.backdrop.style.opacity = '0';
569
1043
  this.container.style.opacity = '0';
570
- this.container.style.transform = 'translate(-50%, -50%) scale(0.9)';
571
- // Clear any stored callbacks
1044
+ this.container.style.transform = 'translateX(-50%) scale(0.94)';
572
1045
  this.closeCallback = undefined;
573
- // Remove after animation
574
1046
  setTimeout(() => {
575
1047
  var _a, _b;
576
- this.cleanup();
577
- this.isOpen = false;
578
- (_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onClose) === null || _b === void 0 ? void 0 : _b.call(_a);
579
- }, 300);
1048
+ if (this.isClosing) {
1049
+ this.cleanup();
1050
+ this.isOpen = false;
1051
+ this.isClosing = false;
1052
+ (_b = (_a = this.callbacks) === null || _a === void 0 ? void 0 : _a.onClose) === null || _b === void 0 ? void 0 : _b.call(_a);
1053
+ }
1054
+ }, 400);
580
1055
  }
581
- /**
582
- * Removes modal elements from DOM
583
- */
584
1056
  cleanup() {
585
1057
  var _a, _b;
586
1058
  (_a = this.container) === null || _a === void 0 ? void 0 : _a.remove();
587
1059
  (_b = this.backdrop) === null || _b === void 0 ? void 0 : _b.remove();
588
1060
  this.container = null;
589
1061
  this.backdrop = null;
590
- }
591
- /**
592
- * Check if modal is currently open
593
- */
594
- isModalOpen() {
595
- return this.isOpen;
1062
+ this.unlockBodyScroll();
596
1063
  }
597
1064
  }
598
1065
  exports.AuthModal = AuthModal;