interview-widget 3.2.22 → 3.2.23

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
@@ -208,14 +208,15 @@ Pass this object to `InterviewWidgetProvider` via the `config` prop. Tables list
208
208
 
209
209
  ### Interview
210
210
 
211
- | Prop | Type | Default | Description |
212
- | -------------------- | --------- | ------- | ------------------------------------------ |
213
- | allow_answer_editing | `boolean` | true | Enable/disable answer editing phase |
214
- | capture_screenshots | `boolean` | false | Enable/disable periodic screenshot captures |
215
- | timers | `object` | [see below](#timers) | Per-phase and global timing configuration |
216
- | stt | `object` | [see below](#stt-speech-to-text) | Speech-to-Text provider settings |
217
- | tts | `object` | [see below](#tts-text-to-speech) | Text-to-Speech provider settings |
218
- | proctoring | `object` | [see below](#proctoring) | Proctoring settings and cheating detection |
211
+ | Prop | Type | Default | Description |
212
+ | -------------------- | --------- | -------------------------------- | ------------------------------------------- |
213
+ | allow_answer_editing | `boolean` | true | Enable/disable answer editing phase |
214
+ | capture_screenshots | `boolean` | false | Enable/disable periodic screenshot captures |
215
+ | record_video | `boolean` | false | Enable/disable video recording |
216
+ | timers | `object` | [see below](#timers) | Per-phase and global timing configuration |
217
+ | stt | `object` | [see below](#stt-speech-to-text) | Speech-to-Text provider settings |
218
+ | tts | `object` | [see below](#tts-text-to-speech) | Text-to-Speech provider settings |
219
+ | proctoring | `object` | [see below](#proctoring) | Proctoring settings and cheating detection |
219
220
 
220
221
  #### Timers
221
222
 
@@ -1,3 +1,4 @@
1
1
  export declare const useCameraSetup: (videoRef: React.RefObject<HTMLVideoElement | null>, isModelLoaded: boolean, renderLoop: () => void, setStatusMessage: (message: string) => void) => {
2
2
  animationFrameRef: import('react').RefObject<number | null>;
3
+ stream: MediaStream | null;
3
4
  };
@@ -2,7 +2,7 @@ import { Landmark, ReferenceData } from '../utils/types';
2
2
  export declare const useGazeAnalysis: () => {
3
3
  engagement: number;
4
4
  isLookingAway: boolean;
5
- analyzeGaze: (landmarks: Landmark[], reference: ReferenceData, gazeAnalysisEnabled: boolean) => {
5
+ analyzeGaze: (landmarks: Landmark[] | null, reference: ReferenceData, gazeAnalysisEnabled: boolean) => {
6
6
  engagement: number;
7
7
  isLookingAway: boolean;
8
8
  } | undefined;
@@ -1,5 +1,5 @@
1
1
  import { Landmark } from '../utils/types';
2
- export declare const useRenderLoop: (videoRef: React.RefObject<HTMLVideoElement | null>, canvasRef: React.RefObject<HTMLCanvasElement | null>, faceLandmarkerRef: React.RefObject<any>, updateFPS: () => void, analyzeFace: (results: any, referenceSet: boolean, referenceRef: Landmark[], setStatusMessage: (message: string) => void) => Landmark[] | null, drawWithEngagementBar: (canvas: HTMLCanvasElement, video: HTMLVideoElement, landmarks: Landmark[], showLandmarks: boolean, showEngagementBar: boolean, gazeAnalysisEnabled: boolean, referenceSet: boolean) => void, clearCanvas: (canvas: HTMLCanvasElement, video: HTMLVideoElement) => void, analyzeGaze: (landmarks: Landmark[], reference: any, gazeAnalysisEnabled: boolean) => void, referenceSet: boolean, referenceRef: React.RefObject<any>, showLandmarks: boolean, showEngagementBar: boolean, gazeAnalysisEnabled: boolean, setStatusMessage: (message: string) => void, setReferenceFaceNotDetected: (detected: boolean) => void) => {
2
+ export declare const useRenderLoop: (videoRef: React.RefObject<HTMLVideoElement | null>, canvasRef: React.RefObject<HTMLCanvasElement | null>, faceLandmarkerRef: React.RefObject<any>, updateFPS: () => void, analyzeFace: (results: any, referenceSet: boolean, referenceRef: Landmark[], setStatusMessage: (message: string) => void) => Landmark[] | null, drawWithEngagementBar: (canvas: HTMLCanvasElement, video: HTMLVideoElement, landmarks: Landmark[], showLandmarks: boolean, showEngagementBar: boolean, gazeAnalysisEnabled: boolean, referenceSet: boolean) => void, clearCanvas: (canvas: HTMLCanvasElement, video: HTMLVideoElement) => void, analyzeGaze: (landmarks: Landmark[] | null, reference: any, gazeAnalysisEnabled: boolean) => void, referenceSet: boolean, referenceRef: React.RefObject<any>, showLandmarks: boolean, showEngagementBar: boolean, gazeAnalysisEnabled: boolean, setStatusMessage: (message: string) => void, setReferenceFaceNotDetected: (detected: boolean) => void) => {
3
3
  renderLoop: () => void;
4
4
  animationFrameRef: import('react').RefObject<number | null>;
5
5
  };
@@ -3,6 +3,8 @@ export interface VideoFeedHandle {
3
3
  setReference: () => void;
4
4
  resetReference: () => void;
5
5
  stopCamera: () => void;
6
+ startRecording: () => void;
7
+ stopRecording: () => void;
6
8
  }
7
9
  interface VideoFeedProps {
8
10
  className?: string;
@@ -1,8 +1,21 @@
1
1
  import { default as React } from 'react';
2
2
  import { ViolationLogger } from '../types';
3
3
  import { GazeViolationCounters, GazeViolationType } from '../components/interview/proctoring/gaze-detection/utils/types';
4
+ export type ProctoringState = {
5
+ logViolations: ViolationLogger[];
6
+ totalViolations: number;
7
+ gazeViolationCounters: GazeViolationCounters;
8
+ referenceSet: boolean;
9
+ referenceRef: {
10
+ x: number;
11
+ y: number;
12
+ noseX: number;
13
+ noseY: number;
14
+ };
15
+ };
4
16
  export declare const ProctoringProvider: React.FC<{
5
17
  children: React.ReactNode;
18
+ interviewId?: string;
6
19
  }>;
7
20
  export declare const useProctoringStore: () => {
8
21
  logViolations: ViolationLogger[];
@@ -0,0 +1,6 @@
1
+ export declare function useVideoRecordingStream(interviewId: string, record_video?: boolean): {
2
+ startRecording: (stream: MediaStream) => void;
3
+ stopRecording: () => void;
4
+ isRecording: boolean;
5
+ isUploading: boolean;
6
+ };
@@ -15,4 +15,5 @@ export declare const API_ENDPOINTS: {
15
15
  EXIT: (interviewId: string) => string;
16
16
  CONFIG: (interviewId: string) => string;
17
17
  TRANSCRIPTION: (interviewId: string) => string;
18
+ VIOLATIONS: (interviewId: string) => string;
18
19
  };
@@ -1,4 +1,4 @@
1
- import { InterviewConfigResponse, InterviewQuestionPayload, InterviewQuestionResponse, InterviewSubmitAnswerPayload, InterviewTranscriptResponse, InterviewWidgetConfig, ScreenshotUploadRequestResponse } from '../../types';
1
+ import { InterviewConfigResponse, InterviewQuestionPayload, InterviewQuestionResponse, InterviewSubmitAnswerPayload, InterviewTranscriptResponse, InterviewViolationsPayload, InterviewViolationsResponse, InterviewWidgetConfig, ScreenshotUploadRequestResponse } from '../../types';
2
2
  declare class InterviewAPI {
3
3
  private config;
4
4
  constructor(config?: InterviewWidgetConfig["api"]);
@@ -31,21 +31,29 @@ declare class InterviewAPI {
31
31
  */
32
32
  getInterviewConfig(interviewId: string): Promise<InterviewConfigResponse>;
33
33
  /**
34
- * Request a signed URL for uploading a screenshot
34
+ * Request a signed URL for uploading an asset
35
35
  */
36
- requestScreenshotUploadUrl(interviewId: string, filename?: string): Promise<ScreenshotUploadRequestResponse>;
36
+ requestUploadUrl(interviewId: string, filename: string | undefined, mimeType: string, assetType: string): Promise<ScreenshotUploadRequestResponse>;
37
37
  /**
38
- * Upload screenshot to the provided signed URL
38
+ * Upload asset to the provided signed URL
39
39
  */
40
- uploadScreenshot(uploadUrl: string, imageBlob: Blob): Promise<void>;
40
+ uploadAsset(uploadUrl: string, blob: Blob, mimeType: string): Promise<void>;
41
41
  /**
42
- * Confirm screenshot upload
42
+ * Confirm asset upload
43
43
  */
44
- confirmScreenshotUpload(assetId: string): Promise<void>;
44
+ confirmAssetUpload(assetId: string): Promise<void>;
45
45
  /**
46
46
  * Get interview transcription
47
47
  */
48
48
  getInterviewTranscription(interviewId: string): Promise<InterviewTranscriptResponse>;
49
+ /**
50
+ * Get violations snapshot for an interview
51
+ */
52
+ getViolations(interviewId: string): Promise<InterviewViolationsResponse>;
53
+ /**
54
+ * Update violations snapshot for an interview
55
+ */
56
+ updateViolations({ interviewId, violations, }: InterviewViolationsPayload): Promise<InterviewViolationsResponse>;
49
57
  }
50
58
  export declare const interviewAPI: InterviewAPI;
51
59
  export { InterviewAPI };
package/dist/types.d.ts CHANGED
@@ -50,6 +50,7 @@ export interface InterviewWidgetConfig {
50
50
  interview?: {
51
51
  allow_answer_editing?: boolean;
52
52
  capture_screenshots?: boolean;
53
+ record_video?: boolean;
53
54
  timers?: {
54
55
  thinkingDuration?: number;
55
56
  answeringDuration?: number;
@@ -135,3 +136,14 @@ export interface TranscriptMessage {
135
136
  question_generated_at: string;
136
137
  answer_submitted_at: string | null;
137
138
  }
139
+ export interface InterviewViolationsResponse {
140
+ success: boolean;
141
+ message: string;
142
+ data: any;
143
+ error?: string | null;
144
+ code?: number;
145
+ }
146
+ export interface InterviewViolationsPayload {
147
+ interviewId: string;
148
+ violations: any;
149
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Safari Audio Utilities
3
+ *
4
+ * Centralises all Safari-specific audio quirks so every call site stays clean.
5
+ * Each function is a transparent no-op / pass-through on non-Safari browsers.
6
+ *
7
+ * Safari-specific lines are tagged with // [SAFARI] for easy auditing.
8
+ */
9
+ /**
10
+ * Returns true only for genuine Safari (desktop + iOS).
11
+ * Chrome/Edge on iOS report as Safari in the UA, but they also advertise
12
+ * "Chrome" or "CriOS" / "EdgA" — we exclude those. [SAFARI]
13
+ */
14
+ export declare function isSafari(): boolean;
15
+ /**
16
+ * Returns the best supported MIME type for MediaRecorder.
17
+ *
18
+ * Safari only supports audio/mp4 (AAC-LC). Offering audio/webm first causes
19
+ * MediaRecorder construction to throw a NotSupportedError on Safari. [SAFARI]
20
+ */
21
+ export declare function getSupportedMimeType(): string;
22
+ /**
23
+ * Strips constraints that throw OverconstrainedError on Safari.
24
+ *
25
+ * Safari rejects `sampleRate` and `channelCount` as MediaTrackConstraints,
26
+ * raising an OverconstrainedError that blocks mic access entirely. [SAFARI]
27
+ * All other browsers receive the full constraint set unchanged.
28
+ */
29
+ export declare function getSafariSafeAudioConstraints(base?: MediaTrackConstraints): MediaTrackConstraints;
30
+ /**
31
+ * Returns the timeslice argument (ms) to pass to MediaRecorder.start().
32
+ *
33
+ * Safari silently suppresses `ondataavailable` events when a timeslice is
34
+ * provided — it will only emit one blob when stop() is called. [SAFARI]
35
+ * Returning `undefined` causes the caller to call `start()` with no argument,
36
+ * which is the correct Safari behaviour.
37
+ *
38
+ * Other browsers get 100 ms streaming chunks for real-time processing.
39
+ */
40
+ export declare function getMediaRecorderStartArg(): number | undefined;
41
+ /**
42
+ * Stops a MediaRecorder, flushing the final chunk on Safari first.
43
+ *
44
+ * Safari sometimes fails to include the last audio segment in the blob
45
+ * unless requestData() is explicitly called just before stop(). [SAFARI]
46
+ * On other browsers requestData() is already called at the stop-promise
47
+ * setup site, so we only call stop() here.
48
+ */
49
+ export declare function safariSafeStop(recorder: MediaRecorder): void;