@xom11/whiteboard 0.29.0 → 0.30.0

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/dist/ai.d.mts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { c as State, b as AiFigureUiResult } from './types-C3FjpoUi.mjs';
2
2
  import { z } from 'zod';
3
+ import { I as ImagePart } from './handleExtractProblem-BrDY9ifM.mjs';
4
+ export { a as ExtractProblemOptions, b as ExtractProblemOutcome, E as ExtractUiResult, H as HandleExtractProblemOptions, e as extractProblemFromImage, h as handleExtractProblem } from './handleExtractProblem-BrDY9ifM.mjs';
3
5
  import 'react';
4
6
  import '@excalidraw/excalidraw/element/types';
5
7
 
@@ -3337,4 +3339,34 @@ declare function tryPartialDeterministic(problem: string): PartialDeterministicR
3337
3339
  */
3338
3340
  declare function mergeIntents(det: readonly IntentT[], llm: readonly IntentT[]): IntentT[];
3339
3341
 
3340
- export { type AddPointIntentT, type AiFigureIntentUiResult, type ConnectIntentT, type DeterministicFigure, type DrawCircleIntentT, type DrawShapeIntentT, type FigureEnvelopeT, FigureEnvelopeZ, type GenerateIntentOptions, type HandleGenerateFigureInput, type HandleGenerateFigureIntentInput, type HandleGenerateFigureIntentOptions, type HandleGenerateFigureOptions, IntentBuilderError, type IntentEnvelopeT, IntentEnvelopeZ, type IntentFailureResult, type IntentGenerateResult, type IntentSuccessResult, type IntentT, IntentZ, type PartialDeterministicResult, type TryDeterministicResult, type VerifyIssue, type VerifyReport, buildIntentSystemPrompt, compareIntents, envelopeBuildDsl, envelopeIntentList, envelopeJsonSchema, generateFigureIntent, handleGenerateFigure, handleGenerateFigureIntent, intentEnvelopeJsonSchema, intentsToDsl, mergeIntents, tryDeterministicFigure, tryPartialDeterministic, verifyGeometry };
3342
+ declare const ALLOWED_TYPES: readonly ["image/png", "image/jpeg", "image/webp"];
3343
+ type AllowedType = (typeof ALLOWED_TYPES)[number];
3344
+ type ValidationResult = {
3345
+ ok: true;
3346
+ mediaType: AllowedType;
3347
+ } | {
3348
+ ok: false;
3349
+ code: 'invalid-format' | 'too-large';
3350
+ message: string;
3351
+ };
3352
+ declare function inferMediaType(file: File): AllowedType | null;
3353
+ declare function validateFile(file: File): ValidationResult;
3354
+ /**
3355
+ * Convert File → ImagePart. Auto-downscale nếu max edge > MAX_EDGE_PX.
3356
+ * Throws nếu file invalid hoặc decode fail.
3357
+ */
3358
+ declare function fileToImagePart(file: File): Promise<ImagePart>;
3359
+
3360
+ interface TesseractOcrOptions {
3361
+ /** Tesseract language code. Default 'vie+eng' cho đề toán VN. */
3362
+ lang?: string;
3363
+ signal?: AbortSignal;
3364
+ }
3365
+ interface TesseractOcrResult {
3366
+ text: string;
3367
+ /** Confidence 0–100 (Tesseract scale). */
3368
+ confidence: number;
3369
+ }
3370
+ declare function runTesseractOcr(image: ImagePart, opts?: TesseractOcrOptions): Promise<TesseractOcrResult>;
3371
+
3372
+ export { type AddPointIntentT, type AiFigureIntentUiResult, type ConnectIntentT, type DeterministicFigure, type DrawCircleIntentT, type DrawShapeIntentT, type FigureEnvelopeT, FigureEnvelopeZ, type GenerateIntentOptions, type HandleGenerateFigureInput, type HandleGenerateFigureIntentInput, type HandleGenerateFigureIntentOptions, type HandleGenerateFigureOptions, ImagePart, IntentBuilderError, type IntentEnvelopeT, IntentEnvelopeZ, type IntentFailureResult, type IntentGenerateResult, type IntentSuccessResult, type IntentT, IntentZ, type PartialDeterministicResult, type TryDeterministicResult, type ValidationResult, type VerifyIssue, type VerifyReport, buildIntentSystemPrompt, compareIntents, envelopeBuildDsl, envelopeIntentList, envelopeJsonSchema, fileToImagePart, generateFigureIntent, handleGenerateFigure, handleGenerateFigureIntent, inferMediaType, intentEnvelopeJsonSchema, intentsToDsl, mergeIntents, runTesseractOcr, tryDeterministicFigure, tryPartialDeterministic, validateFile, verifyGeometry };
package/dist/ai.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { c as State, b as AiFigureUiResult } from './types-C3FjpoUi.js';
2
2
  import { z } from 'zod';
3
+ import { I as ImagePart } from './handleExtractProblem-BrDY9ifM.js';
4
+ export { a as ExtractProblemOptions, b as ExtractProblemOutcome, E as ExtractUiResult, H as HandleExtractProblemOptions, e as extractProblemFromImage, h as handleExtractProblem } from './handleExtractProblem-BrDY9ifM.js';
3
5
  import 'react';
4
6
  import '@excalidraw/excalidraw/element/types';
5
7
 
@@ -3337,4 +3339,34 @@ declare function tryPartialDeterministic(problem: string): PartialDeterministicR
3337
3339
  */
3338
3340
  declare function mergeIntents(det: readonly IntentT[], llm: readonly IntentT[]): IntentT[];
3339
3341
 
3340
- export { type AddPointIntentT, type AiFigureIntentUiResult, type ConnectIntentT, type DeterministicFigure, type DrawCircleIntentT, type DrawShapeIntentT, type FigureEnvelopeT, FigureEnvelopeZ, type GenerateIntentOptions, type HandleGenerateFigureInput, type HandleGenerateFigureIntentInput, type HandleGenerateFigureIntentOptions, type HandleGenerateFigureOptions, IntentBuilderError, type IntentEnvelopeT, IntentEnvelopeZ, type IntentFailureResult, type IntentGenerateResult, type IntentSuccessResult, type IntentT, IntentZ, type PartialDeterministicResult, type TryDeterministicResult, type VerifyIssue, type VerifyReport, buildIntentSystemPrompt, compareIntents, envelopeBuildDsl, envelopeIntentList, envelopeJsonSchema, generateFigureIntent, handleGenerateFigure, handleGenerateFigureIntent, intentEnvelopeJsonSchema, intentsToDsl, mergeIntents, tryDeterministicFigure, tryPartialDeterministic, verifyGeometry };
3342
+ declare const ALLOWED_TYPES: readonly ["image/png", "image/jpeg", "image/webp"];
3343
+ type AllowedType = (typeof ALLOWED_TYPES)[number];
3344
+ type ValidationResult = {
3345
+ ok: true;
3346
+ mediaType: AllowedType;
3347
+ } | {
3348
+ ok: false;
3349
+ code: 'invalid-format' | 'too-large';
3350
+ message: string;
3351
+ };
3352
+ declare function inferMediaType(file: File): AllowedType | null;
3353
+ declare function validateFile(file: File): ValidationResult;
3354
+ /**
3355
+ * Convert File → ImagePart. Auto-downscale nếu max edge > MAX_EDGE_PX.
3356
+ * Throws nếu file invalid hoặc decode fail.
3357
+ */
3358
+ declare function fileToImagePart(file: File): Promise<ImagePart>;
3359
+
3360
+ interface TesseractOcrOptions {
3361
+ /** Tesseract language code. Default 'vie+eng' cho đề toán VN. */
3362
+ lang?: string;
3363
+ signal?: AbortSignal;
3364
+ }
3365
+ interface TesseractOcrResult {
3366
+ text: string;
3367
+ /** Confidence 0–100 (Tesseract scale). */
3368
+ confidence: number;
3369
+ }
3370
+ declare function runTesseractOcr(image: ImagePart, opts?: TesseractOcrOptions): Promise<TesseractOcrResult>;
3371
+
3372
+ export { type AddPointIntentT, type AiFigureIntentUiResult, type ConnectIntentT, type DeterministicFigure, type DrawCircleIntentT, type DrawShapeIntentT, type FigureEnvelopeT, FigureEnvelopeZ, type GenerateIntentOptions, type HandleGenerateFigureInput, type HandleGenerateFigureIntentInput, type HandleGenerateFigureIntentOptions, type HandleGenerateFigureOptions, ImagePart, IntentBuilderError, type IntentEnvelopeT, IntentEnvelopeZ, type IntentFailureResult, type IntentGenerateResult, type IntentSuccessResult, type IntentT, IntentZ, type PartialDeterministicResult, type TryDeterministicResult, type ValidationResult, type VerifyIssue, type VerifyReport, buildIntentSystemPrompt, compareIntents, envelopeBuildDsl, envelopeIntentList, envelopeJsonSchema, fileToImagePart, generateFigureIntent, handleGenerateFigure, handleGenerateFigureIntent, inferMediaType, intentEnvelopeJsonSchema, intentsToDsl, mergeIntents, runTesseractOcr, tryDeterministicFigure, tryPartialDeterministic, validateFile, verifyGeometry };
package/dist/ai.js CHANGED
@@ -6533,6 +6533,179 @@ function mergeIntents(det, llm) {
6533
6533
  return out;
6534
6534
  }
6535
6535
 
6536
+ // src/stamps/geometry-2d/ai/vision/tesseract.ts
6537
+ var DEFAULT_LANG = "vie+eng";
6538
+ async function runTesseractOcr(image, opts = {}) {
6539
+ if (opts.signal?.aborted) {
6540
+ const err = new Error("Tesseract OCR aborted");
6541
+ err.name = "AbortError";
6542
+ throw err;
6543
+ }
6544
+ const { createWorker } = await import('tesseract.js');
6545
+ const lang = opts.lang ?? DEFAULT_LANG;
6546
+ const worker = await createWorker(lang);
6547
+ try {
6548
+ const dataUrl = `data:${image.mediaType};base64,${image.base64}`;
6549
+ const { data } = await worker.recognize(dataUrl);
6550
+ return { text: data.text, confidence: data.confidence };
6551
+ } finally {
6552
+ await worker.terminate();
6553
+ }
6554
+ }
6555
+
6556
+ // src/stamps/geometry-2d/ai/vision/extractProblem.ts
6557
+ var MIN_HIGH_CONFIDENCE_CHARS = 10;
6558
+ var MAX_TEXT_CHARS = 2e3;
6559
+ var TESSERACT_HIGH_CONFIDENCE_THRESHOLD = 70;
6560
+ async function extractProblemFromImage(image, opts = {}) {
6561
+ let raw;
6562
+ try {
6563
+ raw = await runTesseractOcr(image, {
6564
+ ...opts.tesseractLang ? { lang: opts.tesseractLang } : {},
6565
+ ...opts.signal ? { signal: opts.signal } : {}
6566
+ });
6567
+ } catch (e) {
6568
+ const err = e;
6569
+ return {
6570
+ ok: false,
6571
+ reason: "unreadable",
6572
+ message: "Tesseract OCR fail: " + (err.message ?? "?")
6573
+ };
6574
+ }
6575
+ const text = postProcess(raw.text);
6576
+ if (text.length === 0) {
6577
+ return { ok: false, reason: "empty", message: "Tesseract kh\xF4ng tr\xEDch \u0111\u01B0\u1EE3c text." };
6578
+ }
6579
+ const tooShort = text.length < MIN_HIGH_CONFIDENCE_CHARS;
6580
+ const lowConf = raw.confidence < TESSERACT_HIGH_CONFIDENCE_THRESHOLD;
6581
+ const confidence = tooShort || lowConf ? "low" : "high";
6582
+ return {
6583
+ ok: true,
6584
+ text,
6585
+ confidence,
6586
+ usage: { inputTokens: 0, outputTokens: 0 }
6587
+ };
6588
+ }
6589
+ function postProcess(raw) {
6590
+ let t = raw.trim();
6591
+ t = t.replace(/\*\*(.+?)\*\*/g, "$1");
6592
+ t = t.replace(/\*(.+?)\*/g, "$1");
6593
+ t = t.replace(/_(.+?)_/g, "$1");
6594
+ t = t.replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, "$1");
6595
+ t = t.replace(/\s+/g, " ").trim();
6596
+ t = t.normalize("NFC");
6597
+ if (t.length > MAX_TEXT_CHARS) t = t.slice(0, MAX_TEXT_CHARS);
6598
+ return t;
6599
+ }
6600
+
6601
+ // src/stamps/geometry-2d/ai/handleExtractProblem.ts
6602
+ async function handleExtractProblem(image, opts = {}) {
6603
+ try {
6604
+ const r = await extractProblemFromImage(image, opts);
6605
+ if (r.ok) {
6606
+ if (r.confidence === "low") {
6607
+ return {
6608
+ kind: "low-confidence",
6609
+ text: r.text,
6610
+ warning: "OCR c\xF3 th\u1EC3 kh\xF4ng ch\xEDnh x\xE1c, ki\u1EC3m tra tr\u01B0\u1EDBc khi v\u1EBD.",
6611
+ usage: r.usage
6612
+ };
6613
+ }
6614
+ return { kind: "success", text: r.text, usage: r.usage };
6615
+ }
6616
+ if (r.reason === "not-math") {
6617
+ return { kind: "refused", reason: "not-math", message: r.message };
6618
+ }
6619
+ if (r.reason === "unsupported") {
6620
+ return { kind: "error", code: "unsupported", message: r.message };
6621
+ }
6622
+ if (r.reason === "unreadable") {
6623
+ return { kind: "error", code: "network", message: r.message };
6624
+ }
6625
+ return { kind: "error", code: "empty", message: r.message };
6626
+ } catch (e) {
6627
+ return {
6628
+ kind: "error",
6629
+ code: "unexpected",
6630
+ message: e instanceof Error ? e.message : String(e)
6631
+ };
6632
+ }
6633
+ }
6634
+
6635
+ // src/stamps/geometry-2d/ai/vision/preprocess.ts
6636
+ var MAX_EDGE_PX = 2048;
6637
+ var MAX_RAW_BYTES = 10 * 1024 * 1024;
6638
+ var MAX_ENCODED_BYTES = 3 * 1024 * 1024;
6639
+ var ALLOWED_TYPES = ["image/png", "image/jpeg", "image/webp"];
6640
+ function inferMediaType(file) {
6641
+ const t = file.type.toLowerCase();
6642
+ if (ALLOWED_TYPES.includes(t)) return t;
6643
+ return null;
6644
+ }
6645
+ function validateFile(file) {
6646
+ const mt = inferMediaType(file);
6647
+ if (mt == null) {
6648
+ return {
6649
+ ok: false,
6650
+ code: "invalid-format",
6651
+ message: "Ch\u1EC9 h\u1ED7 tr\u1EE3 PNG, JPEG, WEBP."
6652
+ };
6653
+ }
6654
+ if (file.size > MAX_RAW_BYTES) {
6655
+ return {
6656
+ ok: false,
6657
+ code: "too-large",
6658
+ message: `\u1EA2nh qu\xE1 l\u1EDBn (> ${Math.round(MAX_RAW_BYTES / 1024 / 1024)} MB). Crop ho\u1EB7c resize tr\u01B0\u1EDBc.`
6659
+ };
6660
+ }
6661
+ return { ok: true, mediaType: mt };
6662
+ }
6663
+ async function fileToImagePart(file) {
6664
+ const v = validateFile(file);
6665
+ if (!v.ok) throw new Error(v.message);
6666
+ const bitmap = await createImageBitmap(file);
6667
+ const { width, height } = bitmap;
6668
+ const maxEdge = Math.max(width, height);
6669
+ const scale = maxEdge > MAX_EDGE_PX ? MAX_EDGE_PX / maxEdge : 1;
6670
+ const targetW = Math.round(width * scale);
6671
+ const targetH = Math.round(height * scale);
6672
+ const canvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(targetW, targetH) : Object.assign(document.createElement("canvas"), { width: targetW, height: targetH });
6673
+ const ctx = canvas.getContext("2d");
6674
+ if (!ctx) throw new Error("Kh\xF4ng t\u1EA1o \u0111\u01B0\u1EE3c canvas 2D context");
6675
+ ctx.drawImage(bitmap, 0, 0, targetW, targetH);
6676
+ bitmap.close();
6677
+ let outputType = v.mediaType === "image/png" ? "image/png" : "image/jpeg";
6678
+ let finalBlob = await canvasToBlob(canvas, outputType, 0.92);
6679
+ if (finalBlob.size > MAX_ENCODED_BYTES) {
6680
+ outputType = "image/jpeg";
6681
+ finalBlob = await canvasToBlob(canvas, "image/jpeg", 0.7);
6682
+ }
6683
+ const base64 = await blobToBase64(finalBlob);
6684
+ return { mediaType: outputType, base64 };
6685
+ }
6686
+ async function canvasToBlob(canvas, type, quality) {
6687
+ if ("convertToBlob" in canvas) {
6688
+ return canvas.convertToBlob({ type, quality });
6689
+ }
6690
+ return new Promise((resolve, reject) => {
6691
+ canvas.toBlob(
6692
+ (b) => b ? resolve(b) : reject(new Error("Canvas encode fail")),
6693
+ type,
6694
+ quality
6695
+ );
6696
+ });
6697
+ }
6698
+ async function blobToBase64(blob) {
6699
+ const buf = await blob.arrayBuffer();
6700
+ let binary = "";
6701
+ const bytes = new Uint8Array(buf);
6702
+ const chunk = 32768;
6703
+ for (let i = 0; i < bytes.length; i += chunk) {
6704
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
6705
+ }
6706
+ return typeof btoa === "function" ? btoa(binary) : Buffer.from(binary, "binary").toString("base64");
6707
+ }
6708
+
6536
6709
  exports.FigureEnvelopeZ = FigureEnvelopeZ;
6537
6710
  exports.IntentBuilderError = IntentBuilderError;
6538
6711
  exports.IntentEnvelopeZ = IntentEnvelopeZ;
@@ -6542,14 +6715,20 @@ exports.compareIntents = compareIntents;
6542
6715
  exports.envelopeBuildDsl = envelopeBuildDsl;
6543
6716
  exports.envelopeIntentList = envelopeIntentList;
6544
6717
  exports.envelopeJsonSchema = envelopeJsonSchema;
6718
+ exports.extractProblemFromImage = extractProblemFromImage;
6719
+ exports.fileToImagePart = fileToImagePart;
6545
6720
  exports.generateFigureIntent = generateFigureIntent;
6721
+ exports.handleExtractProblem = handleExtractProblem;
6546
6722
  exports.handleGenerateFigure = handleGenerateFigure;
6547
6723
  exports.handleGenerateFigureIntent = handleGenerateFigureIntent;
6724
+ exports.inferMediaType = inferMediaType;
6548
6725
  exports.intentEnvelopeJsonSchema = intentEnvelopeJsonSchema;
6549
6726
  exports.intentsToDsl = intentsToDsl;
6550
6727
  exports.mergeIntents = mergeIntents;
6728
+ exports.runTesseractOcr = runTesseractOcr;
6551
6729
  exports.tryDeterministicFigure = tryDeterministicFigure;
6552
6730
  exports.tryPartialDeterministic = tryPartialDeterministic;
6731
+ exports.validateFile = validateFile;
6553
6732
  exports.verifyGeometry = verifyGeometry;
6554
6733
  //# sourceMappingURL=ai.js.map
6555
6734
  //# sourceMappingURL=ai.js.map