@xom11/whiteboard 0.28.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.
Files changed (42) hide show
  1. package/dist/ai.d.mts +250 -277
  2. package/dist/ai.d.ts +250 -277
  3. package/dist/ai.js +6045 -7459
  4. package/dist/ai.js.map +1 -1
  5. package/dist/ai.mjs +4806 -5457
  6. package/dist/ai.mjs.map +1 -1
  7. package/dist/catalog.json +2 -2
  8. package/dist/chunk-QK6OVDLC.mjs +103 -0
  9. package/dist/chunk-QK6OVDLC.mjs.map +1 -0
  10. package/dist/{chunk-AJAHD35N.mjs → chunk-SF3U7ZF4.mjs} +3 -32
  11. package/dist/chunk-SF3U7ZF4.mjs.map +1 -0
  12. package/dist/{chunk-QCZVFEN4.mjs → chunk-XVVLT6B3.mjs} +3 -3
  13. package/dist/{chunk-QCZVFEN4.mjs.map → chunk-XVVLT6B3.mjs.map} +1 -1
  14. package/dist/geometry-2d.d.mts +1 -2
  15. package/dist/geometry-2d.d.ts +1 -2
  16. package/dist/geometry-2d.js +1457 -3832
  17. package/dist/geometry-2d.js.map +1 -1
  18. package/dist/geometry-2d.mjs +1 -1
  19. package/dist/geometry-3d.d.mts +1 -2
  20. package/dist/geometry-3d.d.ts +1 -2
  21. package/dist/graph-2d.d.mts +1 -2
  22. package/dist/graph-2d.d.ts +1 -2
  23. package/dist/handleExtractProblem-BrDY9ifM.d.mts +58 -0
  24. package/dist/handleExtractProblem-BrDY9ifM.d.ts +58 -0
  25. package/dist/{host-4P766V4J.mjs → host-3UFGFMJ2.mjs} +45 -139
  26. package/dist/host-3UFGFMJ2.mjs.map +1 -0
  27. package/dist/index.d.mts +3 -4
  28. package/dist/index.d.ts +3 -4
  29. package/dist/index.js +1487 -3862
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +4 -4
  32. package/dist/latex.d.mts +1 -2
  33. package/dist/latex.d.ts +1 -2
  34. package/dist/{types-BHYC2Fiw.d.mts → types-C3FjpoUi.d.mts} +1 -232
  35. package/dist/{types-BHYC2Fiw.d.ts → types-C3FjpoUi.d.ts} +1 -232
  36. package/package.json +1 -8
  37. package/dist/chunk-AJAHD35N.mjs.map +0 -1
  38. package/dist/chunk-T3SOHYB2.mjs +0 -851
  39. package/dist/chunk-T3SOHYB2.mjs.map +0 -1
  40. package/dist/handleExtractProblem-C-U5KluK.d.mts +0 -158
  41. package/dist/handleExtractProblem-C-U5KluK.d.ts +0 -158
  42. package/dist/host-4P766V4J.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- export { geometryStamp } from './chunk-QCZVFEN4.mjs';
2
+ export { geometryStamp } from './chunk-XVVLT6B3.mjs';
3
3
  import './chunk-H22OZYTW.mjs';
4
4
  import './chunk-R5FL6S7L.mjs';
5
5
  import './chunk-SZDAS7LK.mjs';
@@ -1,5 +1,4 @@
1
- import { B as BaseStampCustomData, S as StampType } from './types-BHYC2Fiw.mjs';
2
- import 'zod';
1
+ import { B as BaseStampCustomData, S as StampType } from './types-C3FjpoUi.mjs';
3
2
  import 'react';
4
3
  import '@excalidraw/excalidraw/element/types';
5
4
 
@@ -1,5 +1,4 @@
1
- import { B as BaseStampCustomData, S as StampType } from './types-BHYC2Fiw.js';
2
- import 'zod';
1
+ import { B as BaseStampCustomData, S as StampType } from './types-C3FjpoUi.js';
3
2
  import 'react';
4
3
  import '@excalidraw/excalidraw/element/types';
5
4
 
@@ -1,5 +1,4 @@
1
- import { B as BaseStampCustomData, S as StampType } from './types-BHYC2Fiw.mjs';
2
- import 'zod';
1
+ import { B as BaseStampCustomData, S as StampType } from './types-C3FjpoUi.mjs';
3
2
  import 'react';
4
3
  import '@excalidraw/excalidraw/element/types';
5
4
 
@@ -1,5 +1,4 @@
1
- import { B as BaseStampCustomData, S as StampType } from './types-BHYC2Fiw.js';
2
- import 'zod';
1
+ import { B as BaseStampCustomData, S as StampType } from './types-C3FjpoUi.js';
3
2
  import 'react';
4
3
  import '@excalidraw/excalidraw/element/types';
5
4
 
@@ -0,0 +1,58 @@
1
+ interface ImagePart {
2
+ /** Whitelist 3 format browser decode native được. */
3
+ mediaType: 'image/png' | 'image/jpeg' | 'image/webp';
4
+ /** Base64 không bao gồm "data:image/...;base64," prefix. */
5
+ base64: string;
6
+ }
7
+
8
+ interface ExtractProblemOptions {
9
+ /** Tesseract language. Default 'vie+eng' cho đề toán VN. */
10
+ tesseractLang?: string;
11
+ signal?: AbortSignal;
12
+ }
13
+ interface ExtractProblemSuccess {
14
+ ok: true;
15
+ text: string;
16
+ confidence: 'high' | 'low';
17
+ usage: {
18
+ inputTokens: number;
19
+ outputTokens: number;
20
+ };
21
+ }
22
+ interface ExtractProblemFailure {
23
+ ok: false;
24
+ reason: 'not-math' | 'unreadable' | 'empty' | 'unsupported';
25
+ message: string;
26
+ }
27
+ type ExtractProblemOutcome = ExtractProblemSuccess | ExtractProblemFailure;
28
+ declare function extractProblemFromImage(image: ImagePart, opts?: ExtractProblemOptions): Promise<ExtractProblemOutcome>;
29
+
30
+ interface HandleExtractProblemOptions extends ExtractProblemOptions {
31
+ }
32
+ type ExtractUiResult = {
33
+ kind: 'success';
34
+ text: string;
35
+ usage: {
36
+ inputTokens: number;
37
+ outputTokens: number;
38
+ };
39
+ } | {
40
+ kind: 'low-confidence';
41
+ text: string;
42
+ warning: string;
43
+ usage: {
44
+ inputTokens: number;
45
+ outputTokens: number;
46
+ };
47
+ } | {
48
+ kind: 'refused';
49
+ reason: 'not-math';
50
+ message: string;
51
+ } | {
52
+ kind: 'error';
53
+ code: 'network' | 'unsupported' | 'unexpected' | 'empty';
54
+ message: string;
55
+ };
56
+ declare function handleExtractProblem(image: ImagePart, opts?: HandleExtractProblemOptions): Promise<ExtractUiResult>;
57
+
58
+ export { type ExtractUiResult as E, type HandleExtractProblemOptions as H, type ImagePart as I, type ExtractProblemOptions as a, type ExtractProblemOutcome as b, extractProblemFromImage as e, handleExtractProblem as h };
@@ -0,0 +1,58 @@
1
+ interface ImagePart {
2
+ /** Whitelist 3 format browser decode native được. */
3
+ mediaType: 'image/png' | 'image/jpeg' | 'image/webp';
4
+ /** Base64 không bao gồm "data:image/...;base64," prefix. */
5
+ base64: string;
6
+ }
7
+
8
+ interface ExtractProblemOptions {
9
+ /** Tesseract language. Default 'vie+eng' cho đề toán VN. */
10
+ tesseractLang?: string;
11
+ signal?: AbortSignal;
12
+ }
13
+ interface ExtractProblemSuccess {
14
+ ok: true;
15
+ text: string;
16
+ confidence: 'high' | 'low';
17
+ usage: {
18
+ inputTokens: number;
19
+ outputTokens: number;
20
+ };
21
+ }
22
+ interface ExtractProblemFailure {
23
+ ok: false;
24
+ reason: 'not-math' | 'unreadable' | 'empty' | 'unsupported';
25
+ message: string;
26
+ }
27
+ type ExtractProblemOutcome = ExtractProblemSuccess | ExtractProblemFailure;
28
+ declare function extractProblemFromImage(image: ImagePart, opts?: ExtractProblemOptions): Promise<ExtractProblemOutcome>;
29
+
30
+ interface HandleExtractProblemOptions extends ExtractProblemOptions {
31
+ }
32
+ type ExtractUiResult = {
33
+ kind: 'success';
34
+ text: string;
35
+ usage: {
36
+ inputTokens: number;
37
+ outputTokens: number;
38
+ };
39
+ } | {
40
+ kind: 'low-confidence';
41
+ text: string;
42
+ warning: string;
43
+ usage: {
44
+ inputTokens: number;
45
+ outputTokens: number;
46
+ };
47
+ } | {
48
+ kind: 'refused';
49
+ reason: 'not-math';
50
+ message: string;
51
+ } | {
52
+ kind: 'error';
53
+ code: 'network' | 'unsupported' | 'unexpected' | 'empty';
54
+ message: string;
55
+ };
56
+ declare function handleExtractProblem(image: ImagePart, opts?: HandleExtractProblemOptions): Promise<ExtractUiResult>;
57
+
58
+ export { type ExtractUiResult as E, type HandleExtractProblemOptions as H, type ImagePart as I, type ExtractProblemOptions as a, type ExtractProblemOutcome as b, extractProblemFromImage as e, handleExtractProblem as h };
@@ -8,15 +8,15 @@ import { JxgRenderer } from './chunk-SZDAS7LK.mjs';
8
8
  import './chunk-ICR4CVOE.mjs';
9
9
  import { nextLabel, useEditorState, listObjects } from './chunk-ZTQBUKLJ.mjs';
10
10
  import './chunk-IHUFOV7L.mjs';
11
- import { validateFile, fileToImagePart, describeDsl, serializeState } from './chunk-AJAHD35N.mjs';
12
- import { handleExtractProblem } from './chunk-T3SOHYB2.mjs';
11
+ import { validateFile, fileToImagePart, describeDsl } from './chunk-SF3U7ZF4.mjs';
12
+ import { handleExtractProblem } from './chunk-QK6OVDLC.mjs';
13
13
  import { DEFAULT_VIEW_2D } from './chunk-73Q7ADVL.mjs';
14
14
  import './chunk-B4NJJZFR.mjs';
15
15
  import { useIsMobile } from './chunk-P2AOIF7S.mjs';
16
16
  import { insertStampImage } from './chunk-QGNU34T7.mjs';
17
17
  import './chunk-5UTGXHLJ.mjs';
18
18
  import { __export } from './chunk-J5LGTIGS.mjs';
19
- import { forwardRef, useRef, useId, useState, useEffect, useCallback, useImperativeHandle, useSyncExternalStore, useMemo, useLayoutEffect } from 'react';
19
+ import { forwardRef, useRef, useId, useState, useEffect, useCallback, useImperativeHandle, useMemo, useLayoutEffect } from 'react';
20
20
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
21
21
  import { createPortal } from 'react-dom';
22
22
 
@@ -3268,41 +3268,13 @@ var TransformParamPopover = ({ kind, anchor, defaultValue, onConfirm, onCancel,
3268
3268
  );
3269
3269
  return createPortal(node, document.body);
3270
3270
  };
3271
- function useAiFigure(generator, options = {}) {
3272
- const { currentState } = options;
3271
+ function useAiFigure(generator) {
3273
3272
  const [prompt, setPrompt] = useState("");
3274
3273
  const [isLoading, setIsLoading] = useState(false);
3275
3274
  const [error, setError] = useState(null);
3276
3275
  const [tokens, setTokens] = useState(0);
3277
3276
  const abortRef = useRef(null);
3278
3277
  const requestIdRef = useRef(0);
3279
- const { dsl: currentDsl, unsupported, entityCount, hasContent } = useMemo(() => {
3280
- if (!currentState || currentState.order.length === 0) {
3281
- return {
3282
- dsl: null,
3283
- unsupported: [],
3284
- entityCount: { points: 0, shapes: 0 },
3285
- hasContent: false
3286
- };
3287
- }
3288
- const { dsl, unsupported: unsupported2 } = serializeState(currentState);
3289
- return {
3290
- dsl,
3291
- unsupported: unsupported2,
3292
- entityCount: { points: dsl.points.length, shapes: dsl.shapes.length },
3293
- hasContent: true
3294
- };
3295
- }, [currentState]);
3296
- const hasUnsupported = unsupported.length > 0;
3297
- const initialMode = hasContent && !hasUnsupported ? "refine" : "build";
3298
- const [mode, setModeInternal] = useState(initialMode);
3299
- useEffect(() => {
3300
- if (!hasContent && mode === "refine") setModeInternal("build");
3301
- if (hasUnsupported && mode === "refine") setModeInternal("build");
3302
- }, [hasContent, hasUnsupported, mode]);
3303
- const setMode = useCallback((next) => {
3304
- setModeInternal(next);
3305
- }, []);
3306
3278
  useEffect(() => () => abortRef.current?.abort(), []);
3307
3279
  const submit = useCallback(async () => {
3308
3280
  const problem = prompt.trim();
@@ -3326,8 +3298,7 @@ function useAiFigure(generator, options = {}) {
3326
3298
  signal: controller.signal,
3327
3299
  onProgress: (info) => {
3328
3300
  if (requestId === requestIdRef.current) setTokens(info.tokens);
3329
- },
3330
- ...mode === "refine" && currentDsl ? { currentDsl } : {}
3301
+ }
3331
3302
  });
3332
3303
  if (controller.signal.aborted || requestId !== requestIdRef.current) return null;
3333
3304
  if (!generated.ok) {
@@ -3351,7 +3322,7 @@ function useAiFigure(generator, options = {}) {
3351
3322
  setIsLoading(false);
3352
3323
  }
3353
3324
  }
3354
- }, [generator, prompt, mode, currentDsl]);
3325
+ }, [generator, prompt]);
3355
3326
  const cancel = useCallback(() => {
3356
3327
  abortRef.current?.abort();
3357
3328
  }, []);
@@ -3362,51 +3333,42 @@ function useAiFigure(generator, options = {}) {
3362
3333
  error,
3363
3334
  submit,
3364
3335
  cancel,
3365
- tokens,
3366
- mode,
3367
- setMode,
3368
- entityCount,
3369
- hasUnsupported
3336
+ tokens
3370
3337
  };
3371
3338
  }
3372
- var PaperclipIcon = (props) => /* @__PURE__ */ jsx(
3339
+ var ArrowUpIcon = (props) => /* @__PURE__ */ jsxs(
3373
3340
  "svg",
3374
3341
  {
3375
3342
  viewBox: "0 0 24 24",
3376
3343
  fill: "none",
3377
3344
  stroke: "currentColor",
3378
- strokeWidth: 1.75,
3345
+ strokeWidth: 2.25,
3379
3346
  strokeLinecap: "round",
3380
3347
  strokeLinejoin: "round",
3381
3348
  "aria-hidden": true,
3382
3349
  ...props,
3383
- children: /* @__PURE__ */ jsx("path", { d: "M21.44 11.05 12.25 20.24a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66L9.41 17.41a2 2 0 1 1-2.83-2.83l8.49-8.49" })
3350
+ children: [
3351
+ /* @__PURE__ */ jsx("path", { d: "M12 19V5" }),
3352
+ /* @__PURE__ */ jsx("path", { d: "m5 12 7-7 7 7" })
3353
+ ]
3384
3354
  }
3385
3355
  );
3386
- var ArrowUpIcon = (props) => /* @__PURE__ */ jsxs(
3356
+ var StopIcon = (props) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) });
3357
+ var PaperclipIcon = (props) => /* @__PURE__ */ jsx(
3387
3358
  "svg",
3388
3359
  {
3389
3360
  viewBox: "0 0 24 24",
3390
3361
  fill: "none",
3391
3362
  stroke: "currentColor",
3392
- strokeWidth: 2.25,
3363
+ strokeWidth: 1.75,
3393
3364
  strokeLinecap: "round",
3394
3365
  strokeLinejoin: "round",
3395
3366
  "aria-hidden": true,
3396
3367
  ...props,
3397
- children: [
3398
- /* @__PURE__ */ jsx("path", { d: "M12 19V5" }),
3399
- /* @__PURE__ */ jsx("path", { d: "m5 12 7-7 7 7" })
3400
- ]
3368
+ children: /* @__PURE__ */ jsx("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" })
3401
3369
  }
3402
3370
  );
3403
- var StopIcon = (props) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) });
3404
- function AiFigurePrompt({
3405
- generator,
3406
- onGenerated,
3407
- currentState,
3408
- extractProblem = handleExtractProblem
3409
- }) {
3371
+ function AiFigurePrompt({ generator, onGenerated }) {
3410
3372
  const {
3411
3373
  prompt,
3412
3374
  setPrompt,
@@ -3414,12 +3376,8 @@ function AiFigurePrompt({
3414
3376
  error,
3415
3377
  submit,
3416
3378
  cancel,
3417
- tokens,
3418
- mode,
3419
- setMode,
3420
- entityCount,
3421
- hasUnsupported
3422
- } = useAiFigure(generator, { currentState });
3379
+ tokens
3380
+ } = useAiFigure(generator);
3423
3381
  const [elapsed, setElapsed] = useState(0);
3424
3382
  useEffect(() => {
3425
3383
  if (!isLoading) {
@@ -3496,7 +3454,7 @@ function AiFigurePrompt({
3496
3454
  setOcrError(null);
3497
3455
  setOcrWarning(null);
3498
3456
  try {
3499
- const r = await extractProblem(image);
3457
+ const r = await handleExtractProblem(image);
3500
3458
  if (r.kind === "success" || r.kind === "low-confidence") {
3501
3459
  setPrompt(r.text);
3502
3460
  if (r.kind === "low-confidence") setOcrWarning(r.warning);
@@ -3507,7 +3465,7 @@ function AiFigurePrompt({
3507
3465
  } finally {
3508
3466
  setOcrLoading(false);
3509
3467
  }
3510
- }, [image, setPrompt, extractProblem]);
3468
+ }, [image, setPrompt]);
3511
3469
  const handleSendClick = useCallback(async () => {
3512
3470
  if (image && !prompt.trim() && !ocrLoading) {
3513
3471
  await runOcr();
@@ -3516,62 +3474,12 @@ function AiFigurePrompt({
3516
3474
  const generated = await submit();
3517
3475
  if (generated) onGenerated(generated);
3518
3476
  }, [image, prompt, ocrLoading, runOcr, submit, onGenerated]);
3519
- const handleSwitchToBuild = useCallback(() => {
3520
- if (currentState && currentState.order.length > 0) {
3521
- const ok = window.confirm(
3522
- "D\u1EF1ng m\u1EDBi s\u1EBD thay to\xE0n b\u1ED9 h\xECnh hi\u1EC7n t\u1EA1i b\u1EB1ng h\xECnh m\u1EDBi t\u1EEB AI. Ti\u1EBFp t\u1EE5c?"
3523
- );
3524
- if (!ok) return;
3525
- }
3526
- setMode("build");
3527
- }, [currentState, setMode]);
3528
- const hasContent = currentState != null && currentState.order.length > 0;
3529
3477
  const promptEmpty = !prompt.trim();
3530
3478
  const willOcr = image != null && promptEmpty;
3531
3479
  const sendDisabled = !image && promptEmpty || ocrLoading || isLoading && !willOcr;
3532
- const refineChipLabel = entityCount.points + entityCount.shapes > 0 ? `Th\xEAm v\xE0o \xB7 ${entityCount.points}\u0111, ${entityCount.shapes}\u0111o\u1EA1n` : "Th\xEAm v\xE0o";
3533
- const placeholder = willOcr ? "B\u1EA5m g\u1EEDi \u0111\u1EC3 \u0111\u1ECDc \u0111\u1EC1 t\u1EEB \u1EA3nh \u2014 ho\u1EB7c t\u1EF1 g\xF5 \u1EDF \u0111\xE2y\u2026" : mode === "refine" ? "M\xF4 t\u1EA3 ph\u1EA7n c\u1EA7n th\xEAm (vd: trung \u0111i\u1EC3m M c\u1EE7a BC). C\xF3 th\u1EC3 d\xE1n \u1EA3nh \u0111\u1EC1 (Ctrl+V)." : "M\xF4 t\u1EA3 \u0111\u1EC1 b\xE0i c\u1EA7n d\u1EF1ng \u2014 ho\u1EB7c d\xE1n/\u0111\xEDnh \u1EA3nh \u0111\u1EC1 (Ctrl+V).";
3480
+ const placeholder = willOcr ? "B\u1EA5m g\u1EEDi \u0111\u1EC3 \u0111\u1ECDc \u0111\u1EC1 t\u1EEB \u1EA3nh \u2014 ho\u1EB7c t\u1EF1 g\xF5 \u1EDF \u0111\xE2y\u2026" : "M\xF4 t\u1EA3 \u0111\u1EC1 b\xE0i c\u1EA7n d\u1EF1ng \u2014 ho\u1EB7c d\xE1n/\u0111\xEDnh \u1EA3nh \u0111\u1EC1 (Ctrl+V).";
3534
3481
  return /* @__PURE__ */ jsxs("div", { className: "border-b border-slate-200 bg-slate-50 px-3 py-3", children: [
3535
- /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
3536
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium tracking-wide text-slate-600", children: "D\u1EF1ng h\xECnh b\u1EB1ng AI" }),
3537
- hasContent && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", role: "tablist", "aria-label": "Ch\u1EBF \u0111\u1ED9 AI", children: [
3538
- /* @__PURE__ */ jsx(
3539
- "button",
3540
- {
3541
- type: "button",
3542
- role: "tab",
3543
- "aria-selected": mode === "refine",
3544
- "data-testid": "geometry-ai-mode-refine",
3545
- onClick: () => setMode("refine"),
3546
- disabled: isLoading || hasUnsupported,
3547
- title: hasUnsupported ? "H\xECnh c\xF3 \u0111\u1ED1i t\u01B0\u1EE3ng ngo\xE0i DSL \u2014 ch\u1EC9 d\u1EF1ng m\u1EDBi \u0111\u01B0\u1EE3c" : refineChipLabel,
3548
- className: `rounded-full px-2.5 py-0.5 text-[11px] transition ${mode === "refine" ? "bg-emerald-600 text-white shadow-sm" : "text-slate-500 hover:text-emerald-700"} ${hasUnsupported ? "cursor-not-allowed opacity-40" : ""}`,
3549
- children: refineChipLabel
3550
- }
3551
- ),
3552
- /* @__PURE__ */ jsx(
3553
- "button",
3554
- {
3555
- type: "button",
3556
- role: "tab",
3557
- "aria-selected": mode === "build",
3558
- "data-testid": "geometry-ai-mode-build",
3559
- onClick: handleSwitchToBuild,
3560
- disabled: isLoading,
3561
- className: `rounded-full px-2.5 py-0.5 text-[11px] transition ${mode === "build" ? "bg-emerald-600 text-white shadow-sm" : "text-slate-500 hover:text-emerald-700"}`,
3562
- children: "D\u1EF1ng m\u1EDBi"
3563
- }
3564
- )
3565
- ] })
3566
- ] }),
3567
- hasUnsupported && /* @__PURE__ */ jsx(
3568
- "p",
3569
- {
3570
- className: "mb-1.5 text-[10px] text-amber-700",
3571
- "data-testid": "geometry-ai-unsupported-warning",
3572
- children: "H\xECnh c\xF3 \u0111\u1ED1i t\u01B0\u1EE3ng ngo\xE0i DSL \u2014 ch\u1EC9 d\u1EF1ng m\u1EDBi \u0111\u01B0\u1EE3c"
3573
- }
3574
- ),
3482
+ /* @__PURE__ */ jsx("div", { className: "mb-2 flex items-center justify-between gap-2", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium tracking-wide text-slate-600", children: "D\u1EF1ng h\xECnh b\u1EB1ng AI" }) }),
3575
3483
  /* @__PURE__ */ jsxs(
3576
3484
  "div",
3577
3485
  {
@@ -3582,8 +3490,6 @@ function AiFigurePrompt({
3582
3490
  onDragLeave: () => setIsDragOver(false),
3583
3491
  onDrop: handleDrop,
3584
3492
  onPaste: handlePaste,
3585
- "aria-label": "Khu v\u1EF1c k\xE9o th\u1EA3 \u1EA3nh",
3586
- role: "region",
3587
3493
  className: "group relative flex flex-col rounded-2xl bg-white shadow-sm transition-all duration-150 ring-1 ring-slate-200 focus-within:ring-2 focus-within:ring-emerald-400/70 focus-within:shadow-md " + (isDragOver ? "ring-2 ring-emerald-500 bg-emerald-50/40" : ""),
3588
3494
  children: [
3589
3495
  image && imagePreview && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 px-3 pt-2.5", children: /* @__PURE__ */ jsxs("div", { className: "group/chip relative", children: [
@@ -3687,14 +3593,16 @@ function AiFigurePrompt({
3687
3593
  ]
3688
3594
  }
3689
3595
  ),
3690
- /* @__PURE__ */ jsxs("p", { className: "mt-1.5 px-1 text-[10px] text-slate-500", children: [
3691
- "D\xE1n \u1EA3nh (Ctrl+V), k\xE9o th\u1EA3, ho\u1EB7c b\u1EA5m ",
3692
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u{1F4CE}" }),
3693
- " \u0111\u1EC3 \u0111\xEDnh \u1EA3nh \u0111\u1EC1."
3694
- ] }),
3695
- error && /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: error }),
3596
+ ocrWarning && /* @__PURE__ */ jsx(
3597
+ "p",
3598
+ {
3599
+ className: "mt-1 px-1 text-xs text-amber-700",
3600
+ "data-testid": "geometry-ai-ocr-warning",
3601
+ children: ocrWarning
3602
+ }
3603
+ ),
3696
3604
  ocrError && /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: ocrError }),
3697
- ocrWarning && /* @__PURE__ */ jsx("p", { className: "mt-1 rounded bg-amber-50 px-2 py-1 text-[11px] text-amber-700", children: ocrWarning })
3605
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: error })
3698
3606
  ] });
3699
3607
  }
3700
3608
 
@@ -3815,8 +3723,6 @@ var GeometryEditorPanelInner = forwardRef(
3815
3723
  }, [onGeometryDraft]);
3816
3724
  useEditorState({ store, onHistoryChange });
3817
3725
  useGeometryDraftEmit({ store, handleRef, api, showAxis, showGrid, onGeometryDraft });
3818
- const snap = () => store.getState();
3819
- const currentSceneState = useSyncExternalStore((cb) => store.subscribe(cb), snap, snap);
3820
3726
  useEffect(() => {
3821
3727
  const sync = () => setHasContent(Object.keys(store.getState().objects).length > 0);
3822
3728
  sync();
@@ -3826,22 +3732,22 @@ var GeometryEditorPanelInner = forwardRef(
3826
3732
  const h = handleRef.current;
3827
3733
  if (!h) return;
3828
3734
  setReady(true);
3829
- h.onSelect((snap2) => {
3830
- setPropsPopover(snap2);
3735
+ h.onSelect((snap) => {
3736
+ setPropsPopover(snap);
3831
3737
  setMultiSelection(null);
3832
- onSelectionChangeRef.current?.(snap2.id);
3738
+ onSelectionChangeRef.current?.(snap.id);
3833
3739
  });
3834
3740
  h.onTransformParam((info) => setTransformPopover(info));
3835
- h.onSelectionState((snap2) => {
3836
- if (!snap2 || snap2.ids.length === 0) {
3741
+ h.onSelectionState((snap) => {
3742
+ if (!snap || snap.ids.length === 0) {
3837
3743
  setPropsPopover(null);
3838
3744
  setMultiSelection(null);
3839
3745
  onSelectionChangeRef.current?.(void 0);
3840
3746
  return;
3841
3747
  }
3842
- if (snap2.ids.length === 1) {
3843
- const id = snap2.ids[0];
3844
- const single = buildObjectSnapshot(store.getState(), id, snap2.anchor);
3748
+ if (snap.ids.length === 1) {
3749
+ const id = snap.ids[0];
3750
+ const single = buildObjectSnapshot(store.getState(), id, snap.anchor);
3845
3751
  if (single) {
3846
3752
  setPropsPopover(single);
3847
3753
  setMultiSelection(null);
@@ -3849,7 +3755,7 @@ var GeometryEditorPanelInner = forwardRef(
3849
3755
  }
3850
3756
  return;
3851
3757
  }
3852
- setMultiSelection(snap2);
3758
+ setMultiSelection(snap);
3853
3759
  setPropsPopover(null);
3854
3760
  onSelectionChangeRef.current?.(void 0);
3855
3761
  });
@@ -4008,7 +3914,7 @@ var GeometryEditorPanelInner = forwardRef(
4008
3914
  /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" })
4009
3915
  ] }) })
4010
3916
  ] }),
4011
- generateGeometryFigure && /* @__PURE__ */ jsx(AiFigurePrompt, { generator: generateGeometryFigure, onGenerated: loadAiFigure, currentState: currentSceneState }),
3917
+ generateGeometryFigure && /* @__PURE__ */ jsx(AiFigurePrompt, { generator: generateGeometryFigure, onGenerated: loadAiFigure }),
4012
3918
  /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1", children: /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
4013
3919
  MiniBoard2D,
4014
3920
  {
@@ -4293,5 +4199,5 @@ var GeometryStampHost = forwardRef(
4293
4199
  );
4294
4200
 
4295
4201
  export { GeometryStampHost };
4296
- //# sourceMappingURL=host-4P766V4J.mjs.map
4297
- //# sourceMappingURL=host-4P766V4J.mjs.map
4202
+ //# sourceMappingURL=host-3UFGFMJ2.mjs.map
4203
+ //# sourceMappingURL=host-3UFGFMJ2.mjs.map