CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (pull_request) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (pull_request) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Canceled after 0s
CI / Web (dms-browser) (pull_request) Canceled after 0s
CI / Web (pdf-viewer) (pull_request) Canceled after 0s
CI / Web (shl) (pull_request) Canceled after 0s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (pull_request) Canceled after 0s
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (pull_request) Canceled after 0s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
38 lines
1.6 KiB
TypeScript
38 lines
1.6 KiB
TypeScript
// INT-19: Abschalt-Schalter fuer die mobile Erfassung (Akzeptanzkriterium 4).
|
|
// Bewusst OHNE NEXT_PUBLIC_-Praefix: der Wert wird nur serverseitig
|
|
// (app/mobil/page.tsx als Server Component) zur Anfragezeit gelesen, nie in
|
|
// das Client-Bundle gebacken. Standard ist aus (Produkt-DNA: "Kein Kanal ohne
|
|
// ausdrueckliche Freigabe").
|
|
export function mobileCaptureEnabled(): boolean {
|
|
return process.env.NEXARCH_MOBILE_CAPTURE_ENABLED === "true";
|
|
}
|
|
|
|
export interface CapturedPage {
|
|
id: string;
|
|
blob: Blob;
|
|
previewUrl: string;
|
|
}
|
|
|
|
let pageCounter = 0;
|
|
|
|
// captureFrame liest EIN Standbild aus einem laufenden <video>-Element ueber
|
|
// einen Offscreen-<canvas> (Browser-Kamera-API, kein separates App-Paket).
|
|
// Als eigene, reine Funktion, damit CameraCapture.tsx sie mocken kann, ohne
|
|
// echtes getUserMedia/Canvas in Tests zu brauchen.
|
|
export async function captureFrame(video: HTMLVideoElement, canvas: HTMLCanvasElement): Promise<CapturedPage> {
|
|
canvas.width = video.videoWidth;
|
|
canvas.height = video.videoHeight;
|
|
const ctx = canvas.getContext("2d");
|
|
if (!ctx) throw new Error("Kamera-Aufnahme: 2D-Kontext nicht verfuegbar");
|
|
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
const blob = await new Promise<Blob>((resolve, reject) => {
|
|
canvas.toBlob((b) => (b ? resolve(b) : reject(new Error("Kamera-Aufnahme: Bild konnte nicht erzeugt werden"))), "image/jpeg", 0.9);
|
|
});
|
|
pageCounter += 1;
|
|
return { id: `seite-${pageCounter}`, blob, previewUrl: URL.createObjectURL(blob) };
|
|
}
|
|
|
|
export function pageToFile(page: CapturedPage, index: number): File {
|
|
return new File([page.blob], `kassenbeleg-${index + 1}.jpg`, { type: "image/jpeg" });
|
|
}
|