import type { EventKind } from "@a4anthony/proctorkit-types"; export interface ObserverEmitter { emit(kind: EventKind, payload?: Record): void; } export interface ScreenshotObserverConfig { /** * Render a blur overlay over the page whenever a screenshot is * suspected. The candidate sees a frosted overlay and (after 5s) a * "Resume Session" button. Useful as a deterrent and as a way to * obscure delayed-capture tools that snapshot the page after the * candidate switches back. Default: false. */ blurOnSuspicion?: boolean; /** * Milliseconds before the warning text + Resume button reveal on top * of the blur. Default: 5000. */ resumeRevealMs?: number; /** * Customise the deterrent overlay copy. */ resumeButtonText?: string; warningText?: string; } export type ScreenshotTrigger = "keyboard" | "visibility" | "blur"; /** * Watches for behaviours that indicate the candidate is trying to take * a screenshot. Emits a consolidated `screenshot.attempted` event with * the trigger ("keyboard" / "visibility" / "blur") in the payload, so * proctors see a single, semantic row in the timeline. * * Optionally renders a blur overlay over the page so that delayed * capture tools snapshot a blurred view. * * Screenshot-key handling (ported from teq-lib's proven approach): * * 1. Cmd+Shift keydown latches `screenshotKeyActive = true` and shows * the blur. macOS swallows the digit keydown for Cmd+Shift+3/4/5 * and the keyup for the modifiers, so we cannot rely on detecting * the digit — the latch is what carries state across the OS * clipping tool's lifecycle. * 2. window.blur fires while the OS clipping tool is up → re-shows * the blur (idempotent). * 3. window.focus fires when the clipping tool closes. We gate * hideBlur on `!screenshotKeyActive`. The flag is still true * because no keyup ever cleared it, so the overlay stays. * 4. The candidate either clicks Resume (clears flag + hides) or * releases one of the listed modifiers cleanly back into the page * (clears flag + hides). After 5s the Resume button reveals as a * fallback exit so the candidate isn't stuck. * * IMPORTANT: this is a deterrent, not real prevention. OS-level * screenshot keys (Cmd+Shift+3 on macOS, PrintScreen on Windows) are * intercepted by the OS before the page sees them, so the screenshot * usually completes before the observer's preventDefault runs. The * forensic value is the *attempt log*, not the block. */ export declare class ScreenshotObserver { private readonly emitter; private listening; private readonly config; private readonly listeners; private overlay; private resumeButton; private warningEl; private buttonTimer; private isDragging; private screenshotKeyActive; constructor(emitter: ObserverEmitter, config: ScreenshotObserverConfig | boolean | undefined); start(): void; stop(): void; private bind; private emit; private showBlur; private hideBlur; private mountOverlay; private removeOverlay; } //# sourceMappingURL=screenshot-observer.d.ts.map