@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 +33 -1
- package/dist/ai.d.ts +33 -1
- package/dist/ai.js +179 -0
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +3 -1
- package/dist/ai.mjs.map +1 -1
- package/dist/catalog.json +2 -2
- package/dist/chunk-QK6OVDLC.mjs +103 -0
- package/dist/chunk-QK6OVDLC.mjs.map +1 -0
- package/dist/{chunk-E6EDOPGT.mjs → chunk-SF3U7ZF4.mjs} +76 -3
- package/dist/chunk-SF3U7ZF4.mjs.map +1 -0
- package/dist/{chunk-GEC2D2EQ.mjs → chunk-XVVLT6B3.mjs} +3 -3
- package/dist/{chunk-GEC2D2EQ.mjs.map → chunk-XVVLT6B3.mjs.map} +1 -1
- package/dist/geometry-2d.js +392 -32
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +1 -1
- package/dist/handleExtractProblem-BrDY9ifM.d.mts +58 -0
- package/dist/handleExtractProblem-BrDY9ifM.d.ts +58 -0
- package/dist/{host-HKMZSCIT.mjs → host-3UFGFMJ2.mjs} +199 -34
- package/dist/host-3UFGFMJ2.mjs.map +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +396 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-E6EDOPGT.mjs.map +0 -1
- package/dist/host-HKMZSCIT.mjs.map +0 -1
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
|
-
|
|
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
|
-
|
|
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
|