From 38fe278e15d3589cea15c08c749839fe1152878b Mon Sep 17 00:00:00 2001 From: lashman Date: Thu, 26 Feb 2026 22:47:09 +0200 Subject: [PATCH] browser handwriting api engine --- frontend/src/recognition/webApiEngine.ts | 101 +++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 frontend/src/recognition/webApiEngine.ts diff --git a/frontend/src/recognition/webApiEngine.ts b/frontend/src/recognition/webApiEngine.ts new file mode 100644 index 0000000..de254ac --- /dev/null +++ b/frontend/src/recognition/webApiEngine.ts @@ -0,0 +1,101 @@ +import type { Stroke, RecognitionCandidate } from './types' + +interface HandwritingPrediction { + text?: string | null +} + +interface HandwritingDrawing { + addStroke(stroke: { points: { x: number; y: number; t: number }[] }): void + getPrediction(): Promise + clear(): void +} + +interface HandwritingRecognizer { + startDrawing(options: { + recognitionType: string + inputType: string + textContext: string + alternatives: number + }): HandwritingDrawing +} + +let recognizer: HandwritingRecognizer | null = null +let initAttempted = false + +export function isSupported(): boolean { + return 'createHandwritingRecognizer' in navigator +} + +export function isReady(): boolean { + return recognizer !== null +} + +export async function init(): Promise { + if (initAttempted) return + initAttempted = true + + if (!isSupported()) { + console.log('Web Handwriting Recognition API not available') + return + } + + try { + // SAFETY: createHandwritingRecognizer only exists on Chromium navigators and isSupported() gates this call + const chromiumNavigator = navigator as Navigator & { + createHandwritingRecognizer: (options: { languages: string[] }) => Promise + } + recognizer = await chromiumNavigator.createHandwritingRecognizer({ + languages: ['en'], + }) + console.log('Web Handwriting Recognition API ready') + } catch (e) { + console.warn('Failed to create handwriting recognizer:', e) + recognizer = null + } +} + +export async function recognize(strokes: Stroke[]): Promise { + if (!recognizer || strokes.length === 0) return [] + + try { + const drawing = recognizer.startDrawing({ + recognitionType: 'text', + inputType: 'mouse', + textContext: '', + alternatives: 5, + }) + + for (const stroke of strokes) { + const points = stroke.map(pt => ({ + x: pt.x, + y: pt.y, + t: pt.timestamp, + })) + drawing.addStroke({ points }) + } + + const prediction = await drawing.getPrediction() + drawing.clear() + + if (!prediction || prediction.length === 0) return [] + + const candidates: RecognitionCandidate[] = [] + for (const p of prediction) { + const text = p.text?.trim().toUpperCase() + if (text && text.length === 1 && text >= 'A' && text <= 'Z') { + // avoid duplicates + if (!candidates.some(c => c.letter === text)) { + candidates.push({ + letter: text, + confidence: Math.max(0.3, 1 - candidates.length * 0.15), + }) + } + } + } + + return candidates + } catch (e) { + console.warn('Web handwriting recognition failed:', e) + return [] + } +}