@incodetech/web 2.0.0-alpha.1
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.
- package/dev/README.md +163 -0
- package/dev/getToken.ts +36 -0
- package/dev/headless.html +875 -0
- package/dev/index.html +366 -0
- package/dev/main-headless.tsx +1332 -0
- package/dev/main-orchestrated-flow.tsx +1158 -0
- package/dev/main-preact.tsx +323 -0
- package/dev/main-simplified.tsx +123 -0
- package/dev/main-web-component.tsx +256 -0
- package/dev/main.tsx +332 -0
- package/dev/manual.html +27 -0
- package/dev/orchestrated-flow.html +64 -0
- package/dev/simplified.html +64 -0
- package/dev/tiktok-logo.svg +7 -0
- package/package.json +85 -0
- package/src/defineCustomElement.tsx +30 -0
- package/src/email/email.test.tsx +368 -0
- package/src/email/email.tsx +255 -0
- package/src/email/emailInput.test.tsx +264 -0
- package/src/email/emailInput.tsx +85 -0
- package/src/email/styles.css +59 -0
- package/src/flow/flow.test.tsx +796 -0
- package/src/flow/flow.tsx +292 -0
- package/src/flow/flowCompleted.css +30 -0
- package/src/flow/flowCompleted.test.tsx +331 -0
- package/src/flow/flowCompleted.tsx +121 -0
- package/src/flow/flowInit.test.ts +264 -0
- package/src/flow/flowInit.ts +94 -0
- package/src/flow/flowStart.css +58 -0
- package/src/flow/flowStart.test.tsx +49 -0
- package/src/flow/flowStart.tsx +41 -0
- package/src/flow/incode-logo.svg +8 -0
- package/src/flow/index.ts +7 -0
- package/src/flow/preloadFlow.test.ts +421 -0
- package/src/flow/preloadFlow.ts +171 -0
- package/src/flow/styles.css +9 -0
- package/src/flow/unsupportedModule.css +21 -0
- package/src/flow/unsupportedModule.tsx +39 -0
- package/src/flow/useFlowInitialization.test.tsx +292 -0
- package/src/flow/useFlowInitialization.ts +128 -0
- package/src/flow/useModuleLoader.test.tsx +212 -0
- package/src/flow/useModuleLoader.ts +92 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useManager.test.ts +91 -0
- package/src/hooks/useManager.ts +40 -0
- package/src/i18n/index.ts +3 -0
- package/src/i18n/instance.ts +16 -0
- package/src/i18n/setup.ts +184 -0
- package/src/i18n/useTranslation.ts +42 -0
- package/src/index.ts +27 -0
- package/src/permissions/assets/android-dots-icon.svg +7 -0
- package/src/permissions/assets/android-settings-icon.svg +16 -0
- package/src/permissions/assets/android-toggle-icon.svg +20 -0
- package/src/permissions/assets/bank-card-icon.svg +14 -0
- package/src/permissions/assets/camera-icon.svg +12 -0
- package/src/permissions/assets/camera-ios.svg +53 -0
- package/src/permissions/assets/check-icon.svg +8 -0
- package/src/permissions/assets/chrome-icon.svg +43 -0
- package/src/permissions/assets/password-icon.svg +11 -0
- package/src/permissions/assets/permissions-img.svg +51 -0
- package/src/permissions/assets/safari-icon.svg +37 -0
- package/src/permissions/assets/settings-icon.svg +33 -0
- package/src/permissions/assets/toggle-icon.svg +19 -0
- package/src/permissions/assets/warning-icon.svg +6 -0
- package/src/permissions/boldWithArrow.css +9 -0
- package/src/permissions/boldWithArrow.tsx +41 -0
- package/src/permissions/denied.css +37 -0
- package/src/permissions/denied.tsx +29 -0
- package/src/permissions/deniedAndroid.tsx +56 -0
- package/src/permissions/deniedDesktop.css +9 -0
- package/src/permissions/deniedDesktop.tsx +64 -0
- package/src/permissions/deniedIOS.tsx +73 -0
- package/src/permissions/deniedInstructions.tsx +19 -0
- package/src/permissions/iconWrapper.css +9 -0
- package/src/permissions/iconWrapper.tsx +15 -0
- package/src/permissions/learnMore.css +37 -0
- package/src/permissions/learnMore.tsx +85 -0
- package/src/permissions/numberedStep.css +13 -0
- package/src/permissions/numberedStep.tsx +14 -0
- package/src/permissions/permissions.css +13 -0
- package/src/permissions/permissions.tsx +68 -0
- package/src/phone/phone.tsx +246 -0
- package/src/phone/phoneInput.test.tsx +275 -0
- package/src/phone/phoneInput.tsx +249 -0
- package/src/phone/styles.css +158 -0
- package/src/selfie/cameraButton.css +13 -0
- package/src/selfie/cameraButton.tsx +35 -0
- package/src/selfie/capture.css +57 -0
- package/src/selfie/capture.tsx +232 -0
- package/src/selfie/errorModal.tsx +218 -0
- package/src/selfie/errorModalContent.css +33 -0
- package/src/selfie/errorModalContent.tsx +44 -0
- package/src/selfie/faceOutline.css +5 -0
- package/src/selfie/faceOutline.tsx +22 -0
- package/src/selfie/loadingBorder.css +12 -0
- package/src/selfie/loadingBorder.tsx +77 -0
- package/src/selfie/manualCaptureButton.css +13 -0
- package/src/selfie/manualCaptureButton.tsx +35 -0
- package/src/selfie/noMoreAttemptsModal.tsx +44 -0
- package/src/selfie/notification.css +9 -0
- package/src/selfie/notification.tsx +36 -0
- package/src/selfie/retryErrorModal.tsx +56 -0
- package/src/selfie/selfie.test.tsx +458 -0
- package/src/selfie/selfie.tsx +83 -0
- package/src/selfie/selfieTutorial.json +2626 -0
- package/src/selfie/styles.css +1 -0
- package/src/selfie/tutorial.test.tsx +200 -0
- package/src/selfie/tutorial.tsx +43 -0
- package/src/setup.ts +33 -0
- package/src/shared/baseTutorial/baseTutorial.css +21 -0
- package/src/shared/baseTutorial/baseTutorial.test.tsx +184 -0
- package/src/shared/baseTutorial/baseTutorial.tsx +55 -0
- package/src/shared/baseTutorial/replaceBaseTutorial.test.ts +267 -0
- package/src/shared/baseTutorial/replaceBaseTutorial.ts +68 -0
- package/src/shared/button/button.css +55 -0
- package/src/shared/button/button.test.tsx +101 -0
- package/src/shared/button/button.tsx +47 -0
- package/src/shared/componentRoot/incodeComponent.tsx +12 -0
- package/src/shared/countries/countries.test.ts +75 -0
- package/src/shared/countries/countries.ts +139 -0
- package/src/shared/countries/index.ts +6 -0
- package/src/shared/icons/chevronDown.tsx +22 -0
- package/src/shared/icons/index.ts +2 -0
- package/src/shared/icons/successIcon.css +5 -0
- package/src/shared/icons/successIcon.test.tsx +40 -0
- package/src/shared/icons/successIcon.tsx +26 -0
- package/src/shared/loader/loadingIcon.css +28 -0
- package/src/shared/loader/loadingIcon.tsx +67 -0
- package/src/shared/lottie/lottie.tsx +108 -0
- package/src/shared/otpInput/otpInput.css +85 -0
- package/src/shared/otpInput/otpInput.test.tsx +356 -0
- package/src/shared/otpInput/otpInput.tsx +241 -0
- package/src/shared/page/incode-logo.svg +3 -0
- package/src/shared/page/page.css +47 -0
- package/src/shared/page/page.test.tsx +97 -0
- package/src/shared/page/page.tsx +91 -0
- package/src/shared/page/pageUiConfig.test.ts +112 -0
- package/src/shared/page/pageUiConfig.ts +64 -0
- package/src/shared/page/verifiedByIncode.css +5 -0
- package/src/shared/page/verifiedByIncode.tsx +75 -0
- package/src/shared/spacer/spacer.css +149 -0
- package/src/shared/spacer/spacer.test.tsx +143 -0
- package/src/shared/spacer/spacer.tsx +88 -0
- package/src/shared/spinner/index.ts +2 -0
- package/src/shared/spinner/spinner.css +28 -0
- package/src/shared/spinner/spinner.test.tsx +82 -0
- package/src/shared/spinner/spinner.tsx +65 -0
- package/src/shared/title/title.css +7 -0
- package/src/shared/title/title.tsx +12 -0
- package/src/shared/uiConfig/uiConfig.ts +36 -0
- package/src/shared/webComponent/incodeModule.ts +29 -0
- package/src/shared/webComponent/registerIncodeElement.ts +15 -0
- package/src/styles/__mocks__/fetchTheme.ts +19 -0
- package/src/styles/applyTheme.ts +37 -0
- package/src/styles/cn.test.tsx +57 -0
- package/src/styles/cn.tsx +21 -0
- package/src/styles/core.css +12 -0
- package/src/styles/fetchTheme.test.ts +390 -0
- package/src/styles/fetchTheme.ts +88 -0
- package/src/styles/generatePalette.ts +111 -0
- package/src/styles/reset.css +65 -0
- package/src/styles/resolveCssVariableToHex.ts +28 -0
- package/src/styles/tailwind.css +291 -0
- package/src/styles/themeTypes.ts +18 -0
- package/src/styles/tokens/colors.css +190 -0
- package/src/styles/tokens/components.css +174 -0
- package/src/styles/tokens/index.css +4 -0
- package/src/styles/tokens/primitives.css +129 -0
- package/src/styles/tokens/semantic.css +51 -0
- package/src/svg.d.ts +4 -0
- package/src/types/assets.d.ts +1 -0
- package/src/types/custom-elements.d.ts +104 -0
- package/tsconfig.json +22 -0
- package/vite.config.ts +260 -0
- package/vitest.config.ts +40 -0
- package/vitest.setup.ts +16 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { FC } from 'preact/compat';
|
|
2
|
+
import { useEffect } from 'preact/hooks';
|
|
3
|
+
import register from 'preact-custom-element';
|
|
4
|
+
import { useTranslation } from '../i18n';
|
|
5
|
+
import './flowCompleted.css';
|
|
6
|
+
|
|
7
|
+
export type FlowCompletedProps = {
|
|
8
|
+
title?: string;
|
|
9
|
+
message?: string;
|
|
10
|
+
redirectionUrl?: string;
|
|
11
|
+
action?: 'approved' | 'rejected' | 'none';
|
|
12
|
+
scoreStatus?:
|
|
13
|
+
| 'OK'
|
|
14
|
+
| 'WARN'
|
|
15
|
+
| 'MANUAL_OK'
|
|
16
|
+
| 'FAIL'
|
|
17
|
+
| 'UNKNOWN'
|
|
18
|
+
| 'MANUAL_FAIL';
|
|
19
|
+
redirectDelay?: number;
|
|
20
|
+
disableRedirect?: boolean;
|
|
21
|
+
redirectOriginOnly?: boolean;
|
|
22
|
+
flowId?: string;
|
|
23
|
+
interviewId?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
27
|
+
|
|
28
|
+
const getUrlWithQueryParam = (
|
|
29
|
+
link: string,
|
|
30
|
+
flowId?: string,
|
|
31
|
+
interviewId?: string,
|
|
32
|
+
) => {
|
|
33
|
+
try {
|
|
34
|
+
const url = new URL(link);
|
|
35
|
+
if (flowId) {
|
|
36
|
+
url.searchParams.set('flowId', flowId);
|
|
37
|
+
}
|
|
38
|
+
if (interviewId && window.location.origin !== url.origin) {
|
|
39
|
+
url.searchParams.set('interviewId', interviewId);
|
|
40
|
+
}
|
|
41
|
+
return url.toString();
|
|
42
|
+
} catch (_error) {
|
|
43
|
+
const params = new URLSearchParams();
|
|
44
|
+
if (flowId) params.set('flowId', flowId);
|
|
45
|
+
if (interviewId) params.set('interviewId', interviewId);
|
|
46
|
+
const queryString = params.toString();
|
|
47
|
+
return queryString ? `${link}?${queryString}` : link;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const FlowCompleted: FC<FlowCompletedProps> = ({
|
|
52
|
+
title,
|
|
53
|
+
message,
|
|
54
|
+
redirectionUrl,
|
|
55
|
+
redirectDelay = 3000,
|
|
56
|
+
disableRedirect = false,
|
|
57
|
+
redirectOriginOnly = false,
|
|
58
|
+
flowId,
|
|
59
|
+
interviewId,
|
|
60
|
+
}) => {
|
|
61
|
+
const { t } = useTranslation();
|
|
62
|
+
const displayTitle = title || t('verificationComplete.title');
|
|
63
|
+
const displayMessage = message || t('verificationComplete.message');
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const handleRedirect = async () => {
|
|
67
|
+
if (disableRedirect || !redirectionUrl) return;
|
|
68
|
+
|
|
69
|
+
const query = new URLSearchParams(window.location.search);
|
|
70
|
+
if (redirectOriginOnly && query.get('isRedirect') === 'true') {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await sleep(redirectDelay);
|
|
75
|
+
const redirectUrl = getUrlWithQueryParam(
|
|
76
|
+
redirectionUrl,
|
|
77
|
+
flowId,
|
|
78
|
+
interviewId,
|
|
79
|
+
);
|
|
80
|
+
window.location.href = redirectUrl;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
handleRedirect();
|
|
84
|
+
}, [
|
|
85
|
+
redirectionUrl,
|
|
86
|
+
redirectDelay,
|
|
87
|
+
disableRedirect,
|
|
88
|
+
redirectOriginOnly,
|
|
89
|
+
flowId,
|
|
90
|
+
interviewId,
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<section class="IncodeFlowCompleted" aria-live="assertive">
|
|
95
|
+
{displayTitle ? (
|
|
96
|
+
<h1 class="IncodeFlowCompletedTitle">{displayTitle}</h1>
|
|
97
|
+
) : null}
|
|
98
|
+
{displayMessage ? (
|
|
99
|
+
<p class="IncodeFlowCompletedSubtitle">{displayMessage}</p>
|
|
100
|
+
) : null}
|
|
101
|
+
</section>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
register(
|
|
106
|
+
FlowCompleted,
|
|
107
|
+
'incode-flow-completed',
|
|
108
|
+
[
|
|
109
|
+
'title',
|
|
110
|
+
'message',
|
|
111
|
+
'redirectionUrl',
|
|
112
|
+
'action',
|
|
113
|
+
'scoreStatus',
|
|
114
|
+
'redirectDelay',
|
|
115
|
+
'disableRedirect',
|
|
116
|
+
'redirectOriginOnly',
|
|
117
|
+
'flowId',
|
|
118
|
+
'interviewId',
|
|
119
|
+
],
|
|
120
|
+
{ shadow: false },
|
|
121
|
+
);
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const mockSetup = vi.fn().mockResolvedValue(undefined);
|
|
4
|
+
vi.mock('../setup', () => ({
|
|
5
|
+
setup: (...args: unknown[]) => mockSetup(...args),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
const mockFetchAndApplyTheme = vi.fn();
|
|
9
|
+
vi.mock('../styles/fetchTheme', () => ({
|
|
10
|
+
fetchAndApplyTheme: () => mockFetchAndApplyTheme(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const mockSetUiConfig = vi.fn();
|
|
14
|
+
vi.mock('../shared/uiConfig/uiConfig', () => ({
|
|
15
|
+
setUiConfig: (...args: unknown[]) => mockSetUiConfig(...args),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
const mockWarmupWasm = vi.fn().mockResolvedValue(undefined);
|
|
19
|
+
vi.mock('@incodetech/core', () => ({
|
|
20
|
+
warmupWasm: (...args: unknown[]) => mockWarmupWasm(...args),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const mockFlowManager = {
|
|
24
|
+
load: vi.fn(),
|
|
25
|
+
getState: vi.fn(() => ({ status: 'idle' })),
|
|
26
|
+
subscribe: vi.fn(() => vi.fn()),
|
|
27
|
+
stop: vi.fn(),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
vi.mock('@incodetech/core/flow', () => ({
|
|
31
|
+
createOrchestratedFlowManager: vi.fn(() => mockFlowManager),
|
|
32
|
+
getRequiredWasmPipelines: vi.fn(() => ['selfie']),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
vi.mock('@incodetech/core/email', () => ({
|
|
36
|
+
emailMachine: {},
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
vi.mock('@incodetech/core/phone', () => ({
|
|
40
|
+
phoneMachine: {},
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
vi.mock('@incodetech/core/selfie', () => ({
|
|
44
|
+
selfieMachine: {},
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
import {
|
|
48
|
+
createFlowManager,
|
|
49
|
+
fetchTheme,
|
|
50
|
+
LAZY_UI_MODULES,
|
|
51
|
+
preloadFirstModule,
|
|
52
|
+
setupSDK,
|
|
53
|
+
warmupWasmIfNeeded,
|
|
54
|
+
} from './flowInit';
|
|
55
|
+
|
|
56
|
+
describe('flowInit', () => {
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
vi.clearAllMocks();
|
|
59
|
+
mockWarmupWasm.mockResolvedValue(undefined);
|
|
60
|
+
mockSetup.mockResolvedValue(undefined);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
vi.resetAllMocks();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('createFlowManager', () => {
|
|
68
|
+
it('should create a flow manager', () => {
|
|
69
|
+
const manager = createFlowManager();
|
|
70
|
+
expect(manager).toBeDefined();
|
|
71
|
+
expect(manager.load).toBeDefined();
|
|
72
|
+
expect(manager.getState).toBeDefined();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('setupSDK', () => {
|
|
77
|
+
it('should call setup with correct options', async () => {
|
|
78
|
+
await setupSDK({
|
|
79
|
+
apiURL: 'https://api.example.com',
|
|
80
|
+
token: 'test-token',
|
|
81
|
+
lang: 'es',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(mockSetup).toHaveBeenCalledWith({
|
|
85
|
+
apiURL: 'https://api.example.com',
|
|
86
|
+
token: 'test-token',
|
|
87
|
+
i18n: { lang: 'es' },
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should default lang to en', async () => {
|
|
92
|
+
await setupSDK({
|
|
93
|
+
apiURL: 'https://api.example.com',
|
|
94
|
+
token: 'test-token',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(mockSetup).toHaveBeenCalledWith({
|
|
98
|
+
apiURL: 'https://api.example.com',
|
|
99
|
+
token: 'test-token',
|
|
100
|
+
i18n: { lang: 'en' },
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('fetchTheme', () => {
|
|
106
|
+
it('should return theme and uiConfig on success', async () => {
|
|
107
|
+
const mockTheme = {
|
|
108
|
+
logo: 'https://example.com/logo.png',
|
|
109
|
+
hideFooterBranding: true,
|
|
110
|
+
main: '#000000',
|
|
111
|
+
};
|
|
112
|
+
mockFetchAndApplyTheme.mockResolvedValueOnce(mockTheme);
|
|
113
|
+
|
|
114
|
+
const result = await fetchTheme();
|
|
115
|
+
|
|
116
|
+
expect(result.theme).toEqual(mockTheme);
|
|
117
|
+
expect(result.uiConfig).toEqual({
|
|
118
|
+
logoSrc: 'https://example.com/logo.png',
|
|
119
|
+
hideFooterBranding: true,
|
|
120
|
+
});
|
|
121
|
+
expect(mockSetUiConfig).toHaveBeenCalledWith({
|
|
122
|
+
logoSrc: 'https://example.com/logo.png',
|
|
123
|
+
hideFooterBranding: true,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should return empty object on error', async () => {
|
|
128
|
+
mockFetchAndApplyTheme.mockRejectedValueOnce(new Error('Theme error'));
|
|
129
|
+
|
|
130
|
+
const result = await fetchTheme();
|
|
131
|
+
|
|
132
|
+
expect(result).toEqual({});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('warmupWasmIfNeeded', () => {
|
|
137
|
+
it('should call warmupWasm when pipelines are required', () => {
|
|
138
|
+
const managerState = {
|
|
139
|
+
status: 'ready' as const,
|
|
140
|
+
flow: { flowId: 'test', name: 'Test', flowModules: [] },
|
|
141
|
+
steps: ['SELFIE'],
|
|
142
|
+
currentStepIndex: 0,
|
|
143
|
+
currentStep: 'SELFIE',
|
|
144
|
+
config: {},
|
|
145
|
+
moduleState: null,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const wasmConfig = {
|
|
149
|
+
wasmPath: 'https://example.com/wasm',
|
|
150
|
+
wasmSimdPath: 'https://example.com/wasm-simd',
|
|
151
|
+
glueCodePath: 'https://example.com/glue.js',
|
|
152
|
+
modelsBasePath: 'https://example.com/models',
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
warmupWasmIfNeeded(managerState, wasmConfig);
|
|
156
|
+
|
|
157
|
+
expect(mockWarmupWasm).toHaveBeenCalledWith({
|
|
158
|
+
...wasmConfig,
|
|
159
|
+
pipelines: ['selfie'],
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should log error when warmupWasm fails', async () => {
|
|
164
|
+
const consoleSpy = vi
|
|
165
|
+
.spyOn(console, 'error')
|
|
166
|
+
.mockImplementation(() => {});
|
|
167
|
+
mockWarmupWasm.mockRejectedValueOnce(new Error('WASM load failed'));
|
|
168
|
+
|
|
169
|
+
const managerState = {
|
|
170
|
+
status: 'ready' as const,
|
|
171
|
+
flow: { flowId: 'test', name: 'Test', flowModules: [] },
|
|
172
|
+
steps: ['SELFIE'],
|
|
173
|
+
currentStepIndex: 0,
|
|
174
|
+
currentStep: 'SELFIE',
|
|
175
|
+
config: {},
|
|
176
|
+
moduleState: null,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const wasmConfig = {
|
|
180
|
+
wasmPath: 'https://example.com/wasm',
|
|
181
|
+
wasmSimdPath: 'https://example.com/wasm-simd',
|
|
182
|
+
glueCodePath: 'https://example.com/glue.js',
|
|
183
|
+
modelsBasePath: 'https://example.com/models',
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
warmupWasmIfNeeded(managerState, wasmConfig);
|
|
187
|
+
|
|
188
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
189
|
+
|
|
190
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
191
|
+
'WASM warmup failed:',
|
|
192
|
+
expect.any(Error),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
consoleSpy.mockRestore();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should not call warmupWasm when no pipelines are required', async () => {
|
|
199
|
+
const { getRequiredWasmPipelines } = await import(
|
|
200
|
+
'@incodetech/core/flow'
|
|
201
|
+
);
|
|
202
|
+
vi.mocked(getRequiredWasmPipelines).mockReturnValueOnce([]);
|
|
203
|
+
|
|
204
|
+
const managerState = {
|
|
205
|
+
status: 'ready' as const,
|
|
206
|
+
flow: { flowId: 'test', name: 'Test', flowModules: [] },
|
|
207
|
+
steps: ['PHONE'],
|
|
208
|
+
currentStepIndex: 0,
|
|
209
|
+
currentStep: 'PHONE',
|
|
210
|
+
config: {},
|
|
211
|
+
moduleState: null,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const wasmConfig = {
|
|
215
|
+
wasmPath: 'https://example.com/wasm',
|
|
216
|
+
wasmSimdPath: 'https://example.com/wasm-simd',
|
|
217
|
+
glueCodePath: 'https://example.com/glue.js',
|
|
218
|
+
modelsBasePath: 'https://example.com/models',
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
warmupWasmIfNeeded(managerState, wasmConfig);
|
|
222
|
+
|
|
223
|
+
expect(mockWarmupWasm).not.toHaveBeenCalled();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('preloadFirstModule', () => {
|
|
228
|
+
it('should call loader for the current step', async () => {
|
|
229
|
+
const managerState = {
|
|
230
|
+
status: 'ready' as const,
|
|
231
|
+
flow: { flowId: 'test', name: 'Test', flowModules: [] },
|
|
232
|
+
steps: ['PHONE'],
|
|
233
|
+
currentStepIndex: 0,
|
|
234
|
+
currentStep: 'PHONE',
|
|
235
|
+
config: {},
|
|
236
|
+
moduleState: null,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
preloadFirstModule(managerState);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should not throw for unsupported module', () => {
|
|
243
|
+
const managerState = {
|
|
244
|
+
status: 'ready' as const,
|
|
245
|
+
flow: { flowId: 'test', name: 'Test', flowModules: [] },
|
|
246
|
+
steps: ['UNKNOWN_MODULE'],
|
|
247
|
+
currentStepIndex: 0,
|
|
248
|
+
currentStep: 'UNKNOWN_MODULE',
|
|
249
|
+
config: {},
|
|
250
|
+
moduleState: null,
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
expect(() => preloadFirstModule(managerState)).not.toThrow();
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('LAZY_UI_MODULES', () => {
|
|
258
|
+
it('should have loaders for PHONE, EMAIL, and SELFIE', () => {
|
|
259
|
+
expect(LAZY_UI_MODULES.PHONE).toBeDefined();
|
|
260
|
+
expect(LAZY_UI_MODULES.EMAIL).toBeDefined();
|
|
261
|
+
expect(LAZY_UI_MODULES.SELFIE).toBeDefined();
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { WasmConfig } from '@incodetech/core';
|
|
2
|
+
import { warmupWasm } from '@incodetech/core';
|
|
3
|
+
import { emailMachine } from '@incodetech/core/email';
|
|
4
|
+
import {
|
|
5
|
+
createOrchestratedFlowManager,
|
|
6
|
+
getRequiredWasmPipelines,
|
|
7
|
+
type OrchestratedFlowReadyState,
|
|
8
|
+
} from '@incodetech/core/flow';
|
|
9
|
+
import { phoneMachine } from '@incodetech/core/phone';
|
|
10
|
+
import { selfieMachine } from '@incodetech/core/selfie';
|
|
11
|
+
import { setup } from '../setup';
|
|
12
|
+
import type { UiConfig } from '../shared/uiConfig/uiConfig';
|
|
13
|
+
import { setUiConfig } from '../shared/uiConfig/uiConfig';
|
|
14
|
+
import { fetchAndApplyTheme } from '../styles/fetchTheme';
|
|
15
|
+
import type { ThemeConfig } from '../styles/themeTypes';
|
|
16
|
+
|
|
17
|
+
export const LAZY_UI_MODULES = {
|
|
18
|
+
PHONE: () => import('../phone/phone').then((m) => m.Phone),
|
|
19
|
+
EMAIL: () => import('../email/email').then((m) => m.Email),
|
|
20
|
+
SELFIE: () => import('../selfie/selfie').then((m) => m.Selfie),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function createFlowManager() {
|
|
24
|
+
return createOrchestratedFlowManager({
|
|
25
|
+
modules: {
|
|
26
|
+
PHONE: phoneMachine,
|
|
27
|
+
EMAIL: emailMachine,
|
|
28
|
+
SELFIE: selfieMachine,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type SetupSDKOptions = {
|
|
34
|
+
apiURL: string;
|
|
35
|
+
token: string;
|
|
36
|
+
lang?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export async function setupSDK(options: SetupSDKOptions): Promise<void> {
|
|
40
|
+
await setup({
|
|
41
|
+
apiURL: options.apiURL,
|
|
42
|
+
token: options.token,
|
|
43
|
+
i18n: { lang: options.lang ?? 'en' },
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type FetchThemeResult = {
|
|
48
|
+
theme?: ThemeConfig;
|
|
49
|
+
uiConfig?: UiConfig;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export async function fetchTheme(): Promise<FetchThemeResult> {
|
|
53
|
+
try {
|
|
54
|
+
const theme = await fetchAndApplyTheme();
|
|
55
|
+
const uiConfig: UiConfig = {
|
|
56
|
+
logoSrc: theme.logo,
|
|
57
|
+
hideFooterBranding: theme.hideFooterBranding,
|
|
58
|
+
};
|
|
59
|
+
setUiConfig(uiConfig);
|
|
60
|
+
return { theme, uiConfig };
|
|
61
|
+
} catch (themeError) {
|
|
62
|
+
console.warn('Failed to fetch dashboard theme:', themeError);
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function warmupWasmIfNeeded(
|
|
68
|
+
managerState: OrchestratedFlowReadyState,
|
|
69
|
+
wasmConfig: WasmConfig,
|
|
70
|
+
): void {
|
|
71
|
+
const requiredPipelines = getRequiredWasmPipelines(managerState.flow);
|
|
72
|
+
if (requiredPipelines.length > 0) {
|
|
73
|
+
warmupWasm({
|
|
74
|
+
...wasmConfig,
|
|
75
|
+
pipelines: requiredPipelines,
|
|
76
|
+
}).catch((error) => {
|
|
77
|
+
console.error('WASM warmup failed:', error);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function preloadFirstModule(
|
|
83
|
+
managerState: OrchestratedFlowReadyState,
|
|
84
|
+
): void {
|
|
85
|
+
const firstStep = managerState.currentStep;
|
|
86
|
+
if (firstStep) {
|
|
87
|
+
const loader = LAZY_UI_MODULES[firstStep as keyof typeof LAZY_UI_MODULES];
|
|
88
|
+
if (loader) {
|
|
89
|
+
loader().catch(() => {
|
|
90
|
+
// Ignore errors - module will load when needed
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
@reference "../styles/tailwind.css";
|
|
2
|
+
|
|
3
|
+
.IncodeFlowStart {
|
|
4
|
+
@apply flex flex-col items-center justify-center h-full w-full;
|
|
5
|
+
@apply pt-20 px-24 pb-32;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.IncodeFlowStartContent {
|
|
9
|
+
@apply flex flex-col items-center justify-between;
|
|
10
|
+
height: 100%;
|
|
11
|
+
width: 100%;
|
|
12
|
+
text-align: center;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.IncodeFlowStartCenterSection {
|
|
16
|
+
@apply flex flex-col items-center justify-center;
|
|
17
|
+
flex: 1;
|
|
18
|
+
width: 100%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.IncodeFlowStartLogoContainer {
|
|
22
|
+
@apply flex items-center justify-center;
|
|
23
|
+
min-width: 100px;
|
|
24
|
+
max-height: 250px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.IncodeFlowStartLogo {
|
|
28
|
+
width: 260px;
|
|
29
|
+
max-width: 100%;
|
|
30
|
+
max-height: 100%;
|
|
31
|
+
height: auto;
|
|
32
|
+
object-fit: contain;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media screen and (max-width: 767px) {
|
|
36
|
+
.IncodeFlowStartLogo {
|
|
37
|
+
width: 200px;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.IncodeFlowStartSubtitle {
|
|
42
|
+
font-size: 1.5rem;
|
|
43
|
+
line-height: 1.37;
|
|
44
|
+
color: #20263d;
|
|
45
|
+
margin-top: 10px;
|
|
46
|
+
font-weight: bold;
|
|
47
|
+
text-align: center;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.IncodeFlowStartButton {
|
|
51
|
+
margin-top: auto;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media screen and (min-width: 992px) {
|
|
55
|
+
.IncodeFlowStart {
|
|
56
|
+
padding: 5% 20% 15% 20%;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { fireEvent, render, screen } from '@testing-library/preact';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { FlowStart } from './flowStart';
|
|
4
|
+
|
|
5
|
+
describe('FlowStart', () => {
|
|
6
|
+
it('should render with default logo and subtitle', () => {
|
|
7
|
+
const onStart = vi.fn();
|
|
8
|
+
render(<FlowStart onStart={onStart} />);
|
|
9
|
+
|
|
10
|
+
expect(screen.getByText('Demo App')).toBeTruthy();
|
|
11
|
+
expect(screen.getByRole('img')).toBeTruthy();
|
|
12
|
+
expect(screen.getByTestId('flow-start-button')).toBeTruthy();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should render with custom logo URL', () => {
|
|
16
|
+
const onStart = vi.fn();
|
|
17
|
+
render(
|
|
18
|
+
<FlowStart onStart={onStart} logoUrl="https://example.com/logo.png" />,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const img = screen.getByRole('img') as HTMLImageElement;
|
|
22
|
+
expect(img.src).toBe('https://example.com/logo.png');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should render with custom subtitle', () => {
|
|
26
|
+
const onStart = vi.fn();
|
|
27
|
+
render(<FlowStart onStart={onStart} subtitle="Custom Subtitle" />);
|
|
28
|
+
|
|
29
|
+
expect(screen.getByText('Custom Subtitle')).toBeTruthy();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should call onStart when button is clicked', () => {
|
|
33
|
+
const onStart = vi.fn();
|
|
34
|
+
render(<FlowStart onStart={onStart} />);
|
|
35
|
+
|
|
36
|
+
fireEvent.click(screen.getByTestId('flow-start-button'));
|
|
37
|
+
|
|
38
|
+
expect(onStart).toHaveBeenCalledTimes(1);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should have accessible section with aria-live', () => {
|
|
42
|
+
const onStart = vi.fn();
|
|
43
|
+
const { container } = render(<FlowStart onStart={onStart} />);
|
|
44
|
+
|
|
45
|
+
const section = container.querySelector('section');
|
|
46
|
+
expect(section?.getAttribute('aria-live')).toBe('polite');
|
|
47
|
+
expect(section?.classList.contains('IncodeFlowStart')).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { FC } from 'preact/compat';
|
|
2
|
+
import { useTranslation } from '../i18n';
|
|
3
|
+
import { Button } from '../shared/button/button';
|
|
4
|
+
import incodeLogo from './incode-logo.svg';
|
|
5
|
+
import './flowStart.css';
|
|
6
|
+
|
|
7
|
+
export type FlowStartProps = {
|
|
8
|
+
logoUrl?: string;
|
|
9
|
+
subtitle?: string;
|
|
10
|
+
onStart: () => void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const FlowStart: FC<FlowStartProps> = ({
|
|
14
|
+
logoUrl,
|
|
15
|
+
subtitle,
|
|
16
|
+
onStart,
|
|
17
|
+
}) => {
|
|
18
|
+
const { t } = useTranslation();
|
|
19
|
+
const displaySubtitle = subtitle || 'Demo App';
|
|
20
|
+
const displayLogo = logoUrl || incodeLogo;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<section class="IncodeFlowStart" aria-live="polite">
|
|
24
|
+
<div class="IncodeFlowStartContent">
|
|
25
|
+
<div class="IncodeFlowStartCenterSection">
|
|
26
|
+
<div class="IncodeFlowStartLogoContainer">
|
|
27
|
+
<img src={displayLogo} alt="Logo" class="IncodeFlowStartLogo" />
|
|
28
|
+
</div>
|
|
29
|
+
<h2 class="IncodeFlowStartSubtitle">{displaySubtitle}</h2>
|
|
30
|
+
</div>
|
|
31
|
+
<Button
|
|
32
|
+
onClick={onStart}
|
|
33
|
+
class="IncodeFlowStartButton"
|
|
34
|
+
data-testid="flow-start-button"
|
|
35
|
+
>
|
|
36
|
+
{t('home.start')}
|
|
37
|
+
</Button>
|
|
38
|
+
</div>
|
|
39
|
+
</section>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg width="187" height="51" viewBox="0 0 187 51" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M168.419 47.1851C165.3 47.1851 162.576 46.5612 160.247 45.3136C157.918 44.0243 156.109 42.1528 154.819 39.699C153.53 37.2452 152.885 34.2924 152.885 30.8405C152.885 25.3507 154.195 21.171 156.816 18.3013C159.477 15.39 163.2 13.9344 167.982 13.9344C171.434 13.9344 174.2 14.683 176.279 16.1802C178.4 17.6774 179.939 19.8193 180.896 22.6058C181.894 25.3507 182.372 28.6362 182.331 32.4625H158.001L157.377 27.0974H176.03L174.533 29.3433C174.449 26.0577 173.867 23.7079 172.786 22.2939C171.746 20.8382 170.062 20.1104 167.733 20.1104C166.319 20.1104 165.071 20.4431 163.99 21.1086C162.95 21.7324 162.139 22.7929 161.557 24.2901C161.016 25.7874 160.746 27.846 160.746 30.4662C160.746 33.7101 161.515 36.2055 163.054 37.9522C164.593 39.6574 166.943 40.51 170.103 40.51C171.393 40.51 172.62 40.3852 173.784 40.1357C174.99 39.8446 176.092 39.5118 177.09 39.1375C178.13 38.7632 179.004 38.4097 179.711 38.077V45.064C178.255 45.6879 176.591 46.1869 174.72 46.5612C172.89 46.9771 170.79 47.1851 168.419 47.1851Z" fill="#006AFF"/>
|
|
3
|
+
<path d="M133.067 47.1851C130.905 47.1851 128.846 46.9355 126.891 46.4365C124.937 45.9374 123.211 45.064 121.713 43.8163C120.216 42.5687 119.031 40.8427 118.158 38.6385C117.326 36.4342 116.91 33.627 116.91 30.2166C116.91 25.1843 118.137 21.2125 120.59 18.3013C123.044 15.39 126.767 13.9344 131.757 13.9344C132.464 13.9344 133.379 14.0176 134.502 14.1839C135.625 14.3503 136.769 14.6414 137.933 15.0573C139.139 15.4316 140.179 16.0347 141.052 16.8665L139.306 19.3618V1.76953H147.104V38.888C147.104 40.302 146.688 41.5289 145.856 42.5687C145.066 43.6084 143.984 44.4818 142.612 45.1888C141.24 45.8542 139.722 46.3533 138.058 46.686C136.394 47.0187 134.731 47.1851 133.067 47.1851ZM133.317 40.4476C135.022 40.4476 136.436 40.2397 137.559 39.8238C138.723 39.4079 139.306 38.8048 139.306 38.0146V22.0443C138.391 21.5037 137.413 21.1086 136.374 20.859C135.334 20.6095 134.336 20.4847 133.379 20.4847C131.466 20.4847 129.886 20.8382 128.638 21.5453C127.39 22.2107 126.455 23.292 125.831 24.7892C125.207 26.2448 124.895 28.1164 124.895 30.4038C124.895 32.4001 125.145 34.1468 125.644 35.644C126.143 37.1413 126.995 38.3265 128.201 39.1999C129.449 40.0317 131.154 40.4476 133.317 40.4476Z" fill="#006AFF"/>
|
|
4
|
+
<path d="M97.1975 47.1851C92.0404 47.1851 88.1726 45.7502 85.5941 42.8806C83.0155 40.0109 81.7262 35.8728 81.7262 30.4662C81.7262 24.6852 83.0363 20.4847 85.6564 17.8646C88.3182 15.2445 92.1444 13.9344 97.1351 13.9344C100.629 13.9344 103.54 14.5583 105.869 15.8059C108.198 17.012 109.924 18.8419 111.047 21.2957C112.211 23.7495 112.793 26.8063 112.793 30.4662C112.793 35.8728 111.442 40.0109 108.739 42.8806C106.077 45.7502 102.23 47.1851 97.1975 47.1851ZM97.1975 40.6971C99.0274 40.6971 100.504 40.2812 101.627 39.4495C102.791 38.6177 103.644 37.4532 104.184 35.956C104.725 34.4171 104.995 32.5872 104.995 30.4662C104.995 28.0956 104.704 26.1617 104.122 24.6645C103.581 23.1672 102.729 22.0651 101.564 21.3581C100.441 20.6511 98.9858 20.2976 97.1975 20.2976C95.3675 20.2976 93.8911 20.6719 92.7682 21.4205C91.6453 22.1691 90.8135 23.292 90.2729 24.7892C89.7738 26.2864 89.5242 28.1788 89.5242 30.4662C89.5242 33.7933 90.1273 36.3303 91.3334 38.077C92.5395 39.8238 94.4942 40.6971 97.1975 40.6971Z" fill="#006AFF"/>
|
|
5
|
+
<path d="M67.8132 46.9355C65.7337 46.9355 63.7374 46.6444 61.8243 46.0622C59.9112 45.4799 58.206 44.5442 56.7088 43.2549C55.2116 41.9656 54.0263 40.302 53.1529 38.2642C52.2795 36.2263 51.8429 33.7517 51.8429 30.8405C51.8429 27.8044 52.2588 25.2259 53.0905 23.1049C53.9223 20.9838 55.066 19.2578 56.5217 17.927C57.9773 16.5545 59.6617 15.5772 61.5748 14.9949C63.5295 14.3711 65.5881 14.0592 67.7508 14.0592C69.4143 14.0592 71.1195 14.2255 72.8663 14.5583C74.613 14.891 76.1726 15.3692 77.5451 15.9931V22.8553C76.131 22.2315 74.717 21.7324 73.3029 21.3581C71.8889 20.9422 70.4333 20.7343 68.9361 20.7343C66.0248 20.7343 63.7582 21.4621 62.1362 22.9177C60.5142 24.3733 59.7032 26.7647 59.7032 30.0919C59.7032 33.5022 60.4311 36.0391 61.8867 37.7027C63.3839 39.3663 65.8377 40.1981 69.248 40.1981C70.7868 40.1981 72.284 39.9693 73.7396 39.5118C75.1953 39.0128 76.4845 38.4721 77.6075 37.8899V44.8145C76.1518 45.5215 74.6338 46.0414 73.0534 46.3741C71.473 46.7484 69.7263 46.9355 67.8132 46.9355Z" fill="#006AFF"/>
|
|
6
|
+
<path d="M17.5517 17.4279C18.425 17.012 19.4648 16.5961 20.6709 16.1802C21.8769 15.7643 23.187 15.4108 24.601 15.1197C26.0151 14.787 27.4291 14.5375 28.8432 14.3711C30.2988 14.1631 31.6712 14.0592 32.9605 14.0592C35.8717 14.0592 38.2839 14.4543 40.197 15.2445C42.1517 15.9931 43.6074 17.1992 44.5639 18.8627C45.5621 20.4847 46.0611 22.6266 46.0611 25.2883V46.2493H38.2008V26.6607C38.2008 25.8705 38.0968 25.1219 37.8888 24.4149C37.6809 23.6663 37.3274 23.0217 36.8283 22.481C36.3292 21.8988 35.643 21.4621 34.7696 21.171C33.8963 20.8382 32.7733 20.6719 31.4009 20.6719C30.3612 20.6719 29.3006 20.7759 28.2193 20.9838C27.138 21.1917 26.223 21.4621 25.4744 21.7948V46.2493H17.5517V17.4279Z" fill="#006AFF"/>
|
|
7
|
+
<path d="M1.92836 46.2493V15.1197H9.85113L9.91351 46.2493H1.92836ZM1.74121 10.129V3.64105H10.0383V10.129H1.74121Z" fill="#006AFF"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { IncodeFlowProps, SpinnerConfig } from './flow';
|
|
2
|
+
export { IncodeFlow, preloadIncodeFlow } from './flow';
|
|
3
|
+
export type { FlowCompletedProps } from './flowCompleted';
|
|
4
|
+
export { FlowCompleted } from './flowCompleted';
|
|
5
|
+
export type { FlowStartProps } from './flowStart';
|
|
6
|
+
export { FlowStart } from './flowStart';
|
|
7
|
+
export type { PreloadHandle } from './preloadFlow';
|