interview-widget 1.0.12 → 1.0.14

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/README.md CHANGED
@@ -1,306 +1,306 @@
1
- # Interview Widget
2
-
3
- A modern React interview experience widget with built‑in timer flow, STT (speech‑to‑text), and TTS (text‑to‑speech). It can be embedded via NPM or a CDN and configured through a single provider.
4
-
5
- ## Features
6
-
7
- - **Video capture** with camera integration
8
- - **Responsive design** with mobile support
9
- - **TypeScript support** with full type definitions
10
- - **CDN-ready** for easy integration into any website
11
- - **Q&A structured interview flow**
12
- - **Advanced Timer System** with phase management
13
- - **Text-to-Speech integration** for transcribing
14
- - **Speech-to-Text integration** for voice answers
15
- - **Automatic phase transitions** (thinking → answering → editing)
16
- - **Time validation** before starting new questions
17
-
18
- ## Quick Start
19
-
20
- ### 1) Environment Setup
21
-
22
- The Interview Widget uses encryption for session storage integrity. Set up your encryption secret based on your environment:
23
-
24
- #### Vite React
25
-
26
- ```env
27
- # .env
28
- VITE_IW_SECRET=your-secure-encryption-seed-here
29
- ```
30
-
31
- #### Next.js
32
-
33
- ```env
34
- # .env.local
35
- NEXT_PUBLIC_IW_SECRET=your-secure-encryption-seed-here
36
- ```
37
-
38
- #### Vanilla JS / Custom Framework
39
-
40
- ```html
41
- <script>
42
- // Define the secret before loading your widget
43
- window.__IW_SECRET__ = "your-secure-encryption-seed-here";
44
- </script>
45
- ```
46
-
47
- ### 2) Install (React apps)
48
-
49
- ```bash
50
- npm install interview-widget
51
- ```
52
-
53
- Minimal usage:
54
-
55
- ```tsx
56
- import React from "react";
57
- import { InterviewWidget, InterviewWidgetProvider } from "interview-widget";
58
- import "interview-widget/style.css";
59
-
60
- export default function App() {
61
- return (
62
- <InterviewWidgetProvider
63
- config={{
64
- api: {
65
- baseUrl: "/api" // Your backend endpoint
66
- authToken: getTokenFromYourAuthSystem(), // Get from secure backend
67
- },
68
- interview: {
69
- stt: {
70
- provider: "groq",
71
- model: "whisper-large-v3-turbo",
72
- language: "en",
73
- },
74
- tts: { provider: "piper" },
75
- timers: {
76
- thinkingDuration: 30,
77
- answeringDuration: 120,
78
- editingDuration: 30,
79
- },
80
- },
81
- }}
82
- >
83
- <InterviewWidget
84
- interviewId="your_interview_id"
85
- title="Interview for JS developer"
86
- className="iw-rounded-none iw-shadow-none"
87
- onInterviewEnd={() => {
88
- console.log("🎉🎉 Interview ended 🎉🎉");
89
- }}
90
- />
91
- </InterviewWidgetProvider>
92
- );
93
- }
94
- ```
95
-
96
- ```html
97
- <!-- React 18 UMD -->
98
- <script
99
- crossorigin
100
- src="https://unpkg.com/react@18/umd/react.production.min.js"
101
- ></script>
102
- <script
103
- crossorigin
104
- src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"
105
- ></script>
106
-
107
- <!-- Styles + Widget UMD -->
108
- <link
109
- rel="stylesheet"
110
- href="https://unpkg.com/interview-widget@latest/dist/widget.css"
111
- />
112
- <script src="https://unpkg.com/interview-widget@latest/dist/widget.umd.js"></script>
113
-
114
- <div id="interview-widget-container"></div>
115
- <script>
116
- document.addEventListener("DOMContentLoaded", function () {
117
- const { InterviewWidget, InterviewWidgetProvider } = window.InterviewWidget;
118
-
119
- const app = React.createElement(
120
- InterviewWidgetProvider,
121
- {
122
- config: {
123
- api: { baseUrl: "/api" },
124
- interview: {
125
- timers: {
126
- thinkingDuration: 30,
127
- answeringDuration: 120,
128
- editingDuration: 30,
129
- },
130
- stt: {
131
- provider: "groq",
132
- model: "whisper-large-v3-turbo",
133
- language: "en",
134
- },
135
- tts: { provider: "piper" },
136
- },
137
- },
138
- },
139
- React.createElement(InterviewWidget, { interviewId="your_interview_id", title: "Developer Interview", onInterviewEnd={() => {}} }
140
- );
141
-
142
- ReactDOM.render(app, document.getElementById("interview-widget-container"));
143
- });
144
- </script>
145
- ```
146
-
147
- ## Public API
148
-
149
- ### <InterviewWidget />
150
-
151
- ### InterviewWidget Props
152
-
153
- | Prop | Type | Default | Description |
154
- | ----------------------- | ------------ | ------------- | --------------------------------------------------------------------------- |
155
- | `interviewId` | `string` | Required | Unique interview identifier used for backend API calls |
156
- | `title` | `string` | `"Interview"` | Title displayed in the interview header |
157
- | `onInterviewEnd` | `() => void` | `undefined` | Called when the interview completes or the user exits |
158
- | `onInterviewDisqualify` | `() => void` | `undefined` | Called when the interview is disqualified (e.g., due to cheating detection) |
159
- | `className` | `string` | `""` | Additional CSS classes applied to the outer widget container |
160
-
161
- ### <InterviewWidgetProvider />
162
-
163
- Wrap your app (or the widget) to provide configuration. See full config reference below.
164
-
165
- ```tsx
166
- <InterviewWidgetProvider config={{ api: { baseUrl: "/api" } }}>
167
- <InterviewWidget />
168
- </InterviewWidgetProvider>
169
- ```
170
-
171
- ## Configuration reference (Provider)
172
-
173
- Pass this object to `InterviewWidgetProvider` via the `config` prop. Tables list all keys, types, and defaults.
174
-
175
- ### Top-level
176
-
177
- | Prop | Type | Default | Description |
178
- | --------- | -------- | ----------------------- | ---------------------------------------------- |
179
- | api | `object` | [see below](#api) | Backend/API configuration |
180
- | ui | `object` | [see below](#ui) | UI customization tokens |
181
- | interview | `object` | [see below](#interview) | Interview behavior settings (timers, STT, TTS) |
182
-
183
- ### API
184
-
185
- | Prop | Type | Default | Description |
186
- | ----------- | -------- | -------------------------- | ------------------------------------------------------ |
187
- | baseUrl | `string` | "/api" | Base URL for backend endpoints |
188
- | authToken | `string` | undefined | Optional bearer token appended as Authorization header |
189
- | retryConfig | `object` | [see below](#retry-config) | Retry policy for fetch calls |
190
-
191
- #### Retry Config
192
-
193
- | Prop | Type | Default | Description |
194
- | --------- | -------------------------- | ------------- | ------------------------------------------ |
195
- | attempts | `number` | 3 | Number of retry attempts |
196
- | backoff | `"fixed" \| "exponential"` | "exponential" | Backoff strategy |
197
- | baseDelay | `number (ms)` | 1000 | Base delay between retries in milliseconds |
198
-
199
- ### UI
200
-
201
- | Prop | Type | Default | Description |
202
- | ------------ | -------------- | --------- | -------------------------------------- |
203
- | baseColor | `string (hex)` | "#3B82F6" | Primary brand color used by the widget |
204
- | borderRadius | `string (CSS)` | "8px" | Global corner radius for components |
205
-
206
- ### Interview
207
-
208
- | Prop | Type | Default | Description |
209
- | ---------- | -------- | -------------------------------- | ------------------------------------------ |
210
- | timers | `object` | [see below](#timers) | Per-phase and global timing configuration |
211
- | stt | `object` | [see below](#stt-speech-to-text) | Speech-to-Text provider settings |
212
- | tts | `object` | [see below](#tts-text-to-speech) | Text-to-Speech provider settings |
213
- | proctoring | `object` | [see below](#proctoring) | Proctoring settings and cheating detection |
214
-
215
- #### Timers
216
-
217
- | Prop | Type | Default | Description |
218
- | -------------------------- | -------- | ------- | --------------------------------------------------------- |
219
- | thinkingDuration | `number` | 30s | Timebox for the Thinking phase |
220
- | answeringDuration | `number` | 120s | Timebox for the Answering phase |
221
- | editingDuration | `number` | 30s | Timebox for the Editing phase |
222
- | totalInterviewDuration | `number` | 600s | Overall interview time cap |
223
- | minimumTimeForNextQuestion | `number` | 120s | Minimum time required to allow starting the next question |
224
-
225
- #### STT (Speech-To-Text)
226
-
227
- | Prop | Type | Default | Description |
228
- | -------- | ------------------------------------ | ------------------------ | -------------------------------------- |
229
- | provider | `"groq" \| "deepgram"` | "groq" | STT vendor to use |
230
- | model | `"whisper-large-v3-turbo" \| string` | "whisper-large-v3-turbo" | STT model identifier |
231
- | language | `string` | "en" | Language code passed to the STT engine |
232
-
233
- #### TTS (Text-To-Speech)
234
-
235
- | Prop | Type | Default | Description |
236
- | -------- | --------- | ------- | ----------------- |
237
- | provider | `"piper"` | "piper" | TTS vendor to use |
238
-
239
- #### Proctoring
240
-
241
- | Prop | Type | Default | Description |
242
- | ------- | --------- | ------- | --------------------------------------------------------- |
243
- | enabled | `boolean` | true | Enable/disable proctoring and cheating detection features |
244
- | gazeAnalysisEnabled | `boolean` | true | Enable/disable gaze tracking and analysis |
245
- | showControls | `boolean` | false | Show/hide proctoring control panel |
246
- | showEngagementBar | `boolean` | true | Show/hide engagement metrics bar |
247
- | showLandmarks | `boolean` | false | Show/hide facial landmark visualization |
248
-
249
- ## How it works
250
-
251
- The widget composes several parts:
252
-
253
- - TimerService controls phases and timeboxes
254
- - TTS speaks the question in the Reading phase
255
- - STT records and transcribes the answer in the Answering/Transcribing phases
256
- - A resilient API client hits your backend to fetch the next question and submit answers
257
-
258
- ## Backend API Interface
259
-
260
- The Interview Widget interacts with a backend endpoint to fetch questions and submit answers. The main endpoint is:
261
-
262
- ### Endpoint
263
-
264
- **POST** `/interview/questions/next`
265
-
266
- #### Request Body
267
-
268
- Uses the `InterviewQuestionPayload` interface:
269
-
270
- ```json
271
- {
272
- "interviewId": "string",
273
- "isInterviewDone": false,
274
- "qnaId": "string",
275
- "question": "string",
276
- "answer": "string",
277
- "answerDuration": "string"
278
- }
279
- ```
280
-
281
- #### Response Body
282
-
283
- Uses the `InterviewQuestionResponse` interface:
284
-
285
- ```json
286
- {
287
- "success": true,
288
- "message": "Question generated successfully",
289
- "data": {
290
- "interview_id": "string",
291
- "qna_id": "string",
292
- "question": "What is your experience with React?",
293
- "question_audio_data_base64": null,
294
- "audio_length_in_milliseconds": 0,
295
- "estimated_answering_duration": "00:00:30"
296
- },
297
- "error": null,
298
- "code": 200
299
- }
300
- ```
301
-
302
- ### Timer phases
303
-
304
- `idle → fetching_question → reading_question → thinking → answering → transcribing → editing → submitting → completed`
305
-
306
- You’ll see a Start button, a Next Phase button during active phases, a live countdown, and a completion screen.
1
+ # Interview Widget
2
+
3
+ A modern React interview experience widget with built‑in timer flow, STT (speech‑to‑text), and TTS (text‑to‑speech). It can be embedded via NPM or a CDN and configured through a single provider.
4
+
5
+ ## Features
6
+
7
+ - **Video capture** with camera integration
8
+ - **Responsive design** with mobile support
9
+ - **TypeScript support** with full type definitions
10
+ - **CDN-ready** for easy integration into any website
11
+ - **Q&A structured interview flow**
12
+ - **Advanced Timer System** with phase management
13
+ - **Text-to-Speech integration** for transcribing
14
+ - **Speech-to-Text integration** for voice answers
15
+ - **Automatic phase transitions** (thinking → answering → editing)
16
+ - **Time validation** before starting new questions
17
+
18
+ ## Quick Start
19
+
20
+ ### 1) Environment Setup
21
+
22
+ The Interview Widget uses encryption for session storage integrity. Set up your encryption secret based on your environment:
23
+
24
+ #### Vite React
25
+
26
+ ```env
27
+ # .env
28
+ VITE_IW_SECRET=your-secure-encryption-seed-here
29
+ ```
30
+
31
+ #### Next.js
32
+
33
+ ```env
34
+ # .env.local
35
+ NEXT_PUBLIC_IW_SECRET=your-secure-encryption-seed-here
36
+ ```
37
+
38
+ #### Vanilla JS / Custom Framework
39
+
40
+ ```html
41
+ <script>
42
+ // Define the secret before loading your widget
43
+ window.__IW_SECRET__ = "your-secure-encryption-seed-here";
44
+ </script>
45
+ ```
46
+
47
+ ### 2) Install (React apps)
48
+
49
+ ```bash
50
+ npm install interview-widget
51
+ ```
52
+
53
+ Minimal usage:
54
+
55
+ ```tsx
56
+ import React from "react";
57
+ import { InterviewWidget, InterviewWidgetProvider } from "interview-widget";
58
+ import "interview-widget/style.css";
59
+
60
+ export default function App() {
61
+ return (
62
+ <InterviewWidgetProvider
63
+ config={{
64
+ api: {
65
+ baseUrl: "/api" // Your backend endpoint
66
+ authToken: getTokenFromYourAuthSystem(), // Get from secure backend
67
+ },
68
+ interview: {
69
+ stt: {
70
+ provider: "groq",
71
+ model: "whisper-large-v3-turbo",
72
+ language: "en",
73
+ },
74
+ tts: { provider: "piper" },
75
+ timers: {
76
+ thinkingDuration: 30,
77
+ answeringDuration: 120,
78
+ editingDuration: 30,
79
+ },
80
+ },
81
+ }}
82
+ >
83
+ <InterviewWidget
84
+ interviewId="your_interview_id"
85
+ title="Interview for JS developer"
86
+ className="iw-rounded-none iw-shadow-none"
87
+ onInterviewEnd={() => {
88
+ console.log("🎉🎉 Interview ended 🎉🎉");
89
+ }}
90
+ />
91
+ </InterviewWidgetProvider>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ```html
97
+ <!-- React 18 UMD -->
98
+ <script
99
+ crossorigin
100
+ src="https://unpkg.com/react@18/umd/react.production.min.js"
101
+ ></script>
102
+ <script
103
+ crossorigin
104
+ src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"
105
+ ></script>
106
+
107
+ <!-- Styles + Widget UMD -->
108
+ <link
109
+ rel="stylesheet"
110
+ href="https://unpkg.com/interview-widget@latest/dist/widget.css"
111
+ />
112
+ <script src="https://unpkg.com/interview-widget@latest/dist/widget.umd.js"></script>
113
+
114
+ <div id="interview-widget-container"></div>
115
+ <script>
116
+ document.addEventListener("DOMContentLoaded", function () {
117
+ const { InterviewWidget, InterviewWidgetProvider } = window.InterviewWidget;
118
+
119
+ const app = React.createElement(
120
+ InterviewWidgetProvider,
121
+ {
122
+ config: {
123
+ api: { baseUrl: "/api" },
124
+ interview: {
125
+ timers: {
126
+ thinkingDuration: 30,
127
+ answeringDuration: 120,
128
+ editingDuration: 30,
129
+ },
130
+ stt: {
131
+ provider: "groq",
132
+ model: "whisper-large-v3-turbo",
133
+ language: "en",
134
+ },
135
+ tts: { provider: "piper" },
136
+ },
137
+ },
138
+ },
139
+ React.createElement(InterviewWidget, { interviewId="your_interview_id", title: "Developer Interview", onInterviewEnd={() => {}} }
140
+ );
141
+
142
+ ReactDOM.render(app, document.getElementById("interview-widget-container"));
143
+ });
144
+ </script>
145
+ ```
146
+
147
+ ## Public API
148
+
149
+ ### <InterviewWidget />
150
+
151
+ ### InterviewWidget Props
152
+
153
+ | Prop | Type | Default | Description |
154
+ | ----------------------- | ------------ | ------------- | --------------------------------------------------------------------------- |
155
+ | `interviewId` | `string` | Required | Unique interview identifier used for backend API calls |
156
+ | `title` | `string` | `"Interview"` | Title displayed in the interview header |
157
+ | `onInterviewEnd` | `() => void` | `undefined` | Called when the interview completes or the user exits |
158
+ | `onInterviewDisqualify` | `() => void` | `undefined` | Called when the interview is disqualified (e.g., due to cheating detection) |
159
+ | `className` | `string` | `""` | Additional CSS classes applied to the outer widget container |
160
+
161
+ ### <InterviewWidgetProvider />
162
+
163
+ Wrap your app (or the widget) to provide configuration. See full config reference below.
164
+
165
+ ```tsx
166
+ <InterviewWidgetProvider config={{ api: { baseUrl: "/api" } }}>
167
+ <InterviewWidget />
168
+ </InterviewWidgetProvider>
169
+ ```
170
+
171
+ ## Configuration reference (Provider)
172
+
173
+ Pass this object to `InterviewWidgetProvider` via the `config` prop. Tables list all keys, types, and defaults.
174
+
175
+ ### Top-level
176
+
177
+ | Prop | Type | Default | Description |
178
+ | --------- | -------- | ----------------------- | ---------------------------------------------- |
179
+ | api | `object` | [see below](#api) | Backend/API configuration |
180
+ | ui | `object` | [see below](#ui) | UI customization tokens |
181
+ | interview | `object` | [see below](#interview) | Interview behavior settings (timers, STT, TTS) |
182
+
183
+ ### API
184
+
185
+ | Prop | Type | Default | Description |
186
+ | ----------- | -------- | -------------------------- | ------------------------------------------------------ |
187
+ | baseUrl | `string` | "/api" | Base URL for backend endpoints |
188
+ | authToken | `string` | undefined | Optional bearer token appended as Authorization header |
189
+ | retryConfig | `object` | [see below](#retry-config) | Retry policy for fetch calls |
190
+
191
+ #### Retry Config
192
+
193
+ | Prop | Type | Default | Description |
194
+ | --------- | -------------------------- | ------------- | ------------------------------------------ |
195
+ | attempts | `number` | 3 | Number of retry attempts |
196
+ | backoff | `"fixed" \| "exponential"` | "exponential" | Backoff strategy |
197
+ | baseDelay | `number (ms)` | 1000 | Base delay between retries in milliseconds |
198
+
199
+ ### UI
200
+
201
+ | Prop | Type | Default | Description |
202
+ | ------------ | -------------- | --------- | -------------------------------------- |
203
+ | baseColor | `string (hex)` | "#3B82F6" | Primary brand color used by the widget |
204
+ | borderRadius | `string (CSS)` | "8px" | Global corner radius for components |
205
+
206
+ ### Interview
207
+
208
+ | Prop | Type | Default | Description |
209
+ | ---------- | -------- | -------------------------------- | ------------------------------------------ |
210
+ | timers | `object` | [see below](#timers) | Per-phase and global timing configuration |
211
+ | stt | `object` | [see below](#stt-speech-to-text) | Speech-to-Text provider settings |
212
+ | tts | `object` | [see below](#tts-text-to-speech) | Text-to-Speech provider settings |
213
+ | proctoring | `object` | [see below](#proctoring) | Proctoring settings and cheating detection |
214
+
215
+ #### Timers
216
+
217
+ | Prop | Type | Default | Description |
218
+ | -------------------------- | -------- | ------- | --------------------------------------------------------- |
219
+ | thinkingDuration | `number` | 30s | Timebox for the Thinking phase |
220
+ | answeringDuration | `number` | 120s | Timebox for the Answering phase |
221
+ | editingDuration | `number` | 30s | Timebox for the Editing phase |
222
+ | totalInterviewDuration | `number` | 600s | Overall interview time cap |
223
+ | minimumTimeForNextQuestion | `number` | 120s | Minimum time required to allow starting the next question |
224
+
225
+ #### STT (Speech-To-Text)
226
+
227
+ | Prop | Type | Default | Description |
228
+ | -------- | ------------------------------------ | ------------------------ | -------------------------------------- |
229
+ | provider | `"groq" \| "deepgram"` | "groq" | STT vendor to use |
230
+ | model | `"whisper-large-v3-turbo" \| string` | "whisper-large-v3-turbo" | STT model identifier |
231
+ | language | `string` | "en" | Language code passed to the STT engine |
232
+
233
+ #### TTS (Text-To-Speech)
234
+
235
+ | Prop | Type | Default | Description |
236
+ | -------- | --------- | ------- | ----------------- |
237
+ | provider | `"piper"` | "piper" | TTS vendor to use |
238
+
239
+ #### Proctoring
240
+
241
+ | Prop | Type | Default | Description |
242
+ | ------- | --------- | ------- | --------------------------------------------------------- |
243
+ | enabled | `boolean` | true | Enable/disable proctoring and cheating detection features |
244
+ | gazeAnalysisEnabled | `boolean` | true | Enable/disable gaze tracking and analysis |
245
+ | showControls | `boolean` | false | Show/hide proctoring control panel |
246
+ | showEngagementBar | `boolean` | true | Show/hide engagement metrics bar |
247
+ | showLandmarks | `boolean` | false | Show/hide facial landmark visualization |
248
+
249
+ ## How it works
250
+
251
+ The widget composes several parts:
252
+
253
+ - TimerService controls phases and timeboxes
254
+ - TTS speaks the question in the Reading phase
255
+ - STT records and transcribes the answer in the Answering/Transcribing phases
256
+ - A resilient API client hits your backend to fetch the next question and submit answers
257
+
258
+ ## Backend API Interface
259
+
260
+ The Interview Widget interacts with a backend endpoint to fetch questions and submit answers. The main endpoint is:
261
+
262
+ ### Endpoint
263
+
264
+ **POST** `/interview/questions/next`
265
+
266
+ #### Request Body
267
+
268
+ Uses the `InterviewQuestionPayload` interface:
269
+
270
+ ```json
271
+ {
272
+ "interviewId": "string",
273
+ "isInterviewDone": false,
274
+ "qnaId": "string",
275
+ "question": "string",
276
+ "answer": "string",
277
+ "answerDuration": "string"
278
+ }
279
+ ```
280
+
281
+ #### Response Body
282
+
283
+ Uses the `InterviewQuestionResponse` interface:
284
+
285
+ ```json
286
+ {
287
+ "success": true,
288
+ "message": "Question generated successfully",
289
+ "data": {
290
+ "interview_id": "string",
291
+ "qna_id": "string",
292
+ "question": "What is your experience with React?",
293
+ "question_audio_data_base64": null,
294
+ "audio_length_in_milliseconds": 0,
295
+ "estimated_answering_duration": "00:00:30"
296
+ },
297
+ "error": null,
298
+ "code": 200
299
+ }
300
+ ```
301
+
302
+ ### Timer phases
303
+
304
+ `idle → fetching_question → reading_question → thinking → answering → transcribing → editing → submitting → completed`
305
+
306
+ You’ll see a Start button, a Next Phase button during active phases, a live countdown, and a completion screen.
@@ -1,7 +1,5 @@
1
- import { default as React } from 'react';
2
1
  export interface VideoFeedHandle {
3
2
  setReference: () => void;
4
- faceLandmarkerRef?: React.RefObject<any>;
5
3
  }
6
4
  interface VideoFeedProps {
7
5
  className?: string;
@@ -9,5 +7,5 @@ interface VideoFeedProps {
9
7
  interview_duration?: number | undefined;
10
8
  showProctoringUI?: boolean;
11
9
  }
12
- declare const VideoFeed: React.ForwardRefExoticComponent<VideoFeedProps & React.RefAttributes<VideoFeedHandle>>;
10
+ declare const VideoFeed: import('react').ForwardRefExoticComponent<VideoFeedProps & import('react').RefAttributes<VideoFeedHandle>>;
13
11
  export default VideoFeed;