@xom11/whiteboard 0.7.0 → 0.10.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/README.md +51 -1
- package/dist/chunk-74VEEZBV.mjs +619 -0
- package/dist/chunk-74VEEZBV.mjs.map +1 -0
- package/dist/{chunk-BJX4YNA5.mjs → chunk-G7FR3AIV.mjs} +68 -12
- package/dist/chunk-G7FR3AIV.mjs.map +1 -0
- package/dist/{chunk-SHFOGORM.mjs → chunk-PDKKDZ4H.mjs} +4 -4
- package/dist/{chunk-SHFOGORM.mjs.map → chunk-PDKKDZ4H.mjs.map} +1 -1
- package/dist/chunk-PWIMZIB6.mjs +62 -0
- package/dist/chunk-PWIMZIB6.mjs.map +1 -0
- package/dist/{chunk-LPM4MM45.mjs → chunk-SBDMF4NQ.mjs} +3 -2
- package/dist/chunk-SBDMF4NQ.mjs.map +1 -0
- package/dist/chunk-WQOABS6N.mjs +197 -0
- package/dist/chunk-WQOABS6N.mjs.map +1 -0
- package/dist/{chunk-3SSQKRRO.mjs → chunk-ZVN356JZ.mjs} +4 -4
- package/dist/{chunk-3SSQKRRO.mjs.map → chunk-ZVN356JZ.mjs.map} +1 -1
- package/dist/geometry-2d.js +344 -228
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +2 -2
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +3411 -1277
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +3 -2
- package/dist/graph-2d.js +360 -66
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +2 -2
- package/dist/{host-T2W6R6SO.mjs → host-DJETSFCG.mjs} +272 -223
- package/dist/host-DJETSFCG.mjs.map +1 -0
- package/dist/{host-2QGKMGCT.mjs → host-LZH2FZ2N.mjs} +3 -3
- package/dist/{host-2QGKMGCT.mjs.map → host-LZH2FZ2N.mjs.map} +1 -1
- package/dist/host-N6ACNJKI.mjs +3226 -0
- package/dist/host-N6ACNJKI.mjs.map +1 -0
- package/dist/index.d.mts +133 -6
- package/dist/index.d.ts +133 -6
- package/dist/index.js +5634 -1999
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1231 -146
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -6
- package/dist/chunk-BJX4YNA5.mjs.map +0 -1
- package/dist/chunk-DJTBZEAR.mjs +0 -25
- package/dist/chunk-DJTBZEAR.mjs.map +0 -1
- package/dist/chunk-HM7RIXJE.mjs +0 -331
- package/dist/chunk-HM7RIXJE.mjs.map +0 -1
- package/dist/chunk-HYXFHEDJ.mjs +0 -129
- package/dist/chunk-HYXFHEDJ.mjs.map +0 -1
- package/dist/chunk-LPM4MM45.mjs.map +0 -1
- package/dist/host-T2W6R6SO.mjs.map +0 -1
- package/dist/host-XUFON6CQ.mjs +0 -1422
- package/dist/host-XUFON6CQ.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xom11/whiteboard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Excalidraw + JSXGraph + KaTeX whiteboard component (drawing, geometry stamps, LaTeX stamps).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,20 +50,20 @@
|
|
|
50
50
|
"build": "tsup && node scripts/inject-use-client.mjs",
|
|
51
51
|
"dev": "tsup --watch --onSuccess \"node scripts/inject-use-client.mjs\"",
|
|
52
52
|
"test": "jest",
|
|
53
|
+
"test:e2e": "playwright test",
|
|
53
54
|
"typecheck": "tsc --noEmit",
|
|
54
55
|
"clean": "rm -rf dist .yalc yalc.lock",
|
|
55
56
|
"demo": "vite --config scripts/demo/vite.config.ts"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
|
-
"react": ">=18.0.0",
|
|
59
|
-
"react-dom": ">=18.0.0"
|
|
60
|
-
},
|
|
61
|
-
"dependencies": {
|
|
62
59
|
"@excalidraw/excalidraw": "^0.18.1",
|
|
63
60
|
"jsxgraph": "^1.12.2",
|
|
64
|
-
"katex": "^0.16.45"
|
|
61
|
+
"katex": "^0.16.45",
|
|
62
|
+
"react": ">=18.0.0",
|
|
63
|
+
"react-dom": ">=18.0.0"
|
|
65
64
|
},
|
|
66
65
|
"devDependencies": {
|
|
66
|
+
"@playwright/test": "^1.60.0",
|
|
67
67
|
"@tailwindcss/vite": "^4.3.0",
|
|
68
68
|
"@testing-library/jest-dom": "^6.9.1",
|
|
69
69
|
"@testing-library/react": "^16.3.2",
|
|
@@ -85,5 +85,8 @@
|
|
|
85
85
|
"tsup": "^8.3.5",
|
|
86
86
|
"typescript": "^5.6.0",
|
|
87
87
|
"vite": "^8.0.13"
|
|
88
|
+
},
|
|
89
|
+
"dependencies": {
|
|
90
|
+
"pdfjs-dist": "^5.7.284"
|
|
88
91
|
}
|
|
89
92
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stamps/geometry-2d/renderInline.ts","../src/stamps/geometry-2d/serialize.ts","../src/stamps/geometry-2d/render.ts","../src/stamps/geometry-2d/types.ts"],"names":[],"mappings":";;;AAAO,SAAS,oBAAoB,cAAA,EAAqC;AACvE,EAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,aAAA,CAAc,KAAK,CAAA;AAChD,EAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAClF,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,SAAA,CAAU,IAAI,CAAA;AAClC,EAAA,IAAI,CAAC,KAAA,CAAM,YAAA,CAAa,OAAO,CAAA,EAAG;AAChC,IAAA,KAAA,CAAM,YAAA,CAAa,SAAS,4BAA4B,CAAA;AAAA,EAC1D;AACA,EAAA,OAAO,IAAI,aAAA,EAAc,CAAE,iBAAA,CAAkB,KAAK,CAAA;AACpD;;;ACwBO,SAAS,cAAA,CACd,KAAA,EACA,GAAA,EACA,OAAA,GAAsD,EAAC,EACtC;AACjB,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,MAAM,cAAA,EAAe;AAAA,IAC3B,UAAU,GAAA,CAAI,GAAA,CAAI,CAAA,CAAA,MAAM,EAAE,MAAM,CAAA,CAAE,IAAA,EAAM,IAAA,EAAM,CAAA,CAAE,MAAM,KAAA,EAAO,CAAA,CAAE,OAAO,EAAA,EAAI,CAAA,CAAE,IAAG,CAAE,CAAA;AAAA,IACjF,QAAA,EAAU,CAAC,CAAC,OAAA,CAAQ,QAAA;AAAA,IACpB,QAAA,EAAU,CAAC,CAAC,OAAA,CAAQ;AAAA,GACtB;AACF;AAGA,SAAS,gBAAA,CAAiB,OAAY,MAAA,EAAsB;AAC1D,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,MAAA,EAAQ,OAAO,IAAA;AAC9B,EAAA,MAAM,CAAA,GAAA,CAAK,OAAO,MAAA,IAAU,MAAA,CAAO,QAAQ,EAAA,EAAI,QAAA,GAAW,WAAA,EAAY;AACtE,EAAA,IAAI,CAAA,KAAM,SAAA,IAAa,CAAA,KAAM,MAAA,IAAU,MAAM,OAAA,EAAS;AACpD,IAAA,MAAM,EAAA,GAAK,MAAA,CAAO,MAAA,EAAQ,EAAA,GAAK,MAAA,CAAO,MAAA;AACtC,IAAA,IAAI,CAAC,EAAA,IAAM,CAAC,EAAA,EAAI,OAAO,IAAA;AACvB,IAAA,OAAO,KAAA,CAAM,OAAO,MAAA,EAAQ;AAAA,MAC1B,OAAO,EAAA,CAAG,CAAA,KAAM,EAAA,CAAG,CAAA,MAAO,CAAA,GAAI,IAAA;AAAA,MAC9B,OAAO,EAAA,CAAG,CAAA,KAAM,EAAA,CAAG,CAAA,MAAO,CAAA,GAAI,IAAA;AAAA,MAC9B,MAAM;AACJ,QAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,EAAA,CAAG,GAAE,GAAI,EAAA,CAAG,CAAA,EAAE,EAAG,EAAA,CAAG,CAAA,EAAE,GAAI,EAAA,CAAG,GAAG,CAAA;AACvD,QAAA,MAAM,IAAA,GAAO,OAAO,MAAA,CAAO,IAAA,KAAS,YAAY,MAAA,CAAO,IAAA,GAAO,OAAO,IAAA,GAAO,GAAA;AAC5E,QAAA,OAAO,GAAG,IAAI,CAAA,GAAA,EAAM,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA;AAAA,MACpC;AAAA,KACF,EAAG,EAAE,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,WAAW,KAAA,EAAO,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,CAAA;AAAA,EACtE;AACA,EAAA,IAAI,CAAA,KAAM,QAAA,IAAY,CAAA,KAAM,cAAA,EAAgB;AAC1C,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,IAAU,MAAA,CAAO,YAAY,MAAA,CAAO,MAAA;AAC1D,IAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,IAAA,OAAO,KAAA,CAAM,OAAO,MAAA,EAAQ;AAAA,MAC1B,MAAM,MAAA,CAAO,CAAA,EAAE,GAAI,GAAA;AAAA,MACnB,MAAM,MAAA,CAAO,CAAA,EAAE,GAAI,GAAA;AAAA,MACnB,MAAM;AACJ,QAAA,MAAM,IAAI,OAAO,MAAA,CAAO,WAAW,UAAA,GAAa,MAAA,CAAO,QAAO,GAAI,CAAA;AAClE,QAAA,MAAM,IAAA,GAAO,OAAO,MAAA,CAAO,IAAA,KAAS,YAAY,MAAA,CAAO,IAAA,GAAO,OAAO,IAAA,GAAO,GAAA;AAC5E,QAAA,OAAO,GAAG,IAAI,CAAA,GAAA,EAAM,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA;AAAA,MAClC;AAAA,KACF,EAAG,EAAE,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,WAAW,KAAA,EAAO,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,CAAA;AAAA,EACtE;AACA,EAAA,OAAO,IAAA;AACT;AAOO,SAAS,oBAAA,CACd,KAAA,EACA,UAAA,EACA,OAAA,GAA8B,EAAC,EACzB;AAMN,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,UAAA,CAAW,KAAK,CAAA;AACnD,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAqB;AACvC,EAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAwB;AACvC,IAAA,IAAI,OAAO,CAAA,KAAM,QAAA,IAAY,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,EAAG,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA;AAC7D,IAAA,IAAI,MAAM,OAAA,CAAQ,CAAC,GAAG,OAAO,CAAA,CAAE,IAAI,OAAO,CAAA;AAC1C,IAAA,OAAO,CAAA;AAAA,EACT,CAAA;AACA,EAAA,KAAA,MAAW,EAAA,IAAM,WAAW,QAAA,EAAU;AACpC,IAAA,MAAM,YAAA,GAAe,EAAA,CAAG,IAAA,CAAK,GAAA,CAAI,OAAO,CAAA;AACxC,IAAA,IAAI,EAAA,CAAG,SAAS,YAAA,EAAc;AAC5B,MAAA,MAAM,MAAA,GAAS,aAAa,CAAC,CAAA;AAC7B,MAAA,MAAM,GAAA,GAAM,gBAAA,CAAiB,KAAA,EAAO,MAAM,CAAA;AAC1C,MAAA,IAAI,GAAA,EAAK,KAAA,CAAM,GAAA,CAAI,EAAA,CAAG,IAAI,GAAG,CAAA;AAC7B,MAAA;AAAA,IACF;AACA,IAAA,MAAM,cAAc,iBAAA,CAAkB,EAAE,GAAG,EAAA,CAAG,KAAA,IAAS,OAAO,CAAA;AAC9D,IAAA,MAAM,UAAU,KAAA,CAAM,MAAA,CAAO,EAAA,CAAG,IAAA,EAAM,cAAc,WAAW,CAAA;AAC/D,IAAA,KAAA,CAAM,GAAA,CAAI,EAAA,CAAG,EAAA,EAAI,OAAO,CAAA;AAAA,EAC1B;AACF;;;AC3FA,eAAsB,2BAA2B,SAAA,EAAoC;AACnF,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA;AAGnC,EAAA,MAAM,OAAA,GAAU,WAAW,KAAK,CAAA;AAChC,EAAA,MAAM,GAAA,GAAA,CAAO,MAAM,OAAO,UAAU,CAAA,EAAG,OAAA;AACvC,EAAA,IAAI;AAEF,IAAA,MAAM,OAAQ,GAAA,CAAY,OAAA;AAC1B,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,IAAA,CAAK,IAAA,GAAO,IAAA,CAAK,IAAA,IAAQ,EAAC;AAC1B,MAAA,IAAA,CAAK,KAAK,OAAA,GAAU,UAAA;AACpB,MAAA,IAAA,CAAK,KAAK,cAAA,GAAiB,KAAA;AAC3B,MAAA,IAAA,CAAK,KAAK,UAAA,GAAa,KAAA;AACvB,MAAA,IAAA,CAAK,KAAK,QAAA,GAAW,KAAA;AACrB,MAAA,IAAA,CAAK,IAAA,CAAK,cAAc,OAAA,CAAQ,KAAA;AAChC,MAAA,IAAA,CAAK,KAAA,GAAQ,IAAA,CAAK,KAAA,IAAS,EAAC;AAC5B,MAAA,IAAA,CAAK,MAAM,OAAA,GAAU,UAAA;AACrB,MAAA,IAAA,CAAK,KAAA,CAAM,cAAc,OAAA,CAAQ,KAAA;AACjC,MAAA,IAAA,CAAK,IAAA,GAAO,IAAA,CAAK,IAAA,IAAQ,EAAC;AAC1B,MAAA,IAAA,CAAK,IAAA,CAAK,cAAc,OAAA,CAAQ,IAAA;AAChC,MAAA,IAAA,CAAK,IAAA,GAAO,IAAA,CAAK,IAAA,IAAQ,EAAC;AAC1B,MAAA,IAAA,CAAK,IAAA,CAAK,cAAc,OAAA,CAAQ,IAAA;AAAA,IAClC;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAAe;AACvB,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC9C,EAAA,MAAM,WAAA,GAAc,gBAAA,GAAmB,IAAA,CAAK,GAAA,KAAQ,GAAA,GAAM,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,CAAC,CAAA;AAC/F,EAAA,SAAA,CAAU,EAAA,GAAK,WAAA;AACf,EAAA,SAAA,CAAU,MAAM,OAAA,GAAU,8GAAA;AAC1B,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,SAAS,CAAA;AACnC,EAAA,IAAI,KAAA,GAAiB,IAAA;AACrB,EAAA,IAAI;AAEF,IAAA,KAAA,GAAS,GAAA,CAAY,QAAA,CAAS,SAAA,CAAU,WAAA,EAAa;AAAA,MACnD,aAAa,MAAA,CAAO,IAAA;AAAA,MACpB,IAAA,EAAM,CAAC,CAAC,MAAA,CAAO,QAAA;AAAA,MACf,IAAA,EAAM,CAAC,CAAC,MAAA,CAAO,QAAA;AAAA,MACf,aAAA,EAAe,KAAA;AAAA,MACf,cAAA,EAAgB,KAAA;AAAA,MAChB,eAAA,EAAiB;AAAA,KAClB,CAAA;AAED,IAAA,oBAAA,CAAqB,KAAA,EAAc,MAAA,EAAQ,EAAE,OAAA,EAAS,CAAA;AAEtD,IAAC,MAAc,MAAA,EAAO;AACtB,IAAA,OAAO,oBAAoB,SAAS,CAAA;AAAA,EACtC,CAAA,SAAE;AACA,IAAA,IAAI;AAEF,MAAA,IAAI,KAAA,EAAQ,GAAA,CAAY,QAAA,CAAS,UAAU,KAAK,CAAA;AAAA,IAClD,CAAA,CAAA,MAAQ;AAAA,IAAe;AACvB,IAAA,IAAI,SAAA,CAAU,UAAA,EAAY,SAAA,CAAU,UAAA,CAAW,YAAY,SAAS,CAAA;AAAA,EACtE;AACF;;;AChEO,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-BJX4YNA5.mjs","sourcesContent":["export function renderGeometryToSvg(boardContainer: HTMLElement): string {\n const svgEl = boardContainer.querySelector('svg');\n if (!svgEl) throw new Error('renderGeometryToSvg: no SVG found in board container');\n const clone = svgEl.cloneNode(true) as SVGElement;\n if (!clone.getAttribute('xmlns')) {\n clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');\n }\n return new XMLSerializer().serializeToString(clone);\n}\n","// JSXGraph không có built-in getJSON. Component giữ MỘT LOG riêng của các create() call\n// do user trigger, pass log đó vào serializeBoard. Replay = gọi board.create() theo thứ tự log.\n//\n// type === 'transform': args là [refs đến điểm/đường/scalar], attrs là { type: 'translate'|'rotate'|'reflect'|'scale', ... }.\n// Object trả về (kết quả board.create('transform', ...)) được đăng ký vào idMap như mọi element khác\n// để point/line phụ thuộc reference được bằng id ('j5' → JSXGraph transform object).\n//\n// Log lưu màu dưới dạng sentinel ('@stroke', '@axis', '@grid', '@label') để\n// theme-neutral. Khi replay, palette resolve thành màu thực theo `isDark` hiện\n// tại (truyền qua options.palette).\n\nimport { paletteFor, resolveAttrColors, type GeomPalette } from './editor/theme';\n\nexport interface SerializedElement {\n type: string;\n args: unknown[];\n attrs: Record<string, unknown>;\n id: string;\n}\n\nexport interface SerializedBoard {\n bbox: [number, number, number, number];\n elements: SerializedElement[];\n showAxis?: boolean;\n showGrid?: boolean;\n}\n\ninterface BoardLike {\n getBoundingBox(): [number, number, number, number];\n create(type: string, args: unknown[], attrs: Record<string, unknown>): unknown;\n}\n\nexport function serializeBoard(\n board: BoardLike,\n log: SerializedElement[],\n options: { showAxis?: boolean; showGrid?: boolean } = {},\n): SerializedBoard {\n return {\n bbox: board.getBoundingBox(),\n elements: log.map(e => ({ type: e.type, args: e.args, attrs: e.attrs, id: e.id })),\n showAxis: !!options.showAxis,\n showGrid: !!options.showGrid,\n };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction createValueLabel(board: any, target: any): unknown {\n if (!board || !target) return null;\n const e = (target.elType ?? target.type ?? '').toString().toLowerCase();\n if (e === 'segment' || e === 'line' || e === 'arrow') {\n const p1 = target.point1, p2 = target.point2;\n if (!p1 || !p2) return null;\n return board.create('text', [\n () => (p1.X() + p2.X()) / 2 + 0.15,\n () => (p1.Y() + p2.Y()) / 2 + 0.25,\n () => {\n const len = Math.hypot(p2.X() - p1.X(), p2.Y() - p1.Y());\n const name = typeof target.name === 'string' && target.name ? target.name : 'd';\n return `${name} = ${len.toFixed(2)}`;\n },\n ], { fontSize: 12, color: '#dc2626', fixed: true, highlight: false });\n }\n if (e === 'circle' || e === 'circumcircle') {\n const center = target.center ?? target.midpoint ?? target.point1;\n if (!center) return null;\n return board.create('text', [\n () => center.X() + 0.3,\n () => center.Y() + 0.3,\n () => {\n const r = typeof target.Radius === 'function' ? target.Radius() : 0;\n const name = typeof target.name === 'string' && target.name ? target.name : 'r';\n return `${name} = ${r.toFixed(2)}`;\n },\n ], { fontSize: 12, color: '#dc2626', fixed: true, highlight: false });\n }\n return null;\n}\n\nexport interface DeserializeOptions {\n /** Theme-aware palette để resolve sentinel attrs. Mặc định = light. */\n palette?: GeomPalette;\n}\n\nexport function deserializeIntoBoard(\n board: BoardLike,\n serialized: SerializedBoard,\n options: DeserializeOptions = {},\n): void {\n // Replay: args may contain references to earlier elements by our serialized id (\"j0\", \"j1\"…).\n // We resolve those to actual JSXGraph objects via a local id→object map. Nested\n // arrays are also resolved recursively — needed for dilate, which logs the\n // transform parent of a transformed point as [\"j2\",\"j3\",\"j4\"] (a chain of 3\n // transforms passed to `board.create('point', [src, [t1,t2,t3]])`).\n const palette = options.palette ?? paletteFor(false);\n const idMap = new Map<string, unknown>();\n const resolve = (a: unknown): unknown => {\n if (typeof a === 'string' && idMap.has(a)) return idMap.get(a);\n if (Array.isArray(a)) return a.map(resolve);\n return a;\n };\n for (const el of serialized.elements) {\n const resolvedArgs = el.args.map(resolve);\n if (el.type === 'valueLabel') {\n const target = resolvedArgs[0];\n const txt = createValueLabel(board, target);\n if (txt) idMap.set(el.id, txt);\n continue;\n }\n const themedAttrs = resolveAttrColors({ ...el.attrs }, palette);\n const created = board.create(el.type, resolvedArgs, themedAttrs);\n idMap.set(el.id, created);\n }\n}\n","import { renderGeometryToSvg } from './renderInline';\nimport { deserializeIntoBoard, type SerializedBoard } from './serialize';\nimport { paletteFor } from './editor/theme';\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 * 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 */\nexport async function renderGeometrySvgFromState(jsonState: string): Promise<string> {\n const parsed = JSON.parse(jsonState) as SerializedBoard;\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 JXG = (await import('jsxgraph')).default;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const opts = (JXG as any).Options;\n if (opts) {\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 } catch { /* ignore */ }\n const container = document.createElement('div');\n const containerId = 'jxg_offscreen_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8);\n container.id = containerId;\n container.style.cssText = 'position:absolute;top:-99999px;left:-99999px;width:400px;height:300px;visibility:hidden;pointer-events:none;';\n document.body.appendChild(container);\n let board: unknown = null;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n board = (JXG as any).JSXGraph.initBoard(containerId, {\n boundingbox: parsed.bbox,\n axis: !!parsed.showAxis,\n grid: !!parsed.showGrid,\n showCopyright: false,\n showNavigation: false,\n keepAspectRatio: false,\n });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n deserializeIntoBoard(board as any, parsed, { palette });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (board as any).update();\n return renderGeometryToSvg(container);\n } finally {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (board) (JXG as any).JSXGraph.freeBoard(board);\n } catch { /* ignore */ }\n if (container.parentNode) container.parentNode.removeChild(container);\n }\n}\n","import type { BaseStampCustomData } from '../shared/types';\n\nexport interface GeometryCustomData extends BaseStampCustomData {\n kind: 'geometry';\n version: 1;\n jsonState: string;\n svgWidth: number;\n svgHeight: number;\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"]}
|
package/dist/chunk-DJTBZEAR.mjs
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
// src/stamps/geometry-3d/serialize.ts
|
|
3
|
-
function isGeometry3DCustomData(data) {
|
|
4
|
-
if (!data || typeof data !== "object") return false;
|
|
5
|
-
const d = data;
|
|
6
|
-
return d.kind === "geometry3d" && d.version === 1 && typeof d.jsonState === "string";
|
|
7
|
-
}
|
|
8
|
-
function parseSerializedBoard3D(json) {
|
|
9
|
-
const parsed = JSON.parse(json);
|
|
10
|
-
if (!parsed || typeof parsed !== "object") {
|
|
11
|
-
throw new Error("parseSerializedBoard3D: not an object");
|
|
12
|
-
}
|
|
13
|
-
const p = parsed;
|
|
14
|
-
if (p.version !== 1) {
|
|
15
|
-
throw new Error(`parseSerializedBoard3D: unsupported version ${String(p.version)}`);
|
|
16
|
-
}
|
|
17
|
-
if (!Array.isArray(p.elements)) {
|
|
18
|
-
throw new Error("parseSerializedBoard3D: elements missing");
|
|
19
|
-
}
|
|
20
|
-
return parsed;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export { isGeometry3DCustomData, parseSerializedBoard3D };
|
|
24
|
-
//# sourceMappingURL=chunk-DJTBZEAR.mjs.map
|
|
25
|
-
//# sourceMappingURL=chunk-DJTBZEAR.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stamps/geometry-3d/serialize.ts"],"names":[],"mappings":";AAUO,SAAS,uBAAuB,IAAA,EAA6C;AAClF,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,KAAA;AAC9C,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,OAAO,CAAA,CAAE,SAAS,YAAA,IAAgB,CAAA,CAAE,YAAY,CAAA,IAAK,OAAO,EAAE,SAAA,KAAc,QAAA;AAC9E;AAwCO,SAAS,uBAAuB,IAAA,EAAiC;AACtE,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC9B,EAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,EAAU;AACzC,IAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,EACzD;AACA,EAAA,MAAM,CAAA,GAAI,MAAA;AACV,EAAA,IAAI,CAAA,CAAE,YAAY,CAAA,EAAG;AACnB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4CAAA,EAA+C,OAAO,CAAA,CAAE,OAAO,CAAC,CAAA,CAAE,CAAA;AAAA,EACpF;AACA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,QAAQ,CAAA,EAAG;AAC9B,IAAA,MAAM,IAAI,MAAM,0CAA0C,CAAA;AAAA,EAC5D;AACA,EAAA,OAAO,MAAA;AACT","file":"chunk-DJTBZEAR.mjs","sourcesContent":["import type { BaseStampCustomData } from '../shared/types';\n\nexport interface Geometry3DCustomData extends BaseStampCustomData {\n kind: 'geometry3d';\n version: 1;\n jsonState: string;\n svgWidth: number;\n svgHeight: number;\n}\n\nexport function isGeometry3DCustomData(data: unknown): data is Geometry3DCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<Geometry3DCustomData>;\n return d.kind === 'geometry3d' && d.version === 1 && typeof d.jsonState === 'string';\n}\n\nexport type Element3DType =\n | 'point3d'\n | 'line3d'\n | 'plane3d'\n | 'polygon3d'\n | 'sphere3d'\n | 'text3d';\n\nexport interface SerializedElement3D {\n type: Element3DType;\n /**\n * Parents passed to JSXGraph view.create. Either literal values (numbers,\n * strings) or `\"@id:<id>\"` placeholder strings referencing earlier created\n * objects in the log (resolved at deserialize time).\n */\n parents: unknown[];\n attributes: Record<string, unknown>;\n id: string;\n label?: string;\n}\n\nexport interface SerializedBoard3D {\n version: 1;\n bbox: [number, number, number, number];\n view: {\n azimuth: number;\n elevation: number;\n bbox3D: [number, number, number, number, number, number];\n };\n showAxes: boolean;\n showMesh: boolean;\n elements: SerializedElement3D[];\n}\n\nexport function serializeBoard3D(state: SerializedBoard3D): string {\n return JSON.stringify(state);\n}\n\nexport function parseSerializedBoard3D(json: string): SerializedBoard3D {\n const parsed = JSON.parse(json) as unknown;\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('parseSerializedBoard3D: not an object');\n }\n const p = parsed as Partial<SerializedBoard3D>;\n if (p.version !== 1) {\n throw new Error(`parseSerializedBoard3D: unsupported version ${String(p.version)}`);\n }\n if (!Array.isArray(p.elements)) {\n throw new Error('parseSerializedBoard3D: elements missing');\n }\n return parsed as SerializedBoard3D;\n}\n"]}
|
package/dist/chunk-HM7RIXJE.mjs
DELETED
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
// src/stamps/graph-2d/serialize.ts
|
|
3
|
-
var EMPTY_GRAPH = {
|
|
4
|
-
version: 1,
|
|
5
|
-
view: { xMin: -10, xMax: 10, yMin: -10, yMax: 10, showAxis: true, showGrid: true },
|
|
6
|
-
functions: [],
|
|
7
|
-
parameters: [],
|
|
8
|
-
points: [],
|
|
9
|
-
intersections: [],
|
|
10
|
-
tangents: []
|
|
11
|
-
};
|
|
12
|
-
function stringifySerializedGraph(graph) {
|
|
13
|
-
return JSON.stringify(graph);
|
|
14
|
-
}
|
|
15
|
-
function parseSerializedGraph(jsonState) {
|
|
16
|
-
let raw;
|
|
17
|
-
try {
|
|
18
|
-
raw = JSON.parse(jsonState);
|
|
19
|
-
} catch {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
23
|
-
const r = raw;
|
|
24
|
-
if (r.version !== 1) return null;
|
|
25
|
-
if (!r.view || typeof r.view !== "object") return null;
|
|
26
|
-
const v = r.view;
|
|
27
|
-
if (typeof v.xMin !== "number" || typeof v.xMax !== "number" || typeof v.yMin !== "number" || typeof v.yMax !== "number" || typeof v.showAxis !== "boolean" || typeof v.showGrid !== "boolean") {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
for (const key of ["functions", "parameters", "points", "intersections", "tangents"]) {
|
|
31
|
-
if (!Array.isArray(r[key])) return null;
|
|
32
|
-
}
|
|
33
|
-
return raw;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// src/stamps/graph-2d/parser.ts
|
|
37
|
-
var ALLOWED_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
38
|
-
"sin",
|
|
39
|
-
"cos",
|
|
40
|
-
"tan",
|
|
41
|
-
"asin",
|
|
42
|
-
"acos",
|
|
43
|
-
"atan",
|
|
44
|
-
"log",
|
|
45
|
-
"ln",
|
|
46
|
-
"exp",
|
|
47
|
-
"sqrt",
|
|
48
|
-
"abs",
|
|
49
|
-
"floor",
|
|
50
|
-
"ceil",
|
|
51
|
-
"round"
|
|
52
|
-
]);
|
|
53
|
-
var ALLOWED_CHARS = /^[a-zA-Z0-9_.+\-*/^()\s,]+$/;
|
|
54
|
-
var IDENTIFIER_RE = /[a-zA-Z][a-zA-Z0-9_]*/g;
|
|
55
|
-
var SUGGESTIONS = {
|
|
56
|
-
tg: "tan",
|
|
57
|
-
arcsin: "asin",
|
|
58
|
-
arccos: "acos",
|
|
59
|
-
arctan: "atan"
|
|
60
|
-
};
|
|
61
|
-
function errResult(message) {
|
|
62
|
-
return { ok: false, error: message, freeVars: /* @__PURE__ */ new Set() };
|
|
63
|
-
}
|
|
64
|
-
function validate(expr) {
|
|
65
|
-
const trimmed = expr.trim();
|
|
66
|
-
if (!trimmed) return errResult("Bi\u1EC3u th\u1EE9c r\u1ED7ng");
|
|
67
|
-
if (!ALLOWED_CHARS.test(trimmed)) return errResult("K\xFD t\u1EF1 kh\xF4ng h\u1EE3p l\u1EC7");
|
|
68
|
-
const ids = trimmed.match(IDENTIFIER_RE) ?? [];
|
|
69
|
-
const freeVars = /* @__PURE__ */ new Set();
|
|
70
|
-
for (const id of ids) {
|
|
71
|
-
if (id === "x" || id === "pi" || id === "e") continue;
|
|
72
|
-
if (ALLOWED_FUNCTIONS.has(id)) continue;
|
|
73
|
-
if (id.length === 1) {
|
|
74
|
-
freeVars.add(id);
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
const hint = SUGGESTIONS[id];
|
|
78
|
-
return errResult(
|
|
79
|
-
hint ? `T\xEAn h\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${id}". B\u1EA1n c\xF3 \xFD l\xE0 "${hint}" kh\xF4ng?` : `T\xEAn kh\xF4ng h\u1EE3p l\u1EC7: "${id}"`
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
const paramSubs = Object.fromEntries([...freeVars].map((v) => [v, 1]));
|
|
84
|
-
const rewritten = rewriteToJs(trimmed, paramSubs);
|
|
85
|
-
new Function("x", `return (${rewritten})`);
|
|
86
|
-
} catch {
|
|
87
|
-
return errResult("L\u1ED7i c\xFA ph\xE1p");
|
|
88
|
-
}
|
|
89
|
-
return { ok: true, freeVars };
|
|
90
|
-
}
|
|
91
|
-
var FUNCTION_REPLACEMENTS = [
|
|
92
|
-
// longest first để tránh substring conflict (asin trước sin)
|
|
93
|
-
["asin", "Math.asin"],
|
|
94
|
-
["acos", "Math.acos"],
|
|
95
|
-
["atan", "Math.atan"],
|
|
96
|
-
["sqrt", "Math.sqrt"],
|
|
97
|
-
["floor", "Math.floor"],
|
|
98
|
-
["round", "Math.round"],
|
|
99
|
-
["ceil", "Math.ceil"],
|
|
100
|
-
["sin", "Math.sin"],
|
|
101
|
-
["cos", "Math.cos"],
|
|
102
|
-
["tan", "Math.tan"],
|
|
103
|
-
["abs", "Math.abs"],
|
|
104
|
-
["exp", "Math.exp"],
|
|
105
|
-
["log", "Math.log10"],
|
|
106
|
-
["ln", "Math.log"]
|
|
107
|
-
];
|
|
108
|
-
function rewriteToJs(expr, params) {
|
|
109
|
-
let s = expr.replace(/\^/g, "**");
|
|
110
|
-
s = s.replace(/\bpi\b/g, "Math.PI");
|
|
111
|
-
s = s.replace(/\be\b/g, "Math.E");
|
|
112
|
-
for (const [from, to] of FUNCTION_REPLACEMENTS) {
|
|
113
|
-
s = s.replace(new RegExp(`\\b${from}\\b`, "g"), to);
|
|
114
|
-
}
|
|
115
|
-
for (const [name, value] of Object.entries(params)) {
|
|
116
|
-
if (name.length !== 1) continue;
|
|
117
|
-
s = s.replace(new RegExp(`\\b${name}\\b`, "g"), `(${value})`);
|
|
118
|
-
}
|
|
119
|
-
return s;
|
|
120
|
-
}
|
|
121
|
-
function compile(expr, paramValues) {
|
|
122
|
-
const v = validate(expr);
|
|
123
|
-
if (!v.ok) return { error: v.error ?? "Invalid" };
|
|
124
|
-
try {
|
|
125
|
-
const rewritten = rewriteToJs(expr, paramValues);
|
|
126
|
-
const raw = new Function("x", `return (${rewritten})`);
|
|
127
|
-
return (x) => {
|
|
128
|
-
try {
|
|
129
|
-
const y = raw(x);
|
|
130
|
-
return typeof y === "number" ? y : NaN;
|
|
131
|
-
} catch {
|
|
132
|
-
return NaN;
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
} catch (err) {
|
|
136
|
-
return { error: err instanceof Error ? err.message : String(err) };
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// src/stamps/graph-2d/editor/handlers.ts
|
|
141
|
-
function addPointOnCurve(graph, ctx, idFactory) {
|
|
142
|
-
if (!ctx.functionId) return graph;
|
|
143
|
-
const point = {
|
|
144
|
-
id: idFactory(),
|
|
145
|
-
functionId: ctx.functionId,
|
|
146
|
-
x: ctx.x
|
|
147
|
-
};
|
|
148
|
-
return { ...graph, points: [...graph.points, point] };
|
|
149
|
-
}
|
|
150
|
-
function addIntersection(graph, functionIdA, functionIdB, idFactory) {
|
|
151
|
-
if (functionIdA === functionIdB) return graph;
|
|
152
|
-
const exists = graph.intersections.some(
|
|
153
|
-
(i) => i.functionIdA === functionIdA && i.functionIdB === functionIdB || i.functionIdA === functionIdB && i.functionIdB === functionIdA
|
|
154
|
-
);
|
|
155
|
-
if (exists) return graph;
|
|
156
|
-
const intersection = {
|
|
157
|
-
id: idFactory(),
|
|
158
|
-
functionIdA,
|
|
159
|
-
functionIdB
|
|
160
|
-
};
|
|
161
|
-
return { ...graph, intersections: [...graph.intersections, intersection] };
|
|
162
|
-
}
|
|
163
|
-
function numericalDerivative(expression, paramValues, x, h = 1e-4) {
|
|
164
|
-
const fn = compile(expression, paramValues);
|
|
165
|
-
if (typeof fn !== "function") return NaN;
|
|
166
|
-
const y1 = fn(x - h);
|
|
167
|
-
const y2 = fn(x + h);
|
|
168
|
-
return (y2 - y1) / (2 * h);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// src/stamps/graph-2d/renderObjects.ts
|
|
172
|
-
function renderGraphObjects(board, graph) {
|
|
173
|
-
const paramMap = {};
|
|
174
|
-
for (const p of graph.parameters) paramMap[p.name] = p.value;
|
|
175
|
-
for (const f of graph.functions) {
|
|
176
|
-
if (!f.visible) continue;
|
|
177
|
-
const compiled = compile(f.expression, paramMap);
|
|
178
|
-
if (typeof compiled !== "function") continue;
|
|
179
|
-
const domain = f.domain ?? { min: graph.view.xMin, max: graph.view.xMax };
|
|
180
|
-
board.create("functiongraph", [compiled, domain.min, domain.max], {
|
|
181
|
-
strokeColor: f.color,
|
|
182
|
-
strokeWidth: 2,
|
|
183
|
-
name: f.name,
|
|
184
|
-
withLabel: false,
|
|
185
|
-
highlight: false
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
for (const point of graph.points) {
|
|
189
|
-
const fn = graph.functions.find((f) => f.id === point.functionId);
|
|
190
|
-
if (!fn || !fn.visible) continue;
|
|
191
|
-
const compiled = compile(fn.expression, paramMap);
|
|
192
|
-
if (typeof compiled !== "function") continue;
|
|
193
|
-
const y = compiled(point.x);
|
|
194
|
-
board.create("point", [point.x, y], {
|
|
195
|
-
name: point.label ?? "",
|
|
196
|
-
size: 3,
|
|
197
|
-
fillColor: fn.color,
|
|
198
|
-
strokeColor: fn.color,
|
|
199
|
-
withLabel: !!point.label
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
for (const inter of graph.intersections) {
|
|
203
|
-
const fa = graph.functions.find((f) => f.id === inter.functionIdA);
|
|
204
|
-
const fb = graph.functions.find((f) => f.id === inter.functionIdB);
|
|
205
|
-
if (!fa || !fb || !fa.visible || !fb.visible) continue;
|
|
206
|
-
const cfa = compile(fa.expression, paramMap);
|
|
207
|
-
const cfb = compile(fb.expression, paramMap);
|
|
208
|
-
if (typeof cfa !== "function" || typeof cfb !== "function") continue;
|
|
209
|
-
const roots = scanRoots((x) => cfa(x) - cfb(x), graph.view.xMin, graph.view.xMax);
|
|
210
|
-
for (const x of roots) {
|
|
211
|
-
board.create("point", [x, cfa(x)], {
|
|
212
|
-
size: 3,
|
|
213
|
-
fillColor: "#000",
|
|
214
|
-
strokeColor: "#000"
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
for (const tan of graph.tangents) {
|
|
219
|
-
const pt = graph.points.find((p) => p.id === tan.pointId);
|
|
220
|
-
if (!pt) continue;
|
|
221
|
-
const fn = graph.functions.find((f) => f.id === pt.functionId);
|
|
222
|
-
if (!fn || !fn.visible) continue;
|
|
223
|
-
const slope = numericalDerivative(fn.expression, paramMap, pt.x);
|
|
224
|
-
const cfn = compile(fn.expression, paramMap);
|
|
225
|
-
if (typeof cfn !== "function" || !Number.isFinite(slope)) continue;
|
|
226
|
-
const y0 = cfn(pt.x);
|
|
227
|
-
const x1 = graph.view.xMin;
|
|
228
|
-
const x2 = graph.view.xMax;
|
|
229
|
-
board.create(
|
|
230
|
-
"line",
|
|
231
|
-
[
|
|
232
|
-
[x1, slope * (x1 - pt.x) + y0],
|
|
233
|
-
[x2, slope * (x2 - pt.x) + y0]
|
|
234
|
-
],
|
|
235
|
-
{
|
|
236
|
-
strokeColor: fn.color,
|
|
237
|
-
strokeWidth: 1,
|
|
238
|
-
dash: 2,
|
|
239
|
-
straightFirst: false,
|
|
240
|
-
straightLast: false
|
|
241
|
-
}
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
function scanRoots(fn, xMin, xMax, samples = 200) {
|
|
246
|
-
const roots = [];
|
|
247
|
-
const step = (xMax - xMin) / samples;
|
|
248
|
-
let prevX = xMin;
|
|
249
|
-
let prevY = fn(prevX);
|
|
250
|
-
for (let i = 1; i <= samples; i++) {
|
|
251
|
-
const x = xMin + i * step;
|
|
252
|
-
const y = fn(x);
|
|
253
|
-
if (Number.isFinite(prevY) && Number.isFinite(y) && prevY * y < 0) {
|
|
254
|
-
let a = prevX;
|
|
255
|
-
let b = x;
|
|
256
|
-
let ya = prevY;
|
|
257
|
-
for (let j = 0; j < 30; j++) {
|
|
258
|
-
const m = (a + b) / 2;
|
|
259
|
-
const ym = fn(m);
|
|
260
|
-
if (Math.abs(ym) < 1e-6) {
|
|
261
|
-
a = b = m;
|
|
262
|
-
break;
|
|
263
|
-
}
|
|
264
|
-
if (ya * ym < 0) {
|
|
265
|
-
b = m;
|
|
266
|
-
} else {
|
|
267
|
-
a = m;
|
|
268
|
-
ya = ym;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
roots.push((a + b) / 2);
|
|
272
|
-
}
|
|
273
|
-
prevX = x;
|
|
274
|
-
prevY = y;
|
|
275
|
-
}
|
|
276
|
-
return roots;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// src/stamps/graph-2d/render.ts
|
|
280
|
-
async function renderGraph2dSvgFromState(jsonState) {
|
|
281
|
-
const parsed = parseSerializedGraph(jsonState);
|
|
282
|
-
if (!parsed) throw new Error("renderGraph2dSvgFromState: jsonState corrupt");
|
|
283
|
-
const JXG = (await import('jsxgraph')).default;
|
|
284
|
-
const opts = JXG.Options;
|
|
285
|
-
if (opts) {
|
|
286
|
-
opts.text = opts.text || {};
|
|
287
|
-
opts.text.display = "internal";
|
|
288
|
-
opts.text.useASCIIMathML = false;
|
|
289
|
-
opts.text.useMathJax = false;
|
|
290
|
-
opts.text.useKatex = false;
|
|
291
|
-
opts.label = opts.label || {};
|
|
292
|
-
opts.label.display = "internal";
|
|
293
|
-
}
|
|
294
|
-
const container = document.createElement("div");
|
|
295
|
-
container.id = `jxg_graph2d_off_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
296
|
-
container.style.cssText = "position:absolute;top:-99999px;left:-99999px;width:600px;height:400px;visibility:hidden;pointer-events:none;";
|
|
297
|
-
document.body.appendChild(container);
|
|
298
|
-
let board = null;
|
|
299
|
-
try {
|
|
300
|
-
board = JXG.JSXGraph.initBoard(container.id, {
|
|
301
|
-
boundingbox: [parsed.view.xMin, parsed.view.yMax, parsed.view.xMax, parsed.view.yMin],
|
|
302
|
-
axis: parsed.view.showAxis,
|
|
303
|
-
grid: parsed.view.showGrid,
|
|
304
|
-
showCopyright: false,
|
|
305
|
-
showNavigation: false,
|
|
306
|
-
keepAspectRatio: false
|
|
307
|
-
});
|
|
308
|
-
renderGraphObjects(board, parsed);
|
|
309
|
-
board.update();
|
|
310
|
-
const svgEl = container.querySelector("svg");
|
|
311
|
-
if (!svgEl) throw new Error("renderGraph2dSvgFromState: no svg generated");
|
|
312
|
-
return svgEl.outerHTML;
|
|
313
|
-
} finally {
|
|
314
|
-
try {
|
|
315
|
-
if (board) JXG.JSXGraph.freeBoard(board);
|
|
316
|
-
} catch {
|
|
317
|
-
}
|
|
318
|
-
if (container.parentNode) container.parentNode.removeChild(container);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// src/stamps/graph-2d/types.ts
|
|
323
|
-
function isGraph2DCustomData(data) {
|
|
324
|
-
if (!data || typeof data !== "object") return false;
|
|
325
|
-
const d = data;
|
|
326
|
-
return d.kind === "graph2d" && d.version === 1 && typeof d.jsonState === "string";
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
export { EMPTY_GRAPH, addIntersection, addPointOnCurve, compile, isGraph2DCustomData, numericalDerivative, parseSerializedGraph, renderGraph2dSvgFromState, stringifySerializedGraph, validate };
|
|
330
|
-
//# sourceMappingURL=chunk-HM7RIXJE.mjs.map
|
|
331
|
-
//# sourceMappingURL=chunk-HM7RIXJE.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stamps/graph-2d/serialize.ts","../src/stamps/graph-2d/parser.ts","../src/stamps/graph-2d/editor/handlers.ts","../src/stamps/graph-2d/renderObjects.ts","../src/stamps/graph-2d/render.ts","../src/stamps/graph-2d/types.ts"],"names":[],"mappings":"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","file":"chunk-HM7RIXJE.mjs","sourcesContent":["export interface SerializedGraph {\n version: 1;\n view: {\n xMin: number;\n xMax: number;\n yMin: number;\n yMax: number;\n showAxis: boolean;\n showGrid: boolean;\n };\n functions: SerializedFunction[];\n parameters: SerializedParameter[];\n points: SerializedPoint[];\n intersections: SerializedIntersection[];\n tangents: SerializedTangent[];\n}\n\nexport interface SerializedFunction {\n id: string;\n name: string;\n expression: string;\n color: string;\n visible: boolean;\n domain?: { min: number; max: number };\n}\n\nexport interface SerializedParameter {\n name: string;\n value: number;\n min: number;\n max: number;\n step: number;\n}\n\nexport interface SerializedPoint {\n id: string;\n functionId: string;\n x: number;\n label?: string;\n}\n\nexport interface SerializedIntersection {\n id: string;\n functionIdA: string;\n functionIdB: string;\n}\n\nexport interface SerializedTangent {\n id: string;\n pointId: string;\n}\n\nexport const EMPTY_GRAPH: SerializedGraph = {\n version: 1,\n view: { xMin: -10, xMax: 10, yMin: -10, yMax: 10, showAxis: true, showGrid: true },\n functions: [],\n parameters: [],\n points: [],\n intersections: [],\n tangents: [],\n};\n\nexport function stringifySerializedGraph(graph: SerializedGraph): string {\n return JSON.stringify(graph);\n}\n\nexport function parseSerializedGraph(jsonState: string): SerializedGraph | null {\n let raw: unknown;\n try {\n raw = JSON.parse(jsonState);\n } catch {\n return null;\n }\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const r = raw as Record<string, unknown>;\n if (r.version !== 1) return null;\n if (!r.view || typeof r.view !== 'object') return null;\n const v = r.view as Record<string, unknown>;\n if (\n typeof v.xMin !== 'number' ||\n typeof v.xMax !== 'number' ||\n typeof v.yMin !== 'number' ||\n typeof v.yMax !== 'number' ||\n typeof v.showAxis !== 'boolean' ||\n typeof v.showGrid !== 'boolean'\n ) {\n return null;\n }\n for (const key of ['functions', 'parameters', 'points', 'intersections', 'tangents']) {\n if (!Array.isArray(r[key])) return null;\n }\n return raw as SerializedGraph;\n}\n","const ALLOWED_FUNCTIONS = new Set([\n 'sin', 'cos', 'tan', 'asin', 'acos', 'atan',\n 'log', 'ln', 'exp', 'sqrt', 'abs',\n 'floor', 'ceil', 'round',\n]);\n\nconst ALLOWED_CHARS = /^[a-zA-Z0-9_.+\\-*/^()\\s,]+$/;\nconst IDENTIFIER_RE = /[a-zA-Z][a-zA-Z0-9_]*/g;\n\nconst SUGGESTIONS: Record<string, string> = {\n tg: 'tan',\n arcsin: 'asin',\n arccos: 'acos',\n arctan: 'atan',\n};\n\nexport interface ParseResult {\n ok: boolean;\n error?: string;\n freeVars: Set<string>;\n}\n\nfunction errResult(message: string): ParseResult {\n return { ok: false, error: message, freeVars: new Set() };\n}\n\nexport function validate(expr: string): ParseResult {\n const trimmed = expr.trim();\n if (!trimmed) return errResult('Biểu thức rỗng');\n if (!ALLOWED_CHARS.test(trimmed)) return errResult('Ký tự không hợp lệ');\n\n const ids = trimmed.match(IDENTIFIER_RE) ?? [];\n const freeVars = new Set<string>();\n for (const id of ids) {\n if (id === 'x' || id === 'pi' || id === 'e') continue;\n if (ALLOWED_FUNCTIONS.has(id)) continue;\n if (id.length === 1) {\n freeVars.add(id);\n continue;\n }\n const hint = SUGGESTIONS[id];\n return errResult(\n hint\n ? `Tên hàm không hợp lệ: \"${id}\". Bạn có ý là \"${hint}\" không?`\n : `Tên không hợp lệ: \"${id}\"`,\n );\n }\n\n try {\n const paramSubs = Object.fromEntries([...freeVars].map((v) => [v, 1]));\n const rewritten = rewriteToJs(trimmed, paramSubs);\n new Function('x', `return (${rewritten})`);\n } catch {\n return errResult('Lỗi cú pháp');\n }\n\n return { ok: true, freeVars };\n}\n\nconst FUNCTION_REPLACEMENTS: Array<[string, string]> = [\n // longest first để tránh substring conflict (asin trước sin)\n ['asin', 'Math.asin'],\n ['acos', 'Math.acos'],\n ['atan', 'Math.atan'],\n ['sqrt', 'Math.sqrt'],\n ['floor', 'Math.floor'],\n ['round', 'Math.round'],\n ['ceil', 'Math.ceil'],\n ['sin', 'Math.sin'],\n ['cos', 'Math.cos'],\n ['tan', 'Math.tan'],\n ['abs', 'Math.abs'],\n ['exp', 'Math.exp'],\n ['log', 'Math.log10'],\n ['ln', 'Math.log'],\n];\n\nexport function rewriteToJs(\n expr: string,\n params: Record<string, number>,\n): string {\n let s = expr.replace(/\\^/g, '**');\n s = s.replace(/\\bpi\\b/g, 'Math.PI');\n s = s.replace(/\\be\\b/g, 'Math.E');\n for (const [from, to] of FUNCTION_REPLACEMENTS) {\n s = s.replace(new RegExp(`\\\\b${from}\\\\b`, 'g'), to);\n }\n for (const [name, value] of Object.entries(params)) {\n if (name.length !== 1) continue;\n s = s.replace(new RegExp(`\\\\b${name}\\\\b`, 'g'), `(${value})`);\n }\n return s;\n}\n\nexport function compile(\n expr: string,\n paramValues: Record<string, number>,\n): ((x: number) => number) | { error: string } {\n const v = validate(expr);\n if (!v.ok) return { error: v.error ?? 'Invalid' };\n try {\n const rewritten = rewriteToJs(expr, paramValues);\n const raw = new Function('x', `return (${rewritten})`) as (x: number) => number;\n return (x: number) => {\n try {\n const y = raw(x);\n return typeof y === 'number' ? y : NaN;\n } catch {\n return NaN;\n }\n };\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) };\n }\n}\n","import type {\n SerializedGraph,\n SerializedPoint,\n SerializedIntersection,\n SerializedTangent,\n} from '../serialize';\nimport { compile } from '../parser';\n\nexport interface ClickContext {\n x: number;\n y: number;\n functionId?: string;\n}\n\nexport function addPointOnCurve(\n graph: SerializedGraph,\n ctx: ClickContext,\n idFactory: () => string,\n): SerializedGraph {\n if (!ctx.functionId) return graph;\n const point: SerializedPoint = {\n id: idFactory(),\n functionId: ctx.functionId,\n x: ctx.x,\n };\n return { ...graph, points: [...graph.points, point] };\n}\n\nexport function addIntersection(\n graph: SerializedGraph,\n functionIdA: string,\n functionIdB: string,\n idFactory: () => string,\n): SerializedGraph {\n if (functionIdA === functionIdB) return graph;\n const exists = graph.intersections.some(\n (i) =>\n (i.functionIdA === functionIdA && i.functionIdB === functionIdB) ||\n (i.functionIdA === functionIdB && i.functionIdB === functionIdA),\n );\n if (exists) return graph;\n const intersection: SerializedIntersection = {\n id: idFactory(),\n functionIdA,\n functionIdB,\n };\n return { ...graph, intersections: [...graph.intersections, intersection] };\n}\n\nexport function addTangent(\n graph: SerializedGraph,\n pointId: string,\n idFactory: () => string,\n): SerializedGraph {\n const exists = graph.tangents.some((t) => t.pointId === pointId);\n if (exists) return graph;\n const tangent: SerializedTangent = { id: idFactory(), pointId };\n return { ...graph, tangents: [...graph.tangents, tangent] };\n}\n\n/**\n * Numerical derivative via centered difference. Dùng cho tangent tool.\n */\nexport function numericalDerivative(\n expression: string,\n paramValues: Record<string, number>,\n x: number,\n h = 1e-4,\n): number {\n const fn = compile(expression, paramValues);\n if (typeof fn !== 'function') return NaN;\n const y1 = fn(x - h);\n const y2 = fn(x + h);\n return (y2 - y1) / (2 * h);\n}\n","import type { SerializedGraph } from './serialize';\nimport { compile } from './parser';\nimport { numericalDerivative } from './editor/handlers';\n\n/**\n * Render tất cả objects (functions, points, intersections, tangents) lên board JSXGraph.\n *\n * Pure function — không giữ state, dùng cho one-shot render (render.ts).\n * MiniBoard giữ syncObjects riêng để diff/track curves theo id.\n */\nexport function renderGraphObjects(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n board: any,\n graph: SerializedGraph,\n): void {\n const paramMap: Record<string, number> = {};\n for (const p of graph.parameters) paramMap[p.name] = p.value;\n\n // Functions\n for (const f of graph.functions) {\n if (!f.visible) continue;\n const compiled = compile(f.expression, paramMap);\n if (typeof compiled !== 'function') continue;\n const domain = f.domain ?? { min: graph.view.xMin, max: graph.view.xMax };\n board.create('functiongraph', [compiled, domain.min, domain.max], {\n strokeColor: f.color,\n strokeWidth: 2,\n name: f.name,\n withLabel: false,\n highlight: false,\n });\n }\n\n // Points\n for (const point of graph.points) {\n const fn = graph.functions.find((f) => f.id === point.functionId);\n if (!fn || !fn.visible) continue;\n const compiled = compile(fn.expression, paramMap);\n if (typeof compiled !== 'function') continue;\n const y = compiled(point.x);\n board.create('point', [point.x, y], {\n name: point.label ?? '',\n size: 3,\n fillColor: fn.color,\n strokeColor: fn.color,\n withLabel: !!point.label,\n });\n }\n\n // Intersections\n for (const inter of graph.intersections) {\n const fa = graph.functions.find((f) => f.id === inter.functionIdA);\n const fb = graph.functions.find((f) => f.id === inter.functionIdB);\n if (!fa || !fb || !fa.visible || !fb.visible) continue;\n const cfa = compile(fa.expression, paramMap);\n const cfb = compile(fb.expression, paramMap);\n if (typeof cfa !== 'function' || typeof cfb !== 'function') continue;\n const roots = scanRoots((x: number) => cfa(x) - cfb(x), graph.view.xMin, graph.view.xMax);\n for (const x of roots) {\n board.create('point', [x, cfa(x)], {\n size: 3,\n fillColor: '#000',\n strokeColor: '#000',\n });\n }\n }\n\n // Tangents\n for (const tan of graph.tangents) {\n const pt = graph.points.find((p) => p.id === tan.pointId);\n if (!pt) continue;\n const fn = graph.functions.find((f) => f.id === pt.functionId);\n if (!fn || !fn.visible) continue;\n const slope = numericalDerivative(fn.expression, paramMap, pt.x);\n const cfn = compile(fn.expression, paramMap);\n if (typeof cfn !== 'function' || !Number.isFinite(slope)) continue;\n const y0 = cfn(pt.x);\n const x1 = graph.view.xMin;\n const x2 = graph.view.xMax;\n board.create(\n 'line',\n [\n [x1, slope * (x1 - pt.x) + y0],\n [x2, slope * (x2 - pt.x) + y0],\n ],\n {\n strokeColor: fn.color,\n strokeWidth: 1,\n dash: 2,\n straightFirst: false,\n straightLast: false,\n },\n );\n }\n}\n\nfunction scanRoots(\n fn: (x: number) => number,\n xMin: number,\n xMax: number,\n samples = 200,\n): number[] {\n const roots: number[] = [];\n const step = (xMax - xMin) / samples;\n let prevX = xMin;\n let prevY = fn(prevX);\n for (let i = 1; i <= samples; i++) {\n const x = xMin + i * step;\n const y = fn(x);\n if (Number.isFinite(prevY) && Number.isFinite(y) && prevY * y < 0) {\n let a = prevX;\n let b = x;\n let ya = prevY;\n for (let j = 0; j < 30; j++) {\n const m = (a + b) / 2;\n const ym = fn(m);\n if (Math.abs(ym) < 1e-6) {\n a = b = m;\n break;\n }\n if (ya * ym < 0) {\n b = m;\n } else {\n a = m;\n ya = ym;\n }\n }\n roots.push((a + b) / 2);\n }\n prevX = x;\n prevY = y;\n }\n return roots;\n}\n","import { parseSerializedGraph } from './serialize';\nimport { renderGraphObjects } from './renderObjects';\n\n/**\n * Re-render SVG cho graph-2d stamp từ jsonState đã serialize.\n *\n * Dùng cho:\n * 1. Insert vào whiteboard (lúc user nhấn Chèn).\n * 2. Restore stamp file sau khi reload (Excalidraw không persist binary).\n *\n * Pattern mirror `geometry-2d/render.ts`:\n * - LUÔN dùng light palette. Excalidraw apply CSS invert filter trong dark mode.\n * - Đặt JXG.Options.text.display='internal' để label render dưới dạng SVG, không HTML overlay.\n * - Offscreen div 600×400 cố định; cleanup sau khi clone SVG outerHTML.\n */\nexport async function renderGraph2dSvgFromState(jsonState: string): Promise<string> {\n const parsed = parseSerializedGraph(jsonState);\n if (!parsed) throw new Error('renderGraph2dSvgFromState: jsonState corrupt');\n\n const JXG = (await import('jsxgraph')).default;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const opts = (JXG as any).Options;\n if (opts) {\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.label = opts.label || {};\n opts.label.display = 'internal';\n }\n\n const container = document.createElement('div');\n container.id = `jxg_graph2d_off_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n container.style.cssText =\n 'position:absolute;top:-99999px;left:-99999px;width:600px;height:400px;visibility:hidden;pointer-events:none;';\n document.body.appendChild(container);\n\n let board: unknown = null;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n board = (JXG as any).JSXGraph.initBoard(container.id, {\n boundingbox: [parsed.view.xMin, parsed.view.yMax, parsed.view.xMax, parsed.view.yMin],\n axis: parsed.view.showAxis,\n grid: parsed.view.showGrid,\n showCopyright: false,\n showNavigation: false,\n keepAspectRatio: false,\n });\n renderGraphObjects(board, parsed);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (board as any).update();\n const svgEl = container.querySelector('svg');\n if (!svgEl) throw new Error('renderGraph2dSvgFromState: no svg generated');\n return svgEl.outerHTML;\n } finally {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (board) (JXG as any).JSXGraph.freeBoard(board);\n } catch {\n /* ignore */\n }\n if (container.parentNode) container.parentNode.removeChild(container);\n }\n}\n","import type { BaseStampCustomData } from '../shared/types';\n\nexport interface Graph2DCustomData extends BaseStampCustomData {\n kind: 'graph2d';\n version: 1;\n jsonState: string;\n svgWidth: number;\n svgHeight: number;\n}\n\nexport function isGraph2DCustomData(data: unknown): data is Graph2DCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<Graph2DCustomData>;\n return d.kind === 'graph2d' && d.version === 1 && typeof d.jsonState === 'string';\n}\n"]}
|
package/dist/chunk-HYXFHEDJ.mjs
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { isGeometry3DCustomData, parseSerializedBoard3D } from './chunk-DJTBZEAR.mjs';
|
|
3
|
-
import { lazy } from 'react';
|
|
4
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
-
|
|
6
|
-
// src/stamps/geometry-3d/render.ts
|
|
7
|
-
var OUTPUT_WIDTH = 1024;
|
|
8
|
-
var OUTPUT_HEIGHT = 768;
|
|
9
|
-
async function renderGeometry3DSvgFromState(jsonState) {
|
|
10
|
-
const state = parseSerializedBoard3D(jsonState);
|
|
11
|
-
const JXG = (await import('jsxgraph')).default;
|
|
12
|
-
const div = document.createElement("div");
|
|
13
|
-
div.style.cssText = `position:absolute;left:-9999px;top:-9999px;width:${OUTPUT_WIDTH}px;height:${OUTPUT_HEIGHT}px;`;
|
|
14
|
-
document.body.appendChild(div);
|
|
15
|
-
try {
|
|
16
|
-
JXG.Options.text.display = "internal";
|
|
17
|
-
const board = JXG.JSXGraph.initBoard(div, {
|
|
18
|
-
boundingbox: state.bbox,
|
|
19
|
-
axis: false,
|
|
20
|
-
showCopyright: false,
|
|
21
|
-
showNavigation: false,
|
|
22
|
-
renderer: "svg"
|
|
23
|
-
});
|
|
24
|
-
const view = board.create(
|
|
25
|
-
"view3d",
|
|
26
|
-
[
|
|
27
|
-
[-5, -5],
|
|
28
|
-
[10, 10],
|
|
29
|
-
[
|
|
30
|
-
[state.view.bbox3D[0], state.view.bbox3D[3]],
|
|
31
|
-
[state.view.bbox3D[1], state.view.bbox3D[4]],
|
|
32
|
-
[state.view.bbox3D[2], state.view.bbox3D[5]]
|
|
33
|
-
]
|
|
34
|
-
],
|
|
35
|
-
{
|
|
36
|
-
az: { slider: { visible: false }, value: state.view.azimuth },
|
|
37
|
-
el: { slider: { visible: false }, value: state.view.elevation },
|
|
38
|
-
projection: "central"
|
|
39
|
-
}
|
|
40
|
-
);
|
|
41
|
-
if (!state.showAxes) {
|
|
42
|
-
view.defaultAxes = [];
|
|
43
|
-
}
|
|
44
|
-
const idMap = /* @__PURE__ */ new Map();
|
|
45
|
-
for (const el of state.elements) {
|
|
46
|
-
const parents = el.parents.map(
|
|
47
|
-
(p) => typeof p === "string" && p.startsWith("@id:") ? idMap.get(p.slice(4)) : p
|
|
48
|
-
);
|
|
49
|
-
const obj = view.create(el.type, parents, {
|
|
50
|
-
...el.attributes,
|
|
51
|
-
id: el.id,
|
|
52
|
-
name: el.label
|
|
53
|
-
});
|
|
54
|
-
idMap.set(el.id, obj);
|
|
55
|
-
}
|
|
56
|
-
const svg = div.querySelector("svg");
|
|
57
|
-
if (!svg) {
|
|
58
|
-
throw new Error("renderGeometry3DSvgFromState: SVG not produced");
|
|
59
|
-
}
|
|
60
|
-
const clone = svg.cloneNode(true);
|
|
61
|
-
clone.setAttribute("width", String(OUTPUT_WIDTH));
|
|
62
|
-
clone.setAttribute("height", String(OUTPUT_HEIGHT));
|
|
63
|
-
const svgString = new XMLSerializer().serializeToString(clone);
|
|
64
|
-
try {
|
|
65
|
-
JXG.JSXGraph.freeBoard(board);
|
|
66
|
-
} catch {
|
|
67
|
-
}
|
|
68
|
-
return { svgString, width: OUTPUT_WIDTH, height: OUTPUT_HEIGHT };
|
|
69
|
-
} finally {
|
|
70
|
-
document.body.removeChild(div);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
var Geometry3DStampHost = lazy(
|
|
74
|
-
() => import('./host-XUFON6CQ.mjs').then((m) => ({ default: m.Geometry3DStampHost }))
|
|
75
|
-
);
|
|
76
|
-
var Geometry3DIcon = /* @__PURE__ */ jsxs(
|
|
77
|
-
"svg",
|
|
78
|
-
{
|
|
79
|
-
width: "20",
|
|
80
|
-
height: "20",
|
|
81
|
-
viewBox: "0 0 24 24",
|
|
82
|
-
fill: "none",
|
|
83
|
-
stroke: "currentColor",
|
|
84
|
-
strokeWidth: "1.6",
|
|
85
|
-
strokeLinecap: "round",
|
|
86
|
-
strokeLinejoin: "round",
|
|
87
|
-
"aria-hidden": "true",
|
|
88
|
-
children: [
|
|
89
|
-
/* @__PURE__ */ jsx("path", { d: "M4 9 L4 20 L14 20 L14 9 Z" }),
|
|
90
|
-
/* @__PURE__ */ jsx("path", { d: "M4 9 L10 4 L20 4 L14 9 Z" }),
|
|
91
|
-
/* @__PURE__ */ jsx("path", { d: "M14 9 L20 4 L20 15 L14 20 Z" })
|
|
92
|
-
]
|
|
93
|
-
}
|
|
94
|
-
);
|
|
95
|
-
var geometry3dStamp = {
|
|
96
|
-
kind: "geometry3d",
|
|
97
|
-
experimental: true,
|
|
98
|
-
shortcutKey: "d",
|
|
99
|
-
toolbarLabel: "D",
|
|
100
|
-
toolbarTitle: "H\xECnh 3D (D)",
|
|
101
|
-
toolbarIcon: Geometry3DIcon,
|
|
102
|
-
toolbarTestId: "stamp-toolbar-geometry3d",
|
|
103
|
-
matchesCustomData: isGeometry3DCustomData,
|
|
104
|
-
async renderSvgFromCustomData(data) {
|
|
105
|
-
if (!isGeometry3DCustomData(data)) {
|
|
106
|
-
throw new Error("geometry3dStamp.renderSvgFromCustomData: customData kh\xF4ng ph\u1EA3i geometry3d");
|
|
107
|
-
}
|
|
108
|
-
const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);
|
|
109
|
-
return svgString;
|
|
110
|
-
},
|
|
111
|
-
restoreFileFromCustomData: async (element) => {
|
|
112
|
-
const data = element.customData;
|
|
113
|
-
const fileId = element.fileId;
|
|
114
|
-
if (!data || !fileId) return null;
|
|
115
|
-
if (!isGeometry3DCustomData(data)) return null;
|
|
116
|
-
try {
|
|
117
|
-
const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);
|
|
118
|
-
const dataURL = `data:image/svg+xml;base64,${typeof btoa !== "undefined" ? btoa(unescape(encodeURIComponent(svgString))) : Buffer.from(svgString).toString("base64")}`;
|
|
119
|
-
return { fileId, dataURL, mimeType: "image/svg+xml" };
|
|
120
|
-
} catch {
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
Host: Geometry3DStampHost
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
export { geometry3dStamp };
|
|
128
|
-
//# sourceMappingURL=chunk-HYXFHEDJ.mjs.map
|
|
129
|
-
//# sourceMappingURL=chunk-HYXFHEDJ.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stamps/geometry-3d/render.ts","../src/stamps/geometry-3d/index.tsx"],"names":[],"mappings":"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","file":"chunk-HYXFHEDJ.mjs","sourcesContent":["\"use client\";\n\nimport { parseSerializedBoard3D } from './serialize';\n\nexport interface RenderResult {\n svgString: string;\n width: number;\n height: number;\n}\n\nconst OUTPUT_WIDTH = 1024;\nconst OUTPUT_HEIGHT = 768;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype JxgObj = any;\n\nexport async function renderGeometry3DSvgFromState(\n jsonState: string,\n): Promise<RenderResult> {\n const state = parseSerializedBoard3D(jsonState);\n const JXG = (await import('jsxgraph')).default;\n\n const div = document.createElement('div');\n div.style.cssText = `position:absolute;left:-9999px;top:-9999px;width:${OUTPUT_WIDTH}px;height:${OUTPUT_HEIGHT}px;`;\n document.body.appendChild(div);\n\n try {\n JXG.Options.text.display = 'internal';\n\n const board = JXG.JSXGraph.initBoard(div, {\n boundingbox: state.bbox,\n axis: false,\n showCopyright: false,\n showNavigation: false,\n renderer: 'svg',\n }) as { create: (k: string, p: unknown[], a: unknown) => JxgObj };\n\n const view: JxgObj = board.create(\n 'view3d',\n [\n [-5, -5],\n [10, 10],\n [\n [state.view.bbox3D[0], state.view.bbox3D[3]],\n [state.view.bbox3D[1], state.view.bbox3D[4]],\n [state.view.bbox3D[2], state.view.bbox3D[5]],\n ],\n ],\n {\n az: { slider: { visible: false }, value: state.view.azimuth },\n el: { slider: { visible: false }, value: state.view.elevation },\n projection: 'central',\n },\n );\n\n if (!state.showAxes) {\n (view as { defaultAxes?: unknown[] }).defaultAxes = [];\n }\n\n const idMap = new Map<string, JxgObj>();\n for (const el of state.elements) {\n const parents = el.parents.map((p) =>\n typeof p === 'string' && p.startsWith('@id:')\n ? idMap.get(p.slice(4))\n : p,\n );\n const obj = (\n view as { create: (k: string, p: unknown[], a: unknown) => JxgObj }\n ).create(el.type, parents, {\n ...el.attributes,\n id: el.id,\n name: el.label,\n });\n idMap.set(el.id, obj);\n }\n\n const svg = div.querySelector('svg');\n if (!svg) {\n throw new Error('renderGeometry3DSvgFromState: SVG not produced');\n }\n const clone = svg.cloneNode(true) as SVGElement;\n clone.setAttribute('width', String(OUTPUT_WIDTH));\n clone.setAttribute('height', String(OUTPUT_HEIGHT));\n const svgString = new XMLSerializer().serializeToString(clone);\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n JXG.JSXGraph.freeBoard(board as any);\n } catch {\n /* ignore teardown */\n }\n\n return { svgString, width: OUTPUT_WIDTH, height: OUTPUT_HEIGHT };\n } finally {\n document.body.removeChild(div);\n }\n}\n","'use client';\n\nimport { lazy, type ReactNode } from 'react';\nimport {\n isGeometry3DCustomData,\n type Geometry3DCustomData,\n} from './serialize';\nimport { renderGeometry3DSvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\n\nexport { isGeometry3DCustomData };\nexport type { Geometry3DCustomData };\n\nconst Geometry3DStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.Geometry3DStampHost })),\n);\n\nconst Geometry3DIcon: 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 {/* Mặt trước */}\n <path d=\"M4 9 L4 20 L14 20 L14 9 Z\" />\n {/* Mặt trên */}\n <path d=\"M4 9 L10 4 L20 4 L14 9 Z\" />\n {/* Mặt phải */}\n <path d=\"M14 9 L20 4 L20 15 L14 20 Z\" />\n </svg>\n);\n\nexport const geometry3dStamp: StampType = {\n kind: 'geometry3d',\n experimental: true,\n shortcutKey: 'd',\n toolbarLabel: 'D',\n toolbarTitle: 'Hình 3D (D)',\n toolbarIcon: Geometry3DIcon,\n toolbarTestId: 'stamp-toolbar-geometry3d',\n matchesCustomData: isGeometry3DCustomData,\n async renderSvgFromCustomData(data: unknown): Promise<string> {\n if (!isGeometry3DCustomData(data)) {\n throw new Error('geometry3dStamp.renderSvgFromCustomData: customData không phải geometry3d');\n }\n const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);\n return svgString;\n },\n restoreFileFromCustomData: async (element): Promise<RestoredStampFile | null> => {\n const data = element.customData as Geometry3DCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId) return null;\n if (!isGeometry3DCustomData(data)) return null;\n try {\n const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);\n const dataURL = `data:image/svg+xml;base64,${\n typeof btoa !== 'undefined'\n ? btoa(unescape(encodeURIComponent(svgString)))\n : Buffer.from(svgString).toString('base64')\n }`;\n return { fileId, dataURL, mimeType: 'image/svg+xml' };\n } catch {\n return null;\n }\n },\n Host: Geometry3DStampHost,\n};\n"]}
|