interview-widget 0.0.8 → 0.0.9

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 (35) hide show
  1. package/README.md +197 -101
  2. package/dist/components/interview/interview-controller.d.ts +8 -0
  3. package/dist/components/interview/interview-header.d.ts +1 -0
  4. package/dist/components/interview/question-display.d.ts +3 -2
  5. package/dist/components/modals/exit-confirmation-modal.d.ts +7 -0
  6. package/dist/components/timer/timer-display.d.ts +13 -0
  7. package/dist/components/ui/dialog.d.ts +13 -0
  8. package/dist/context/interview-widget-context.d.ts +34 -0
  9. package/dist/hooks/use-api.d.ts +26 -0
  10. package/dist/hooks/use-dialog.d.ts +6 -0
  11. package/dist/hooks/use-interview-api.d.ts +5 -0
  12. package/dist/hooks/use-stt.d.ts +20 -0
  13. package/dist/hooks/use-timer.d.ts +12 -0
  14. package/dist/hooks/use-tts.d.ts +15 -0
  15. package/dist/index.d.ts +3 -2
  16. package/dist/services/api/index.d.ts +2 -0
  17. package/dist/services/api/interview-api.d.ts +23 -0
  18. package/dist/services/stt/index.d.ts +1 -0
  19. package/dist/services/stt/stt-service.d.ts +78 -0
  20. package/dist/services/timer/index.d.ts +6 -0
  21. package/dist/services/timer/timer-service.d.ts +81 -0
  22. package/dist/services/tts/index.d.ts +1 -0
  23. package/dist/services/tts/tts-service.d.ts +43 -0
  24. package/dist/types.d.ts +55 -29
  25. package/dist/utils/constants.d.ts +3 -0
  26. package/dist/utils/helper.d.ts +6 -0
  27. package/dist/widget.css +1 -1
  28. package/dist/widget.es.js +1594 -432
  29. package/dist/widget.umd.js +2 -2
  30. package/package.json +1 -1
  31. package/dist/examples/APIUsageExample.d.ts +0 -5
  32. package/dist/hooks/useAPI.d.ts +0 -12
  33. package/dist/services/index.d.ts +0 -2
  34. package/dist/services/interview-api.d.ts +0 -47
  35. /package/dist/components/{onboarding-modal.d.ts → modals/onboarding-modal.d.ts} +0 -0
package/dist/widget.es.js CHANGED
@@ -1,8 +1,9 @@
1
- var L = Object.defineProperty;
2
- var q = (i, e, s) => e in i ? L(i, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : i[e] = s;
3
- var $ = (i, e, s) => q(i, typeof e != "symbol" ? e + "" : e, s);
4
- import { useRef as j, useState as m, useEffect as P, useCallback as T, useMemo as H } from "react";
5
- var M = { exports: {} }, N = {};
1
+ var me = Object.defineProperty;
2
+ var ge = (i, t, e) => t in i ? me(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e;
3
+ var I = (i, t, e) => ge(i, typeof t != "symbol" ? t + "" : t, e);
4
+ import { createContext as he, useContext as fe, useEffect as A, useState as y, useRef as D, useCallback as v, useMemo as xe } from "react";
5
+ import { createPortal as pe } from "react-dom";
6
+ var X = { exports: {} }, U = {};
6
7
  /**
7
8
  * @license React
8
9
  * react-jsx-runtime.production.js
@@ -12,56 +13,281 @@ var M = { exports: {} }, N = {};
12
13
  * This source code is licensed under the MIT license found in the
13
14
  * LICENSE file in the root directory of this source tree.
14
15
  */
15
- var G = Symbol.for("react.transitional.element"), z = Symbol.for("react.fragment");
16
- function I(i, e, s) {
17
- var n = null;
18
- if (s !== void 0 && (n = "" + s), e.key !== void 0 && (n = "" + e.key), "key" in e) {
19
- s = {};
20
- for (var r in e)
21
- r !== "key" && (s[r] = e[r]);
22
- } else s = e;
23
- return e = s.ref, {
24
- $$typeof: G,
16
+ var be = Symbol.for("react.transitional.element"), ye = Symbol.for("react.fragment");
17
+ function Z(i, t, e) {
18
+ var a = null;
19
+ if (e !== void 0 && (a = "" + e), t.key !== void 0 && (a = "" + t.key), "key" in t) {
20
+ e = {};
21
+ for (var s in t)
22
+ s !== "key" && (e[s] = t[s]);
23
+ } else e = t;
24
+ return t = e.ref, {
25
+ $$typeof: be,
25
26
  type: i,
26
- key: n,
27
- ref: e !== void 0 ? e : null,
28
- props: s
27
+ key: a,
28
+ ref: t !== void 0 ? t : null,
29
+ props: e
29
30
  };
30
31
  }
31
- N.Fragment = z;
32
- N.jsx = I;
33
- N.jsxs = I;
34
- M.exports = N;
35
- var t = M.exports;
36
- const E = ({
32
+ U.Fragment = ye;
33
+ U.jsx = Z;
34
+ U.jsxs = Z;
35
+ X.exports = U;
36
+ var r = X.exports;
37
+ const C = {
38
+ api: {
39
+ baseUrl: "/api",
40
+ retryConfig: {
41
+ attempts: 3,
42
+ backoff: "exponential",
43
+ baseDelay: 1e3
44
+ }
45
+ },
46
+ ui: {
47
+ baseColor: "#3B82F6",
48
+ borderRadius: "8px"
49
+ },
50
+ interview: {
51
+ timers: {
52
+ thinkingDuration: 30,
53
+ answeringDuration: 120,
54
+ editingDuration: 30
55
+ },
56
+ stt: {
57
+ provider: "groq",
58
+ model: "whisper-large-v3-turbo",
59
+ language: "en"
60
+ },
61
+ tts: {
62
+ provider: "piper"
63
+ }
64
+ }
65
+ }, ee = he(
66
+ null
67
+ );
68
+ function Ne({
69
+ config: i = {},
70
+ children: t
71
+ }) {
72
+ var a, s, c, n;
73
+ const e = {
74
+ api: {
75
+ ...C.api,
76
+ ...i.api,
77
+ retryConfig: {
78
+ ...C.api.retryConfig,
79
+ ...(a = i.api) == null ? void 0 : a.retryConfig
80
+ }
81
+ },
82
+ ui: {
83
+ ...C.ui,
84
+ ...i.ui
85
+ },
86
+ interview: {
87
+ ...C.interview,
88
+ ...i.interview,
89
+ timers: {
90
+ ...C.interview.timers,
91
+ ...(s = i.interview) == null ? void 0 : s.timers
92
+ },
93
+ stt: {
94
+ ...C.interview.stt,
95
+ ...(c = i.interview) == null ? void 0 : c.stt
96
+ },
97
+ tts: {
98
+ ...C.interview.tts,
99
+ ...(n = i.interview) == null ? void 0 : n.tts
100
+ }
101
+ }
102
+ };
103
+ return /* @__PURE__ */ r.jsx(ee.Provider, { value: e, children: t });
104
+ }
105
+ function q() {
106
+ const i = fe(ee);
107
+ if (!i)
108
+ throw new Error(
109
+ "useInterviewConfig must be used within an InterviewWidgetProvider. Wrap your component tree with <InterviewWidgetProvider config={...}>"
110
+ );
111
+ return i;
112
+ }
113
+ function Te() {
114
+ return q().api || C.api;
115
+ }
116
+ function F() {
117
+ return q().ui || C.ui;
118
+ }
119
+ function Ie() {
120
+ return q().interview || C.interview;
121
+ }
122
+ const ve = ({
123
+ isOpen: i,
124
+ onClose: t,
125
+ children: e,
126
+ title: a,
127
+ showCloseButton: s = !0,
128
+ closeOnOverlayClick: c = !0,
129
+ closeOnEscape: n = !0,
130
+ className: o = ""
131
+ }) => {
132
+ if (A(() => {
133
+ if (!i || !n) return;
134
+ const d = (u) => {
135
+ u.key === "Escape" && t();
136
+ };
137
+ return document.addEventListener("keydown", d), () => document.removeEventListener("keydown", d);
138
+ }, [i, n, t]), A(() => (i ? (document.body.style.overflow = "hidden", document.body.classList.add("interview-widget-container")) : (document.body.style.overflow = "unset", document.body.classList.remove("interview-widget-container")), () => {
139
+ document.body.style.overflow = "unset";
140
+ }), [i]), !i) return null;
141
+ const m = (d) => {
142
+ c && d.target === d.currentTarget && t();
143
+ }, g = /* @__PURE__ */ r.jsxs("div", { className: "iw-fixed iw-inset-0 iw-z-50 iw-flex iw-items-center iw-justify-center", children: [
144
+ /* @__PURE__ */ r.jsx(
145
+ "div",
146
+ {
147
+ className: "iw-fixed iw-inset-0 iw-bg-black iw-bg-opacity-50 iw-transition-opacity",
148
+ onClick: m
149
+ }
150
+ ),
151
+ /* @__PURE__ */ r.jsxs(
152
+ "div",
153
+ {
154
+ className: `iw-relative iw-bg-white iw-rounded-lg iw-shadow-xl iw-max-w-md iw-w-full iw-mx-4 iw-max-h-[90vh] iw-overflow-hidden ${o}`,
155
+ role: "dialog",
156
+ "aria-modal": "true",
157
+ "aria-labelledby": a ? "dialog-title" : void 0,
158
+ children: [
159
+ (a || s) && /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-justify-between iw-p-4 iw-border-b iw-border-gray-200", children: [
160
+ a && /* @__PURE__ */ r.jsx(
161
+ "h2",
162
+ {
163
+ id: "dialog-title",
164
+ className: "iw-text-lg iw-font-semibold iw-text-gray-900",
165
+ children: a
166
+ }
167
+ ),
168
+ s && /* @__PURE__ */ r.jsx(
169
+ "button",
170
+ {
171
+ onClick: t,
172
+ className: "iw-p-1 iw-text-gray-400 iw-hover:text-gray-600 iw-transition-colors iw-rounded iw-hover:bg-gray-100",
173
+ "aria-label": "Close dialog",
174
+ children: /* @__PURE__ */ r.jsx(
175
+ "svg",
176
+ {
177
+ className: "iw-w-5 iw-h-5",
178
+ fill: "none",
179
+ stroke: "currentColor",
180
+ viewBox: "0 0 24 24",
181
+ children: /* @__PURE__ */ r.jsx(
182
+ "path",
183
+ {
184
+ strokeLinecap: "round",
185
+ strokeLinejoin: "round",
186
+ strokeWidth: 2,
187
+ d: "M6 18L18 6M6 6l12 12"
188
+ }
189
+ )
190
+ }
191
+ )
192
+ }
193
+ )
194
+ ] }),
195
+ /* @__PURE__ */ r.jsx("div", { className: "iw-p-4 iw-overflow-y-auto", children: e })
196
+ ]
197
+ }
198
+ )
199
+ ] });
200
+ return pe(g, document.body);
201
+ }, Ee = ({
202
+ confirmExitInterview: i,
203
+ isOpen: t,
204
+ onClose: e
205
+ }) => /* @__PURE__ */ r.jsx(ve, { isOpen: t, onClose: e, title: "Exit Interview", children: /* @__PURE__ */ r.jsxs("div", { className: "iw-space-y-4", children: [
206
+ /* @__PURE__ */ r.jsx("p", { className: "iw-text-base iw-text-gray-700", children: "Are you sure you want to exit the interview? All progress will be lost and you will not be able to resume." }),
207
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-justify-end iw-space-x-2", children: [
208
+ /* @__PURE__ */ r.jsx(
209
+ "button",
210
+ {
211
+ className: "iw-px-4 iw-py-2 iw-bg-gray-200 iw-rounded iw-text-gray-700 iw-font-medium",
212
+ onClick: e,
213
+ children: "Cancel"
214
+ }
215
+ ),
216
+ /* @__PURE__ */ r.jsx(
217
+ "button",
218
+ {
219
+ className: "iw-px-4 iw-py-2 iw-bg-red-600 iw-text-white iw-rounded iw-font-medium",
220
+ onClick: i,
221
+ children: "Confirm Exit"
222
+ }
223
+ )
224
+ ] })
225
+ ] }) }), je = ({ title: i, onExit: t }) => {
226
+ const { baseColor: e } = F(), [a, s] = y(!1);
227
+ return /* @__PURE__ */ r.jsxs("header", { className: "iw-w-full iw-text-gray-900", children: [
228
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-mx-auto iw-flex iw-items-center iw-justify-between iw-px-4 iw-py-3", children: [
229
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-2", children: [
230
+ /* @__PURE__ */ r.jsx(
231
+ "div",
232
+ {
233
+ className: "iw-h-7 iw-w-7 iw-rounded-md iw-flex iw-items-center iw-justify-center iw-text-white iw-font-semibold",
234
+ style: { backgroundColor: e },
235
+ children: "N"
236
+ }
237
+ ),
238
+ /* @__PURE__ */ r.jsx("p", { className: "iw-text-sm iw-font-medium", children: "Novara" })
239
+ ] }),
240
+ /* @__PURE__ */ r.jsx("h1", { className: "iw-text-base iw-font-medium", children: i }),
241
+ /* @__PURE__ */ r.jsx(
242
+ "button",
243
+ {
244
+ className: "iw-text-sm iw-text-gray-500 hover:iw-text-gray-700",
245
+ onClick: () => s(!0),
246
+ children: "Exit Interview"
247
+ }
248
+ )
249
+ ] }),
250
+ /* @__PURE__ */ r.jsx("div", { className: "iw-h-px iw-bg-gray-200" }),
251
+ /* @__PURE__ */ r.jsx(
252
+ Ee,
253
+ {
254
+ isOpen: a,
255
+ confirmExitInterview: () => {
256
+ t(), s(!1);
257
+ },
258
+ onClose: () => s(!1)
259
+ }
260
+ )
261
+ ] });
262
+ }, $ = ({
37
263
  children: i,
38
- variant: e = "primary",
39
- size: s = "md",
40
- fullWidth: n = !1,
41
- isLoading: r = !1,
42
- disabled: a,
43
- className: o = "",
44
- ...l
264
+ variant: t = "primary",
265
+ size: e = "md",
266
+ fullWidth: a = !1,
267
+ isLoading: s = !1,
268
+ disabled: c,
269
+ className: n = "",
270
+ ...o
45
271
  }) => {
46
- const w = "iw-inline-flex iw-items-center iw-justify-center iw-rounded-md iw-font-medium iw-transition-colors iw-focus:outline-none iw-focus:ring-2 iw-focus:ring-primary-500 iw-focus:ring-offset-2", d = {
272
+ const m = "iw-inline-flex iw-items-center iw-justify-center iw-rounded-md iw-font-medium iw-transition-colors iw-focus:outline-none iw-focus:ring-2 iw-focus:ring-primary-500 iw-focus:ring-offset-2", g = {
47
273
  primary: "iw-bg-primary-600 iw-text-white iw-hover:bg-primary-700 iw-border iw-border-transparent",
48
274
  secondary: "iw-bg-primary-100 iw-text-primary-700 iw-hover:bg-primary-200 iw-border iw-border-transparent",
49
275
  outline: "iw-bg-transparent iw-text-primary-700 iw-border iw-border-primary-500 iw-hover:bg-primary-50",
50
276
  text: "iw-bg-transparent iw-text-primary-600 iw-hover:bg-primary-50 iw-border iw-border-transparent",
51
277
  gradient: "iw-text-white iw-border iw-border-transparent iw-bg-gradient-to-r iw-from-purple-500 iw-to-indigo-500 hover:iw-from-purple-600 hover:iw-to-indigo-600"
52
- }, u = {
278
+ }, d = {
53
279
  sm: "iw-px-3 iw-py-1.5 iw-text-sm",
54
280
  md: "iw-px-4 iw-py-2 iw-text-sm",
55
281
  lg: "iw-px-5 iw-py-2.5 iw-text-base"
56
- }, h = "iw-disabled:opacity-50 iw-disabled:cursor-not-allowed iw-disabled:pointer-events-none", f = n ? "iw-w-full" : "";
57
- return /* @__PURE__ */ t.jsxs(
282
+ }, u = "iw-disabled:opacity-50 iw-disabled:cursor-not-allowed iw-disabled:pointer-events-none", h = a ? "iw-w-full" : "";
283
+ return /* @__PURE__ */ r.jsxs(
58
284
  "button",
59
285
  {
60
- className: `${w} ${d[e]} ${u[s]} ${f} ${h} ${o}`,
61
- disabled: a || r,
62
- ...l,
286
+ className: `${m} ${g[t]} ${d[e]} ${h} ${u} ${n}`,
287
+ disabled: c || s,
288
+ ...o,
63
289
  children: [
64
- r && /* @__PURE__ */ t.jsxs(
290
+ s && /* @__PURE__ */ r.jsxs(
65
291
  "svg",
66
292
  {
67
293
  className: "iw-animate-spin iw-mr-2 iw-h-4 iw-w-4 iw-text-white",
@@ -69,7 +295,7 @@ const E = ({
69
295
  fill: "none",
70
296
  viewBox: "0 0 24 24",
71
297
  children: [
72
- /* @__PURE__ */ t.jsx(
298
+ /* @__PURE__ */ r.jsx(
73
299
  "circle",
74
300
  {
75
301
  className: "iw-opacity-25",
@@ -80,7 +306,7 @@ const E = ({
80
306
  strokeWidth: "4"
81
307
  }
82
308
  ),
83
- /* @__PURE__ */ t.jsx(
309
+ /* @__PURE__ */ r.jsx(
84
310
  "path",
85
311
  {
86
312
  className: "iw-opacity-75",
@@ -95,165 +321,321 @@ const E = ({
95
321
  ]
96
322
  }
97
323
  );
98
- }, O = ({
324
+ }, Se = ({
99
325
  isOpen: i,
100
- onStart: e,
101
- onClose: s
326
+ onStart: t,
327
+ onClose: e
102
328
  }) => {
103
- var y;
104
- const n = j(null), r = j(null), [a, o] = m(!1), [l, w] = m(null), [d, u] = m(!1), h = () => {
105
- r.current && (r.current.getTracks().forEach((c) => c.stop()), r.current = null);
106
- }, f = async () => {
107
- u(!0), w(null);
329
+ var N;
330
+ const a = D(null), s = D(null), { baseColor: c, borderRadius: n } = F(), [o, m] = y(!1), [g, d] = y(null), [u, h] = y(!1), x = () => {
331
+ s.current && (s.current.getTracks().forEach((w) => w.stop()), s.current = null);
332
+ }, k = async () => {
333
+ h(!0), d(null);
108
334
  try {
109
- const c = await navigator.mediaDevices.getUserMedia({
335
+ const w = await navigator.mediaDevices.getUserMedia({
110
336
  video: { width: { ideal: 1280 }, height: { ideal: 720 } },
111
337
  audio: !0
112
338
  });
113
- r.current = c, n.current && (n.current.srcObject = c), o(!0);
114
- } catch (c) {
115
- console.error("Media permission error:", c);
116
- let g = "Unable to access camera or microphone.";
117
- (c == null ? void 0 : c.name) === "NotAllowedError" ? g = "Permissions denied. Please allow access to camera and microphone." : (c == null ? void 0 : c.name) === "NotFoundError" ? g = "No camera/microphone found. Please connect a device and retry." : c != null && c.message && (g = c.message), o(!1), w(g);
339
+ s.current = w, a.current && (a.current.srcObject = w), m(!0);
340
+ } catch (w) {
341
+ console.error("Media permission error:", w);
342
+ let p = "Unable to access camera or microphone.";
343
+ (w == null ? void 0 : w.name) === "NotAllowedError" ? p = "Permissions denied. Please allow access to camera and microphone." : (w == null ? void 0 : w.name) === "NotFoundError" ? p = "No camera/microphone found. Please connect a device and retry." : w != null && w.message && (p = w.message), m(!1), d(p);
118
344
  } finally {
119
- u(!1);
345
+ h(!1);
120
346
  }
121
347
  };
122
- if (P(() => {
348
+ if (A(() => {
123
349
  if (!i) {
124
- h();
350
+ x();
125
351
  return;
126
352
  }
127
- return f(), () => {
128
- h();
353
+ return k(), () => {
354
+ x();
129
355
  };
130
356
  }, [i]), !i) return null;
131
- const k = () => {
132
- e(), h();
357
+ const j = () => {
358
+ t(), x();
133
359
  };
134
- return /* @__PURE__ */ t.jsx("div", { className: "iw-fixed iw-inset-0 iw-z-50 iw-flex iw-items-center iw-justify-center iw-bg-black/50 iw-backdrop-blur-sm", children: /* @__PURE__ */ t.jsxs("div", { className: "iw-bg-white iw-rounded-xl iw-shadow-2xl iw-w-full iw-max-w-3xl iw-mx-4", children: [
135
- /* @__PURE__ */ t.jsxs("div", { className: "iw-px-5 iw-py-4 iw-border-b iw-border-gray-200 iw-flex iw-items-center iw-justify-between", children: [
136
- /* @__PURE__ */ t.jsx("h2", { className: "iw-text-base iw-font-semibold", children: "Camera & Microphone Check" }),
137
- s && /* @__PURE__ */ t.jsx(
360
+ return /* @__PURE__ */ r.jsx("div", { className: "iw-fixed iw-inset-0 iw-z-50 iw-flex iw-items-center iw-justify-center iw-bg-black/50 iw-backdrop-blur-sm", children: /* @__PURE__ */ r.jsxs("div", { className: "iw-bg-white iw-rounded-xl iw-shadow-2xl iw-w-full iw-max-w-3xl iw-mx-4", children: [
361
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-px-5 iw-py-4 iw-border-b iw-border-gray-200 iw-flex iw-items-center iw-justify-between", children: [
362
+ /* @__PURE__ */ r.jsx("h2", { className: "iw-text-base iw-font-semibold", children: "Camera & Microphone Check" }),
363
+ e && /* @__PURE__ */ r.jsx(
138
364
  "button",
139
365
  {
140
366
  "aria-label": "Close",
141
367
  className: "iw-text-gray-500 hover:iw-text-gray-700",
142
368
  onClick: () => {
143
- h(), s == null || s();
369
+ x(), e == null || e();
144
370
  },
145
371
  children: "✕"
146
372
  }
147
373
  )
148
374
  ] }),
149
- /* @__PURE__ */ t.jsxs("div", { className: "iw-p-4 iw-grid iw-grid-cols-2 iw-gap-4", children: [
150
- /* @__PURE__ */ t.jsx("div", { className: "iw-border iw-border-gray-200 iw-rounded-lg iw-overflow-hidden iw-bg-gray-900", children: /* @__PURE__ */ t.jsx(
375
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-p-4 iw-grid iw-grid-cols-2 iw-gap-4", children: [
376
+ /* @__PURE__ */ r.jsx("div", { className: "iw-border iw-border-gray-200 iw-rounded-lg iw-overflow-hidden iw-bg-gray-900", children: /* @__PURE__ */ r.jsx(
151
377
  "video",
152
378
  {
153
- ref: n,
379
+ ref: a,
154
380
  autoPlay: !0,
155
381
  playsInline: !0,
156
382
  muted: !0,
157
383
  className: "iw-w-full iw-h-64 iw-object-cover"
158
384
  }
159
385
  ) }),
160
- /* @__PURE__ */ t.jsxs("div", { className: "iw-flex iw-flex-col iw-gap-3", children: [
161
- /* @__PURE__ */ t.jsx("p", { className: "iw-text-sm iw-text-gray-700", children: "We will need access to your camera and microphone. Grant permission to preview your setup and to enable the Start Interview button." }),
162
- !((y = navigator.mediaDevices) != null && y.getUserMedia) && /* @__PURE__ */ t.jsx("div", { className: "iw-text-xs iw-text-red-600", children: "Your browser does not support media devices. Please use a modern browser like Chrome, Edge, or Firefox." }),
163
- l && /* @__PURE__ */ t.jsx("div", { className: "iw-text-xs iw-text-red-600", children: l }),
164
- /* @__PURE__ */ t.jsxs("div", { className: "iw-flex iw-items-center iw-gap-2", children: [
165
- /* @__PURE__ */ t.jsx(
166
- E,
386
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-flex-col iw-gap-3", children: [
387
+ /* @__PURE__ */ r.jsx("p", { className: "iw-text-sm iw-text-gray-700", children: "We will need access to your camera and microphone. Grant permission to preview your setup and to enable the Start Interview button." }),
388
+ !((N = navigator.mediaDevices) != null && N.getUserMedia) && /* @__PURE__ */ r.jsx("div", { className: "iw-text-xs iw-text-red-600", children: "Your browser does not support media devices. Please use a modern browser like Chrome, Edge, or Firefox." }),
389
+ g && /* @__PURE__ */ r.jsx("div", { className: "iw-text-xs iw-text-red-600", children: g }),
390
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-gap-2", children: [
391
+ /* @__PURE__ */ r.jsx(
392
+ $,
167
393
  {
168
- onClick: f,
169
- isLoading: d,
394
+ onClick: k,
395
+ isLoading: u,
170
396
  variant: "outline",
171
397
  size: "sm",
172
- children: a ? "Recheck Permissions" : "Enable Camera & Mic"
398
+ style: {
399
+ borderColor: c,
400
+ borderRadius: n,
401
+ color: c
402
+ },
403
+ children: o ? "Recheck Permissions" : "Enable Camera & Mic"
173
404
  }
174
405
  ),
175
- /* @__PURE__ */ t.jsx(
176
- E,
406
+ /* @__PURE__ */ r.jsx(
407
+ $,
177
408
  {
178
- onClick: k,
179
- disabled: !a,
409
+ onClick: j,
410
+ disabled: !o,
180
411
  size: "sm",
181
- children: "Start Interview"
412
+ style: { backgroundColor: c, borderRadius: n },
413
+ children: "Proceed"
182
414
  }
183
415
  )
184
416
  ] }),
185
- /* @__PURE__ */ t.jsxs("ul", { className: "iw-text-xs iw-text-gray-500 iw-pt-2 iw-list-disc iw-pl-4", children: [
186
- /* @__PURE__ */ t.jsx("li", { children: "Your preview is muted to avoid echo." }),
187
- /* @__PURE__ */ t.jsx("li", { children: "You can change devices from your browser’s site settings." })
417
+ /* @__PURE__ */ r.jsxs("ul", { className: "iw-text-xs iw-text-gray-500 iw-pt-2 iw-list-disc iw-pl-4", children: [
418
+ /* @__PURE__ */ r.jsx("li", { children: "Your preview is muted to avoid echo." }),
419
+ /* @__PURE__ */ r.jsx("li", { children: "You can change devices from your browser’s site settings." })
188
420
  ] })
189
421
  ] })
190
422
  ] })
191
423
  ] }) });
192
- }, Q = ({ title: i }) => /* @__PURE__ */ t.jsxs("header", { className: "iw-w-full iw-text-gray-900", children: [
193
- /* @__PURE__ */ t.jsxs("div", { className: "iw-mx-auto iw-flex iw-items-center iw-justify-between iw-px-4 iw-py-3", children: [
194
- /* @__PURE__ */ t.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-2", children: [
195
- /* @__PURE__ */ t.jsx(
196
- "div",
197
- {
198
- className: "iw-h-7 iw-w-7 iw-rounded-md iw-bg-purple-500 iw-flex iw-items-center iw-justify-center iw-text-white iw-font-semibold",
199
- children: "N"
200
- }
201
- ),
202
- /* @__PURE__ */ t.jsx("p", { className: "iw-text-sm iw-font-medium", children: "Novara" })
203
- ] }),
204
- /* @__PURE__ */ t.jsx("h1", { className: "iw-text-base iw-font-medium", children: i }),
205
- /* @__PURE__ */ t.jsx("button", { className: "iw-text-sm iw-text-gray-500 hover:iw-text-gray-700", children: "Exit Interview" })
206
- ] }),
207
- /* @__PURE__ */ t.jsx("div", { className: "iw-h-px iw-bg-gray-200" })
208
- ] });
209
- function Y(i, e = {
210
- immediate: !0,
211
- enabled: !0
212
- }) {
213
- const [s, n] = m({
424
+ };
425
+ var l = /* @__PURE__ */ ((i) => (i.IDLE = "idle", i.FETCHING_QUESTION = "fetching_question", i.READING_QUESTION = "reading_question", i.THINKING = "thinking", i.ANSWERING = "answering", i.TRANSCRIBING = "transcribing", i.EDITING = "editing", i.SUBMITTING = "submitting", i.COMPLETED = "completed", i))(l || {});
426
+ const Re = {
427
+ thinkingDuration: 30,
428
+ answeringDuration: 120,
429
+ editingDuration: 30,
430
+ totalInterviewDuration: 600,
431
+ minimumTimeForNextQuestion: 120
432
+ }, Ce = {
433
+ idle: {
434
+ next: "fetching_question"
435
+ /* FETCHING_QUESTION */
436
+ },
437
+ fetching_question: {
438
+ next: "reading_question"
439
+ /* READING_QUESTION */
440
+ },
441
+ reading_question: {
442
+ next: "thinking"
443
+ /* THINKING */
444
+ },
445
+ thinking: { next: "answering", duration: 30 },
446
+ answering: { next: "transcribing", duration: 120 },
447
+ transcribing: {
448
+ next: "editing"
449
+ /* EDITING */
450
+ },
451
+ editing: { next: "submitting", duration: 30 },
452
+ submitting: {
453
+ next: "completed"
454
+ /* COMPLETED */
455
+ },
456
+ completed: {
457
+ next: "completed"
458
+ /* COMPLETED */
459
+ }
460
+ };
461
+ class ke {
462
+ constructor(t = {}, e = {}) {
463
+ I(this, "config");
464
+ I(this, "state");
465
+ I(this, "phaseIntervalId", null);
466
+ I(this, "globalIntervalId", null);
467
+ I(this, "callbacks");
468
+ this.config = { ...Re, ...t }, this.callbacks = e, this.state = {
469
+ phase: "idle",
470
+ currentPhaseTimeRemaining: 0,
471
+ totalTimeRemaining: this.config.totalInterviewDuration,
472
+ totalTimeElapsed: 0,
473
+ currentQuestionNumber: 0
474
+ };
475
+ }
476
+ /**
477
+ * Get current state
478
+ */
479
+ getState() {
480
+ return { ...this.state };
481
+ }
482
+ /**
483
+ * Start a new question cycle
484
+ */
485
+ startQuestion() {
486
+ var t, e;
487
+ if (this.state.totalTimeRemaining < this.config.minimumTimeForNextQuestion) {
488
+ this.state.phase = "completed", this.stopGlobalTimer(), this.stopPhaseTimer(), (e = (t = this.callbacks).onInterviewEnd) == null || e.call(t), this.notifyChange();
489
+ return;
490
+ }
491
+ this.state.currentQuestionNumber === 0 && this.startGlobalTimer(), this.state.currentQuestionNumber = this.state.currentQuestionNumber + 1, this.state.phase = "fetching_question", this.notifyChange();
492
+ }
493
+ /**
494
+ * Move to next phase
495
+ */
496
+ nextPhase() {
497
+ const t = this.state.phase, e = Ce[t];
498
+ if (!e) return;
499
+ this.stopPhaseTimer(), this.state.phase = e.next;
500
+ const a = this.getDurationForPhase(e.next);
501
+ a > 0 ? (this.state.currentPhaseTimeRemaining = a, this.startPhaseTimer()) : this.state.currentPhaseTimeRemaining = 0, this.notifyChange();
502
+ }
503
+ /**
504
+ * Cleanup
505
+ */
506
+ destroy() {
507
+ this.stopPhaseTimer(), this.stopGlobalTimer();
508
+ }
509
+ /**
510
+ * Get duration for a phase
511
+ */
512
+ getDurationForPhase(t) {
513
+ switch (t) {
514
+ case "thinking":
515
+ return this.config.thinkingDuration;
516
+ case "answering":
517
+ return this.config.answeringDuration;
518
+ case "editing":
519
+ return this.config.editingDuration;
520
+ default:
521
+ return 0;
522
+ }
523
+ }
524
+ /**
525
+ * Start global timer (runs continuously during entire interview)
526
+ */
527
+ startGlobalTimer() {
528
+ this.globalIntervalId = setInterval(() => {
529
+ var t, e, a, s;
530
+ this.state.totalTimeRemaining--, this.state.totalTimeElapsed++, (e = (t = this.callbacks).onTick) == null || e.call(t, this.getState()), this.state.totalTimeRemaining <= 0 && (this.stopGlobalTimer(), this.stopPhaseTimer(), this.state.phase = "completed", (s = (a = this.callbacks).onInterviewEnd) == null || s.call(a), this.notifyChange());
531
+ }, 1e3);
532
+ }
533
+ /**
534
+ * Stop global timer
535
+ */
536
+ stopGlobalTimer() {
537
+ this.globalIntervalId && (clearInterval(this.globalIntervalId), this.globalIntervalId = null);
538
+ }
539
+ /**
540
+ * Start phase timer (only for timed phases)
541
+ */
542
+ startPhaseTimer() {
543
+ this.phaseIntervalId = setInterval(() => {
544
+ this.state.currentPhaseTimeRemaining--, this.state.currentPhaseTimeRemaining <= 0 && this.nextPhase();
545
+ }, 1e3);
546
+ }
547
+ /**
548
+ * Stop phase timer
549
+ */
550
+ stopPhaseTimer() {
551
+ this.phaseIntervalId && (clearInterval(this.phaseIntervalId), this.phaseIntervalId = null);
552
+ }
553
+ /**
554
+ * Notify phase change
555
+ */
556
+ notifyChange() {
557
+ var t, e;
558
+ (e = (t = this.callbacks).onPhaseChange) == null || e.call(t, this.state.phase, this.getState());
559
+ }
560
+ }
561
+ function De(i = {}) {
562
+ const { config: t = {}, callbacks: e = {} } = i, a = D(null);
563
+ a.current || (a.current = new ke(t, {}));
564
+ const s = a.current, [c, n] = y(s.getState()), o = v(() => {
565
+ n(s.getState());
566
+ }, [s]);
567
+ A(() => {
568
+ const d = {
569
+ onPhaseChange: (u, h) => {
570
+ var x;
571
+ o(), (x = e.onPhaseChange) == null || x.call(e, u, h);
572
+ },
573
+ onTick: (u) => {
574
+ var h;
575
+ o(), (h = e.onTick) == null || h.call(e, u);
576
+ },
577
+ onInterviewEnd: () => {
578
+ var u;
579
+ o(), (u = e.onInterviewEnd) == null || u.call(e);
580
+ }
581
+ };
582
+ s.callbacks = d;
583
+ }, [s, e, o]), A(() => () => {
584
+ s.destroy();
585
+ }, [s]);
586
+ const m = v(() => {
587
+ s.startQuestion(), o();
588
+ }, [s, o]), g = v(() => {
589
+ s.nextPhase(), o();
590
+ }, [s, o]);
591
+ return {
592
+ state: c,
593
+ startQuestion: m,
594
+ nextPhase: g,
595
+ timerService: s
596
+ };
597
+ }
598
+ function Pe(i, t = {}) {
599
+ const [e, a] = y({
214
600
  data: null,
215
601
  loading: !1,
216
602
  error: null
217
- }), r = T(async () => {
218
- var o, l;
219
- if (e.enabled !== !1) {
220
- n((w) => ({ ...w, loading: !0, error: null }));
603
+ }), s = v(
604
+ async (...c) => {
605
+ var n, o, m, g;
606
+ a((d) => ({ ...d, loading: !0, error: null }));
221
607
  try {
222
- const w = await i();
223
- n((d) => ({
224
- ...d,
225
- data: w,
608
+ const d = await i(...c);
609
+ a((u) => ({
610
+ ...u,
611
+ data: d,
226
612
  loading: !1,
227
613
  error: null
228
- })), (o = e.onSuccess) == null || o.call(e, w);
229
- } catch (w) {
230
- const d = w.type ? w : {
614
+ })), (n = t.onSuccess) == null || n.call(t, d), (o = t.onSettled) == null || o.call(t, d, null);
615
+ } catch (d) {
616
+ const u = d.type ? d : {
231
617
  type: "unknown",
232
- message: w.message || "Unknown error",
618
+ message: d.message || "Unknown error",
233
619
  retryable: !0,
234
620
  userMessage: "Something went wrong. Please try again.",
235
- originalError: w
621
+ originalError: d
236
622
  };
237
- n((u) => ({
238
- ...u,
623
+ a((h) => ({
624
+ ...h,
239
625
  loading: !1,
240
- error: d
241
- })), (l = e.onError) == null || l.call(e, d);
626
+ error: u
627
+ })), (m = t.onError) == null || m.call(t, u), (g = t.onSettled) == null || g.call(t, null, u);
242
628
  }
243
- }
244
- }, [i, e]), a = T(() => {
245
- e.enabled !== !1 && r();
246
- }, [r, e.enabled]);
247
- return P(() => {
248
- (e.immediate && e.enabled !== !1 || e.enabled === !0) && r();
249
- }, [e.immediate, e.enabled]), {
250
- ...s,
251
- execute: r,
252
- retry: a
629
+ },
630
+ [i, t]
631
+ );
632
+ return {
633
+ ...e,
634
+ execute: s
253
635
  };
254
636
  }
255
- function _(i) {
256
- var e;
637
+ function Ae(i) {
638
+ var t;
257
639
  if (!navigator.onLine)
258
640
  return {
259
641
  type: "network",
@@ -261,7 +643,7 @@ function _(i) {
261
643
  retryable: !0,
262
644
  userMessage: "Please check your internet connection and try again."
263
645
  };
264
- if (i.name === "AbortError" || (e = i.message) != null && e.includes("timeout"))
646
+ if (i.name === "AbortError" || (t = i.message) != null && t.includes("timeout"))
265
647
  return {
266
648
  type: "timeout",
267
649
  message: "Request timed out",
@@ -269,36 +651,36 @@ function _(i) {
269
651
  userMessage: "The request is taking longer than expected. Please try again."
270
652
  };
271
653
  if (i.status) {
272
- const { status: s } = i;
273
- if (s === 401 || s === 403)
654
+ const { status: e } = i;
655
+ if (e === 401 || e === 403)
274
656
  return {
275
657
  type: "auth",
276
- status: s,
658
+ status: e,
277
659
  message: "Authentication failed",
278
660
  retryable: !1,
279
661
  userMessage: "Your session has expired. Please refresh the page."
280
662
  };
281
- if (s === 429)
663
+ if (e === 429)
282
664
  return {
283
665
  type: "rate-limit",
284
- status: s,
666
+ status: e,
285
667
  message: "Too many requests",
286
668
  retryable: !0,
287
669
  userMessage: "Please wait a moment before trying again."
288
670
  };
289
- if (s >= 500)
671
+ if (e >= 500)
290
672
  return {
291
673
  type: "server",
292
- status: s,
293
- message: `Server error: ${s}`,
674
+ status: e,
675
+ message: `Server error: ${e}`,
294
676
  retryable: !0,
295
677
  userMessage: "Our servers are experiencing issues. Please try again in a few moments."
296
678
  };
297
- if (s >= 400)
679
+ if (e >= 400)
298
680
  return {
299
681
  type: "client",
300
- status: s,
301
- message: `Client error: ${s}`,
682
+ status: e,
683
+ message: `Client error: ${e}`,
302
684
  retryable: !1,
303
685
  userMessage: "There was an issue with your request. Please check your input."
304
686
  };
@@ -311,329 +693,798 @@ function _(i) {
311
693
  originalError: i
312
694
  };
313
695
  }
314
- async function x(i, e = {}, s = {
696
+ async function H(i, t = {}, e = {
315
697
  attempts: 3,
316
698
  backoff: "exponential",
317
699
  baseDelay: 1e3,
318
700
  maxDelay: 1e4,
319
701
  jitter: !0
320
702
  }) {
321
- let n;
322
- for (let r = 1; r <= s.attempts; r++)
703
+ let a;
704
+ for (let s = 1; s <= e.attempts; s++)
323
705
  try {
324
- const a = new AbortController(), o = setTimeout(() => a.abort(), 6e4), l = await fetch(i, {
325
- ...e,
326
- signal: a.signal
706
+ const c = new AbortController(), n = setTimeout(() => c.abort(), 6e4), o = await fetch(i, {
707
+ ...t,
708
+ signal: c.signal
327
709
  });
328
- if (clearTimeout(o), l.status >= 400 && l.status < 500 && l.status !== 429)
329
- return l;
330
- if (!l.ok)
331
- throw new Error(`HTTP ${l.status}: ${l.statusText}`);
332
- return l;
333
- } catch (a) {
334
- n = a;
335
- const o = _(a);
336
- if (!o.retryable || r === s.attempts)
337
- throw o;
338
- const l = W(r, s);
710
+ if (clearTimeout(n), o.status >= 400 && o.status < 500 && o.status !== 429)
711
+ return o;
712
+ if (!o.ok)
713
+ throw new Error(`HTTP ${o.status}: ${o.statusText}`);
714
+ return o;
715
+ } catch (c) {
716
+ a = c;
717
+ const n = Ae(c);
718
+ if (!n.retryable || s === e.attempts)
719
+ throw n;
720
+ const o = Ge(s, e);
339
721
  console.warn(
340
- `API request failed (attempt ${r}/${s.attempts}), retrying in ${l}ms:`,
341
- o.message
342
- ), await new Promise((w) => setTimeout(w, l));
722
+ `API request failed (attempt ${s}/${e.attempts}), retrying in ${o}ms:`,
723
+ n.message
724
+ ), await new Promise((m) => setTimeout(m, o));
343
725
  }
344
- throw n;
726
+ throw a;
345
727
  }
346
- function W(i, e) {
347
- let s;
348
- return e.backoff === "exponential" ? s = e.baseDelay * Math.pow(2, i - 1) : s = e.baseDelay, s = Math.min(s, e.maxDelay), e.jitter && (s = s * (0.5 + Math.random() * 0.5)), Math.round(s);
728
+ function Ge(i, t) {
729
+ let e;
730
+ return t.backoff === "exponential" ? e = t.baseDelay * Math.pow(2, i - 1) : e = t.baseDelay, e = Math.min(e, t.maxDelay), t.jitter && (e = e * (0.5 + Math.random() * 0.5)), Math.round(e);
349
731
  }
350
- class B {
351
- constructor(e = "/api", s) {
352
- $(this, "baseURL");
353
- $(this, "authToken");
354
- this.baseURL = e, this.authToken = s;
732
+ class Me {
733
+ constructor(t = {}) {
734
+ I(this, "config");
735
+ this.config = t;
355
736
  }
356
737
  /**
357
- * Set authentication token for API requests
738
+ * Update configuration
358
739
  */
359
- setAuthToken(e) {
360
- this.authToken = e;
740
+ updateConfig(t) {
741
+ this.config = { ...this.config, ...t };
361
742
  }
362
743
  /**
363
744
  * Get default headers for API requests
364
745
  */
365
746
  getHeaders() {
366
- const e = {
367
- "Content-Type": "application/json"
747
+ var e;
748
+ const t = {
749
+ "Content-Type": "application/json",
750
+ "X-Auth-Token": "appkey"
368
751
  };
369
- return this.authToken && (e.Authorization = `Bearer ${this.authToken}`), e;
752
+ return (e = this.config) != null && e.authToken && (t.Authorization = `Bearer ${this.config.authToken}`), t;
370
753
  }
371
754
  /**
372
- * Start a new interview session
755
+ * Get base URL from config with fallback
373
756
  */
374
- async startInterview(e) {
375
- const s = await x(`${this.baseURL}/interviews/start`, {
376
- method: "POST",
377
- headers: this.getHeaders(),
378
- body: JSON.stringify({ candidateId: e })
379
- });
380
- if (!s.ok)
381
- throw new Error(`Failed to start interview: ${s.status}`);
382
- return s.json();
757
+ getBaseUrl() {
758
+ var t;
759
+ return ((t = this.config) == null ? void 0 : t.baseUrl) || "/api";
383
760
  }
384
761
  /**
385
762
  * Get questions for an interview
386
763
  */
387
- async getQuestions(e) {
388
- const s = await x(
389
- `${this.baseURL}/interviews/${e}/questions`,
764
+ async generateQuestion({
765
+ interviewId: t,
766
+ isInterviewDone: e = !1,
767
+ qnaId: a,
768
+ question: s,
769
+ answer: c,
770
+ answerDuration: n
771
+ }) {
772
+ const o = await H(
773
+ `${this.getBaseUrl()}/interview/generate-question`,
390
774
  {
391
- method: "GET",
392
- headers: this.getHeaders()
775
+ method: "POST",
776
+ headers: this.getHeaders(),
777
+ body: JSON.stringify({
778
+ interview_id: t,
779
+ is_interview_done: e,
780
+ qna_id: a,
781
+ question: s,
782
+ answer: c,
783
+ // TODO: Hardcoded for now, will be dynamic later
784
+ answer_duration: n ?? "00:00:30"
785
+ })
393
786
  }
394
787
  );
395
- if (!s.ok)
396
- throw new Error(`Failed to get questions: ${s.status}`);
397
- const n = await s.json();
398
- return n.questions || n;
788
+ if (!o.ok)
789
+ throw new Error(`Failed to get questions: ${o.status}`);
790
+ return await o.json();
791
+ }
792
+ }
793
+ function Oe() {
794
+ const i = q();
795
+ return xe(() => {
796
+ const e = i.api || {};
797
+ return new Me(e);
798
+ }, [i.api]);
799
+ }
800
+ class b extends Error {
801
+ constructor(t, e, a = !1) {
802
+ super(t), this.code = e, this.recoverable = a, this.name = "STTError";
803
+ }
804
+ }
805
+ class $e {
806
+ constructor(t = {}) {
807
+ I(this, "config");
808
+ I(this, "mediaRecorder", null);
809
+ I(this, "audioChunks", []);
810
+ I(this, "recordingStream", null);
811
+ I(this, "autoStopTimeoutId", null);
812
+ this.config = {
813
+ baseUrl: "http://localhost:8000",
814
+ provider: "groq",
815
+ model: "whisper-large-v3-turbo",
816
+ language: "en",
817
+ includeTimestamps: !1,
818
+ temperature: 0,
819
+ ...t
820
+ };
399
821
  }
400
822
  /**
401
- * Submit an answer for a question
823
+ * Update STT configuration
402
824
  */
403
- async submitAnswer(e) {
404
- const s = await x(`${this.baseURL}/answers`, {
405
- method: "POST",
406
- headers: this.getHeaders(),
407
- body: JSON.stringify(e)
408
- });
409
- if (!s.ok)
410
- throw new Error(`Failed to submit answer: ${s.status}`);
825
+ updateConfig(t) {
826
+ this.config = { ...this.config, ...t };
411
827
  }
412
828
  /**
413
- * Complete an interview session
829
+ * Check if browser supports audio recording
414
830
  */
415
- async completeInterview(e) {
416
- const s = await x(
417
- `${this.baseURL}/interviews/${e}/complete`,
418
- {
419
- method: "POST",
420
- headers: this.getHeaders()
831
+ isRecordingSupported() {
832
+ return !!(navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia == "function" && window.MediaRecorder);
833
+ }
834
+ /**
835
+ * Start recording audio from user's microphone
836
+ */
837
+ async startRecording(t, e) {
838
+ var a;
839
+ if (!this.isRecordingSupported())
840
+ throw new b(
841
+ "Audio recording is not supported in this browser",
842
+ "RECORDING_NOT_SUPPORTED",
843
+ !1
844
+ );
845
+ if (this.isRecording())
846
+ throw new b(
847
+ "Recording is already in progress",
848
+ "ALREADY_RECORDING",
849
+ !0
850
+ );
851
+ try {
852
+ this.recordingStream = await navigator.mediaDevices.getUserMedia({
853
+ audio: {
854
+ echoCancellation: !0,
855
+ noiseSuppression: !0,
856
+ sampleRate: 44100
857
+ }
858
+ }), this.audioChunks = [];
859
+ const s = this.getSupportedMimeType();
860
+ this.mediaRecorder = new MediaRecorder(this.recordingStream, {
861
+ mimeType: s
862
+ }), this.mediaRecorder.ondataavailable = (c) => {
863
+ var n;
864
+ c.data.size > 0 && (this.audioChunks.push(c.data), (n = e == null ? void 0 : e.onDataAvailable) == null || n.call(e, c.data));
865
+ }, this.mediaRecorder.onstop = () => {
866
+ var c;
867
+ (c = e == null ? void 0 : e.onStop) == null || c.call(e);
868
+ }, this.mediaRecorder.onerror = (c) => {
869
+ var o;
870
+ const n = new b(
871
+ `Recording failed: ${c.error}`,
872
+ "RECORDING_ERROR",
873
+ !0
874
+ );
875
+ (o = e == null ? void 0 : e.onError) == null || o.call(e, n), this.cleanup();
876
+ }, this.mediaRecorder.start(100), (a = e == null ? void 0 : e.onStart) == null || a.call(e), t && t > 0 && (this.autoStopTimeoutId = setTimeout(() => {
877
+ this.isRecording() && this.stopRecording();
878
+ }, t * 1e3));
879
+ } catch (s) {
880
+ if (this.cleanup(), s instanceof Error) {
881
+ if (s.name === "NotAllowedError" || s.name === "PermissionDeniedError")
882
+ throw new b(
883
+ "Microphone permission was denied",
884
+ "PERMISSION_DENIED",
885
+ !1
886
+ );
887
+ if (s.name === "NotFoundError")
888
+ throw new b("No microphone found", "NO_MICROPHONE", !1);
421
889
  }
422
- );
423
- if (!s.ok)
424
- throw new Error(`Failed to complete interview: ${s.status}`);
425
- return s.json();
890
+ throw new b(
891
+ `Failed to start recording: ${s instanceof Error ? s.message : String(s)}`,
892
+ "START_RECORDING_FAILED",
893
+ !0
894
+ );
895
+ }
426
896
  }
427
897
  /**
428
- * Get interview statistics
898
+ * Stop recording and return the audio blob
429
899
  */
430
- async getInterviewStats() {
431
- const e = await x(`${this.baseURL}/interviews/stats`, {
432
- method: "GET",
433
- headers: this.getHeaders()
900
+ async stopRecording() {
901
+ if (this.autoStopTimeoutId && (clearTimeout(this.autoStopTimeoutId), this.autoStopTimeoutId = null), !this.mediaRecorder || !this.isRecording())
902
+ throw new b(
903
+ "No active recording to stop",
904
+ "NO_ACTIVE_RECORDING",
905
+ !1
906
+ );
907
+ return new Promise((t, e) => {
908
+ if (!this.mediaRecorder) {
909
+ e(
910
+ new b("MediaRecorder is null", "MEDIARECORDER_NULL", !1)
911
+ );
912
+ return;
913
+ }
914
+ const a = this.mediaRecorder, s = () => {
915
+ try {
916
+ const c = a.mimeType || "audio/webm", n = new Blob(this.audioChunks, { type: c });
917
+ this.cleanup(), t(n);
918
+ } catch (c) {
919
+ e(
920
+ new b(
921
+ `Failed to create audio blob: ${c instanceof Error ? c.message : String(c)}`,
922
+ "BLOB_CREATION_FAILED",
923
+ !1
924
+ )
925
+ );
926
+ }
927
+ };
928
+ a.addEventListener("stop", s, { once: !0 }), a.stop();
434
929
  });
435
- if (!e.ok)
436
- throw new Error(`Failed to get stats: ${e.status}`);
437
- return e.json();
438
930
  }
439
931
  /**
440
- * Get interview session details
932
+ * Transcribe audio blob using the STT API
441
933
  */
442
- async getInterviewSession(e) {
443
- const s = await x(
444
- `${this.baseURL}/interviews/${e}`,
445
- {
446
- method: "GET",
447
- headers: this.getHeaders()
934
+ async transcribe(t) {
935
+ const {
936
+ audioBlob: e,
937
+ model: a = this.config.model,
938
+ language: s = this.config.language,
939
+ includeTimestamps: c = this.config.includeTimestamps,
940
+ temperature: n = this.config.temperature
941
+ } = t;
942
+ if (!e || e.size === 0)
943
+ throw new b(
944
+ "Audio blob is empty or invalid",
945
+ "INVALID_AUDIO",
946
+ !1
947
+ );
948
+ try {
949
+ const o = new FormData(), m = new File([e], "recording.wav", {
950
+ type: e.type || "audio/wav"
951
+ });
952
+ o.append("file", m), o.append("model", a || "whisper-large-v3-turbo"), o.append("language", s || "en"), o.append("include_timestamps", String(c || !1)), o.append("temperature", String(n || 0));
953
+ const g = await H(
954
+ `${this.config.baseUrl}/speech/transcribe`,
955
+ {
956
+ method: "POST",
957
+ headers: {
958
+ accept: "application/json",
959
+ "X-STT-Provider": this.config.provider || "groq"
960
+ },
961
+ body: o
962
+ },
963
+ {
964
+ attempts: 1,
965
+ backoff: "exponential",
966
+ baseDelay: 1e3,
967
+ maxDelay: 3e3,
968
+ jitter: !0
969
+ }
970
+ );
971
+ if (!g.ok) {
972
+ const u = await g.text();
973
+ let h = `STT request failed: ${g.status} ${g.statusText}`;
974
+ try {
975
+ const x = JSON.parse(u);
976
+ h = x.message || x.error || h;
977
+ } catch {
978
+ h = u || h;
979
+ }
980
+ throw new b(
981
+ h,
982
+ `HTTP_${g.status}`,
983
+ g.status >= 500
984
+ );
448
985
  }
449
- );
450
- if (!s.ok)
451
- throw new Error(`Failed to get session: ${s.status}`);
452
- return s.json();
986
+ return {
987
+ transcript: (await g.json()).data.text ?? ""
988
+ };
989
+ } catch (o) {
990
+ throw o instanceof b ? o : new b(
991
+ `Transcription failed: ${o instanceof Error ? o.message : String(o)}`,
992
+ "TRANSCRIPTION_FAILED",
993
+ !0
994
+ );
995
+ }
453
996
  }
454
997
  /**
455
- * Get Products
456
- * Fake API example
457
- * This API endpoint retrieves a list of products from the fake store API.
998
+ * Cancel current recording without processing
458
999
  */
459
- async getProducts() {
460
- const e = await x(`${this.baseURL}/products`, {
461
- method: "GET",
462
- headers: this.getHeaders()
463
- });
464
- if (!e.ok)
465
- throw new Error(`Failed to get products: ${e.status}`);
466
- return e.json();
467
- }
468
- // Delete product
469
- async deleteProduct(e) {
470
- const s = await x(
471
- `${this.baseURL}/products/${e}`,
472
- {
473
- method: "DELETE",
474
- headers: this.getHeaders()
475
- }
476
- );
477
- if (!s.ok)
478
- throw new Error(`Failed to delete product: ${s.status}`);
1000
+ cancelRecording() {
1001
+ this.autoStopTimeoutId && (clearTimeout(this.autoStopTimeoutId), this.autoStopTimeoutId = null), this.mediaRecorder && this.isRecording() && this.mediaRecorder.stop(), this.cleanup();
1002
+ }
1003
+ /**
1004
+ * Check if currently recording
1005
+ */
1006
+ isRecording() {
1007
+ return this.mediaRecorder !== null && this.mediaRecorder.state === "recording";
1008
+ }
1009
+ /**
1010
+ * Get supported MIME type for recording
1011
+ */
1012
+ getSupportedMimeType() {
1013
+ const t = [
1014
+ "audio/webm",
1015
+ "audio/webm;codecs=opus",
1016
+ "audio/ogg;codecs=opus",
1017
+ "audio/mp4",
1018
+ "audio/wav"
1019
+ ];
1020
+ for (const e of t)
1021
+ if (MediaRecorder.isTypeSupported(e))
1022
+ return e;
1023
+ return "audio/webm";
1024
+ }
1025
+ /**
1026
+ * Clean up recording resources
1027
+ */
1028
+ cleanup() {
1029
+ this.recordingStream && (this.recordingStream.getTracks().forEach((t) => t.stop()), this.recordingStream = null), this.mediaRecorder = null, this.audioChunks = [], this.autoStopTimeoutId && (clearTimeout(this.autoStopTimeoutId), this.autoStopTimeoutId = null);
479
1030
  }
480
1031
  }
481
- const S = new B("https://fakestoreapi.com"), J = ({ question: i }) => {
482
- console.log("🚀 ~ question:", i);
483
- const {
484
- data: e,
485
- loading: s,
486
- retry: n
487
- } = Y(() => S.getProducts(), {
488
- immediate: !0,
489
- onSuccess: (a) => {
490
- console.log("Products loaded:", a);
1032
+ const O = new $e(), _e = (i = {}) => {
1033
+ const [t, e] = y(!1), [a, s] = y(!1), [c, n] = y(null), [o, m] = y(null), [g, d] = y(null);
1034
+ i.config && O.updateConfig(i.config);
1035
+ const u = v(async (j) => {
1036
+ var N;
1037
+ try {
1038
+ m(null), n(null), d(null), await O.startRecording(j, {
1039
+ onStart: () => {
1040
+ var w;
1041
+ e(!0), (w = i.onStart) == null || w.call(i);
1042
+ },
1043
+ onStop: () => {
1044
+ var w;
1045
+ e(!1), (w = i.onStop) == null || w.call(i);
1046
+ },
1047
+ onError: (w) => {
1048
+ var T;
1049
+ const p = w instanceof b ? w : new b(w.message, "RECORDING_ERROR", !0);
1050
+ m(p), e(!1), (T = i.onError) == null || T.call(i, p);
1051
+ }
1052
+ });
1053
+ } catch (w) {
1054
+ const p = w instanceof b ? w : new b(
1055
+ w instanceof Error ? w.message : String(w),
1056
+ "START_FAILED",
1057
+ !1
1058
+ );
1059
+ throw m(p), e(!1), (N = i.onError) == null || N.call(i, p), p;
1060
+ }
1061
+ }, []), h = v(async () => {
1062
+ var j, N;
1063
+ try {
1064
+ const w = await O.stopRecording();
1065
+ return d(w), e(!1), (j = i.onStop) == null || j.call(i), w;
1066
+ } catch (w) {
1067
+ const p = w instanceof b ? w : new b(
1068
+ w instanceof Error ? w.message : String(w),
1069
+ "STOP_FAILED",
1070
+ !1
1071
+ );
1072
+ throw m(p), e(!1), (N = i.onError) == null || N.call(i, p), p;
1073
+ }
1074
+ }, []), x = v(
1075
+ async (j, N = {}) => {
1076
+ var w, p;
1077
+ try {
1078
+ m(null), s(!0);
1079
+ const T = {
1080
+ audioBlob: j,
1081
+ ...N
1082
+ }, S = await O.transcribe(T);
1083
+ return n(S.transcript), s(!1), (w = i.onTranscriptionComplete) == null || w.call(i, S), S;
1084
+ } catch (T) {
1085
+ const S = T instanceof b ? T : new b(
1086
+ T instanceof Error ? T.message : String(T),
1087
+ "TRANSCRIPTION_FAILED",
1088
+ !0
1089
+ );
1090
+ throw m(S), s(!1), (p = i.onError) == null || p.call(i, S), S;
1091
+ }
491
1092
  },
492
- onError: (a) => {
493
- console.error("Failed to load products:", a.userMessage);
1093
+ []
1094
+ ), k = v(() => {
1095
+ O.cancelRecording(), e(!1), d(null);
1096
+ }, []);
1097
+ return {
1098
+ startRecording: u,
1099
+ stopRecording: h,
1100
+ transcribe: x,
1101
+ cancelRecording: k,
1102
+ isRecording: t,
1103
+ isTranscribing: a,
1104
+ transcript: c,
1105
+ error: o,
1106
+ audioBlob: g
1107
+ };
1108
+ };
1109
+ class Le {
1110
+ constructor(t = {}) {
1111
+ I(this, "config");
1112
+ I(this, "currentAudio", null);
1113
+ this.config = {
1114
+ baseUrl: "http://localhost:8000",
1115
+ provider: "piper",
1116
+ voice: "string",
1117
+ speed: 1,
1118
+ ...t
1119
+ };
1120
+ }
1121
+ /**
1122
+ * Update TTS configuration
1123
+ */
1124
+ updateConfig(t) {
1125
+ this.config = { ...this.config, ...t };
1126
+ }
1127
+ /**
1128
+ * Get TTS audio from the API and return as blob
1129
+ */
1130
+ async synthesizeSpeech(t) {
1131
+ const {
1132
+ text: e,
1133
+ voice: a = this.config.voice,
1134
+ speed: s = this.config.speed
1135
+ } = t, c = new URLSearchParams();
1136
+ c.append("text", e), c.append("voice", a || "string"), c.append("speed", (s == null ? void 0 : s.toString()) || "1");
1137
+ const n = await H(
1138
+ `${this.config.baseUrl}/speech/synthesize`,
1139
+ {
1140
+ method: "POST",
1141
+ headers: {
1142
+ accept: "application/json",
1143
+ "X-TTS-Provider": this.config.provider || "piper",
1144
+ "Content-Type": "application/x-www-form-urlencoded"
1145
+ },
1146
+ body: c
1147
+ },
1148
+ {
1149
+ attempts: 1,
1150
+ backoff: "fixed",
1151
+ baseDelay: 1e3,
1152
+ maxDelay: 1e3,
1153
+ jitter: !1
1154
+ }
1155
+ );
1156
+ if (!n.ok)
1157
+ throw new Error(
1158
+ `TTS request failed: ${n.status} ${n.statusText}`
1159
+ );
1160
+ const o = n.headers.get("content-type");
1161
+ if (o && o.includes("audio/"))
1162
+ return n.blob();
1163
+ try {
1164
+ const m = await n.json();
1165
+ throw new Error(`TTS Error: ${JSON.stringify(m)}`);
1166
+ } catch {
1167
+ throw new Error("TTS request failed with unknown error");
494
1168
  }
495
- }), r = async (a) => {
1169
+ }
1170
+ /**
1171
+ * Speak text and return a promise that resolves when playback completes
1172
+ */
1173
+ async speak(t, e) {
1174
+ var a, s;
496
1175
  try {
497
- await S.deleteProduct(a), console.log(`Product ${a} deleted successfully`), n();
498
- } catch (o) {
499
- console.error("Failed to delete product:", o);
1176
+ this.stop(), (a = e == null ? void 0 : e.onStart) == null || a.call(e);
1177
+ const c = await this.synthesizeSpeech(t), n = URL.createObjectURL(c);
1178
+ return this.currentAudio = new Audio(n), new Promise((o, m) => {
1179
+ if (!this.currentAudio) {
1180
+ m(new Error("Audio element not created"));
1181
+ return;
1182
+ }
1183
+ const g = this.currentAudio;
1184
+ g.onended = () => {
1185
+ var d;
1186
+ URL.revokeObjectURL(n), this.currentAudio = null, (d = e == null ? void 0 : e.onEnd) == null || d.call(e), o();
1187
+ }, g.onerror = (d) => {
1188
+ var h;
1189
+ URL.revokeObjectURL(n), this.currentAudio = null;
1190
+ const u = new Error(`Audio playback failed: ${d}`);
1191
+ (h = e == null ? void 0 : e.onError) == null || h.call(e, u), m(u);
1192
+ }, g.play().catch((d) => {
1193
+ var h;
1194
+ URL.revokeObjectURL(n), this.currentAudio = null;
1195
+ const u = new Error(`Failed to play audio: ${d.message}`);
1196
+ (h = e == null ? void 0 : e.onError) == null || h.call(e, u), m(u);
1197
+ });
1198
+ });
1199
+ } catch (c) {
1200
+ const n = c instanceof Error ? c : new Error(`TTS Error: ${String(c)}`);
1201
+ throw (s = e == null ? void 0 : e.onError) == null || s.call(e, n), n;
500
1202
  }
1203
+ }
1204
+ /**
1205
+ * Stop current audio playback
1206
+ */
1207
+ stop() {
1208
+ this.currentAudio && (this.currentAudio.pause(), this.currentAudio.currentTime = 0, this.currentAudio = null);
1209
+ }
1210
+ /**
1211
+ * Check if audio is currently playing
1212
+ */
1213
+ isPlaying() {
1214
+ return this.currentAudio !== null && !this.currentAudio.paused;
1215
+ }
1216
+ }
1217
+ const B = new Le(), Ue = (i = {}) => {
1218
+ const [t, e] = y(!1), [a, s] = y(!1), [c, n] = y(null);
1219
+ i.config && B.updateConfig(i.config);
1220
+ const o = v(
1221
+ async (g, d = {}) => {
1222
+ var u;
1223
+ try {
1224
+ n(null), s(!0);
1225
+ const h = {
1226
+ text: g,
1227
+ ...d
1228
+ };
1229
+ await B.speak(h, {
1230
+ onStart: () => {
1231
+ var x;
1232
+ s(!1), e(!0), (x = i.onStart) == null || x.call(i);
1233
+ },
1234
+ onEnd: () => {
1235
+ var x;
1236
+ e(!1), (x = i.onEnd) == null || x.call(i);
1237
+ },
1238
+ onError: (x) => {
1239
+ var k;
1240
+ e(!1), s(!1), n(x), (k = i.onError) == null || k.call(i, x);
1241
+ }
1242
+ });
1243
+ } catch (h) {
1244
+ const x = h instanceof Error ? h : new Error(String(h));
1245
+ throw n(x), e(!1), s(!1), (u = i.onError) == null || u.call(i, x), x;
1246
+ }
1247
+ },
1248
+ [i]
1249
+ ), m = v(() => {
1250
+ B.stop(), e(!1), s(!1);
1251
+ }, []);
1252
+ return {
1253
+ speak: o,
1254
+ stop: m,
1255
+ isPlaying: t,
1256
+ isLoading: a,
1257
+ error: c
1258
+ };
1259
+ };
1260
+ function qe(i) {
1261
+ const t = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(i);
1262
+ return t ? {
1263
+ r: parseInt(t[1], 16),
1264
+ g: parseInt(t[2], 16),
1265
+ b: parseInt(t[3], 16)
1266
+ } : null;
1267
+ }
1268
+ function Fe(i, t, e) {
1269
+ return "#" + [i, t, e].map((a) => {
1270
+ const s = Math.round(a).toString(16);
1271
+ return s.length === 1 ? "0" + s : s;
1272
+ }).join("");
1273
+ }
1274
+ function V(i, t) {
1275
+ const e = qe(i);
1276
+ if (!e) return i;
1277
+ const a = e.r + (255 - e.r) * t, s = e.g + (255 - e.g) * t, c = e.b + (255 - e.b) * t;
1278
+ return Fe(a, s, c);
1279
+ }
1280
+ function Qe(i, t = 0.8) {
1281
+ const e = V(i, t), a = V(i, 0.7);
1282
+ return {
1283
+ background: `linear-gradient(to bottom, ${e}, #ffffff)`,
1284
+ border: `1px solid ${a}`,
1285
+ color: "#1f2937"
501
1286
  };
502
- return /* @__PURE__ */ t.jsxs(
1287
+ }
1288
+ function Be(i) {
1289
+ return new Promise((t) => setTimeout(t, i));
1290
+ }
1291
+ const He = ({
1292
+ state: i,
1293
+ className: t = "",
1294
+ defaultTimers: e = { thinking: 30, answering: 120, editing: 30 }
1295
+ }) => {
1296
+ const a = (d) => {
1297
+ const u = Math.floor(d / 60), h = d % 60;
1298
+ return `${u.toString().padStart(2, "0")}:${h.toString().padStart(2, "0")}`;
1299
+ }, s = (d) => ({
1300
+ [l.IDLE]: "Ready",
1301
+ [l.FETCHING_QUESTION]: "Loading Question...",
1302
+ [l.READING_QUESTION]: "Reading Question...",
1303
+ [l.THINKING]: "Preparation Time",
1304
+ [l.ANSWERING]: "Recording Answer",
1305
+ [l.TRANSCRIBING]: "Processing Speech...",
1306
+ [l.EDITING]: "Edit Your Answer",
1307
+ [l.SUBMITTING]: "Submitting...",
1308
+ [l.COMPLETED]: "Interview Complete"
1309
+ })[d] || d, c = (d) => ({
1310
+ [l.IDLE]: "iw-bg-gray-500",
1311
+ [l.FETCHING_QUESTION]: "iw-bg-blue-500",
1312
+ [l.READING_QUESTION]: "iw-bg-indigo-500",
1313
+ [l.THINKING]: "iw-bg-yellow-500",
1314
+ [l.ANSWERING]: "iw-bg-red-500",
1315
+ [l.TRANSCRIBING]: "iw-bg-purple-500",
1316
+ [l.EDITING]: "iw-bg-orange-500",
1317
+ [l.SUBMITTING]: "iw-bg-green-500",
1318
+ [l.COMPLETED]: "iw-bg-green-600"
1319
+ })[d] || "iw-bg-gray-500", n = (d) => ({
1320
+ [l.IDLE]: "iw-bg-gray-100 iw-text-gray-800",
1321
+ [l.FETCHING_QUESTION]: "iw-bg-blue-100 iw-text-blue-800",
1322
+ [l.READING_QUESTION]: "iw-bg-indigo-100 iw-text-indigo-800",
1323
+ [l.THINKING]: "iw-bg-yellow-100 iw-text-yellow-800",
1324
+ [l.ANSWERING]: "iw-bg-red-100 iw-text-red-800",
1325
+ [l.TRANSCRIBING]: "iw-bg-purple-100 iw-text-purple-800",
1326
+ [l.EDITING]: "iw-bg-orange-100 iw-text-orange-800",
1327
+ [l.SUBMITTING]: "iw-bg-green-100 iw-text-green-800",
1328
+ [l.COMPLETED]: "iw-bg-green-200 iw-text-green-900"
1329
+ })[d] || "iw-bg-gray-100 iw-text-gray-800", o = (d) => ({
1330
+ [l.IDLE]: "Ready to start next question.",
1331
+ [l.FETCHING_QUESTION]: "Please wait while we load the next question...",
1332
+ [l.READING_QUESTION]: "Listening to the question...",
1333
+ [l.THINKING]: "Take time to think about your answer.",
1334
+ [l.ANSWERING]: "Speak clearly into your microphone.",
1335
+ [l.TRANSCRIBING]: "Converting your speech to text...",
1336
+ [l.EDITING]: "Review and edit your answer.",
1337
+ [l.SUBMITTING]: "Submitting your answer...",
1338
+ [l.COMPLETED]: "Thank you for completing the interview!"
1339
+ })[d] || "";
1340
+ function m(d) {
1341
+ return {
1342
+ [l.THINKING]: e.thinking || 30,
1343
+ [l.ANSWERING]: e.answering || 120,
1344
+ [l.EDITING]: e.editing || 30
1345
+ }[d] || 1;
1346
+ }
1347
+ const g = i.currentPhaseTimeRemaining > 0;
1348
+ return /* @__PURE__ */ r.jsxs(
503
1349
  "div",
504
1350
  {
505
- className: "iw-rounded-xl iw-mb-4 message-animation iw-bg-gradient-to-b iw-from-indigo-50 iw-to-white iw-text-gray-800 iw-border iw-border-indigo-100 iw-p-5",
506
- style: {
507
- // Fallback styles in case CSS classes don't load properly
508
- background: "linear-gradient(to bottom, #eef2ff, #ffffff)",
509
- border: "1px solid #e0e7ff",
510
- color: "#1f2937"
511
- },
1351
+ className: `iw-iw-bg-white iw-rounded-lg iw-shadow-md iw-p-4 iw-border iw-border-gray-200 ${t}`,
512
1352
  children: [
513
- /* @__PURE__ */ t.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-3 iw-mb-3", children: [
514
- /* @__PURE__ */ t.jsx(
1353
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-justify-between iw-items-center iw-mb-4", children: [
1354
+ /* @__PURE__ */ r.jsxs("div", { children: [
1355
+ /* @__PURE__ */ r.jsx("h3", { className: "iw-text-sm iw-font-semibold iw-text-gray-700", children: "Interview Timer" }),
1356
+ /* @__PURE__ */ r.jsxs("p", { className: "iw-text-xs iw-text-gray-700 iw-my-3 iw-font-mono", children: [
1357
+ "Phase",
1358
+ " ",
1359
+ /* @__PURE__ */ r.jsx(
1360
+ "span",
1361
+ {
1362
+ className: `iw-px-3 iw-py-1.5 iw-rounded-md iw-font-medium ${n(
1363
+ i.phase
1364
+ )}`,
1365
+ children: i.phase
1366
+ }
1367
+ )
1368
+ ] }),
1369
+ /* @__PURE__ */ r.jsxs("p", { className: "iw-text-xs iw-text-gray-700 iw-font-mono", children: [
1370
+ "Question ",
1371
+ i.currentQuestionNumber
1372
+ ] })
1373
+ ] }),
1374
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-text-right", children: [
1375
+ /* @__PURE__ */ r.jsx("div", { className: "iw-text-2xl iw-font-bold iw-text-gray-900", children: a(i.totalTimeRemaining) }),
1376
+ /* @__PURE__ */ r.jsx("div", { className: "iw-text-xs iw-text-gray-500", children: "Total Time Left" })
1377
+ ] })
1378
+ ] }),
1379
+ /* @__PURE__ */ r.jsx("div", { className: "iw-mb-4", children: /* @__PURE__ */ r.jsx("div", { className: "iw-w-full iw-h-2 iw-iw-bg-gray-200 iw-rounded-full iw-overflow-hidden", children: /* @__PURE__ */ r.jsx(
1380
+ "div",
1381
+ {
1382
+ className: "iw-h-full iw-iw-bg-gradient-to-r iw-from-blue-500 iw-to-purple-500 iw-transition-all iw-duration-300",
1383
+ style: {
1384
+ width: `${i.totalTimeElapsed / (i.totalTimeElapsed + i.totalTimeRemaining) * 100}%`
1385
+ }
1386
+ }
1387
+ ) }) }),
1388
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-mb-4", children: [
1389
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-justify-between iw-mb-2", children: [
1390
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-2", children: [
1391
+ /* @__PURE__ */ r.jsx(
1392
+ "div",
1393
+ {
1394
+ className: `iw-w-3 iw-h-3 iw-rounded-full ${c(
1395
+ i.phase
1396
+ )} ${g ? "iw-animate-pulse" : ""}`
1397
+ }
1398
+ ),
1399
+ /* @__PURE__ */ r.jsx("span", { className: "iw-text-sm iw-font-medium iw-text-gray-700", children: s(i.phase) })
1400
+ ] }),
1401
+ g && /* @__PURE__ */ r.jsx("span", { className: "iw-text-lg iw-font-bold iw-text-gray-900", children: a(i.currentPhaseTimeRemaining) })
1402
+ ] }),
1403
+ g && /* @__PURE__ */ r.jsx("div", { className: "iw-w-full iw-h-1.5 iw-iw-bg-gray-200 iw-rounded-full iw-overflow-hidden", children: /* @__PURE__ */ r.jsx(
515
1404
  "div",
516
1405
  {
517
- className: "iw-h-12 iw-w-12 iw-rounded-lg iw-bg-purple-500 iw-flex iw-items-center iw-justify-center iw-text-white iw-font-semibold",
518
- children: "N"
1406
+ className: `iw-h-full ${c(
1407
+ i.phase
1408
+ )} iw-transition-all iw-duration-300`,
1409
+ style: {
1410
+ width: `${i.currentPhaseTimeRemaining / m(i.phase) * 100}%`
1411
+ }
519
1412
  }
520
- ),
521
- /* @__PURE__ */ t.jsxs("div", { children: [
522
- /* @__PURE__ */ t.jsx("div", { className: "iw-text-sm iw-font-semibold", children: "Novara" }),
523
- /* @__PURE__ */ t.jsx("div", { className: "iw-text-xs iw-text-gray-500", children: "Assistant" })
524
- ] })
1413
+ ) })
525
1414
  ] }),
526
- s ? /* @__PURE__ */ t.jsx("p", { children: "Loading products..." }) : /* @__PURE__ */ t.jsx("ul", { className: "iw-list-disc iw-pl-5 iw-space-y-1", children: e == null ? void 0 : e.slice(0, 10).map((a) => /* @__PURE__ */ t.jsxs(
527
- "li",
528
- {
529
- className: "iw-cursor-pointer",
530
- onClick: () => r(a.id),
531
- children: [
532
- /* @__PURE__ */ t.jsx("span", { className: "iw-font-medium", children: a.title }),
533
- " - $",
534
- a.price
535
- ]
536
- },
537
- a.id
538
- )) })
1415
+ /* @__PURE__ */ r.jsx("div", { className: "iw-mt-3 iw-pt-3 iw-border-t iw-border-gray-200", children: /* @__PURE__ */ r.jsx("p", { className: "iw-text-xs iw-text-gray-600", children: o(i.phase) }) })
539
1416
  ]
540
1417
  }
541
1418
  );
542
- }, V = ({ className: i = "" }) => {
543
- const e = j(null);
544
- return P(() => {
545
- let s = null;
546
- return (async () => {
547
- try {
548
- s = await navigator.mediaDevices.getUserMedia({
549
- video: !0,
550
- audio: !1
551
- }), e.current && (e.current.srcObject = s);
552
- } catch (r) {
553
- console.error("Error accessing camera:", r);
554
- }
555
- })(), () => {
556
- s && s.getTracks().forEach((r) => r.stop());
557
- };
558
- }, []), /* @__PURE__ */ t.jsx("div", { className: `iw-relative ${i}`, children: /* @__PURE__ */ t.jsx(
559
- "video",
560
- {
561
- ref: e,
562
- autoPlay: !0,
563
- playsInline: !0,
564
- muted: !0,
565
- className: "iw-w-full iw-h-full iw-object-cover iw-rounded-md"
566
- }
567
- ) });
568
- }, X = ({
1419
+ }, We = ({
569
1420
  label: i,
570
- error: e,
571
- fullWidth: s = !1,
572
- className: n = "",
573
- id: r,
574
- ...a
1421
+ error: t,
1422
+ fullWidth: e = !1,
1423
+ className: a = "",
1424
+ id: s,
1425
+ ...c
575
1426
  }) => {
576
- const o = r || `textarea-${Math.random().toString(36).substring(2, 9)}`, l = "iw-block iw-rounded-md iw-border iw-border-gray-300 iw-shadow-sm iw-px-4 iw-py-2 iw-text-sm iw-focus:border-primary-500 iw-focus:ring-primary-500 iw-focus:outline-none iw-transition-all", w = e ? "iw-border-red-500 iw-focus:border-red-500 iw-focus:ring-red-500" : "", d = s ? "iw-w-full" : "", u = n.includes("iw-h-full") ? "iw-h-full" : "";
577
- return /* @__PURE__ */ t.jsxs(
1427
+ const n = s || `textarea-${Math.random().toString(36).substring(2, 9)}`, o = "iw-block iw-rounded-md iw-border iw-border-gray-300 iw-shadow-sm iw-px-4 iw-py-2 iw-text-sm iw-focus:border-primary-500 iw-focus:ring-primary-500 iw-focus:outline-none iw-transition-all", m = t ? "iw-border-red-500 iw-focus:border-red-500 iw-focus:ring-red-500" : "", g = e ? "iw-w-full" : "", d = a.includes("iw-h-full") ? "iw-h-full" : "";
1428
+ return /* @__PURE__ */ r.jsxs(
578
1429
  "div",
579
1430
  {
580
- className: `${s ? "iw-w-full iw-h-full" : ""} ${u ? "iw-flex iw-flex-col" : ""}`,
1431
+ className: `${e ? "iw-w-full iw-h-full" : ""} ${d ? "iw-flex iw-flex-col" : ""}`,
581
1432
  children: [
582
- i && /* @__PURE__ */ t.jsx(
1433
+ i && /* @__PURE__ */ r.jsx(
583
1434
  "label",
584
1435
  {
585
- htmlFor: o,
1436
+ htmlFor: n,
586
1437
  className: "iw-block iw-text-sm iw-font-medium iw-text-gray-700 iw-mb-1",
587
1438
  children: i
588
1439
  }
589
1440
  ),
590
- /* @__PURE__ */ t.jsx(
1441
+ /* @__PURE__ */ r.jsx(
591
1442
  "textarea",
592
1443
  {
593
- id: o,
594
- className: `${l} ${w} ${d} ${u} ${n}`,
595
- "aria-invalid": e ? "true" : "false",
596
- ...a
1444
+ id: n,
1445
+ className: `${o} ${m} ${g} ${d} ${a}`,
1446
+ "aria-invalid": t ? "true" : "false",
1447
+ ...c
597
1448
  }
598
1449
  ),
599
- e && /* @__PURE__ */ t.jsx("p", { className: "iw-mt-1 iw-text-sm iw-text-red-600", children: e })
1450
+ t && /* @__PURE__ */ r.jsx("p", { className: "iw-mt-1 iw-text-sm iw-text-red-600", children: t })
600
1451
  ]
601
1452
  }
602
1453
  );
603
- }, Z = ({
1454
+ }, ze = ({
604
1455
  value: i,
605
- onChange: e,
606
- onSubmit: s,
607
- isSubmitDisabled: n,
608
- remainingTimeText: r
1456
+ onChange: t,
1457
+ onSubmit: e,
1458
+ isSubmitDisabled: a,
1459
+ remainingTimeText: s
609
1460
  }) => {
610
- const a = (o) => {
611
- o.key === "Enter" && (o.ctrlKey || o.metaKey) && !n && (o.preventDefault(), s());
1461
+ const c = (n) => {
1462
+ n.key === "Enter" && (n.ctrlKey || n.metaKey) && !a && (n.preventDefault(), e());
612
1463
  };
613
- return /* @__PURE__ */ t.jsx("div", { className: "iw-mt-auto", children: /* @__PURE__ */ t.jsxs("div", { className: "iw-rounded-xl iw-overflow-hidden iw-border iw-border-gray-200", children: [
614
- /* @__PURE__ */ t.jsxs("div", { className: "iw-flex iw-items-center iw-justify-between iw-px-3 iw-py-2 iw-bg-gray-50 iw-border-b iw-border-gray-200", children: [
615
- /* @__PURE__ */ t.jsx("div", { className: "iw-text-sm iw-font-medium", children: "Your answer" }),
616
- r && /* @__PURE__ */ t.jsx("div", { className: "iw-text-xs iw-text-gray-500", children: r })
1464
+ return /* @__PURE__ */ r.jsx("div", { className: "iw-mt-auto", children: /* @__PURE__ */ r.jsxs("div", { className: "iw-rounded-xl iw-overflow-hidden iw-border iw-border-gray-200", children: [
1465
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-justify-between iw-px-3 iw-py-2 iw-bg-gray-50 iw-border-b iw-border-gray-200", children: [
1466
+ /* @__PURE__ */ r.jsx("div", { className: "iw-text-sm iw-font-medium", children: "Your answer" }),
1467
+ s && /* @__PURE__ */ r.jsx("div", { className: "iw-text-xs iw-text-gray-500", children: s })
617
1468
  ] }),
618
- /* @__PURE__ */ t.jsx(
619
- X,
1469
+ /* @__PURE__ */ r.jsx(
1470
+ We,
620
1471
  {
621
1472
  value: i,
622
- onChange: e,
623
- onKeyDown: a,
1473
+ onChange: t,
1474
+ onKeyDown: c,
624
1475
  placeholder: "Type your answer here...",
625
1476
  className: "iw-bg-gray-50 iw-w-full iw-resize-none iw-focus:outline-none iw-bg-transparent iw-min-h-[112px]",
626
1477
  rows: 5,
627
1478
  fullWidth: !0
628
1479
  }
629
1480
  ),
630
- /* @__PURE__ */ t.jsxs("div", { className: "iw-p-2 iw-flex iw-justify-between iw-items-center iw-bg-gray-50", children: [
631
- /* @__PURE__ */ t.jsx("div", { className: "iw-text-xs iw-text-gray-500", children: /* @__PURE__ */ t.jsx("kbd", { children: " Press Ctrl+Enter to submit" }) }),
632
- /* @__PURE__ */ t.jsx(
633
- E,
1481
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-p-2 iw-flex iw-justify-between iw-items-center iw-bg-gray-50", children: [
1482
+ /* @__PURE__ */ r.jsx("div", { className: "iw-text-xs iw-text-gray-500", children: /* @__PURE__ */ r.jsx("kbd", { children: " Press Ctrl+Enter to submit" }) }),
1483
+ /* @__PURE__ */ r.jsx(
1484
+ $,
634
1485
  {
635
- onClick: s,
636
- disabled: n,
1486
+ onClick: e,
1487
+ disabled: a,
637
1488
  size: "sm",
638
1489
  variant: "gradient",
639
1490
  children: "Submit Answer"
@@ -641,72 +1492,383 @@ const S = new B("https://fakestoreapi.com"), J = ({ question: i }) => {
641
1492
  )
642
1493
  ] })
643
1494
  ] }) });
644
- }, K = ({
645
- title: i = "Interview",
646
- questions: e = [],
647
- onComplete: s,
648
- onAnswerSubmit: n,
649
- className: r = ""
1495
+ }, Ye = ({
1496
+ question: i,
1497
+ isLoading: t = !1
650
1498
  }) => {
651
- const [a, o] = m(0), [l, w] = m([]), [d, u] = m(""), [h, f] = m(!1), [k, y] = m(!0), [c, g] = m(!1), [C] = m(!0), R = j(null), p = e[a], A = () => {
652
- const b = {
653
- questionId: (p == null ? void 0 : p.id) || "",
654
- answerText: d,
655
- timestamp: /* @__PURE__ */ new Date()
656
- };
657
- w([...l, b]), n && n(b), a < e.length - 1 ? (o((v) => v + 1), u("")) : (f(!0), s && s(l));
658
- }, D = H(() => {
659
- const v = 60 - Math.floor(Date.now() / 1e3 % 60), F = Math.floor(v / 60).toString().padStart(2, "0"), U = (v % 60).toString().padStart(2, "0");
660
- return `Time to Talk: ${F}:${U} min`;
661
- }, [a]);
662
- return c ? /* @__PURE__ */ t.jsx("div", { className: "interview-widget-container", children: /* @__PURE__ */ t.jsx(
1499
+ const { baseColor: e } = F();
1500
+ return t ? /* @__PURE__ */ r.jsxs(
663
1501
  "div",
664
1502
  {
665
- ref: R,
666
- className: `iw-flex iw-flex-col iw-rounded-xl iw-shadow-lg iw-overflow-hidden iw-h-[calc(100vh-1rem)] ${r}`,
667
- children: /* @__PURE__ */ t.jsxs("div", { className: " iw-h-full iw-flex iw-flex-col", children: [
668
- /* @__PURE__ */ t.jsx(Q, { title: i }),
669
- /* @__PURE__ */ t.jsx("div", { className: "iw-flex iw-flex-col iw-flex-grow iw-overflow-hidden iw-px-4 iw-py-5", children: h ? /* @__PURE__ */ t.jsxs("div", { className: "iw-flex iw-flex-col iw-items-center iw-justify-center iw-h-full", children: [
670
- /* @__PURE__ */ t.jsx("div", { className: "iw-text-xl iw-font-bold iw-mb-2", children: "Interview Complete!" }),
671
- /* @__PURE__ */ t.jsx("p", { className: "iw-text-center iw-mb-4", children: "Thank you for participating in this interview." })
672
- ] }) : /* @__PURE__ */ t.jsxs("div", { className: "iw-h-full iw-flex iw-flex-col ", children: [
673
- /* @__PURE__ */ t.jsx("div", { className: "iw-flex-1", children: p && /* @__PURE__ */ t.jsx(J, { question: p }) }),
674
- /* @__PURE__ */ t.jsxs("div", { className: "iw-grid iw-grid-cols-2 iw-gap-4 iw-mt-4", children: [
675
- C && /* @__PURE__ */ t.jsx(
676
- "div",
677
- {
678
- className: "iw-mt-2 iw-border iw-border-gray-200 iw-rounded-xl iw-p-2",
679
- children: /* @__PURE__ */ t.jsx(V, { className: "iw-w-full iw-h-[400px]" })
680
- }
681
- ),
682
- /* @__PURE__ */ t.jsx(
683
- Z,
684
- {
685
- value: d,
686
- onChange: (b) => u(b.target.value),
687
- onSubmit: A,
688
- isSubmitDisabled: !d.trim() || !c,
689
- remainingTimeText: D
690
- }
691
- )
1503
+ className: "iw-rounded-xl iw-mb-4 iw-p-5",
1504
+ style: {
1505
+ background: "linear-gradient(to bottom, #eef2ff, #ffffff)",
1506
+ border: "1px solid #e0e7ff",
1507
+ color: "#1f2937"
1508
+ },
1509
+ children: [
1510
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-3 iw-mb-3", children: [
1511
+ /* @__PURE__ */ r.jsx(
1512
+ "div",
1513
+ {
1514
+ className: "iw-h-12 iw-w-12 iw-rounded-lg iw-flex iw-items-center iw-justify-center iw-text-white iw-font-semibold iw-animate-pulse",
1515
+ style: { backgroundColor: e },
1516
+ children: "N"
1517
+ }
1518
+ ),
1519
+ /* @__PURE__ */ r.jsxs("div", { children: [
1520
+ /* @__PURE__ */ r.jsx("div", { className: "iw-text-sm iw-font-semibold", children: "Novara" }),
1521
+ /* @__PURE__ */ r.jsx("div", { className: "iw-text-xs iw-text-gray-500", children: "Assistant" })
692
1522
  ] })
693
- ] }) })
694
- ] })
1523
+ ] }),
1524
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-animate-pulse", children: [
1525
+ /* @__PURE__ */ r.jsx("div", { className: "iw-h-4 iw-bg-gray-200 iw-rounded iw-w-3/4 iw-mb-2" }),
1526
+ /* @__PURE__ */ r.jsx("div", { className: "iw-h-4 iw-bg-gray-200 iw-rounded iw-w-full" })
1527
+ ] })
1528
+ ]
1529
+ }
1530
+ ) : i ? /* @__PURE__ */ r.jsxs(
1531
+ "div",
1532
+ {
1533
+ className: "iw-rounded-xl iw-mb-4 message-animation iw-text-gray-800 iw-border iw-border-indigo-100 iw-p-5",
1534
+ style: Qe(e ?? "#8C75FB", 0.85),
1535
+ children: [
1536
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-3 iw-mb-3", children: [
1537
+ /* @__PURE__ */ r.jsx(
1538
+ "div",
1539
+ {
1540
+ className: "iw-h-12 iw-w-12 iw-rounded-lg iw-flex iw-items-center iw-justify-center iw-text-white iw-font-semibold",
1541
+ style: { backgroundColor: e },
1542
+ children: "N"
1543
+ }
1544
+ ),
1545
+ /* @__PURE__ */ r.jsxs("div", { children: [
1546
+ /* @__PURE__ */ r.jsx("div", { className: "iw-text-sm iw-font-semibold", children: "Novara" }),
1547
+ /* @__PURE__ */ r.jsx("div", { className: "iw-text-xs iw-text-gray-500", children: "Assistant" })
1548
+ ] })
1549
+ ] }),
1550
+ /* @__PURE__ */ r.jsx("p", { className: "iw-text-[15px] iw-leading-6", children: i.question })
1551
+ ]
1552
+ }
1553
+ ) : /* @__PURE__ */ r.jsx(
1554
+ "div",
1555
+ {
1556
+ className: "iw-rounded-xl iw-mb-4 iw-bg-gray-50 iw-text-gray-500 iw-border iw-border-gray-200 iw-p-5 iw-text-center",
1557
+ children: /* @__PURE__ */ r.jsx("p", { className: "iw-text-sm", children: "No question available" })
1558
+ }
1559
+ );
1560
+ }, Ke = ({
1561
+ interviewId: i,
1562
+ onComplete: t,
1563
+ className: e = ""
1564
+ }) => {
1565
+ const a = Oe(), { baseColor: s } = F(), { baseUrl: c } = Te(), { timers: n, stt: o, tts: m } = Ie(), [g, d] = y(null), [u, h] = y(""), [x, k] = y(!1), j = D(""), N = D(null), w = {
1566
+ thinkingTime: (n == null ? void 0 : n.thinkingDuration) || 30,
1567
+ answeringTime: (n == null ? void 0 : n.answeringDuration) || 120,
1568
+ editingTime: (n == null ? void 0 : n.editingDuration) || 30,
1569
+ totalTime: (n == null ? void 0 : n.totalInterviewDuration) || 600,
1570
+ minimumBufferTime: (n == null ? void 0 : n.minimumTimeForNextQuestion) || 120
1571
+ }, {
1572
+ thinkingTime: p,
1573
+ answeringTime: T,
1574
+ editingTime: S,
1575
+ totalTime: ie,
1576
+ minimumBufferTime: re
1577
+ } = w;
1578
+ A(() => {
1579
+ j.current = u;
1580
+ }, [u]);
1581
+ const _ = D(!1), {
1582
+ speak: te,
1583
+ isPlaying: se,
1584
+ error: W
1585
+ } = Ue({
1586
+ config: {
1587
+ baseUrl: c,
1588
+ provider: m == null ? void 0 : m.provider
1589
+ },
1590
+ onEnd: () => {
1591
+ console.log("TTS playback completed"), _.current = !1, E();
1592
+ },
1593
+ onError: (f) => {
1594
+ console.error("TTS Error:", f), _.current || (_.current = !0, E());
1595
+ }
1596
+ }), G = D(!1), L = D(!1), {
1597
+ startRecording: z,
1598
+ stopRecording: Y,
1599
+ transcribe: K,
1600
+ isRecording: ne,
1601
+ isTranscribing: ae,
1602
+ error: M
1603
+ } = _e({
1604
+ config: {
1605
+ baseUrl: c,
1606
+ provider: o == null ? void 0 : o.provider,
1607
+ model: o == null ? void 0 : o.model,
1608
+ language: o == null ? void 0 : o.language
1609
+ },
1610
+ onStart: () => {
1611
+ console.log("STT recording started"), G.current = !1, L.current = !1;
1612
+ },
1613
+ onStop: () => {
1614
+ console.log("STT recording stopped");
1615
+ },
1616
+ onTranscriptionComplete: (f) => {
1617
+ console.log("Transcription completed:", f), h(f.transcript), L.current || (L.current = !0, E());
1618
+ },
1619
+ onError: (f) => {
1620
+ console.error("STT Error:", f), G.current || (G.current = !0, E());
1621
+ }
1622
+ }), { state: R, startQuestion: Q, nextPhase: E } = De({
1623
+ config: {
1624
+ thinkingDuration: p,
1625
+ answeringDuration: T,
1626
+ editingDuration: S,
1627
+ totalInterviewDuration: ie,
1628
+ minimumTimeForNextQuestion: re
1629
+ },
1630
+ callbacks: {
1631
+ onPhaseChange: (f) => {
1632
+ switch (console.log("Phase changed:", f), f) {
1633
+ case l.FETCHING_QUESTION:
1634
+ oe();
1635
+ break;
1636
+ case l.READING_QUESTION:
1637
+ le();
1638
+ break;
1639
+ case l.ANSWERING:
1640
+ de();
1641
+ break;
1642
+ case l.TRANSCRIBING:
1643
+ we();
1644
+ break;
1645
+ case l.SUBMITTING:
1646
+ ce();
1647
+ break;
1648
+ }
1649
+ },
1650
+ onInterviewEnd: () => {
1651
+ t == null || t();
1652
+ }
695
1653
  }
696
- ) }) : /* @__PURE__ */ t.jsx("div", { className: "interview-widget-container iw-h-screen iw-w-screen", children: /* @__PURE__ */ t.jsx(
697
- O,
1654
+ }), { execute: oe } = Pe(
1655
+ async () => {
1656
+ var P, J;
1657
+ const f = await a.generateQuestion({
1658
+ interviewId: i,
1659
+ isInterviewDone: R.totalTimeRemaining < 120,
1660
+ question: ((P = N.current) == null ? void 0 : P.question) || "",
1661
+ qnaId: ((J = N.current) == null ? void 0 : J.qna_id) || "",
1662
+ answer: j.current
1663
+ });
1664
+ return N.current = f.data, f;
1665
+ },
698
1666
  {
699
- isOpen: k,
700
- onStart: () => {
701
- console.log("Permissions granted, starting interview"), g(!0), y(!1);
1667
+ onSuccess: async (f) => {
1668
+ console.log("Questions fetched successfully", f), f && f.data && (h(""), d(f.data), E());
1669
+ },
1670
+ onError: (f) => {
1671
+ console.error("Failed to fetch questions:", f);
702
1672
  }
703
1673
  }
1674
+ ), ce = v(async () => {
1675
+ console.log("Answer submitted successfully"), await Be(1500), Q();
1676
+ }, [Q]), le = v(async () => {
1677
+ var f;
1678
+ if ((f = N.current) != null && f.question)
1679
+ try {
1680
+ _.current = !1, console.log("Starting TTS for question"), await te(N.current.question);
1681
+ } catch (P) {
1682
+ console.error("Failed to speak question:", P);
1683
+ }
1684
+ else
1685
+ E();
1686
+ }, []), de = v(async () => {
1687
+ console.log("Starting recording...");
1688
+ try {
1689
+ const f = T;
1690
+ console.log("🚀 ~ maxDuration:", f), await z(f);
1691
+ } catch (f) {
1692
+ console.error("Failed to start recording:", f), E();
1693
+ }
1694
+ }, [z, E]), we = v(async () => {
1695
+ console.log("Stopping recording and processing STT...");
1696
+ try {
1697
+ const f = await Y();
1698
+ console.log("Recording stopped, audio blob size:", f.size), await K(f);
1699
+ } catch (f) {
1700
+ console.error("STT processing failed:", f), !G.current && !L.current && (G.current = !0, E());
1701
+ }
1702
+ }, [Y, K, E]), ue = () => {
1703
+ const { phase: f } = R;
1704
+ switch (f) {
1705
+ case l.IDLE:
1706
+ return /* @__PURE__ */ r.jsx("div", { className: "iw-text-center iw-py-8", children: /* @__PURE__ */ r.jsx("p", { className: "iw-text-gray-600", children: "Ready to start..." }) });
1707
+ case l.FETCHING_QUESTION:
1708
+ return /* @__PURE__ */ r.jsx("div", { className: "iw-space-y-4" });
1709
+ case l.READING_QUESTION:
1710
+ return /* @__PURE__ */ r.jsx("div", { className: "iw-space-y-4", children: /* @__PURE__ */ r.jsxs("div", { className: "iw-p-4 iw-bg-indigo-50 iw-border iw-border-indigo-200 iw-rounded-lg", children: [
1711
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-2", children: [
1712
+ /* @__PURE__ */ r.jsx("div", { className: "iw-w-3 iw-h-3 iw-bg-indigo-500 iw-rounded-full iw-animate-pulse" }),
1713
+ /* @__PURE__ */ r.jsx("span", { className: "iw-text-sm iw-font-medium iw-text-indigo-700", children: se ? "Playing question audio..." : "Reading the question..." })
1714
+ ] }),
1715
+ W && /* @__PURE__ */ r.jsxs("div", { className: "iw-mt-2 iw-text-xs iw-text-red-600", children: [
1716
+ "Audio playback failed: ",
1717
+ W.message
1718
+ ] })
1719
+ ] }) });
1720
+ case l.THINKING:
1721
+ case l.ANSWERING:
1722
+ return /* @__PURE__ */ r.jsx("div", { className: "iw-space-y-4", children: f === l.ANSWERING && /* @__PURE__ */ r.jsxs("div", { className: "iw-p-4 iw-bg-red-50 iw-border iw-border-red-200 iw-rounded-lg", children: [
1723
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-2", children: [
1724
+ /* @__PURE__ */ r.jsx("div", { className: "iw-w-3 iw-h-3 iw-bg-red-500 iw-rounded-full iw-animate-pulse" }),
1725
+ /* @__PURE__ */ r.jsx("span", { className: "iw-text-sm iw-font-medium iw-text-red-700", children: ne ? "Recording in progress..." : "Preparing to record..." })
1726
+ ] }),
1727
+ M && /* @__PURE__ */ r.jsxs("div", { className: "iw-mt-2 iw-text-xs iw-text-red-600", children: [
1728
+ "Recording error: ",
1729
+ M.message
1730
+ ] })
1731
+ ] }) });
1732
+ case l.TRANSCRIBING:
1733
+ return /* @__PURE__ */ r.jsx("div", { className: "iw-space-y-4", children: /* @__PURE__ */ r.jsxs("div", { className: "iw-p-4 iw-bg-purple-50 iw-border iw-border-purple-200 iw-rounded-lg", children: [
1734
+ /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-2", children: [
1735
+ /* @__PURE__ */ r.jsx("div", { className: "iw-animate-spin iw-h-4 iw-w-4 iw-border-2 iw-border-purple-500 iw-border-t-transparent iw-rounded-full" }),
1736
+ /* @__PURE__ */ r.jsx("span", { className: "iw-text-sm iw-font-medium iw-text-purple-700", children: ae ? "Transcribing your speech..." : "Processing audio..." })
1737
+ ] }),
1738
+ M && /* @__PURE__ */ r.jsxs("div", { className: "iw-mt-2 iw-text-xs iw-text-red-600", children: [
1739
+ "Transcription error: ",
1740
+ M.message,
1741
+ M.recoverable && " (attempting to continue)"
1742
+ ] })
1743
+ ] }) });
1744
+ case l.EDITING:
1745
+ return /* @__PURE__ */ r.jsx("div", { className: "iw-space-y-4", children: /* @__PURE__ */ r.jsx(
1746
+ ze,
1747
+ {
1748
+ value: u,
1749
+ onChange: (P) => h(P.target.value),
1750
+ onSubmit: () => E(),
1751
+ isSubmitDisabled: !u.trim(),
1752
+ remainingTimeText: `${R.currentPhaseTimeRemaining}s remaining`
1753
+ }
1754
+ ) });
1755
+ case l.SUBMITTING:
1756
+ return /* @__PURE__ */ r.jsx("div", { className: "iw-space-y-4", children: /* @__PURE__ */ r.jsx("div", { className: "iw-p-4 iw-bg-green-50 iw-border iw-border-green-200 iw-rounded-lg", children: /* @__PURE__ */ r.jsxs("div", { className: "iw-flex iw-items-center iw-space-x-2", children: [
1757
+ /* @__PURE__ */ r.jsx("div", { className: "iw-animate-spin iw-h-4 iw-w-4 iw-border-2 iw-border-green-500 iw-border-t-transparent iw-rounded-full" }),
1758
+ /* @__PURE__ */ r.jsx("span", { className: "iw-text-sm iw-font-medium iw-text-green-700", children: "Submitting your answer..." })
1759
+ ] }) }) });
1760
+ case l.COMPLETED:
1761
+ return /* @__PURE__ */ r.jsxs("div", { className: "iw-text-center iw-py-8", children: [
1762
+ /* @__PURE__ */ r.jsx("div", { className: "iw-mb-4", children: /* @__PURE__ */ r.jsx(
1763
+ "svg",
1764
+ {
1765
+ className: "iw-w-16 iw-h-16 iw-mx-auto iw-text-green-500",
1766
+ fill: "none",
1767
+ stroke: "currentColor",
1768
+ viewBox: "0 0 24 24",
1769
+ children: /* @__PURE__ */ r.jsx(
1770
+ "path",
1771
+ {
1772
+ strokeLinecap: "round",
1773
+ strokeLinejoin: "round",
1774
+ strokeWidth: 2,
1775
+ d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
1776
+ }
1777
+ )
1778
+ }
1779
+ ) }),
1780
+ /* @__PURE__ */ r.jsx("h3", { className: "iw-text-xl iw-font-bold iw-text-gray-900 iw-mb-2", children: "Interview Complete!" }),
1781
+ /* @__PURE__ */ r.jsx("p", { className: "iw-text-gray-600", children: "Thank you for your time. Your answers have been recorded." })
1782
+ ] });
1783
+ default:
1784
+ return null;
1785
+ }
1786
+ };
1787
+ return /* @__PURE__ */ r.jsx("div", { className: "interview-widget-container", children: /* @__PURE__ */ r.jsxs(
1788
+ "div",
1789
+ {
1790
+ className: ` iw-max-w-3xl iw-mx-auto iw-space-y-4 iw-py-8 ${e}`,
1791
+ children: [
1792
+ R.phase !== l.IDLE && R.phase !== l.COMPLETED && /* @__PURE__ */ r.jsx(
1793
+ Ye,
1794
+ {
1795
+ question: g,
1796
+ isLoading: R.phase === l.FETCHING_QUESTION
1797
+ }
1798
+ ),
1799
+ /* @__PURE__ */ r.jsx("div", { className: "iw-bg-white iw-rounded-lg iw-shadow-md iw-p-6", children: ue() }),
1800
+ /* @__PURE__ */ r.jsx(
1801
+ He,
1802
+ {
1803
+ state: R,
1804
+ defaultTimers: {
1805
+ thinking: p,
1806
+ answering: T,
1807
+ editing: S
1808
+ }
1809
+ }
1810
+ ),
1811
+ x && (R.phase === l.THINKING || R.phase === l.ANSWERING || R.phase === l.EDITING) && /* @__PURE__ */ r.jsx($, { onClick: E, children: "Next Phase" }),
1812
+ !x && /* @__PURE__ */ r.jsx("div", { className: "iw-text-center iw-py-12 w-full", children: /* @__PURE__ */ r.jsx(
1813
+ $,
1814
+ {
1815
+ className: "w-full",
1816
+ style: { backgroundColor: s },
1817
+ onClick: () => {
1818
+ Q(), k(!0);
1819
+ },
1820
+ children: "Start Interview"
1821
+ }
1822
+ ) })
1823
+ ]
1824
+ }
1825
+ ) });
1826
+ }, Je = ({
1827
+ interviewId: i,
1828
+ title: t = "Interview",
1829
+ onInterviewEnd: e,
1830
+ className: a = ""
1831
+ }) => {
1832
+ const [s, c] = y(!1), [n, o] = y(!0), m = D(null);
1833
+ if (!s)
1834
+ return /* @__PURE__ */ r.jsx("div", { className: "interview-widget-container", children: /* @__PURE__ */ r.jsx(
1835
+ Se,
1836
+ {
1837
+ isOpen: n,
1838
+ onStart: () => {
1839
+ console.log("Permissions granted, starting interview"), c(!0), o(!1);
1840
+ }
1841
+ }
1842
+ ) });
1843
+ const g = () => {
1844
+ e == null || e();
1845
+ };
1846
+ return /* @__PURE__ */ r.jsx("div", { className: "interview-widget-container", children: /* @__PURE__ */ r.jsx(
1847
+ "div",
1848
+ {
1849
+ ref: m,
1850
+ className: `iw-flex iw-flex-col iw-rounded-xl iw-shadow-lg iw-overflow-hidden iw-h-[calc(100vh-1rem)] ${a}`,
1851
+ children: /* @__PURE__ */ r.jsxs("div", { className: " iw-h-full iw-flex iw-flex-col", children: [
1852
+ /* @__PURE__ */ r.jsx(je, { title: t, onExit: g }),
1853
+ /* @__PURE__ */ r.jsx(
1854
+ Ke,
1855
+ {
1856
+ interviewId: i,
1857
+ className: a,
1858
+ onComplete: e || (() => {
1859
+ })
1860
+ }
1861
+ )
1862
+ ] })
1863
+ }
704
1864
  ) });
705
1865
  };
706
1866
  typeof window < "u" && (window.InterviewWidget = {
707
- InterviewWidget: K
1867
+ InterviewWidget: Je,
1868
+ InterviewWidgetProvider: Ne
708
1869
  });
709
1870
  export {
710
- K as InterviewWidget,
711
- K as default
1871
+ Je as InterviewWidget,
1872
+ Ne as InterviewWidgetProvider,
1873
+ Je as default
712
1874
  };