@xom11/whiteboard 0.32.0 → 0.33.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 (68) hide show
  1. package/dist/ai.d.mts +1 -1
  2. package/dist/ai.d.ts +1 -1
  3. package/dist/ai.mjs +2 -2
  4. package/dist/catalog.json +4 -4
  5. package/dist/{chunk-FI2MGX5H.mjs → chunk-4NJUTCFF.mjs} +3 -3
  6. package/dist/{chunk-FI2MGX5H.mjs.map → chunk-4NJUTCFF.mjs.map} +1 -1
  7. package/dist/{chunk-XSTGTEGP.mjs → chunk-BZZ5BROM.mjs} +4 -5
  8. package/dist/chunk-BZZ5BROM.mjs.map +1 -0
  9. package/dist/{host-JNLJ5BVR.mjs → chunk-C3RDFBQ5.mjs} +17 -32
  10. package/dist/chunk-C3RDFBQ5.mjs.map +1 -0
  11. package/dist/chunk-C7C66BAV.mjs +11 -0
  12. package/dist/chunk-C7C66BAV.mjs.map +1 -0
  13. package/dist/{chunk-DBG6DZ2V.mjs → chunk-FOPZOVI3.mjs} +5 -4
  14. package/dist/chunk-FOPZOVI3.mjs.map +1 -0
  15. package/dist/{chunk-MOAXHVKI.mjs → chunk-GKDQ5IV7.mjs} +3 -3
  16. package/dist/{chunk-MOAXHVKI.mjs.map → chunk-GKDQ5IV7.mjs.map} +1 -1
  17. package/dist/{chunk-KLGPAMJS.mjs → chunk-NBMYET5H.mjs} +3 -10
  18. package/dist/{chunk-KLGPAMJS.mjs.map → chunk-NBMYET5H.mjs.map} +1 -1
  19. package/dist/{chunk-DJM5EWTT.mjs → chunk-NTFG7ZRN.mjs} +2 -2
  20. package/dist/{chunk-DJM5EWTT.mjs.map → chunk-NTFG7ZRN.mjs.map} +1 -1
  21. package/dist/{chunk-C76SOFXF.mjs → chunk-QCK3Z5WN.mjs} +3 -3
  22. package/dist/{chunk-C76SOFXF.mjs.map → chunk-QCK3Z5WN.mjs.map} +1 -1
  23. package/dist/geometry-2d.d.mts +1 -1
  24. package/dist/geometry-2d.d.ts +1 -1
  25. package/dist/geometry-2d.js +146 -115
  26. package/dist/geometry-2d.js.map +1 -1
  27. package/dist/geometry-2d.mjs +4 -3
  28. package/dist/geometry-3d.d.mts +1 -1
  29. package/dist/geometry-3d.d.ts +1 -1
  30. package/dist/geometry-3d.js +6 -4
  31. package/dist/geometry-3d.js.map +1 -1
  32. package/dist/geometry-3d.mjs +2 -2
  33. package/dist/graph-2d.d.mts +1 -1
  34. package/dist/graph-2d.d.ts +1 -1
  35. package/dist/graph-2d.js +6 -4
  36. package/dist/graph-2d.js.map +1 -1
  37. package/dist/graph-2d.mjs +2 -2
  38. package/dist/host-6WTLCXUE.mjs +62 -0
  39. package/dist/host-6WTLCXUE.mjs.map +1 -0
  40. package/dist/{host-H2IGOKJU.mjs → host-BWIXT5TR.mjs} +3 -3
  41. package/dist/{host-H2IGOKJU.mjs.map → host-BWIXT5TR.mjs.map} +1 -1
  42. package/dist/{host-VRWLQSMH.mjs → host-QDVRULJV.mjs} +10 -7
  43. package/dist/host-QDVRULJV.mjs.map +1 -0
  44. package/dist/{host-W4U6YCSX.mjs → host-USOIVTQB.mjs} +10 -7
  45. package/dist/host-USOIVTQB.mjs.map +1 -0
  46. package/dist/index.d.mts +13 -3
  47. package/dist/index.d.ts +13 -3
  48. package/dist/index.js +173 -119
  49. package/dist/index.js.map +1 -1
  50. package/dist/index.mjs +31 -16
  51. package/dist/index.mjs.map +1 -1
  52. package/dist/latex.d.mts +1 -1
  53. package/dist/latex.d.ts +1 -1
  54. package/dist/latex.mjs +1 -1
  55. package/dist/studio.d.mts +35 -0
  56. package/dist/studio.d.ts +35 -0
  57. package/dist/studio.js +10883 -0
  58. package/dist/studio.js.map +1 -0
  59. package/dist/studio.mjs +28 -0
  60. package/dist/studio.mjs.map +1 -0
  61. package/dist/{types-tePd94vW.d.mts → types-eEEEVsLF.d.mts} +1 -1
  62. package/dist/{types-tePd94vW.d.ts → types-eEEEVsLF.d.ts} +1 -1
  63. package/package.json +9 -2
  64. package/dist/chunk-DBG6DZ2V.mjs.map +0 -1
  65. package/dist/chunk-XSTGTEGP.mjs.map +0 -1
  66. package/dist/host-JNLJ5BVR.mjs.map +0 -1
  67. package/dist/host-VRWLQSMH.mjs.map +0 -1
  68. package/dist/host-W4U6YCSX.mjs.map +0 -1
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ // src/stamps/geometry-2d/types.ts
3
+ function isGeometryCustomData(data) {
4
+ if (!data || typeof data !== "object") return false;
5
+ const d = data;
6
+ return d.kind === "geometry" && d.version === 1 && typeof d.jsonState === "string";
7
+ }
8
+
9
+ export { isGeometryCustomData };
10
+ //# sourceMappingURL=chunk-C7C66BAV.mjs.map
11
+ //# sourceMappingURL=chunk-C7C66BAV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stamps/geometry-2d/types.ts"],"names":[],"mappings":";AAQO,SAAS,qBAAqB,IAAA,EAA2C;AAC9E,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,KAAA;AAC9C,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,OAAO,CAAA,CAAE,SAAS,UAAA,IAAc,CAAA,CAAE,YAAY,CAAA,IAAK,OAAO,EAAE,SAAA,KAAc,QAAA;AAC5E","file":"chunk-C7C66BAV.mjs","sourcesContent":["import type { BaseStampCustomData } from '../shared/types';\n\nexport interface GeometryCustomData extends BaseStampCustomData {\n kind: 'geometry';\n version: 1;\n jsonState: string;\n}\n\nexport function isGeometryCustomData(data: unknown): data is GeometryCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<GeometryCustomData>;\n return d.kind === 'geometry' && d.version === 1 && typeof d.jsonState === 'string';\n}\n"]}
@@ -1,11 +1,12 @@
1
1
  "use client";
2
- import { isGeometryCustomData, renderGeometrySvgFromState } from './chunk-KLGPAMJS.mjs';
2
+ import { isGeometryCustomData } from './chunk-C7C66BAV.mjs';
3
3
  import { svgToStampFile } from './chunk-5UTGXHLJ.mjs';
4
+ import { renderGeometrySvgFromState } from './chunk-NBMYET5H.mjs';
4
5
  import { lazy } from 'react';
5
6
  import { jsxs, jsx } from 'react/jsx-runtime';
6
7
 
7
8
  var GeometryStampHost = lazy(
8
- () => import('./host-JNLJ5BVR.mjs').then((m) => ({ default: m.GeometryStampHost }))
9
+ () => import('./host-6WTLCXUE.mjs').then((m) => ({ default: m.GeometryStampHost }))
9
10
  );
10
11
  var GeometryIcon = /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
11
12
  /* @__PURE__ */ jsx("polygon", { points: "4,20 20,20 12,5" }),
@@ -38,5 +39,5 @@ var geometryStamp = {
38
39
  };
39
40
 
40
41
  export { geometryStamp };
41
- //# sourceMappingURL=chunk-DBG6DZ2V.mjs.map
42
- //# sourceMappingURL=chunk-DBG6DZ2V.mjs.map
42
+ //# sourceMappingURL=chunk-FOPZOVI3.mjs.map
43
+ //# sourceMappingURL=chunk-FOPZOVI3.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stamps/geometry-2d/index.tsx"],"names":[],"mappings":";;;;;;AAgBA,IAAM,iBAAA,GAAoB,IAAA;AAAA,EAAK,MAC7B,OAAO,qBAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,iBAAA,EAAkB,CAAE;AACjE,CAAA;AAEA,IAAM,YAAA,wBACH,KAAA,EAAA,EAAI,KAAA,EAAM,MAAK,MAAA,EAAO,IAAA,EAAK,SAAQ,WAAA,EAAY,IAAA,EAAK,QAAO,MAAA,EAAO,cAAA,EAAe,aAAY,KAAA,EAAM,aAAA,EAAc,SAAQ,cAAA,EAAe,OAAA,EAAQ,eAAY,MAAA,EAC3J,QAAA,EAAA;AAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAQ,QAAO,iBAAA,EAAkB,CAAA;AAAA,kBAClC,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,IAAA,EAAK,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO,CAAA;AAAA,kBACjE,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO,CAAA;AAAA,kBAClE,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,GAAA,EAAI,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO;AAAA,CAAA,EACnE,CAAA;AAGK,IAAM,aAAA,GAA+C;AAAA,EAC1D,IAAA,EAAM,UAAA;AAAA,EACN,WAAA,EAAa,GAAA;AAAA,EACb,YAAA,EAAc,GAAA;AAAA,EACd,YAAA,EAAc,8BAAA;AAAA,EACd,WAAA,EAAa,YAAA;AAAA,EACb,aAAA,EAAe,wBAAA;AAAA,EACf,iBAAA,EAAmB,oBAAA;AAAA,EACnB,MAAM,wBAAwB,IAAA,EAAM;AAClC,IAAA,IAAI,CAAC,oBAAA,CAAqB,IAAI,CAAA,EAAG;AAC/B,MAAA,MAAM,IAAI,MAAM,+EAAuE,CAAA;AAAA,IACzF;AACA,IAAA,OAAO,0BAAA,CAA2B,KAAK,SAAS,CAAA;AAAA,EAClD,CAAA;AAAA,EACA,MAAM,0BAA0B,OAAA,EAA4C;AAC1E,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA;AACrB,IAAA,MAAM,SAAU,OAAA,CAAuC,MAAA;AACvD,IAAA,IAAI,CAAC,QAAQ,CAAC,MAAA,IAAU,CAAC,oBAAA,CAAqB,IAAI,GAAG,OAAO,IAAA;AAC5D,IAAA,MAAM,SAAA,GAAY,MAAM,0BAAA,CAA2B,IAAA,CAAK,SAAS,CAAA;AACjE,IAAA,OAAO,cAAA,CAAe,WAAW,MAAM,CAAA;AAAA,EACzC,CAAA;AAAA,EACA,IAAA,EAAM;AACR","file":"chunk-FOPZOVI3.mjs","sourcesContent":["'use client';\n\nimport { lazy, type ReactNode } from 'react';\nimport { renderGeometrySvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\nimport { svgToStampFile } from '../shared/svgToStampFile';\nimport {\n isGeometryCustomData,\n type GeometryCustomData,\n} from './types';\n\nexport type { GeometryCustomData };\n\nconst GeometryStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.GeometryStampHost })),\n);\n\nconst GeometryIcon: ReactNode = (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <polygon points=\"4,20 20,20 12,5\" />\n <circle cx=\"4\" cy=\"20\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"20\" cy=\"20\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"12\" cy=\"5\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n);\n\nexport const geometryStamp: StampType<GeometryCustomData> = {\n kind: 'geometry',\n shortcutKey: 'g',\n toolbarLabel: 'G',\n toolbarTitle: 'Chèn hình học (G)',\n toolbarIcon: GeometryIcon,\n toolbarTestId: 'stamp-toolbar-geometry',\n matchesCustomData: isGeometryCustomData,\n async renderSvgFromCustomData(data) {\n if (!isGeometryCustomData(data)) {\n throw new Error('geometryStamp.renderSvgFromCustomData: customData không phải geometry');\n }\n return renderGeometrySvgFromState(data.jsonState);\n },\n async restoreFileFromCustomData(element): Promise<RestoredStampFile | null> {\n const data = element.customData as GeometryCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId || !isGeometryCustomData(data)) return null;\n const svgString = await renderGeometrySvgFromState(data.jsonState);\n return svgToStampFile(svgString, fileId);\n },\n Host: GeometryStampHost,\n};\n"]}
@@ -7,7 +7,7 @@ import { lazy } from 'react';
7
7
  import { jsxs, jsx } from 'react/jsx-runtime';
8
8
 
9
9
  var Graph2DStampHost = lazy(
10
- () => import('./host-VRWLQSMH.mjs').then((m) => ({ default: m.Graph2DStampHost }))
10
+ () => import('./host-QDVRULJV.mjs').then((m) => ({ default: m.Graph2DStampHost }))
11
11
  );
12
12
  var Graph2DIcon = /* @__PURE__ */ jsxs(
13
13
  "svg",
@@ -57,5 +57,5 @@ var graph2dStamp = {
57
57
  };
58
58
 
59
59
  export { graph2dStamp };
60
- //# sourceMappingURL=chunk-MOAXHVKI.mjs.map
61
- //# sourceMappingURL=chunk-MOAXHVKI.mjs.map
60
+ //# sourceMappingURL=chunk-GKDQ5IV7.mjs.map
61
+ //# sourceMappingURL=chunk-GKDQ5IV7.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stamps/graph-2d/index.tsx"],"names":[],"mappings":";;;;;;;AAUA,IAAM,gBAAA,GAAmB,IAAA;AAAA,EAAK,MAC5B,OAAO,qBAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,gBAAA,EAAiB,CAAE;AAChE,CAAA;AAEA,IAAM,WAAA,mBACJ,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,IAAA;AAAA,IACN,MAAA,EAAO,IAAA;AAAA,IACP,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAY,KAAA;AAAA,IACZ,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,aAAA,EAAY,MAAA;AAAA,IAEZ,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,GAAE,iBAAA,EAAkB,CAAA;AAAA,sBAC1B,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,4BAAA,EAA6B;AAAA;AAAA;AACvC,CAAA;AAGK,IAAM,YAAA,GAA6C;AAAA,EACxD,IAAA,EAAM,SAAA;AAAA,EACN,WAAA,EAAa,GAAA;AAAA,EACb,YAAA,EAAc,WAAA;AAAA,EACd,YAAA,EAAc,sCAAA;AAAA,EACd,WAAA,EAAa,WAAA;AAAA,EACb,aAAA,EAAe,eAAA;AAAA,EACf,YAAA,EAAc,IAAA;AAAA,EACd,iBAAA,EAAmB,mBAAA;AAAA,EAEnB,MAAM,wBAAwB,IAAA,EAAgC;AAC5D,IAAA,IAAI,CAAC,mBAAA,CAAoB,IAAI,CAAA,EAAG;AAC9B,MAAA,MAAM,IAAI,MAAM,gFAAwE,CAAA;AAAA,IAC1F;AACA,IAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,IAAA,CAAK,SAAS,CAAA;AAC5C,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,MAAM,2EAA8D,CAAA;AAC1F,IAAA,OAAO,uBAAA,CAAwB,OAAO,KAAK,CAAA;AAAA,EAC7C,CAAA;AAAA,EAEA,MAAM,0BAA0B,OAAA,EAA4C;AAC1E,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA;AACrB,IAAA,MAAM,SAAU,OAAA,CAAuC,MAAA;AACvD,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,mBAAA,CAAoB,IAAI,GAAG,OAAO,IAAA;AAClD,IAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,IAAA,CAAK,SAAS,CAAA;AAC5C,IAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,IAAA,MAAM,SAAA,GAAY,MAAM,uBAAA,CAAwB,KAAA,EAAO,KAAK,CAAA;AAC5D,IAAA,OAAO,cAAA,CAAe,WAAW,MAAM,CAAA;AAAA,EACzC,CAAA;AAAA,EAEA,IAAA,EAAM;AACR","file":"chunk-MOAXHVKI.mjs","sourcesContent":["'use client';\nimport { lazy, type ReactNode } from 'react';\nimport { renderGraphSvgFromState } from './render';\nimport { isGraph2DCustomData, type Graph2DCustomData } from './types';\nimport { parseSceneState } from './serialize';\nimport { svgToStampFile } from '../shared/svgToStampFile';\nimport type { RestoredStampFile, StampType } from '../shared/types';\n\nexport type { Graph2DCustomData };\n\nconst Graph2DStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.Graph2DStampHost })),\n);\n\nconst Graph2DIcon: ReactNode = (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M3 3L3 21L21 21\" />\n <path d=\"M6 16Q9 8 12 10Q15 12 18 6\" />\n </svg>\n);\n\nexport const graph2dStamp: StampType<Graph2DCustomData> = {\n kind: 'graph2d',\n shortcutKey: 'h',\n toolbarLabel: '📈',\n toolbarTitle: 'Chèn đồ thị 2D (H)',\n toolbarIcon: Graph2DIcon,\n toolbarTestId: 'graph2d-stamp',\n experimental: true,\n matchesCustomData: isGraph2DCustomData,\n\n async renderSvgFromCustomData(data: unknown): Promise<string> {\n if (!isGraph2DCustomData(data)) {\n throw new Error('graph2dStamp.renderSvgFromCustomData: customData không phải graph2d v2');\n }\n const state = parseSceneState(data.jsonState);\n if (!state) throw new Error('graph2dStamp.renderSvgFromCustomData: jsonState không hợp lệ');\n return renderGraphSvgFromState(state, false);\n },\n\n async restoreFileFromCustomData(element): Promise<RestoredStampFile | null> {\n const data = element.customData;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!fileId || !isGraph2DCustomData(data)) return null;\n const state = parseSceneState(data.jsonState);\n if (!state) return null;\n const svgString = await renderGraphSvgFromState(state, false);\n return svgToStampFile(svgString, fileId);\n },\n\n Host: Graph2DStampHost,\n};\n"]}
1
+ {"version":3,"sources":["../src/stamps/graph-2d/index.tsx"],"names":[],"mappings":";;;;;;;AAUA,IAAM,gBAAA,GAAmB,IAAA;AAAA,EAAK,MAC5B,OAAO,qBAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,gBAAA,EAAiB,CAAE;AAChE,CAAA;AAEA,IAAM,WAAA,mBACJ,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,IAAA;AAAA,IACN,MAAA,EAAO,IAAA;AAAA,IACP,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAY,KAAA;AAAA,IACZ,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,aAAA,EAAY,MAAA;AAAA,IAEZ,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,GAAE,iBAAA,EAAkB,CAAA;AAAA,sBAC1B,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,4BAAA,EAA6B;AAAA;AAAA;AACvC,CAAA;AAGK,IAAM,YAAA,GAA6C;AAAA,EACxD,IAAA,EAAM,SAAA;AAAA,EACN,WAAA,EAAa,GAAA;AAAA,EACb,YAAA,EAAc,WAAA;AAAA,EACd,YAAA,EAAc,sCAAA;AAAA,EACd,WAAA,EAAa,WAAA;AAAA,EACb,aAAA,EAAe,eAAA;AAAA,EACf,YAAA,EAAc,IAAA;AAAA,EACd,iBAAA,EAAmB,mBAAA;AAAA,EAEnB,MAAM,wBAAwB,IAAA,EAAgC;AAC5D,IAAA,IAAI,CAAC,mBAAA,CAAoB,IAAI,CAAA,EAAG;AAC9B,MAAA,MAAM,IAAI,MAAM,gFAAwE,CAAA;AAAA,IAC1F;AACA,IAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,IAAA,CAAK,SAAS,CAAA;AAC5C,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,MAAM,2EAA8D,CAAA;AAC1F,IAAA,OAAO,uBAAA,CAAwB,OAAO,KAAK,CAAA;AAAA,EAC7C,CAAA;AAAA,EAEA,MAAM,0BAA0B,OAAA,EAA4C;AAC1E,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA;AACrB,IAAA,MAAM,SAAU,OAAA,CAAuC,MAAA;AACvD,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,mBAAA,CAAoB,IAAI,GAAG,OAAO,IAAA;AAClD,IAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,IAAA,CAAK,SAAS,CAAA;AAC5C,IAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,IAAA,MAAM,SAAA,GAAY,MAAM,uBAAA,CAAwB,KAAA,EAAO,KAAK,CAAA;AAC5D,IAAA,OAAO,cAAA,CAAe,WAAW,MAAM,CAAA;AAAA,EACzC,CAAA;AAAA,EAEA,IAAA,EAAM;AACR","file":"chunk-GKDQ5IV7.mjs","sourcesContent":["'use client';\nimport { lazy, type ReactNode } from 'react';\nimport { renderGraphSvgFromState } from './render';\nimport { isGraph2DCustomData, type Graph2DCustomData } from './types';\nimport { parseSceneState } from './serialize';\nimport { svgToStampFile } from '../shared/svgToStampFile';\nimport type { RestoredStampFile, StampType } from '../shared/types';\n\nexport type { Graph2DCustomData };\n\nconst Graph2DStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.Graph2DStampHost })),\n);\n\nconst Graph2DIcon: ReactNode = (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M3 3L3 21L21 21\" />\n <path d=\"M6 16Q9 8 12 10Q15 12 18 6\" />\n </svg>\n);\n\nexport const graph2dStamp: StampType<Graph2DCustomData> = {\n kind: 'graph2d',\n shortcutKey: 'h',\n toolbarLabel: '📈',\n toolbarTitle: 'Chèn đồ thị 2D (H)',\n toolbarIcon: Graph2DIcon,\n toolbarTestId: 'graph2d-stamp',\n experimental: true,\n matchesCustomData: isGraph2DCustomData,\n\n async renderSvgFromCustomData(data: unknown): Promise<string> {\n if (!isGraph2DCustomData(data)) {\n throw new Error('graph2dStamp.renderSvgFromCustomData: customData không phải graph2d v2');\n }\n const state = parseSceneState(data.jsonState);\n if (!state) throw new Error('graph2dStamp.renderSvgFromCustomData: jsonState không hợp lệ');\n return renderGraphSvgFromState(state, false);\n },\n\n async restoreFileFromCustomData(element): Promise<RestoredStampFile | null> {\n const data = element.customData;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!fileId || !isGraph2DCustomData(data)) return null;\n const state = parseSceneState(data.jsonState);\n if (!state) return null;\n const svgString = await renderGraphSvgFromState(state, false);\n return svgToStampFile(svgString, fileId);\n },\n\n Host: Graph2DStampHost,\n};\n"]}
@@ -265,13 +265,6 @@ async function renderGeometrySvgFromState(jsonState) {
265
265
  return svgString;
266
266
  }
267
267
 
268
- // src/stamps/geometry-2d/types.ts
269
- function isGeometryCustomData(data) {
270
- if (!data || typeof data !== "object") return false;
271
- const d = data;
272
- return d.kind === "geometry" && d.version === 1 && typeof d.jsonState === "string";
273
- }
274
-
275
- export { autoFitBoardToContent, deserializeBoard, isDefaultBbox, isGeometryCustomData, renderGeometrySvgFromState, serializeBoard };
276
- //# sourceMappingURL=chunk-KLGPAMJS.mjs.map
277
- //# sourceMappingURL=chunk-KLGPAMJS.mjs.map
268
+ export { autoFitBoardToContent, deserializeBoard, isDefaultBbox, renderGeometrySvgFromState, serializeBoard };
269
+ //# sourceMappingURL=chunk-NBMYET5H.mjs.map
270
+ //# sourceMappingURL=chunk-NBMYET5H.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stamps/geometry-2d/serialize.ts","../src/stamps/geometry-2d/autoFitBbox.ts","../src/stamps/geometry-2d/autoFitBoard.ts","../src/stamps/geometry-2d/labelLayout.ts","../src/stamps/geometry-2d/render.ts","../src/stamps/geometry-2d/types.ts"],"names":[],"mappings":"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","file":"chunk-KLGPAMJS.mjs","sourcesContent":["// src/stamps/geometry-2d/serialize.ts\n//\n// Sau Tier D PR 3: customData.jsonState chỉ chứa `JSON.stringify(state)`\n// (không còn envelope `{version, bbox, state, showAxis, showGrid}`).\n// View info (bbox/axis/grid) nằm trong `state.meta.view` (View2D shape).\n\nimport { serializeScene, deserializeScene } from '../shared/serializeScene';\nimport type { State, View2D } from '../../core/scene';\n\nexport function serializeBoard(state: State, view: View2D): string {\n const withView: State = {\n ...state,\n meta: { domain: '2d', version: state.meta.version, view },\n };\n return serializeScene(withView);\n}\n\nexport function deserializeBoard(raw: string): State {\n return deserializeScene('2d', raw);\n}\n","// Pure geometry helpers for auto-fitting an AI-generated figure into a viewport.\n//\n// Two concerns, both visual-quality fixes:\n// 1. Outlier-robust extent — a single far-flung point or an oversized circle\n// must not compress the main cluster into a sliver (eval cau-13, cau-04).\n// We use Tukey IQR fences per axis to trim statistical outliers.\n// 2. Aspect correction — the offscreen container is sized from the DEFAULT\n// square bbox, so the fitted bbox must be expanded to the same aspect or\n// JSXGraph's setBoundingBox stretches the units → circles become ellipses\n// (eval cau-08). We grow the shorter axis (never shrink) so everything in\n// the robust extent stays visible.\n\n/**\n * Robust [min, max] of a sample using Tukey fences (Q1 - k·IQR, Q3 + k·IQR).\n * Values outside the fence are dropped before taking min/max. With no outliers\n * this returns the raw [min, max]. For < 4 samples IQR is meaningless → raw range.\n */\nexport function robustRange(values: number[], k = 1.5): [number, number] {\n const n = values.length;\n if (n === 0) return [0, 0];\n const sorted = [...values].sort((a, b) => a - b);\n if (n < 4) return [sorted[0], sorted[n - 1]];\n const quantile = (p: number): number => {\n const idx = (n - 1) * p;\n const lo = Math.floor(idx);\n const hi = Math.ceil(idx);\n if (lo === hi) return sorted[lo];\n return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo);\n };\n const q1 = quantile(0.25);\n const q3 = quantile(0.75);\n const iqr = q3 - q1;\n const fenceLo = q1 - k * iqr;\n const fenceHi = q3 + k * iqr;\n let min = Infinity;\n let max = -Infinity;\n for (const v of sorted) {\n if (v < fenceLo || v > fenceHi) continue;\n if (v < min) min = v;\n if (v > max) max = v;\n }\n if (!Number.isFinite(min) || !Number.isFinite(max)) return [sorted[0], sorted[n - 1]];\n return [min, max];\n}\n\nexport interface CircleExtent {\n cx: number;\n cy: number;\n r: number;\n}\n\n// A circle whose radius exceeds this multiple of the point-cluster diagonal is\n// treated as an oversized outlier and excluded from the fit (it still draws,\n// just clipped). Keeps a normal circumcircle in-frame while preventing a single\n// degenerate giant circle from compressing the whole figure (eval cau-13).\nconst CIRCLE_MAX_FACTOR = 1.0;\n\n/**\n * Compute a JSXGraph bbox `[xmin, ymax, xmax, ymin]` fitted to the figure.\n *\n * Points are reduced to a robust extent via Tukey IQR fences (trims stray\n * intersection points that fly off to huge coords). Circles are included WHOLE\n * (never per-edge trimmed — a clipped circle looks broken) unless a circle is\n * egregiously larger than the point cluster, in which case it is excluded. The\n * result is padded and expanded to `aspect` (= container width / height) so\n * units stay square (round circles). Returns null when there is no geometry.\n */\nexport function computeAutoFitBbox(\n points: ReadonlyArray<readonly [number, number]>,\n circles: ReadonlyArray<CircleExtent>,\n aspect: number,\n padPct = 0.12,\n): [number, number, number, number] | null {\n const xs = points.map((p) => p[0]).filter(Number.isFinite);\n const ys = points.map((p) => p[1]).filter(Number.isFinite);\n\n let xmin = Infinity, xmax = -Infinity, ymin = Infinity, ymax = -Infinity;\n let clusterDiag = 0;\n // FULL (untrimmed) point diagonal — dùng cho ngưỡng loại circle. clusterDiag\n // (robust) có thể trim NHẦM đỉnh tam giác khi điểm phái sinh CỤM ở trong →\n // clusterDiag nhỏ giả → circumcircle bình thường (r ~ bán kính) bị loại oan →\n // bbox co về cụm nhỏ → tam giác/đtròn tràn kín khung (\"khối đặc\", C72/C91).\n // fullDiag chứa cả đỉnh tam giác (~đường kính) nên circumcircle được GIỮ.\n let fullDiag = 0;\n if (xs.length >= 2 && ys.length >= 2) {\n [xmin, xmax] = robustRange(xs);\n [ymin, ymax] = robustRange(ys);\n clusterDiag = Math.hypot(xmax - xmin, ymax - ymin);\n fullDiag = Math.hypot(\n Math.max(...xs) - Math.min(...xs),\n Math.max(...ys) - Math.min(...ys),\n );\n }\n\n for (const c of circles) {\n if (!Number.isFinite(c.cx) || !Number.isFinite(c.cy) || !Number.isFinite(c.r)) continue;\n // Loại circle CHỈ khi nó to bất thường so với SPAN THÔ của điểm (không phải\n // cụm robust) → circumcircle nội tiếp tam giác KHÔNG bị loại oan; chỉ circle\n // degenerate khổng lồ (eval cau-13) mới rớt.\n if (fullDiag > 0 && c.r > CIRCLE_MAX_FACTOR * fullDiag) continue;\n xmin = Math.min(xmin, c.cx - c.r); xmax = Math.max(xmax, c.cx + c.r);\n ymin = Math.min(ymin, c.cy - c.r); ymax = Math.max(ymax, c.cy + c.r);\n }\n\n if (!Number.isFinite(xmin) || !Number.isFinite(xmax) || !Number.isFinite(ymin) || !Number.isFinite(ymax)) {\n return null;\n }\n let w = xmax - xmin;\n let h = ymax - ymin;\n\n // Degenerate (all coincident / fully collinear on one axis): floor to 1 unit.\n if (w < 0.5) { const cx = (xmin + xmax) / 2; xmin = cx - 0.5; xmax = cx + 0.5; w = 1; }\n if (h < 0.5) { const cy = (ymin + ymax) / 2; ymin = cy - 0.5; ymax = cy + 0.5; h = 1; }\n\n const padX = w * padPct;\n const padY = h * padPct;\n xmin -= padX; xmax += padX; ymin -= padY; ymax += padY;\n w = xmax - xmin; h = ymax - ymin;\n\n // Expand the shorter axis so width/height matches the container aspect.\n const curAspect = w / h;\n if (curAspect < aspect) {\n const newW = h * aspect;\n const cx = (xmin + xmax) / 2;\n xmin = cx - newW / 2; xmax = cx + newW / 2;\n } else if (curAspect > aspect) {\n const newH = w / aspect;\n const cy = (ymin + ymax) / 2;\n ymin = cy - newH / 2; ymax = cy + newH / 2;\n }\n\n return [xmin, ymax, xmax, ymin];\n}\n","import { DEFAULT_VIEW_2D } from '../../core/scene/types';\nimport { computeAutoFitBbox } from './autoFitBbox';\n\n/**\n * Auto-fit helpers chia sẻ giữa offscreen render (render.ts) và editor MiniBoard.\n *\n * Trước đây logic này CHỈ nằm trong render.ts (offscreen) → ảnh chèn vào canvas\n * được fit gọn, NHƯNG editor MiniBoard lúc re-edit boot ở bbox default (không\n * fit) → figure trong editor nhỏ/lệch so với ảnh ngoài canvas. Tách ra để cả\n * hai dùng chung → editor view khớp ảnh đã chèn.\n */\n\ntype JxgBoardLike = {\n objectsList?: unknown[];\n setBoundingBox?: (bbox: [number, number, number, number]) => void;\n update?: () => void;\n fullUpdate?: () => void;\n};\n\nexport function isDefaultBbox(bbox: readonly number[]): boolean {\n const d = DEFAULT_VIEW_2D.bbox;\n return bbox.length === 4 && bbox[0] === d[0] && bbox[1] === d[1] && bbox[2] === d[2] && bbox[3] === d[3];\n}\n\n/**\n * Thu thập toạ độ point + tâm/bán kính circle từ board rồi tính bbox fit\n * (Tukey IQR trim outlier + expand theo `aspect`). KHÔNG mutate board.\n * Trả null nếu board không có entity hợp lệ.\n */\nexport function fittedBboxFromBoard(\n board: unknown,\n aspect: number,\n): [number, number, number, number] | null {\n const objs = (board as JxgBoardLike)?.objectsList;\n if (!Array.isArray(objs)) return null;\n const points: [number, number][] = [];\n const circles: { cx: number; cy: number; r: number }[] = [];\n for (const raw of objs) {\n\n const o = raw as any;\n // OBJECT_CLASS_POINT = 1\n if (o?.elementClass === 1 && typeof o.X === 'function' && typeof o.Y === 'function') {\n const x = o.X(), y = o.Y();\n if (Number.isFinite(x) && Number.isFinite(y)) points.push([x, y]);\n } else if (o?.elementClass === 3 && o.center?.X && typeof o.Radius === 'function') {\n // OBJECT_CLASS_CIRCLE\n const cx = o.center.X(), cy = o.center.Y(), r = o.Radius();\n if (Number.isFinite(cx) && Number.isFinite(cy) && Number.isFinite(r)) {\n circles.push({ cx, cy, r });\n }\n }\n }\n const safeAspect = Number.isFinite(aspect) && aspect > 0 ? aspect : 1;\n return computeAutoFitBbox(points, circles, safeAspect);\n}\n\n/**\n * Tính bbox fit từ board rồi setBoundingBox (mutate board). No-op nếu board\n * rỗng / không tính được bbox.\n */\nexport function autoFitBoardToContent(board: unknown, aspect: number): void {\n const bbox = fittedBboxFromBoard(board, aspect);\n if (!bbox) return;\n const b = board as JxgBoardLike;\n try {\n b.setBoundingBox?.(bbox);\n if (typeof b.update === 'function') b.update();\n if (typeof b.fullUpdate === 'function') b.fullUpdate();\n } catch { /* ignore */ }\n}\n","// Anti-collision label placement for AI-generated figures.\n//\n// JSXGraph places every point label at a fixed [10,10] pixel offset (up-right).\n// When points cluster, labels overlap and overlap interior lines. We push each\n// label radially OUTWARD from the figure centroid: points on the left get a\n// left offset, points on the right get a right offset, etc. Offsets are in the\n// SAME sign convention as JSXGraph's label `offset` attribute (it negates the\n// y internally for screen space), so [dirX·R, dirY·R] in math coords maps\n// directly to a screen offset away from the centroid.\n\nexport interface LabeledPoint {\n id: string;\n x: number;\n y: number;\n}\n\nexport function radialLabelOffsets(\n points: LabeledPoint[],\n R = 14,\n): Map<string, [number, number]> {\n const out = new Map<string, [number, number]>();\n if (points.length < 2) return out;\n\n const cx = points.reduce((s, p) => s + p.x, 0) / points.length;\n const cy = points.reduce((s, p) => s + p.y, 0) / points.length;\n\n for (const p of points) {\n let dx = p.x - cx;\n let dy = p.y - cy;\n const len = Math.hypot(dx, dy);\n if (len < 1e-9) {\n // Point sits on the centroid → no meaningful direction; push up.\n dx = 0;\n dy = 1;\n } else {\n dx /= len;\n dy /= len;\n }\n out.set(p.id, [Math.round(dx * R), Math.round(dy * R)]);\n }\n return out;\n}\n","import { deserializeBoard } from './serialize';\nimport { paletteFor } from './editor/theme';\nimport { createStore } from '../../core/scene';\nimport { DEFAULT_VIEW_2D } from '../../core/scene/types';\nimport { JxgRenderer } from '../../core/scene/render/JxgRenderer';\nimport { renderJsxgOffscreen } from '../shared/jxgOffscreenRender';\nimport { autoFitBoardToContent, isDefaultBbox } from './autoFitBoard';\nimport { radialLabelOffsets } from './labelLayout';\n\n/**\n * Re-render geometry SVG từ jsonState đã serialize. Dùng cho:\n * 1. Restore math-stamp file sau khi reload page (Excalidraw mất binary files).\n * 2. Generate SVG lúc INSERT (thay vì clone DOM với màu theo theme editor).\n *\n * LƯU Ý quan trọng — luôn dùng LIGHT palette (nét đậm). Excalidraw apply CSS\n * `filter: invert(93%) hue-rotate(180deg)` lên canvas trong dark mode → nét\n * đậm tự đảo thành sáng. Nếu ta bake nét sáng vào SVG cho dark mode, filter\n * sẽ đảo thành đậm → chìm vào nền tối. Giải pháp: luôn dùng nét đậm + để\n * Excalidraw tự lo invert.\n *\n * Implementation: tạo 1 div ẩn (off-screen, real dimensions để JSXGraph render\n * chuẩn), initBoard, replay creation log từ jsonState, dump SVG, dọn dẹp.\n *\n * Container dimensions phải MATCH aspect ratio của bbox (đã được editor lưu\n * sau khi JSXGraph adjust với keepAspectRatio:true). Trước đây hardcode\n * 400×300 + keepAspectRatio:false làm shape bị kéo dãn (circle thành ellipse,\n * góc vuông lệch) khi bbox không 4:3 → ảnh hiển thị khác với editor lúc\n * double-click. Fix: tính container W/H từ bbox + keepAspectRatio:true để\n * SVG output khớp với view trong editor.\n *\n * Lý do JXG.Options.text.display = 'internal': JSXGraph mặc định render\n * label bằng HTML <div> overlay → clone SVG export sẽ thiếu label.\n */\n\n// Canvas max-axis (px) cho view tham chiếu (un-zoomed). Trước đây dùng\n// PIXELS_PER_UNIT=20 cố định → figure px BẤT BIẾN với zoom (chỉ canvas đổi),\n// và zoom-in (bbox nhỏ lại) lại cho element NHỎ hơn — ngược trực giác. Giờ\n// pxPerUnit = DEFAULT_VIEW_PX / maxSpan(bbox): max-axis canvas luôn ~constant,\n// figure bên trong scale theo zoom (zoom-in → span nhỏ → figure to ra) → ảnh\n// chèn KHỚP với view trong editor (WYSIWYG). Bump 400→500 nên figure mặc định\n// (gen ra) cũng to hơn một tí.\nconst DEFAULT_VIEW_PX = 500;\nconst MIN_DIM = 100;\nconst MAX_DIM = 1200;\nconst FALLBACK_W = 400;\nconst FALLBACK_H = 300;\n\nexport function containerDimsForBbox(bbox: [number, number, number, number]): { width: number; height: number } {\n const [xmin, ymax, xmax, ymin] = bbox;\n const w = Math.abs(xmax - xmin);\n const h = Math.abs(ymax - ymin);\n if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) {\n return { width: FALLBACK_W, height: FALLBACK_H };\n }\n const pxPerUnit = DEFAULT_VIEW_PX / Math.max(w, h);\n let width = w * pxPerUnit;\n let height = h * pxPerUnit;\n const maxAxis = Math.max(width, height);\n if (maxAxis > MAX_DIM) {\n const ratio = MAX_DIM / maxAxis;\n width *= ratio;\n height *= ratio;\n }\n const minAxis = Math.min(width, height);\n if (minAxis < MIN_DIM) {\n const ratio = MIN_DIM / minAxis;\n width *= ratio;\n height *= ratio;\n }\n return { width: Math.round(width), height: Math.round(height) };\n}\n\n/**\n * Đẩy label điểm ra xa centroid (radial) để giảm chồng nhãn. Chỉ chạy cho\n * AI-generated figure (shouldAutoFit) — stamp user-edit giữ layout nhãn mặc định.\n */\nfunction applyRadialLabelOffsets(board: any): void {\n const objs = board?.objectsList;\n if (!Array.isArray(objs)) return;\n const pts: { id: string; x: number; y: number; el: any }[] = [];\n for (const o of objs) {\n if (o?.elementClass === 1 && typeof o.X === 'function' && o.label) {\n const x = o.X(), y = o.Y();\n if (Number.isFinite(x) && Number.isFinite(y)) pts.push({ id: o.id, x, y, el: o });\n }\n }\n const offsets = radialLabelOffsets(pts.map((p) => ({ id: p.id, x: p.x, y: p.y })));\n if (offsets.size === 0) return;\n for (const p of pts) {\n const off = offsets.get(p.id);\n if (!off) continue;\n try { p.el.setAttribute({ label: { offset: off } }); } catch { /* ignore */ }\n }\n try {\n if (typeof board.update === 'function') board.update();\n } catch { /* ignore */ }\n}\n\nexport async function renderGeometrySvgFromState(jsonState: string): Promise<string> {\n const state = deserializeBoard(jsonState);\n const view = state.meta.domain === '2d' ? state.meta.view : DEFAULT_VIEW_2D;\n const bbox = view.bbox as [number, number, number, number];\n const shouldAutoFit = isDefaultBbox(bbox);\n // Stamps inserted vào Excalidraw canvas → luôn dùng light palette.\n // Excalidraw's THEME_FILTER tự đảo nét trong dark mode.\n const palette = paletteFor(false);\n const dims = containerDimsForBbox(bbox);\n const { svgString } = await renderJsxgOffscreen({\n bbox,\n dims,\n axis: view.showAxis,\n grid: view.showGrid,\n keepAspectRatio: true,\n applyOptions: (JXG) => {\n \n const opts = (JXG as any).Options;\n if (!opts) return;\n opts.text = opts.text || {};\n opts.text.display = 'internal';\n opts.text.useASCIIMathML = false;\n opts.text.useMathJax = false;\n opts.text.useKatex = false;\n opts.text.strokeColor = palette.label;\n opts.label = opts.label || {};\n opts.label.display = 'internal';\n opts.label.strokeColor = palette.label;\n opts.axis = opts.axis || {};\n opts.axis.strokeColor = palette.axis;\n opts.grid = opts.grid || {};\n opts.grid.strokeColor = palette.grid;\n },\n setup: (board) => {\n const store = createStore(state);\n const renderer = new JxgRenderer(store, board);\n if (shouldAutoFit) {\n autoFitBoardToContent(board, dims.width / dims.height);\n applyRadialLabelOffsets(board);\n }\n return renderer;\n },\n });\n return svgString;\n}\n","import type { BaseStampCustomData } from '../shared/types';\n\nexport interface GeometryCustomData extends BaseStampCustomData {\n kind: 'geometry';\n version: 1;\n jsonState: string;\n}\n\nexport function isGeometryCustomData(data: unknown): data is GeometryCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<GeometryCustomData>;\n return d.kind === 'geometry' && d.version === 1 && typeof d.jsonState === 'string';\n}\n"]}
1
+ {"version":3,"sources":["../src/stamps/geometry-2d/serialize.ts","../src/stamps/geometry-2d/autoFitBbox.ts","../src/stamps/geometry-2d/autoFitBoard.ts","../src/stamps/geometry-2d/labelLayout.ts","../src/stamps/geometry-2d/render.ts"],"names":[],"mappings":"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","file":"chunk-NBMYET5H.mjs","sourcesContent":["// src/stamps/geometry-2d/serialize.ts\n//\n// Sau Tier D PR 3: customData.jsonState chỉ chứa `JSON.stringify(state)`\n// (không còn envelope `{version, bbox, state, showAxis, showGrid}`).\n// View info (bbox/axis/grid) nằm trong `state.meta.view` (View2D shape).\n\nimport { serializeScene, deserializeScene } from '../shared/serializeScene';\nimport type { State, View2D } from '../../core/scene';\n\nexport function serializeBoard(state: State, view: View2D): string {\n const withView: State = {\n ...state,\n meta: { domain: '2d', version: state.meta.version, view },\n };\n return serializeScene(withView);\n}\n\nexport function deserializeBoard(raw: string): State {\n return deserializeScene('2d', raw);\n}\n","// Pure geometry helpers for auto-fitting an AI-generated figure into a viewport.\n//\n// Two concerns, both visual-quality fixes:\n// 1. Outlier-robust extent — a single far-flung point or an oversized circle\n// must not compress the main cluster into a sliver (eval cau-13, cau-04).\n// We use Tukey IQR fences per axis to trim statistical outliers.\n// 2. Aspect correction — the offscreen container is sized from the DEFAULT\n// square bbox, so the fitted bbox must be expanded to the same aspect or\n// JSXGraph's setBoundingBox stretches the units → circles become ellipses\n// (eval cau-08). We grow the shorter axis (never shrink) so everything in\n// the robust extent stays visible.\n\n/**\n * Robust [min, max] of a sample using Tukey fences (Q1 - k·IQR, Q3 + k·IQR).\n * Values outside the fence are dropped before taking min/max. With no outliers\n * this returns the raw [min, max]. For < 4 samples IQR is meaningless → raw range.\n */\nexport function robustRange(values: number[], k = 1.5): [number, number] {\n const n = values.length;\n if (n === 0) return [0, 0];\n const sorted = [...values].sort((a, b) => a - b);\n if (n < 4) return [sorted[0], sorted[n - 1]];\n const quantile = (p: number): number => {\n const idx = (n - 1) * p;\n const lo = Math.floor(idx);\n const hi = Math.ceil(idx);\n if (lo === hi) return sorted[lo];\n return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo);\n };\n const q1 = quantile(0.25);\n const q3 = quantile(0.75);\n const iqr = q3 - q1;\n const fenceLo = q1 - k * iqr;\n const fenceHi = q3 + k * iqr;\n let min = Infinity;\n let max = -Infinity;\n for (const v of sorted) {\n if (v < fenceLo || v > fenceHi) continue;\n if (v < min) min = v;\n if (v > max) max = v;\n }\n if (!Number.isFinite(min) || !Number.isFinite(max)) return [sorted[0], sorted[n - 1]];\n return [min, max];\n}\n\nexport interface CircleExtent {\n cx: number;\n cy: number;\n r: number;\n}\n\n// A circle whose radius exceeds this multiple of the point-cluster diagonal is\n// treated as an oversized outlier and excluded from the fit (it still draws,\n// just clipped). Keeps a normal circumcircle in-frame while preventing a single\n// degenerate giant circle from compressing the whole figure (eval cau-13).\nconst CIRCLE_MAX_FACTOR = 1.0;\n\n/**\n * Compute a JSXGraph bbox `[xmin, ymax, xmax, ymin]` fitted to the figure.\n *\n * Points are reduced to a robust extent via Tukey IQR fences (trims stray\n * intersection points that fly off to huge coords). Circles are included WHOLE\n * (never per-edge trimmed — a clipped circle looks broken) unless a circle is\n * egregiously larger than the point cluster, in which case it is excluded. The\n * result is padded and expanded to `aspect` (= container width / height) so\n * units stay square (round circles). Returns null when there is no geometry.\n */\nexport function computeAutoFitBbox(\n points: ReadonlyArray<readonly [number, number]>,\n circles: ReadonlyArray<CircleExtent>,\n aspect: number,\n padPct = 0.12,\n): [number, number, number, number] | null {\n const xs = points.map((p) => p[0]).filter(Number.isFinite);\n const ys = points.map((p) => p[1]).filter(Number.isFinite);\n\n let xmin = Infinity, xmax = -Infinity, ymin = Infinity, ymax = -Infinity;\n let clusterDiag = 0;\n // FULL (untrimmed) point diagonal — dùng cho ngưỡng loại circle. clusterDiag\n // (robust) có thể trim NHẦM đỉnh tam giác khi điểm phái sinh CỤM ở trong →\n // clusterDiag nhỏ giả → circumcircle bình thường (r ~ bán kính) bị loại oan →\n // bbox co về cụm nhỏ → tam giác/đtròn tràn kín khung (\"khối đặc\", C72/C91).\n // fullDiag chứa cả đỉnh tam giác (~đường kính) nên circumcircle được GIỮ.\n let fullDiag = 0;\n if (xs.length >= 2 && ys.length >= 2) {\n [xmin, xmax] = robustRange(xs);\n [ymin, ymax] = robustRange(ys);\n clusterDiag = Math.hypot(xmax - xmin, ymax - ymin);\n fullDiag = Math.hypot(\n Math.max(...xs) - Math.min(...xs),\n Math.max(...ys) - Math.min(...ys),\n );\n }\n\n for (const c of circles) {\n if (!Number.isFinite(c.cx) || !Number.isFinite(c.cy) || !Number.isFinite(c.r)) continue;\n // Loại circle CHỈ khi nó to bất thường so với SPAN THÔ của điểm (không phải\n // cụm robust) → circumcircle nội tiếp tam giác KHÔNG bị loại oan; chỉ circle\n // degenerate khổng lồ (eval cau-13) mới rớt.\n if (fullDiag > 0 && c.r > CIRCLE_MAX_FACTOR * fullDiag) continue;\n xmin = Math.min(xmin, c.cx - c.r); xmax = Math.max(xmax, c.cx + c.r);\n ymin = Math.min(ymin, c.cy - c.r); ymax = Math.max(ymax, c.cy + c.r);\n }\n\n if (!Number.isFinite(xmin) || !Number.isFinite(xmax) || !Number.isFinite(ymin) || !Number.isFinite(ymax)) {\n return null;\n }\n let w = xmax - xmin;\n let h = ymax - ymin;\n\n // Degenerate (all coincident / fully collinear on one axis): floor to 1 unit.\n if (w < 0.5) { const cx = (xmin + xmax) / 2; xmin = cx - 0.5; xmax = cx + 0.5; w = 1; }\n if (h < 0.5) { const cy = (ymin + ymax) / 2; ymin = cy - 0.5; ymax = cy + 0.5; h = 1; }\n\n const padX = w * padPct;\n const padY = h * padPct;\n xmin -= padX; xmax += padX; ymin -= padY; ymax += padY;\n w = xmax - xmin; h = ymax - ymin;\n\n // Expand the shorter axis so width/height matches the container aspect.\n const curAspect = w / h;\n if (curAspect < aspect) {\n const newW = h * aspect;\n const cx = (xmin + xmax) / 2;\n xmin = cx - newW / 2; xmax = cx + newW / 2;\n } else if (curAspect > aspect) {\n const newH = w / aspect;\n const cy = (ymin + ymax) / 2;\n ymin = cy - newH / 2; ymax = cy + newH / 2;\n }\n\n return [xmin, ymax, xmax, ymin];\n}\n","import { DEFAULT_VIEW_2D } from '../../core/scene/types';\nimport { computeAutoFitBbox } from './autoFitBbox';\n\n/**\n * Auto-fit helpers chia sẻ giữa offscreen render (render.ts) và editor MiniBoard.\n *\n * Trước đây logic này CHỈ nằm trong render.ts (offscreen) → ảnh chèn vào canvas\n * được fit gọn, NHƯNG editor MiniBoard lúc re-edit boot ở bbox default (không\n * fit) → figure trong editor nhỏ/lệch so với ảnh ngoài canvas. Tách ra để cả\n * hai dùng chung → editor view khớp ảnh đã chèn.\n */\n\ntype JxgBoardLike = {\n objectsList?: unknown[];\n setBoundingBox?: (bbox: [number, number, number, number]) => void;\n update?: () => void;\n fullUpdate?: () => void;\n};\n\nexport function isDefaultBbox(bbox: readonly number[]): boolean {\n const d = DEFAULT_VIEW_2D.bbox;\n return bbox.length === 4 && bbox[0] === d[0] && bbox[1] === d[1] && bbox[2] === d[2] && bbox[3] === d[3];\n}\n\n/**\n * Thu thập toạ độ point + tâm/bán kính circle từ board rồi tính bbox fit\n * (Tukey IQR trim outlier + expand theo `aspect`). KHÔNG mutate board.\n * Trả null nếu board không có entity hợp lệ.\n */\nexport function fittedBboxFromBoard(\n board: unknown,\n aspect: number,\n): [number, number, number, number] | null {\n const objs = (board as JxgBoardLike)?.objectsList;\n if (!Array.isArray(objs)) return null;\n const points: [number, number][] = [];\n const circles: { cx: number; cy: number; r: number }[] = [];\n for (const raw of objs) {\n\n const o = raw as any;\n // OBJECT_CLASS_POINT = 1\n if (o?.elementClass === 1 && typeof o.X === 'function' && typeof o.Y === 'function') {\n const x = o.X(), y = o.Y();\n if (Number.isFinite(x) && Number.isFinite(y)) points.push([x, y]);\n } else if (o?.elementClass === 3 && o.center?.X && typeof o.Radius === 'function') {\n // OBJECT_CLASS_CIRCLE\n const cx = o.center.X(), cy = o.center.Y(), r = o.Radius();\n if (Number.isFinite(cx) && Number.isFinite(cy) && Number.isFinite(r)) {\n circles.push({ cx, cy, r });\n }\n }\n }\n const safeAspect = Number.isFinite(aspect) && aspect > 0 ? aspect : 1;\n return computeAutoFitBbox(points, circles, safeAspect);\n}\n\n/**\n * Tính bbox fit từ board rồi setBoundingBox (mutate board). No-op nếu board\n * rỗng / không tính được bbox.\n */\nexport function autoFitBoardToContent(board: unknown, aspect: number): void {\n const bbox = fittedBboxFromBoard(board, aspect);\n if (!bbox) return;\n const b = board as JxgBoardLike;\n try {\n b.setBoundingBox?.(bbox);\n if (typeof b.update === 'function') b.update();\n if (typeof b.fullUpdate === 'function') b.fullUpdate();\n } catch { /* ignore */ }\n}\n","// Anti-collision label placement for AI-generated figures.\n//\n// JSXGraph places every point label at a fixed [10,10] pixel offset (up-right).\n// When points cluster, labels overlap and overlap interior lines. We push each\n// label radially OUTWARD from the figure centroid: points on the left get a\n// left offset, points on the right get a right offset, etc. Offsets are in the\n// SAME sign convention as JSXGraph's label `offset` attribute (it negates the\n// y internally for screen space), so [dirX·R, dirY·R] in math coords maps\n// directly to a screen offset away from the centroid.\n\nexport interface LabeledPoint {\n id: string;\n x: number;\n y: number;\n}\n\nexport function radialLabelOffsets(\n points: LabeledPoint[],\n R = 14,\n): Map<string, [number, number]> {\n const out = new Map<string, [number, number]>();\n if (points.length < 2) return out;\n\n const cx = points.reduce((s, p) => s + p.x, 0) / points.length;\n const cy = points.reduce((s, p) => s + p.y, 0) / points.length;\n\n for (const p of points) {\n let dx = p.x - cx;\n let dy = p.y - cy;\n const len = Math.hypot(dx, dy);\n if (len < 1e-9) {\n // Point sits on the centroid → no meaningful direction; push up.\n dx = 0;\n dy = 1;\n } else {\n dx /= len;\n dy /= len;\n }\n out.set(p.id, [Math.round(dx * R), Math.round(dy * R)]);\n }\n return out;\n}\n","import { deserializeBoard } from './serialize';\nimport { paletteFor } from './editor/theme';\nimport { createStore } from '../../core/scene';\nimport { DEFAULT_VIEW_2D } from '../../core/scene/types';\nimport { JxgRenderer } from '../../core/scene/render/JxgRenderer';\nimport { renderJsxgOffscreen } from '../shared/jxgOffscreenRender';\nimport { autoFitBoardToContent, isDefaultBbox } from './autoFitBoard';\nimport { radialLabelOffsets } from './labelLayout';\n\n/**\n * Re-render geometry SVG từ jsonState đã serialize. Dùng cho:\n * 1. Restore math-stamp file sau khi reload page (Excalidraw mất binary files).\n * 2. Generate SVG lúc INSERT (thay vì clone DOM với màu theo theme editor).\n *\n * LƯU Ý quan trọng — luôn dùng LIGHT palette (nét đậm). Excalidraw apply CSS\n * `filter: invert(93%) hue-rotate(180deg)` lên canvas trong dark mode → nét\n * đậm tự đảo thành sáng. Nếu ta bake nét sáng vào SVG cho dark mode, filter\n * sẽ đảo thành đậm → chìm vào nền tối. Giải pháp: luôn dùng nét đậm + để\n * Excalidraw tự lo invert.\n *\n * Implementation: tạo 1 div ẩn (off-screen, real dimensions để JSXGraph render\n * chuẩn), initBoard, replay creation log từ jsonState, dump SVG, dọn dẹp.\n *\n * Container dimensions phải MATCH aspect ratio của bbox (đã được editor lưu\n * sau khi JSXGraph adjust với keepAspectRatio:true). Trước đây hardcode\n * 400×300 + keepAspectRatio:false làm shape bị kéo dãn (circle thành ellipse,\n * góc vuông lệch) khi bbox không 4:3 → ảnh hiển thị khác với editor lúc\n * double-click. Fix: tính container W/H từ bbox + keepAspectRatio:true để\n * SVG output khớp với view trong editor.\n *\n * Lý do JXG.Options.text.display = 'internal': JSXGraph mặc định render\n * label bằng HTML <div> overlay → clone SVG export sẽ thiếu label.\n */\n\n// Canvas max-axis (px) cho view tham chiếu (un-zoomed). Trước đây dùng\n// PIXELS_PER_UNIT=20 cố định → figure px BẤT BIẾN với zoom (chỉ canvas đổi),\n// và zoom-in (bbox nhỏ lại) lại cho element NHỎ hơn — ngược trực giác. Giờ\n// pxPerUnit = DEFAULT_VIEW_PX / maxSpan(bbox): max-axis canvas luôn ~constant,\n// figure bên trong scale theo zoom (zoom-in → span nhỏ → figure to ra) → ảnh\n// chèn KHỚP với view trong editor (WYSIWYG). Bump 400→500 nên figure mặc định\n// (gen ra) cũng to hơn một tí.\nconst DEFAULT_VIEW_PX = 500;\nconst MIN_DIM = 100;\nconst MAX_DIM = 1200;\nconst FALLBACK_W = 400;\nconst FALLBACK_H = 300;\n\nexport function containerDimsForBbox(bbox: [number, number, number, number]): { width: number; height: number } {\n const [xmin, ymax, xmax, ymin] = bbox;\n const w = Math.abs(xmax - xmin);\n const h = Math.abs(ymax - ymin);\n if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) {\n return { width: FALLBACK_W, height: FALLBACK_H };\n }\n const pxPerUnit = DEFAULT_VIEW_PX / Math.max(w, h);\n let width = w * pxPerUnit;\n let height = h * pxPerUnit;\n const maxAxis = Math.max(width, height);\n if (maxAxis > MAX_DIM) {\n const ratio = MAX_DIM / maxAxis;\n width *= ratio;\n height *= ratio;\n }\n const minAxis = Math.min(width, height);\n if (minAxis < MIN_DIM) {\n const ratio = MIN_DIM / minAxis;\n width *= ratio;\n height *= ratio;\n }\n return { width: Math.round(width), height: Math.round(height) };\n}\n\n/**\n * Đẩy label điểm ra xa centroid (radial) để giảm chồng nhãn. Chỉ chạy cho\n * AI-generated figure (shouldAutoFit) — stamp user-edit giữ layout nhãn mặc định.\n */\nfunction applyRadialLabelOffsets(board: any): void {\n const objs = board?.objectsList;\n if (!Array.isArray(objs)) return;\n const pts: { id: string; x: number; y: number; el: any }[] = [];\n for (const o of objs) {\n if (o?.elementClass === 1 && typeof o.X === 'function' && o.label) {\n const x = o.X(), y = o.Y();\n if (Number.isFinite(x) && Number.isFinite(y)) pts.push({ id: o.id, x, y, el: o });\n }\n }\n const offsets = radialLabelOffsets(pts.map((p) => ({ id: p.id, x: p.x, y: p.y })));\n if (offsets.size === 0) return;\n for (const p of pts) {\n const off = offsets.get(p.id);\n if (!off) continue;\n try { p.el.setAttribute({ label: { offset: off } }); } catch { /* ignore */ }\n }\n try {\n if (typeof board.update === 'function') board.update();\n } catch { /* ignore */ }\n}\n\nexport async function renderGeometrySvgFromState(jsonState: string): Promise<string> {\n const state = deserializeBoard(jsonState);\n const view = state.meta.domain === '2d' ? state.meta.view : DEFAULT_VIEW_2D;\n const bbox = view.bbox as [number, number, number, number];\n const shouldAutoFit = isDefaultBbox(bbox);\n // Stamps inserted vào Excalidraw canvas → luôn dùng light palette.\n // Excalidraw's THEME_FILTER tự đảo nét trong dark mode.\n const palette = paletteFor(false);\n const dims = containerDimsForBbox(bbox);\n const { svgString } = await renderJsxgOffscreen({\n bbox,\n dims,\n axis: view.showAxis,\n grid: view.showGrid,\n keepAspectRatio: true,\n applyOptions: (JXG) => {\n \n const opts = (JXG as any).Options;\n if (!opts) return;\n opts.text = opts.text || {};\n opts.text.display = 'internal';\n opts.text.useASCIIMathML = false;\n opts.text.useMathJax = false;\n opts.text.useKatex = false;\n opts.text.strokeColor = palette.label;\n opts.label = opts.label || {};\n opts.label.display = 'internal';\n opts.label.strokeColor = palette.label;\n opts.axis = opts.axis || {};\n opts.axis.strokeColor = palette.axis;\n opts.grid = opts.grid || {};\n opts.grid.strokeColor = palette.grid;\n },\n setup: (board) => {\n const store = createStore(state);\n const renderer = new JxgRenderer(store, board);\n if (shouldAutoFit) {\n autoFitBoardToContent(board, dims.width / dims.height);\n applyRadialLabelOffsets(board);\n }\n return renderer;\n },\n });\n return svgString;\n}\n"]}
@@ -1890,5 +1890,5 @@ function describeDsl(obj, state) {
1890
1890
  }
1891
1891
 
1892
1892
  export { CIRCLE_KINDS, KIND_REGISTRY, LINE_LIKE_SHAPE_KINDS, POINT_KINDS, describeDsl, fileToImagePart, inferMediaType, validateFile };
1893
- //# sourceMappingURL=chunk-DJM5EWTT.mjs.map
1894
- //# sourceMappingURL=chunk-DJM5EWTT.mjs.map
1893
+ //# sourceMappingURL=chunk-NTFG7ZRN.mjs.map
1894
+ //# sourceMappingURL=chunk-NTFG7ZRN.mjs.map