browser handwriting api engine

This commit is contained in:
2026-02-26 22:47:09 +02:00
parent f9f90f552b
commit 38fe278e15
+101
View File
@@ -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<HandwritingPrediction[]>
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<void> {
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<HandwritingRecognizer>
}
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<RecognitionCandidate[]> {
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 []
}
}