@xom11/whiteboard 0.31.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.
- package/dist/ai.d.mts +149 -28
- package/dist/ai.d.ts +149 -28
- package/dist/ai.js +2652 -430
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +2482 -426
- package/dist/ai.mjs.map +1 -1
- package/dist/catalog.json +4 -4
- package/dist/{chunk-JJ4FPCBE.mjs → chunk-4EXRNZRX.mjs} +641 -75
- package/dist/chunk-4EXRNZRX.mjs.map +1 -0
- package/dist/{chunk-TMRFSOM7.mjs → chunk-4NJUTCFF.mjs} +4 -4
- package/dist/{chunk-TMRFSOM7.mjs.map → chunk-4NJUTCFF.mjs.map} +1 -1
- package/dist/{chunk-LTLLQUMN.mjs → chunk-BZZ5BROM.mjs} +6 -7
- package/dist/chunk-BZZ5BROM.mjs.map +1 -0
- package/dist/{host-KMWP7KBT.mjs → chunk-C3RDFBQ5.mjs} +20 -35
- package/dist/chunk-C3RDFBQ5.mjs.map +1 -0
- package/dist/chunk-C7C66BAV.mjs +11 -0
- package/dist/chunk-C7C66BAV.mjs.map +1 -0
- package/dist/{chunk-T3N4BSJV.mjs → chunk-DCFZTKFE.mjs} +13 -5
- package/dist/chunk-DCFZTKFE.mjs.map +1 -0
- package/dist/{chunk-44JY2AKC.mjs → chunk-F42MZ745.mjs} +3 -3
- package/dist/{chunk-44JY2AKC.mjs.map → chunk-F42MZ745.mjs.map} +1 -1
- package/dist/{chunk-BMYC2ILT.mjs → chunk-FOPZOVI3.mjs} +5 -4
- package/dist/chunk-FOPZOVI3.mjs.map +1 -0
- package/dist/{chunk-K5BS2H56.mjs → chunk-GKDQ5IV7.mjs} +4 -4
- package/dist/{chunk-K5BS2H56.mjs.map → chunk-GKDQ5IV7.mjs.map} +1 -1
- package/dist/{chunk-73Q7ADVL.mjs → chunk-MNHOFOM5.mjs} +2 -2
- package/dist/{chunk-73Q7ADVL.mjs.map → chunk-MNHOFOM5.mjs.map} +1 -1
- package/dist/{chunk-KOXOC2FI.mjs → chunk-NBMYET5H.mjs} +11 -15
- package/dist/chunk-NBMYET5H.mjs.map +1 -0
- package/dist/{chunk-DWIEVCGK.mjs → chunk-NTFG7ZRN.mjs} +53 -3
- package/dist/chunk-NTFG7ZRN.mjs.map +1 -0
- package/dist/{chunk-C76SOFXF.mjs → chunk-QCK3Z5WN.mjs} +3 -3
- package/dist/{chunk-C76SOFXF.mjs.map → chunk-QCK3Z5WN.mjs.map} +1 -1
- package/dist/chunk-SUXIMLYN.mjs +238 -0
- package/dist/chunk-SUXIMLYN.mjs.map +1 -0
- package/dist/geometry-2d.d.mts +1 -1
- package/dist/geometry-2d.d.ts +1 -1
- package/dist/geometry-2d.js +1026 -231
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +6 -5
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +4001 -475
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +5 -5
- package/dist/graph-2d.d.mts +1 -1
- package/dist/graph-2d.d.ts +1 -1
- package/dist/graph-2d.js +712 -115
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +5 -5
- package/dist/{handleExtractProblem-BrDY9ifM.d.ts → handleExtractProblem-mtJyhEw3.d.mts} +2 -0
- package/dist/{handleExtractProblem-BrDY9ifM.d.mts → handleExtractProblem-mtJyhEw3.d.ts} +2 -0
- package/dist/host-6WTLCXUE.mjs +62 -0
- package/dist/host-6WTLCXUE.mjs.map +1 -0
- package/dist/{host-H2IGOKJU.mjs → host-BWIXT5TR.mjs} +3 -3
- package/dist/{host-H2IGOKJU.mjs.map → host-BWIXT5TR.mjs.map} +1 -1
- package/dist/{host-4FIUNIDQ.mjs → host-QDVRULJV.mjs} +14 -11
- package/dist/host-QDVRULJV.mjs.map +1 -0
- package/dist/host-USOIVTQB.mjs +4976 -0
- package/dist/host-USOIVTQB.mjs.map +1 -0
- package/dist/index.d.mts +14 -4
- package/dist/index.d.ts +14 -4
- package/dist/index.js +4273 -521
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48 -27
- package/dist/index.mjs.map +1 -1
- package/dist/latex.d.mts +1 -1
- package/dist/latex.d.ts +1 -1
- package/dist/latex.mjs +1 -1
- package/dist/serialize-7UZWQQ4I.mjs +9 -0
- package/dist/{serialize-PGHQZEPV.mjs.map → serialize-7UZWQQ4I.mjs.map} +1 -1
- package/dist/studio.d.mts +35 -0
- package/dist/studio.d.ts +35 -0
- package/dist/studio.js +10883 -0
- package/dist/studio.js.map +1 -0
- package/dist/studio.mjs +28 -0
- package/dist/studio.mjs.map +1 -0
- package/dist/{types-tePd94vW.d.ts → types-eEEEVsLF.d.mts} +1 -1
- package/dist/{types-tePd94vW.d.mts → types-eEEEVsLF.d.ts} +1 -1
- package/package.json +12 -2
- package/dist/chunk-BMYC2ILT.mjs.map +0 -1
- package/dist/chunk-DWIEVCGK.mjs.map +0 -1
- package/dist/chunk-JJ4FPCBE.mjs.map +0 -1
- package/dist/chunk-KOXOC2FI.mjs.map +0 -1
- package/dist/chunk-LTLLQUMN.mjs.map +0 -1
- package/dist/chunk-QK6OVDLC.mjs +0 -103
- package/dist/chunk-QK6OVDLC.mjs.map +0 -1
- package/dist/chunk-T3N4BSJV.mjs.map +0 -1
- package/dist/host-4FIUNIDQ.mjs.map +0 -1
- package/dist/host-4ZB4XD4S.mjs +0 -2378
- package/dist/host-4ZB4XD4S.mjs.map +0 -1
- package/dist/host-KMWP7KBT.mjs.map +0 -1
- package/dist/serialize-PGHQZEPV.mjs +0 -9
|
@@ -1 +0,0 @@
|
|
|
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-KOXOC2FI.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 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 }\n\n for (const c of circles) {\n if (!Number.isFinite(c.cx) || !Number.isFinite(c.cy) || !Number.isFinite(c.r)) continue;\n // Exclude an oversized circle only when we have a point cluster to compare to.\n if (clusterDiag > 0 && c.r > CIRCLE_MAX_FACTOR * clusterDiag) 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 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/scene/ui/LeftPanelShell.tsx","../src/core/scene/ui/kindMeta.ts","../src/core/scene/ui/ObjectRowMenu.tsx","../src/core/scene/ui/ObjectRow.tsx","../src/core/scene/ui/ObjectListPanel.tsx","../src/stamps/shared/StampLeftPanel/AxisGridSection.tsx","../src/stamps/shared/StampLeftPanel/types.ts","../src/stamps/shared/StampLeftPanel/useToolHoverTooltip.ts","../src/stamps/shared/StampLeftPanel/ToolGrid.tsx","../src/stamps/shared/StampLeftPanel/Desktop.tsx","../src/stamps/shared/MobileToolDrawer.tsx","../src/stamps/shared/StampLeftPanel/Mobile.tsx","../src/stamps/shared/StampLeftPanel/index.tsx","../src/stamps/shared/useStampStore.ts","../src/stamps/shared/StampLeftPanel/constants.ts","../src/stamps/shared/Toast/ToastProvider.tsx","../src/stamps/shared/Toast/useToast.ts","../src/stamps/shared/Toast/Toast.tsx","../src/stamps/shared/Toast/ToastHost.tsx","../src/stamps/shared/safeJsx.ts","../src/stamps/shared/attachJxgWheelZoom.ts","../src/stamps/shared/initJxgBoard.ts"],"names":["React2","jsxs","jsx","React3","Fragment","useState","useRef","useEffect","useCallback","React","UndoIcon","RedoIcon","useMemo"],"mappings":"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","file":"chunk-LTLLQUMN.mjs","sourcesContent":["'use client';\nimport * as React from 'react';\n\nexport interface TabSpec<K extends string = string> {\n key: K;\n label: React.ReactNode;\n testId?: string;\n}\n\nexport interface LeftPanelShellProps<K extends string = string> {\n title: string;\n icon: React.ReactNode;\n onClose: () => void;\n isDark?: boolean;\n tabs?: readonly TabSpec<K>[];\n activeTab?: K;\n onTabChange?: (k: K) => void;\n /** data-testid trên <aside> root. Mặc định \"left-panel\". */\n testId?: string;\n /** Opt-in: cho phép kéo viền phải để đổi width. */\n resizable?: boolean;\n /** Key localStorage để persist width khi resizable=true. */\n widthStorageKey?: string;\n /** Width mặc định khi resizable. Default 240 (≈ w-60). */\n defaultWidth?: number;\n minWidth?: number;\n maxWidth?: number;\n children: React.ReactNode;\n}\n\nconst FALLBACK_DEFAULT_WIDTH = 240;\nconst FALLBACK_MIN_WIDTH = 220;\nconst FALLBACK_MAX_WIDTH = 480;\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, n));\n}\n\nfunction readStoredWidth(\n key: string | undefined,\n fallback: number,\n min: number,\n max: number,\n): number {\n if (!key || typeof window === 'undefined') return fallback;\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) return fallback;\n const n = parseInt(raw, 10);\n if (Number.isFinite(n)) return clamp(n, min, max);\n } catch {\n /* SSR or storage disabled */\n }\n return fallback;\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n </svg>\n );\n}\n\nexport function LeftPanelShell<K extends string>(props: LeftPanelShellProps<K>): React.ReactElement {\n const {\n title,\n icon,\n onClose,\n isDark,\n tabs,\n activeTab,\n onTabChange,\n testId,\n resizable,\n widthStorageKey,\n defaultWidth,\n minWidth,\n maxWidth,\n children,\n } = props;\n const showTabs = !!tabs && tabs.length >= 2;\n\n const min = minWidth ?? FALLBACK_MIN_WIDTH;\n const max = maxWidth ?? FALLBACK_MAX_WIDTH;\n const initial = clamp(defaultWidth ?? FALLBACK_DEFAULT_WIDTH, min, max);\n\n const [width, setWidth] = React.useState<number>(() =>\n resizable ? readStoredWidth(widthStorageKey, initial, min, max) : initial,\n );\n const widthRef = React.useRef(width);\n widthRef.current = width;\n\n React.useEffect(() => {\n if (!resizable || !widthStorageKey || typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(widthStorageKey, String(width));\n } catch {\n /* storage disabled */\n }\n }, [resizable, widthStorageKey, width]);\n\n const onResizeStart = React.useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (!resizable) return;\n e.preventDefault();\n const startX = e.clientX;\n const startW = widthRef.current;\n const onMove = (ev: MouseEvent) => {\n setWidth(clamp(startW + (ev.clientX - startX), min, max));\n };\n const onUp = () => {\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n document.body.style.cursor = 'ew-resize';\n document.body.style.userSelect = 'none';\n },\n [resizable, min, max],\n );\n\n const onResizeDoubleClick = React.useCallback(() => {\n if (!resizable) return;\n setWidth(initial);\n }, [resizable, initial]);\n\n return (\n <aside\n role=\"complementary\"\n aria-label={title}\n data-testid={testId ?? 'left-panel'}\n data-stamp-area=\"true\"\n style={resizable ? { width: `${width}px` } : undefined}\n className={[\n isDark ? 'theme--dark ' : '',\n 'absolute left-0 top-0 z-30 flex h-full flex-col border-r border-slate-200 bg-white shadow-md animate-in slide-in-from-left duration-200',\n resizable ? '' : 'w-60',\n ].join(' ')}\n >\n <header className=\"flex items-center justify-between border-b border-slate-200 bg-gradient-to-r from-slate-50 to-white px-3 py-2\">\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-slate-800\">\n <span className=\"text-base leading-none\">{icon}</span>\n {title}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Đóng\"\n className=\"rounded p-1 text-slate-500 transition hover:bg-slate-100 hover:text-slate-800\"\n >\n <CloseIcon />\n </button>\n </header>\n\n {showTabs && (\n <div role=\"tablist\" className=\"flex gap-1 rounded-md bg-slate-100 p-0.5 mx-3 mt-3\">\n {tabs!.map((t) => (\n <TabPill\n key={t.key}\n active={t.key === activeTab}\n onClick={() => onTabChange?.(t.key)}\n testId={t.testId}\n >\n {t.label}\n </TabPill>\n ))}\n </div>\n )}\n\n <div\n {...(showTabs ? { role: 'tabpanel' } : {})}\n className=\"min-h-0 flex-1 overflow-y-auto p-3 space-y-3\"\n >\n {children}\n </div>\n\n {resizable && (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Kéo để đổi rộng panel\"\n data-testid=\"left-panel-resizer\"\n onMouseDown={onResizeStart}\n onDoubleClick={onResizeDoubleClick}\n className=\"group absolute right-0 top-0 z-40 h-full w-1.5 -mr-0.5 cursor-ew-resize select-none\"\n title=\"Kéo để đổi rộng (double-click để reset)\"\n >\n <div className=\"pointer-events-none absolute inset-y-0 right-0 w-px bg-slate-200 transition group-hover:bg-emerald-400 group-hover:w-0.5 group-active:bg-emerald-500 group-active:w-0.5\" />\n </div>\n )}\n </aside>\n );\n}\n\nexport function TabPill(props: {\n active: boolean;\n onClick: () => void;\n testId?: string;\n children: React.ReactNode;\n}): React.ReactElement {\n const { active, onClick, testId, children } = props;\n return (\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={active}\n onClick={onClick}\n data-testid={testId}\n className={[\n 'flex-1 rounded px-2 py-1 text-[11px] font-medium transition',\n active\n ? 'bg-white text-slate-900 shadow-sm ring-1 ring-slate-200'\n : 'text-slate-500 hover:text-slate-800',\n ].join(' ')}\n >\n {children}\n </button>\n );\n}\n\nexport function Section(props: { label: string; children: React.ReactNode }): React.ReactElement {\n return (\n <section>\n <h4 className=\"mb-1.5 text-[10px] font-semibold uppercase tracking-wider text-slate-500\">\n {props.label}\n </h4>\n {props.children}\n </section>\n );\n}\n","// src/core/scene/ui/kindMeta.ts\nexport interface KindUiMeta {\n displayName: string;\n icon: string;\n /** Fallback color khi obj.attrs.color không có. Phải khớp default ở renderer. */\n defaultColor: string;\n}\n\n// Default colors:\n// - Points: blue (#1e40af)\n// - Curves / shapes: dark slate (#0f172a)\n// - 3D planes / spheres: light blue (#60a5fa)\nconst POINT_COLOR = '#1e40af';\nconst CURVE_COLOR = '#0f172a';\nconst PLANE_COLOR = '#60a5fa';\n\nexport const KIND_UI_META: Readonly<Record<string, KindUiMeta>> = {\n // 2D\n point: { displayName: 'Điểm', icon: '·', defaultColor: POINT_COLOR },\n segment: { displayName: 'Đoạn thẳng', icon: '—', defaultColor: CURVE_COLOR },\n line: { displayName: 'Đường thẳng', icon: '/', defaultColor: CURVE_COLOR },\n ray: { displayName: 'Tia', icon: '→', defaultColor: CURVE_COLOR },\n vector: { displayName: 'Vector', icon: '↗', defaultColor: CURVE_COLOR },\n circle: { displayName: 'Đường tròn', icon: '○', defaultColor: CURVE_COLOR },\n polygon: { displayName: 'Đa giác', icon: '◇', defaultColor: CURVE_COLOR },\n intersection: { displayName: 'Giao điểm', icon: '✕', defaultColor: POINT_COLOR },\n angle: { displayName: 'Góc', icon: '∠', defaultColor: '#16a34a' },\n distance: { displayName: 'Khoảng cách', icon: '↔', defaultColor: '#dc2626' },\n // 3D\n point3d: { displayName: 'Điểm', icon: '·', defaultColor: POINT_COLOR },\n segment3d: { displayName: 'Đoạn thẳng', icon: '—', defaultColor: CURVE_COLOR },\n line3d: { displayName: 'Đường thẳng', icon: '/', defaultColor: CURVE_COLOR },\n ray3d: { displayName: 'Tia', icon: '→', defaultColor: CURVE_COLOR },\n vector3d: { displayName: 'Vector', icon: '↗', defaultColor: CURVE_COLOR },\n plane3d: { displayName: 'Mặt phẳng', icon: '▱', defaultColor: PLANE_COLOR },\n polygon3d: { displayName: 'Đa giác', icon: '◇', defaultColor: CURVE_COLOR },\n sphere3d: { displayName: 'Mặt cầu', icon: '◯', defaultColor: PLANE_COLOR },\n polyhedron3d: { displayName: 'Đa diện', icon: '⬢', defaultColor: PLANE_COLOR },\n cylinder3d: { displayName: 'Hình trụ', icon: '⌭', defaultColor: PLANE_COLOR },\n cone3d: { displayName: 'Hình nón', icon: '▲', defaultColor: PLANE_COLOR },\n // Graph 2D\n function2d: { displayName: 'Hàm số', icon: 'ƒ', defaultColor: CURVE_COLOR },\n parameter: { displayName: 'Tham số', icon: 'α', defaultColor: '#7c3aed' },\n pointOnCurve: { displayName: 'Điểm trên đồ thị', icon: '◉', defaultColor: POINT_COLOR },\n tangent2d: { displayName: 'Tiếp tuyến', icon: '╱', defaultColor: CURVE_COLOR },\n extremum2d: { displayName: 'Cực trị', icon: '∧', defaultColor: POINT_COLOR },\n root2d: { displayName: 'Nghiệm', icon: '0', defaultColor: POINT_COLOR },\n slope2d: { displayName: 'Hệ số góc', icon: '△', defaultColor: '#dc2626' },\n};\n\nexport function getKindUiMeta(kind: string): KindUiMeta {\n return KIND_UI_META[kind] ?? { displayName: kind, icon: '?', defaultColor: '#888888' };\n}\n","'use client';\nimport * as React from 'react';\n\nexport interface ObjectRowMenuProps {\n locked: boolean;\n onToggleLocked: () => void;\n onRename: () => void;\n onChangeColor: () => void;\n onDelete: () => void;\n}\n\nexport function ObjectRowMenu(props: ObjectRowMenuProps): React.ReactElement {\n const { locked, onToggleLocked, onRename, onChangeColor, onDelete } = props;\n const [open, setOpen] = React.useState(false);\n\n return (\n <div className=\"relative inline-block\">\n <button\n type=\"button\"\n aria-label=\"Row menu\"\n onClick={(e) => { e.stopPropagation(); setOpen((v) => !v); }}\n className=\"rounded px-1.5 text-black\"\n >\n ⋮\n </button>\n {open ? (\n <div\n role=\"menu\"\n className=\"absolute right-0 z-10 mt-1 w-40 rounded-md border border-zinc-200 bg-white py-1 text-xs shadow-lg dark:border-zinc-700 dark:bg-zinc-900\"\n onClick={(e) => e.stopPropagation()}\n >\n <MenuItem onClick={() => { setOpen(false); onRename(); }}>Đổi tên</MenuItem>\n <MenuItem onClick={() => { setOpen(false); onChangeColor(); }}>Đổi màu</MenuItem>\n <MenuItem onClick={() => { setOpen(false); onToggleLocked(); }}>\n {locked ? 'Mở khoá' : 'Khoá'}\n </MenuItem>\n <MenuItem\n onClick={() => { setOpen(false); onDelete(); }}\n className=\"text-red-600 dark:text-red-400\"\n >\n Xoá\n </MenuItem>\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction MenuItem({\n children,\n onClick,\n className,\n}: React.PropsWithChildren<{ onClick: () => void; className?: string }>) {\n return (\n <button\n type=\"button\"\n role=\"menuitem\"\n onClick={onClick}\n className={`block w-full px-3 py-1 text-left text-black ${className ?? ''}`}\n >\n {children}\n </button>\n );\n}\n","'use client';\nimport * as React from 'react';\nimport type { SceneObject, State } from '../types';\nimport { getKind } from '../registry';\nimport { getKindUiMeta } from './kindMeta';\nimport { ObjectRowMenu } from './ObjectRowMenu';\n\nexport interface ObjectRowProps {\n obj: SceneObject;\n state: State;\n selected: boolean;\n onSelect: (id: string) => void;\n onToggleVisible: (id: string) => void;\n onToggleLocked: (id: string) => void;\n onRename: (id: string) => void;\n onChangeColor: (id: string) => void;\n onDelete: (id: string) => void;\n /**\n * Override mô tả title của row. Mặc định dùng `getKind(obj.kind).describe()`.\n * Stamp consumer (vd geometry-2d) có thể inject DSL-style description.\n */\n describe?: (obj: SceneObject, state: State) => string;\n}\n\nfunction formatMeasure(items: { label: string; value: number }[]): string {\n return items.map((it) => `${it.label} = ${it.value.toFixed(2)}`).join(', ');\n}\n\nexport function ObjectRow(props: ObjectRowProps): React.ReactElement {\n const { obj, state, selected, onSelect, onToggleVisible, onToggleLocked, onRename, onChangeColor, onDelete, describe } = props;\n\n const meta = getKindUiMeta(obj.kind);\n\n let title = '';\n try {\n title = describe\n ? describe(obj, state)\n : getKind(obj.kind).describe(obj, state);\n } catch {\n title = `${meta.displayName} ${obj.label}`;\n }\n\n let measureText: string | null = null;\n if (selected) {\n try {\n const m = getKind(obj.kind).measure?.(obj, state);\n if (m && m.length > 0) measureText = formatMeasure(m);\n } catch {\n measureText = null;\n }\n }\n\n const color = (obj.attrs as { color?: string }).color ?? meta.defaultColor;\n\n return (\n <li\n data-testid={`object-row-${obj.id}`}\n aria-selected={selected}\n onClick={() => onSelect(obj.id)}\n className={\n 'flex flex-col border-b border-zinc-100 cursor-pointer dark:border-zinc-800 ' +\n (selected ? 'bg-slate-200' : '')\n }\n >\n <div className=\"flex items-center gap-2 px-3 py-1.5 text-xs\">\n <button\n type=\"button\"\n aria-label=\"Toggle visibility\"\n aria-pressed={!obj.visible}\n onClick={(e) => { e.stopPropagation(); onToggleVisible(obj.id); }}\n className=\"h-4 w-4 shrink-0 rounded-full border-2 transition\"\n style={{\n backgroundColor: obj.visible ? color : 'transparent',\n borderColor: color,\n }}\n />\n <span className=\"flex-1 truncate text-black\">\n {title}\n </span>\n <ObjectRowMenu\n locked={obj.locked}\n onToggleLocked={() => onToggleLocked(obj.id)}\n onRename={() => onRename(obj.id)}\n onChangeColor={() => onChangeColor(obj.id)}\n onDelete={() => onDelete(obj.id)}\n />\n </div>\n {selected && measureText && (\n <div\n data-testid={`object-row-detail-${obj.id}`}\n className=\"pl-9 pr-3 pb-1.5 text-[11px] text-black\"\n >\n {measureText}\n </div>\n )}\n </li>\n );\n}\n","'use client';\nimport * as React from 'react';\nimport type { Store } from '../store';\nimport type { SceneObject } from '../types';\nimport { listObjects } from '../selectors';\nimport { ObjectRow } from './ObjectRow';\n\nexport interface ObjectListPanelProps {\n store: Store;\n selectedId?: string;\n /**\n * Called when user clicks a row. Receives the row id, or `null` when the\n * user clicks the already-selected row (toggle off — request to deselect).\n * Parent should treat `null` as \"clear selection\".\n */\n onSelect?: (id: string | null) => void;\n /**\n * Optional per-kind row renderer. Called with the SceneObject and default props\n * (selected, onClick). Return a ReactNode to override the default ObjectRow.\n * Return null/undefined to fall back to the default ObjectRow.\n */\n renderRow?: (\n obj: SceneObject,\n defaults: { selected: boolean; onClick: () => void },\n ) => React.ReactNode;\n}\n\nexport function ObjectListPanel(props: ObjectListPanelProps): React.ReactElement {\n const { store, selectedId, onSelect, renderRow } = props;\n // useSyncExternalStore expects subscribe to receive () => void callback,\n // but Store.subscribe takes (next, prev, action) => void. Wrap to adapt.\n const subscribe = React.useCallback(\n (cb: () => void) => store.subscribe(() => cb()),\n [store],\n );\n const state = React.useSyncExternalStore(subscribe, store.getState, store.getState);\n const objects = listObjects(state);\n\n function handleSelect(id: string) {\n // Toggle: click vào row đang selected → deselect (collapse detail + tắt highlight).\n onSelect?.(id === selectedId ? null : id);\n }\n\n function handleToggleVisible(id: string) {\n const obj = state.objects[id];\n if (!obj) return;\n store.dispatch({ type: 'UPDATE', payload: { id, patch: { visible: !obj.visible } } });\n }\n\n function handleToggleLocked(id: string) {\n const obj = state.objects[id];\n if (!obj) return;\n store.dispatch({ type: 'UPDATE', payload: { id, patch: { locked: !obj.locked } } });\n }\n\n function handleDelete(id: string) {\n store.dispatch({ type: 'DELETE', payload: { id } });\n }\n\n function noop() { /* rename + change color stubbed for Phase 3 */ }\n\n return (\n <ul\n data-testid=\"object-list-panel\"\n className=\"flex max-h-[calc(100vh-200px)] flex-col overflow-y-auto\"\n >\n {objects.length === 0 ? (\n <li className=\"px-3 py-4 text-center text-xs text-zinc-500\">Chưa có đối tượng nào</li>\n ) : (\n objects.map((obj) => {\n const selected = obj.id === selectedId;\n const onClick = () => handleSelect(obj.id);\n if (renderRow) {\n const custom = renderRow(obj, { selected, onClick });\n if (custom != null) {\n return <React.Fragment key={obj.id}>{custom}</React.Fragment>;\n }\n }\n return (\n <ObjectRow\n key={obj.id}\n obj={obj}\n state={state}\n selected={selected}\n onSelect={handleSelect}\n onToggleVisible={handleToggleVisible}\n onToggleLocked={handleToggleLocked}\n onRename={noop}\n onChangeColor={noop}\n onDelete={handleDelete}\n />\n );\n })\n )}\n </ul>\n );\n}\n","'use client';\n// src/stamps/shared/StampLeftPanel/AxisGridSection.tsx\n//\n// Section \"Bố cục\" / \"Góc nhìn\": 2 checkbox axis/grid + (optional) undo/redo\n// button ở mép phải. Render khi có ít nhất 1 trong (view, history).\n// Skip toàn bộ section khi cả 2 đều undefined.\n\nimport React from 'react';\nimport { Section } from '../../../core/scene/ui/LeftPanelShell';\nimport type {\n StampLeftPanelHistoryProps,\n StampLeftPanelViewProps,\n} from './types';\n\nexport interface AxisGridSectionProps {\n view?: StampLeftPanelViewProps;\n history?: StampLeftPanelHistoryProps;\n}\n\nfunction UndoIcon(): React.ReactElement {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M3 10 L8 5 L8 8 L15 8 A5 5 0 0 1 20 13 L20 16\" />\n <path d=\"M3 10 L8 15 L8 12\" />\n </svg>\n );\n}\n\nfunction RedoIcon(): React.ReactElement {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 10 L16 5 L16 8 L9 8 A5 5 0 0 0 4 13 L4 16\" />\n <path d=\"M21 10 L16 15 L16 12\" />\n </svg>\n );\n}\n\nexport function AxisGridSection(props: AxisGridSectionProps): React.ReactElement | null {\n const { view, history } = props;\n if (!view && !history) return null;\n\n const sectionLabel = view?.sectionLabel ?? 'Bố cục';\n const axisLabel = view?.axisLabel ?? 'Trục';\n const gridLabel = view?.gridLabel ?? 'Lưới';\n\n return (\n <Section label={sectionLabel}>\n <div className=\"flex items-center gap-3 text-[11px] text-slate-700\">\n {view && (\n <>\n <label className=\"inline-flex select-none items-center gap-1.5\">\n <input\n type=\"checkbox\"\n checked={view.showAxis}\n onChange={(e) => view.onShowAxisChange(e.target.checked)}\n data-testid=\"toggle-axis\"\n />\n {axisLabel}\n </label>\n <label className=\"inline-flex select-none items-center gap-1.5\">\n <input\n type=\"checkbox\"\n checked={view.showGrid}\n onChange={(e) => view.onShowGridChange(e.target.checked)}\n data-testid=\"toggle-grid\"\n />\n {gridLabel}\n </label>\n </>\n )}\n {history && (\n <div className=\"ml-auto flex items-center gap-0.5\">\n <button\n type=\"button\"\n onClick={history.onUndo}\n disabled={!history.canUndo}\n title=\"Hoàn tác (Ctrl/Cmd+Z)\"\n aria-label=\"Hoàn tác\"\n data-testid=\"undo-btn\"\n className=\"inline-flex items-center justify-center rounded p-1 text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent\"\n >\n <UndoIcon />\n </button>\n <button\n type=\"button\"\n onClick={history.onRedo}\n disabled={!history.canRedo}\n title=\"Làm lại (Ctrl/Cmd+Shift+Z)\"\n aria-label=\"Làm lại\"\n data-testid=\"redo-btn\"\n className=\"inline-flex items-center justify-center rounded p-1 text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent\"\n >\n <RedoIcon />\n </button>\n </div>\n )}\n </div>\n </Section>\n );\n}\n","// src/stamps/shared/StampLeftPanel/types.ts\n//\n// Shared types cho StampLeftPanel template. Dùng generic TKey/TGroup để\n// preserve type safety tại call site (host của từng stamp truyền union type\n// riêng); internal cài theo string là OK.\n\nimport type { ReactNode } from 'react';\nimport type { Store } from '../../../core/scene/store';\nimport type { SceneObject } from '../../../core/scene/types';\n\nexport const TOOLTIP_DELAY_MS = 400;\nexport type HoverState = { label: string; hint?: string; x: number; y: number } | null;\n\n/** Tool descriptor unified cho 3 stamp editor. */\nexport interface StampToolDef<TKey extends string = string, TGroup extends string = string> {\n key: TKey;\n label: string;\n hint?: string;\n icon: ReactNode;\n group: TGroup;\n shortcut?: string;\n}\n\nexport interface StampLeftPanelTabs {\n /** Label tab \"Đối tượng\" (default \"📐 Đối tượng\"). */\n objectsLabel?: ReactNode;\n /** Label tab \"Công cụ\" (default \"🧰 Công cụ\"). */\n toolsLabel?: ReactNode;\n}\n\nexport interface StampLeftPanelViewProps {\n /** Section header label, default \"Bố cục\". */\n sectionLabel?: string;\n /** Checkbox label cho axis, default \"Trục\". */\n axisLabel?: string;\n /** Checkbox label cho grid, default \"Lưới\". */\n gridLabel?: string;\n showAxis: boolean;\n showGrid: boolean;\n onShowAxisChange: (b: boolean) => void;\n onShowGridChange: (b: boolean) => void;\n}\n\nexport interface StampLeftPanelHistoryProps {\n onUndo: () => void;\n canUndo: boolean;\n onRedo: () => void;\n canRedo: boolean;\n}\n\nexport interface StampLeftPanelChordProps<TGroup extends string = string> {\n /** Group đang được focus sau khi user bấm letter. null = không active. */\n activeGroup: TGroup | null;\n /** Mapping group → letter hint (vd \"P\" cho group \"point\"). */\n letterForGroup: (g: TGroup) => string;\n}\n\nexport interface StampLeftPanelObjectsProps {\n store: Store;\n selectedObjectId?: string;\n onObjectSelect?: (id: string | null) => void;\n /** Custom row render (vd graph-2d cần FunctionRow/ParameterRow). Trả null để fallback default ObjectRow. */\n renderRow?: (\n obj: SceneObject,\n defaults: { selected: boolean; onClick: () => void },\n ) => ReactNode | null;\n /** Optional buttons phía trên ObjectListPanel (vd graph-2d \"+Hàm\" \"+Tham số\"). */\n addButtons?: ReadonlyArray<{ label: string; testId?: string; onClick: () => void }>;\n}\n\nexport interface StampLeftPanelProps<\n TKey extends string = string,\n TGroup extends string = string,\n> {\n // Header\n title: string;\n icon: ReactNode;\n onClose: () => void;\n isDark?: boolean;\n /** data-testid trên <aside> root. Default \"stamp-left-panel\". */\n testId?: string;\n\n // Tools (required)\n tools: ReadonlyArray<StampToolDef<TKey, TGroup>>;\n groupOrder: ReadonlyArray<TGroup>;\n groupLabels: Record<TGroup, string>;\n activeTool: TKey;\n onToolChange: (k: TKey) => void;\n\n // Optional sections\n view?: StampLeftPanelViewProps;\n history?: StampLeftPanelHistoryProps;\n chord?: StampLeftPanelChordProps<TGroup>;\n objects?: StampLeftPanelObjectsProps;\n tabs?: StampLeftPanelTabs;\n\n // Mobile\n isMobile?: boolean;\n drawerOpen?: boolean;\n onDrawerClose?: () => void;\n}\n","// src/stamps/shared/StampLeftPanel/useToolHoverTooltip.ts\n//\n// Generic hover tooltip hook. Trả về { hover, portalReady, showHover, hideHover }\n// để consumer render tooltip qua createPortal. Delay 400ms tránh flash.\n//\n// Generic theo StampToolDef → 3 stamp dùng chung. Moved từ\n// geometry-2d/editor/LeftPanel/useToolHoverTooltip.ts (Phase 1.2).\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { TOOLTIP_DELAY_MS, type HoverState, type StampToolDef } from './types';\n\nexport function useToolHoverTooltip() {\n const [hover, setHover] = useState<HoverState>(null);\n const [portalReady, setPortalReady] = useState(false);\n const hoverTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n setPortalReady(true);\n return () => {\n if (hoverTimerRef.current) clearTimeout(hoverTimerRef.current);\n };\n }, []);\n\n const showHover = useCallback((el: HTMLElement, t: StampToolDef) => {\n if (hoverTimerRef.current) clearTimeout(hoverTimerRef.current);\n hoverTimerRef.current = setTimeout(() => {\n const r = el.getBoundingClientRect();\n setHover({ label: t.label, hint: t.hint, x: r.right, y: r.top + r.height / 2 });\n }, TOOLTIP_DELAY_MS);\n }, []);\n\n const hideHover = useCallback(() => {\n if (hoverTimerRef.current) {\n clearTimeout(hoverTimerRef.current);\n hoverTimerRef.current = null;\n }\n setHover(null);\n }, []);\n\n return { hover, portalReady, showHover, hideHover };\n}\n","'use client';\n// src/stamps/shared/StampLeftPanel/ToolGrid.tsx\n//\n// Tool button grid với:\n// - Search input ở đầu (filter theo label/hint, ignore diacritics + case).\n// - Group section render 4-col icon button.\n// - Khi chord.activeGroup set: section đó highlight (ring emerald + bg),\n// section khác dimmed. KHÔNG render letter / number badge / hint footer\n// nữa (đã bỏ phím tắt visual ở v0.27).\n\nimport React, { useMemo, useState } from 'react';\nimport type {\n StampLeftPanelChordProps,\n StampToolDef,\n} from './types';\nimport { useToolHoverTooltip } from './useToolHoverTooltip';\nimport { createPortal } from 'react-dom';\n\nexport interface ToolGridProps<TKey extends string, TGroup extends string> {\n tools: ReadonlyArray<StampToolDef<TKey, TGroup>>;\n groupOrder: ReadonlyArray<TGroup>;\n groupLabels: Record<TGroup, string>;\n activeTool: TKey;\n onToolChange: (k: TKey) => void;\n chord?: StampLeftPanelChordProps<TGroup>;\n}\n\nfunction normalize(s: string): string {\n return s\n .toLowerCase()\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .replace(/đ/g, 'd')\n .replace(/Đ/g, 'd');\n}\n\nfunction SearchIcon(): React.ReactElement {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <line x1=\"20\" y1=\"20\" x2=\"16.5\" y2=\"16.5\" />\n </svg>\n );\n}\n\nfunction ClearIcon(): React.ReactElement {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n </svg>\n );\n}\n\nfunction ToolResultList<TKey extends string, TGroup extends string>(props: {\n tools: ReadonlyArray<StampToolDef<TKey, TGroup>>;\n activeTool: TKey;\n onToolChange: (k: TKey) => void;\n}): React.ReactElement {\n const { tools, activeTool, onToolChange } = props;\n return (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"tool-result-list\">\n {tools.map((t) => {\n const active = activeTool === t.key;\n return (\n <button\n key={t.key}\n type=\"button\"\n data-tool={t.key}\n aria-label={t.label}\n aria-pressed={active}\n onClick={() => onToolChange(t.key)}\n className={[\n 'flex items-center gap-2 rounded-md px-2 py-1.5 text-left transition',\n active ? 'bg-emerald-600 text-white' : 'text-slate-700 hover:bg-slate-100',\n ].join(' ')}\n >\n <span className=\"flex h-6 w-6 shrink-0 items-center justify-center\">{t.icon}</span>\n <span className=\"min-w-0\">\n <span className=\"block truncate text-[12px] font-medium leading-tight\">{t.label}</span>\n {t.hint && (\n <span className={['block truncate text-[10px] leading-tight', active ? 'text-emerald-50' : 'text-slate-400'].join(' ')}>\n {t.hint}\n </span>\n )}\n </span>\n </button>\n );\n })}\n </div>\n );\n}\n\nexport function ToolGrid<TKey extends string, TGroup extends string>(\n props: ToolGridProps<TKey, TGroup>,\n): React.ReactElement {\n const { tools, groupOrder, groupLabels, activeTool, onToolChange, chord } = props;\n const { hover, portalReady, showHover, hideHover } = useToolHoverTooltip();\n\n const [query, setQuery] = useState('');\n const normalizedQuery = useMemo(() => normalize(query.trim()), [query]);\n\n const filteredTools = useMemo(() => {\n if (!normalizedQuery) return tools;\n return tools.filter((t) => {\n if (normalize(t.label).includes(normalizedQuery)) return true;\n if (t.hint && normalize(t.hint).includes(normalizedQuery)) return true;\n return false;\n });\n }, [tools, normalizedQuery]);\n\n const grouped = useMemo(() => {\n const acc: Partial<Record<TGroup, StampToolDef<TKey, TGroup>[]>> = {};\n for (const t of filteredTools) {\n (acc[t.group] ??= []).push(t);\n }\n return acc;\n }, [filteredTools]);\n\n const groupKeys = useMemo(\n () => groupOrder.filter((g) => grouped[g] && grouped[g]!.length > 0),\n [grouped, groupOrder],\n );\n\n const noMatch = normalizedQuery !== '' && groupKeys.length === 0;\n\n return (\n <>\n <div className=\"relative\">\n <span className=\"pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 text-slate-400\">\n <SearchIcon />\n </span>\n <input\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Tìm công cụ…\"\n aria-label=\"Tìm công cụ\"\n data-testid=\"tool-search-input\"\n className=\"w-full rounded-md border border-slate-200 bg-slate-50 py-1.5 pl-7 pr-7 text-[12px] text-slate-800 placeholder:text-slate-400 focus:border-emerald-400 focus:bg-white focus:outline-none focus:ring-1 focus:ring-emerald-300\"\n />\n {query && (\n <button\n type=\"button\"\n onClick={() => setQuery('')}\n aria-label=\"Xoá tìm kiếm\"\n data-testid=\"tool-search-clear\"\n className=\"absolute right-1.5 top-1/2 -translate-y-1/2 rounded p-0.5 text-slate-400 transition hover:bg-slate-200 hover:text-slate-700\"\n >\n <ClearIcon />\n </button>\n )}\n </div>\n\n {noMatch && (\n <div\n data-testid=\"tool-search-empty\"\n className=\"rounded-md border border-dashed border-slate-200 bg-slate-50 px-3 py-4 text-center text-[11px] text-slate-500\"\n >\n Không có công cụ nào khớp “{query.trim()}”.\n </div>\n )}\n\n {normalizedQuery !== '' && !noMatch ? (\n <ToolResultList tools={filteredTools} activeTool={activeTool} onToolChange={onToolChange} />\n ) : (\n groupKeys.map((group) => {\n const isChordActive = chord?.activeGroup === group;\n const dimmed = chord?.activeGroup != null && !isChordActive;\n return (\n <section\n key={group}\n data-chord-group={group}\n data-chord-active={isChordActive ? 'true' : 'false'}\n className={[\n 'rounded-md transition',\n isChordActive ? 'bg-emerald-50 ring-1 ring-emerald-400 p-1' : 'p-0',\n dimmed ? 'opacity-55' : 'opacity-100',\n ].join(' ')}\n >\n <h4 className=\"mb-1.5 text-[10px] font-semibold uppercase tracking-wider text-slate-500\">\n {groupLabels[group]}\n </h4>\n <div className=\"grid grid-cols-4 gap-1\">\n {grouped[group]!.map((t) => {\n const active = activeTool === t.key;\n return (\n <button\n key={t.key}\n type=\"button\"\n aria-label={t.label}\n aria-pressed={active}\n data-tool={t.key}\n title={t.label + (t.shortcut ? ` (${t.shortcut})` : '')}\n onClick={() => onToolChange(t.key)}\n onMouseEnter={(e) => showHover(e.currentTarget, t)}\n onMouseLeave={hideHover}\n onFocus={(e) => showHover(e.currentTarget, t)}\n onBlur={hideHover}\n className={[\n 'relative flex h-10 items-center justify-center rounded-md transition',\n active\n ? 'bg-emerald-600 text-white shadow-sm'\n : 'text-slate-700 hover:bg-slate-100 hover:text-slate-900',\n ].join(' ')}\n >\n {t.icon}\n </button>\n );\n })}\n </div>\n </section>\n );\n })\n )}\n\n {portalReady && hover && typeof document !== 'undefined'\n ? createPortal(\n <div\n role=\"tooltip\"\n className=\"pointer-events-none fixed w-max max-w-[220px] rounded-md bg-slate-900 px-2 py-1 text-left text-[11px] leading-tight text-white shadow-lg\"\n style={{\n left: hover.x + 8,\n top: hover.y,\n transform: 'translate(0, -50%)',\n zIndex: 2147483600,\n }}\n >\n <span className=\"block font-medium\">{hover.label}</span>\n {hover.hint && <span className=\"mt-0.5 block text-slate-300\">{hover.hint}</span>}\n </div>,\n document.body,\n )\n : null}\n </>\n );\n}\n","'use client';\n// src/stamps/shared/StampLeftPanel/Desktop.tsx\n//\n// Desktop layout cho StampLeftPanel. Render:\n// 1. LeftPanelShell chrome + tabs (chỉ render tablist khi có objects)\n// 2. Tab \"tools\": <AxisGridSection> + <ToolGrid> chord-aware\n// 3. Tab \"objects\": optional add buttons + <ObjectListPanel> với custom renderRow\n\nimport React, { useEffect, useState } from 'react';\nimport { LeftPanelShell } from '../../../core/scene/ui/LeftPanelShell';\nimport { ObjectListPanel } from '../../../core/scene/ui/ObjectListPanel';\nimport { AxisGridSection } from './AxisGridSection';\nimport { ToolGrid } from './ToolGrid';\nimport type { StampLeftPanelProps } from './types';\n\nexport function StampLeftPanelDesktop<TKey extends string, TGroup extends string>(\n props: StampLeftPanelProps<TKey, TGroup>,\n): React.ReactElement {\n const {\n title,\n icon,\n onClose,\n isDark,\n testId,\n tools,\n groupOrder,\n groupLabels,\n activeTool,\n onToolChange,\n view,\n history,\n chord,\n objects,\n tabs,\n } = props;\n\n const [tab, setTab] = useState<'tools' | 'objects'>('tools');\n const hasObjects = !!objects;\n\n useEffect(() => {\n if (!hasObjects && tab === 'objects') setTab('tools');\n }, [hasObjects, tab]);\n\n const tabSpecs = hasObjects\n ? [\n { key: 'tools' as const, label: tabs?.toolsLabel ?? '🧰 Công cụ', testId: 'tab-tools' },\n { key: 'objects' as const, label: tabs?.objectsLabel ?? '📐 Đối tượng', testId: 'tab-objects' },\n ]\n : undefined;\n\n return (\n <LeftPanelShell\n title={title}\n icon={icon}\n onClose={onClose}\n isDark={isDark}\n testId={testId ?? 'stamp-left-panel'}\n tabs={tabSpecs}\n activeTab={hasObjects ? tab : undefined}\n onTabChange={hasObjects ? setTab : undefined}\n resizable\n widthStorageKey=\"xom11.stamp-left-panel.width\"\n >\n {(!hasObjects || tab === 'tools') ? (\n <>\n <AxisGridSection view={view} history={history} />\n <ToolGrid\n tools={tools}\n groupOrder={groupOrder}\n groupLabels={groupLabels}\n activeTool={activeTool}\n onToolChange={onToolChange}\n chord={chord}\n />\n </>\n ) : (\n <section data-testid=\"objects-panel\" className=\"flex flex-col gap-2\">\n {objects!.addButtons && objects!.addButtons.length > 0 && (\n <div className=\"flex gap-1\">\n {objects!.addButtons.map((b) => (\n <button\n key={b.label}\n type=\"button\"\n data-testid={b.testId}\n onClick={b.onClick}\n className=\"flex-1 rounded border border-slate-300 bg-slate-50 px-2 py-1 text-[11px] font-medium text-slate-700 transition hover:bg-slate-100\"\n >\n {b.label}\n </button>\n ))}\n </div>\n )}\n <ObjectListPanel\n store={objects!.store}\n selectedId={objects!.selectedObjectId}\n onSelect={objects!.onObjectSelect}\n renderRow={objects!.renderRow}\n />\n </section>\n )}\n </LeftPanelShell>\n );\n}\n","'use client';\n\nimport React from 'react';\n\n/**\n * Generic mobile tool drawer dùng chung cho geometry-2d + geometry-3d.\n *\n * Layout:\n * - Header: icon + title + close\n * - Sticky toolbar: chip switches (Trục/Lưới) + icon-actions (Reset, Undo)\n * - Body: section dọc, mỗi section là 1 nhóm tools, grid 3-col card có nhãn\n *\n * Style: soft-modern, emerald accent, khớp các class trong shared/stamp.css.\n */\n\nexport interface MobileChip {\n /** Label hiển thị + aria-label */\n label: string;\n icon: React.ReactNode;\n pressed: boolean;\n onToggle: (next: boolean) => void;\n /** data-testid optional (để test cũ chạy được) */\n testId?: string;\n}\n\nexport interface MobileActionButton {\n label: string;\n icon: React.ReactNode;\n onClick: () => void;\n disabled?: boolean;\n title?: string;\n /** data-testid optional (cho phép test target button cụ thể) */\n testId?: string;\n}\n\nexport interface MobileTool<TKey extends string> {\n key: TKey;\n label: string;\n icon: React.ReactNode;\n}\n\nexport interface MobileToolGroup<TKey extends string, TGroup extends string> {\n group: TGroup;\n groupLabel: string;\n tools: MobileTool<TKey>[];\n}\n\ninterface MobileToolDrawerProps<TKey extends string, TGroup extends string> {\n title: string;\n headerIcon: React.ReactNode;\n chips: MobileChip[];\n actions: MobileActionButton[];\n groups: MobileToolGroup<TKey, TGroup>[];\n activeTool: TKey;\n onToolSelect: (key: TKey) => void;\n drawerOpen: boolean;\n onDrawerClose: () => void;\n isDark?: boolean;\n /** data-testid trên <aside> — giữ để test cũ tìm được panel */\n testId?: string;\n /** Optional: thêm tab \"Đối tượng\" trong drawer body. */\n objectsTab?: {\n label: React.ReactNode;\n render: () => React.ReactNode;\n };\n}\n\nexport function MobileToolDrawer<TKey extends string, TGroup extends string>({\n title,\n headerIcon,\n chips,\n actions,\n groups,\n activeTool,\n onToolSelect,\n drawerOpen,\n onDrawerClose,\n isDark,\n testId,\n objectsTab,\n}: MobileToolDrawerProps<TKey, TGroup>) {\n const [mobileTab, setMobileTab] = React.useState<'tools' | 'objects'>('tools');\n const prevOpenRef = React.useRef(drawerOpen);\n React.useEffect(() => {\n if (!prevOpenRef.current && drawerOpen) setMobileTab('tools');\n prevOpenRef.current = drawerOpen;\n }, [drawerOpen]);\n\n return (\n <>\n {drawerOpen && (\n <div\n className=\"stamp-drawer-backdrop\"\n onPointerDown={onDrawerClose}\n aria-hidden=\"true\"\n />\n )}\n <aside\n role=\"complementary\"\n aria-label={title}\n aria-hidden={!drawerOpen ? 'true' : undefined}\n data-testid={testId}\n data-stamp-area=\"true\"\n data-mobile-drawer=\"true\"\n data-geo-mobile=\"true\"\n data-drawer-state={drawerOpen ? 'open' : 'closed'}\n className={[\n isDark ? 'theme--dark ' : '',\n 'stamp-drawer-mobile flex flex-col border-r border-slate-200 bg-white shadow-md',\n ].join('')}\n >\n {/* Header */}\n <header className=\"flex items-center justify-between border-b border-slate-200 bg-gradient-to-r from-slate-50 to-white px-4 py-3\">\n <h3 className=\"flex items-center gap-2 text-base font-semibold text-slate-800\">\n <span className=\"inline-flex h-7 w-7 items-center justify-center rounded-lg bg-emerald-50 text-emerald-700\">\n {headerIcon}\n </span>\n {title}\n </h3>\n <button\n type=\"button\"\n onClick={onDrawerClose}\n aria-label=\"Đóng ngăn công cụ\"\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-full text-slate-500 transition hover:bg-slate-100 hover:text-slate-800\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n </svg>\n </button>\n </header>\n\n {/* Sticky toolbar: chips + actions */}\n <div className=\"sticky top-0 z-10 flex items-center gap-2 border-b border-slate-200 bg-white/95 px-3 py-2 backdrop-blur\">\n {chips.map((c) => (\n <button\n key={c.label}\n type=\"button\"\n role=\"switch\"\n aria-pressed={c.pressed}\n aria-label={c.label}\n data-testid={c.testId}\n onClick={() => c.onToggle(!c.pressed)}\n className=\"geo-mobile-chip\"\n >\n {c.icon}\n {c.label}\n </button>\n ))}\n {actions.length > 0 && <div className=\"ml-auto flex items-center gap-1\">\n {actions.map((a) => (\n <button\n key={a.label}\n type=\"button\"\n onClick={a.onClick}\n disabled={a.disabled}\n aria-label={a.label}\n title={a.title ?? a.label}\n data-testid={a.testId}\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-full text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent\"\n >\n {a.icon}\n </button>\n ))}\n </div>}\n </div>\n\n {/* Tab row (chỉ render khi objectsTab được cung cấp) */}\n {objectsTab && (\n <div role=\"tablist\" className=\"flex gap-1 rounded-md bg-slate-100 p-0.5 mx-3 mt-2\">\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mobileTab === 'tools'}\n onClick={() => setMobileTab('tools')}\n className={[\n 'flex-1 rounded px-2 py-1 text-[11px] font-medium transition',\n mobileTab === 'tools'\n ? 'bg-white text-slate-900 shadow-sm ring-1 ring-slate-200'\n : 'text-slate-500 hover:text-slate-800',\n ].join(' ')}\n >\n 🧰 Công cụ\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mobileTab === 'objects'}\n onClick={() => setMobileTab('objects')}\n className={[\n 'flex-1 rounded px-2 py-1 text-[11px] font-medium transition',\n mobileTab === 'objects'\n ? 'bg-white text-slate-900 shadow-sm ring-1 ring-slate-200'\n : 'text-slate-500 hover:text-slate-800',\n ].join(' ')}\n >\n {objectsTab.label}\n </button>\n </div>\n )}\n\n {/* Body: groups xếp dọc */}\n <div\n className=\"min-h-0 flex-1 overflow-y-auto\"\n style={{ paddingBottom: 'calc(0.75rem + env(safe-area-inset-bottom))' }}\n >\n {objectsTab && mobileTab === 'objects' ? (\n <div className=\"px-3 pt-3\">{objectsTab.render()}</div>\n ) : (\n groups.map((g) => (\n <section key={g.group} className=\"px-3 pt-3 pb-1\">\n <h4 className=\"mb-2 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-wider text-slate-500\">\n <span className=\"h-1 w-1 rounded-full bg-emerald-500\" />\n {g.groupLabel}\n </h4>\n <div className=\"grid grid-cols-3 gap-2\">\n {g.tools.map((t) => {\n const active = activeTool === t.key;\n return (\n <button\n key={t.key}\n type=\"button\"\n aria-label={t.label}\n aria-pressed={active}\n data-tool={t.key}\n onClick={() => {\n onToolSelect(t.key);\n onDrawerClose();\n }}\n className={[\n 'flex flex-col items-center justify-center gap-1.5 rounded-2xl px-2 py-3 transition active:scale-95',\n active\n ? 'geo-mobile-tool-active'\n : 'bg-slate-50 text-slate-700 hover:bg-slate-100',\n ].join(' ')}\n >\n <span className=\"flex h-8 w-8 items-center justify-center\">{t.icon}</span>\n <span className=\"text-center text-[11px] font-medium leading-tight line-clamp-2\">\n {t.label}\n </span>\n </button>\n );\n })}\n </div>\n </section>\n ))\n )}\n </div>\n </aside>\n </>\n );\n}\n","'use client';\n// src/stamps/shared/StampLeftPanel/Mobile.tsx\n//\n// Mobile layout: wrap shared MobileToolDrawer với mapping:\n// chips = axis/grid (from view)\n// actions = undo/redo (from history)\n// groups = tools grouped by group\n// objects = objectsTab (objects.store + addButtons + custom renderRow)\n\nimport React, { useMemo } from 'react';\nimport { MobileToolDrawer, type MobileToolGroup } from '../MobileToolDrawer';\nimport { ObjectListPanel } from '../../../core/scene/ui/ObjectListPanel';\nimport type { StampLeftPanelProps } from './types';\n\nfunction AxisIcon(): React.ReactElement {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"4\" y1=\"20\" x2=\"20\" y2=\"20\" />\n <line x1=\"4\" y1=\"20\" x2=\"4\" y2=\"4\" />\n <polyline points=\"2 6 4 4 6 6\" />\n <polyline points=\"18 18 20 20 18 22\" />\n </svg>\n );\n}\n\nfunction GridIcon(): React.ReactElement {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"1\" />\n <line x1=\"4\" y1=\"10\" x2=\"20\" y2=\"10\" />\n <line x1=\"4\" y1=\"16\" x2=\"20\" y2=\"16\" />\n <line x1=\"10\" y1=\"4\" x2=\"10\" y2=\"20\" />\n <line x1=\"16\" y1=\"4\" x2=\"16\" y2=\"20\" />\n </svg>\n );\n}\n\nfunction UndoIcon(): React.ReactElement {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M3 10 L8 5 L8 8 L15 8 A5 5 0 0 1 20 13 L20 16\" />\n <path d=\"M3 10 L8 15 L8 12\" />\n </svg>\n );\n}\n\nfunction RedoIcon(): React.ReactElement {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 10 L16 5 L16 8 L9 8 A5 5 0 0 0 4 13 L4 16\" />\n <path d=\"M21 10 L16 15 L16 12\" />\n </svg>\n );\n}\n\nexport function StampLeftPanelMobile<TKey extends string, TGroup extends string>(\n props: StampLeftPanelProps<TKey, TGroup>,\n): React.ReactElement {\n const {\n title,\n icon,\n isDark,\n testId,\n tools,\n groupOrder,\n groupLabels,\n activeTool,\n onToolChange,\n view,\n history,\n objects,\n tabs,\n drawerOpen,\n onDrawerClose,\n } = props;\n\n const groups = useMemo<MobileToolGroup<TKey, TGroup>[]>(() => {\n const acc = new Map<TGroup, typeof tools[number][]>();\n for (const t of tools) {\n if (!acc.has(t.group)) acc.set(t.group, []);\n acc.get(t.group)!.push(t);\n }\n // Preserve groupOrder\n return groupOrder\n .filter((g) => acc.has(g))\n .map((group) => ({\n group,\n groupLabel: groupLabels[group],\n tools: acc.get(group)!.map((t) => ({ key: t.key, label: t.label, icon: t.icon })),\n }));\n }, [tools, groupOrder, groupLabels]);\n\n const chips = view\n ? [\n {\n label: view.axisLabel ?? 'Trục',\n icon: <AxisIcon />,\n pressed: view.showAxis,\n onToggle: view.onShowAxisChange,\n testId: 'toggle-axis',\n },\n {\n label: view.gridLabel ?? 'Lưới',\n icon: <GridIcon />,\n pressed: view.showGrid,\n onToggle: view.onShowGridChange,\n testId: 'toggle-grid',\n },\n ]\n : [];\n\n const actions = history\n ? [\n {\n label: 'Hoàn tác',\n title: 'Hoàn tác (Ctrl/Cmd+Z)',\n icon: <UndoIcon />,\n onClick: history.onUndo,\n disabled: !history.canUndo,\n testId: 'undo-btn',\n },\n {\n label: 'Làm lại',\n title: 'Làm lại (Ctrl/Cmd+Shift+Z)',\n icon: <RedoIcon />,\n onClick: history.onRedo,\n disabled: !history.canRedo,\n testId: 'redo-btn',\n },\n ]\n : [];\n\n return (\n <MobileToolDrawer\n title={title}\n headerIcon={icon}\n testId={testId ?? 'stamp-left-panel'}\n isDark={isDark}\n drawerOpen={!!drawerOpen}\n onDrawerClose={() => onDrawerClose?.()}\n chips={chips}\n actions={actions}\n groups={groups}\n activeTool={activeTool}\n onToolSelect={onToolChange}\n objectsTab={\n objects\n ? {\n label: tabs?.objectsLabel ?? '📐 Đối tượng',\n render: () => (\n <div className=\"flex flex-col gap-2 px-3\">\n {objects.addButtons && objects.addButtons.length > 0 && (\n <div className=\"flex gap-1 pt-3\">\n {objects.addButtons.map((b) => (\n <button\n key={b.label}\n type=\"button\"\n data-testid={b.testId}\n onClick={b.onClick}\n className=\"flex-1 rounded border border-slate-300 bg-slate-50 px-2 py-1 text-[11px] font-medium text-slate-700 transition hover:bg-slate-100\"\n >\n {b.label}\n </button>\n ))}\n </div>\n )}\n <ObjectListPanel\n store={objects.store}\n selectedId={objects.selectedObjectId}\n onSelect={objects.onObjectSelect}\n renderRow={objects.renderRow}\n />\n </div>\n ),\n }\n : undefined\n }\n />\n );\n}\n","'use client';\n// src/stamps/shared/StampLeftPanel/index.tsx\n//\n// Public API. Dispatch isMobile → Desktop hoặc Mobile.\n//\n// Usage:\n//\n// <StampLeftPanel\n// title=\"Hình học\"\n// icon={<GeomIcon />}\n// tools={TOOLS} // ReadonlyArray<StampToolDef>\n// groupOrder={GROUP_ORDER}\n// groupLabels={GROUP_LABELS}\n// activeTool={tool}\n// onToolChange={setTool}\n// view={{ showAxis, showGrid, onShowAxisChange, onShowGridChange }} // optional\n// history={{ onUndo, canUndo, onRedo, canRedo }} // optional\n// chord={{ activeGroup, letterForGroup }} // optional\n// objects={{ store, addButtons, renderRow, ... }} // optional\n// isMobile={isMobile}\n// drawerOpen={drawerOpen}\n// onDrawerClose={...}\n// onClose={onClose}\n// isDark={isDark}\n// />\n\nimport React from 'react';\nimport { StampLeftPanelDesktop } from './Desktop';\nimport { StampLeftPanelMobile } from './Mobile';\nimport type { StampLeftPanelProps } from './types';\n\nexport function StampLeftPanel<TKey extends string, TGroup extends string>(\n props: StampLeftPanelProps<TKey, TGroup>,\n): React.ReactElement {\n if (props.isMobile) return <StampLeftPanelMobile {...props} />;\n return <StampLeftPanelDesktop {...props} />;\n}\n\nexport type {\n StampLeftPanelProps,\n StampToolDef,\n StampLeftPanelViewProps,\n StampLeftPanelHistoryProps,\n StampLeftPanelChordProps,\n StampLeftPanelObjectsProps,\n StampLeftPanelTabs,\n HoverState,\n} from './types';\nexport { TOOLTIP_DELAY_MS } from './types';\nexport { useToolHoverTooltip } from './useToolHoverTooltip';\n","// Hook tạo + giữ scene store tại Host level cho mọi stamp interactive\n// (geometry-2d, geometry-3d, graph-2d).\n//\n// Trước đây mỗi stamp dùng pattern riêng:\n// - 3D: useRef + createStore(createEmptyState('3d')) inline tại host.\n// - 2D + graph-2d: useState<Store|null> + callback `onStoreReady` từ editor.\n//\n// Hook này hợp nhất về 1 mental model: store sống ở host, identity stable,\n// pre-load từ customData ngay frame đầu → bỏ ternary `store ? ... : undefined`\n// + bỏ flash 1 frame trên 2D + graph-2d.\n//\n// Roundtrip edit: khi double-click stamp existing element, `editingElement`\n// được pass vào, hook gọi `parseInitial(customData)` để extract State trước\n// khi createStore. Stamp tự define parseInitial vì format customData khác nhau.\n\nimport { useRef } from 'react';\nimport { createStore, createEmptyState, type Store } from '../../core/scene';\nimport type { State } from '../../core/scene/types';\nimport type { StampHostProps } from './types';\n\nexport type StampDomain = '2d' | '3d' | 'graph2d';\n\nexport type ParseInitialStateFn = (customData: unknown) => State | null;\n\nexport function useStampStore(\n domain: StampDomain,\n editingElement: StampHostProps['editingElement'],\n parseInitial: ParseInitialStateFn,\n): Store {\n const ref = useRef<Store | null>(null);\n if (!ref.current) {\n const initial = editingElement?.customData\n ? parseInitial(editingElement.customData) ?? createEmptyState(domain)\n : createEmptyState(domain);\n ref.current = createStore(initial);\n }\n return ref.current;\n}\n","// src/stamps/shared/StampLeftPanel/constants.ts\n//\n// Desktop popover sizing chia sẻ giữa 3 stamp editor (geometry-2d, geometry-3d,\n// graph-2d). Trước đây mỗi EditorPanel hardcode size riêng (640×540 cho 2D/graph,\n// 800×600 cho 3D) → kích thước canvas nhỏ + không nhất quán. Gom vào constant\n// 880×700 + cap responsive để dùng được trên màn nhỏ.\n\n/** Tailwind class string áp cho outer popover của stamp editor (desktop). */\nexport const STAMP_PANEL_DESKTOP =\n 'h-[700px] w-[880px] max-h-[85vh] max-w-[calc(100vw-280px)]';\n","import React, {\n createContext,\n useCallback,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n} from 'react';\nimport type { ShowToastFn, DismissToastFn, ToastItem, ToastOptions } from './types';\n\nexport interface ToastContextValue {\n items: ToastItem[];\n showToast: ShowToastFn;\n dismiss: DismissToastFn;\n}\n\nexport const ToastContext = createContext<ToastContextValue | null>(null);\n\ntype Action =\n | { type: 'PUSH'; item: ToastItem; maxVisible: number }\n | { type: 'REPLACE'; item: ToastItem }\n | { type: 'DISMISS'; id: string };\n\nfunction reducer(state: ToastItem[], action: Action): ToastItem[] {\n switch (action.type) {\n case 'PUSH': {\n const next = [...state, action.item];\n return next.length > action.maxVisible ? next.slice(next.length - action.maxVisible) : next;\n }\n case 'REPLACE':\n return state.map((it) => (it.id === action.item.id ? action.item : it));\n case 'DISMISS':\n return state.filter((it) => it.id !== action.id);\n }\n}\n\ninterface ToastProviderProps {\n children: React.ReactNode;\n /** Max simultaneously visible toasts. Default: 3. Pushing more drops oldest. */\n maxVisible?: number;\n}\n\nlet autoIdCounter = 0;\n\nexport function ToastProvider({ children, maxVisible = 3 }: ToastProviderProps) {\n const [items, dispatch] = useReducer(reducer, [] as ToastItem[]);\n const timersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\n const itemsRef = useRef(items);\n itemsRef.current = items;\n\n const clearTimer = useCallback((id: string) => {\n const t = timersRef.current.get(id);\n if (t) {\n clearTimeout(t);\n timersRef.current.delete(id);\n }\n }, []);\n\n const dismiss = useCallback<DismissToastFn>((id) => {\n clearTimer(id);\n dispatch({ type: 'DISMISS', id });\n }, [clearTimer]);\n\n const scheduleAutoDismiss = useCallback((id: string, duration: number) => {\n if (duration <= 0) return;\n const t = setTimeout(() => dismiss(id), duration);\n timersRef.current.set(id, t);\n }, [dismiss]);\n\n const showToast = useCallback<ShowToastFn>((message, opts: ToastOptions = {}) => {\n const variant = opts.variant ?? 'info';\n const duration = opts.duration ?? 3000;\n const id = opts.id ?? `toast-${++autoIdCounter}`;\n const item: ToastItem = { id, message, variant, duration };\n const existing = itemsRef.current.find((it) => it.id === id);\n if (existing) {\n clearTimer(id);\n dispatch({ type: 'REPLACE', item });\n } else {\n dispatch({ type: 'PUSH', item, maxVisible });\n }\n scheduleAutoDismiss(id, duration);\n }, [clearTimer, maxVisible, scheduleAutoDismiss]);\n\n useEffect(() => () => {\n timersRef.current.forEach((t) => clearTimeout(t));\n timersRef.current.clear();\n }, []);\n\n const value = useMemo(() => ({ items, showToast, dismiss }), [items, showToast, dismiss]);\n return <ToastContext.Provider value={value}>{children}</ToastContext.Provider>;\n}\n","import { useContext } from 'react';\nimport { ToastContext } from './ToastProvider';\n\nexport function useToast() {\n const ctx = useContext(ToastContext);\n if (!ctx) {\n throw new Error('useToast must be used inside <ToastProvider>');\n }\n return ctx;\n}\n","import React from 'react';\nimport type { ToastVariant } from './types';\n\nconst VARIANT_CLASS: Record<ToastVariant, string> = {\n info: 'border-l-sky-500',\n warning: 'border-l-amber-500',\n error: 'border-l-rose-500',\n};\n\nconst VARIANT_ICON: Record<ToastVariant, React.ReactNode> = {\n info: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\" />\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\" />\n </svg>\n ),\n warning: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\" />\n <line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\" />\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\" />\n </svg>\n ),\n error: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\" />\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\" />\n </svg>\n ),\n};\n\ninterface ToastProps {\n id: string;\n message: string;\n variant: ToastVariant;\n onDismiss: (id: string) => void;\n}\n\nexport function Toast({ id, message, variant, onDismiss }: ToastProps) {\n return (\n <div\n role=\"status\"\n className={[\n 'pointer-events-auto flex max-w-sm items-start gap-2 rounded-lg border-l-4 bg-white px-3 py-2 text-sm text-slate-800 shadow-md ring-1 ring-black/5',\n VARIANT_CLASS[variant],\n ].join(' ')}\n >\n <span className=\"mt-0.5 shrink-0 text-slate-500\">{VARIANT_ICON[variant]}</span>\n <span className=\"flex-1 leading-snug\">{message}</span>\n <button\n type=\"button\"\n aria-label=\"Đóng thông báo\"\n onClick={() => onDismiss(id)}\n className=\"-mr-1 ml-1 inline-flex h-5 w-5 shrink-0 items-center justify-center rounded text-slate-400 hover:bg-slate-100 hover:text-slate-700\"\n >\n ×\n </button>\n </div>\n );\n}\n","import React from 'react';\nimport { Toast } from './Toast';\nimport { useToast } from './useToast';\n\n/**\n * Renders the active toast queue. Mount once near the root of each stamp\n * EditorPanel (inside ToastProvider). Positions itself absolutely at\n * bottom-center of the nearest positioned ancestor.\n */\nexport function ToastHost() {\n const { items, dismiss } = useToast();\n if (items.length === 0) return null;\n return (\n <div\n aria-live=\"polite\"\n className=\"pointer-events-none absolute inset-x-0 bottom-3 z-50 flex flex-col items-center gap-2\"\n >\n {items.map((it) => (\n <Toast key={it.id} id={it.id} message={it.message} variant={it.variant} onDismiss={dismiss} />\n ))}\n </div>\n );\n}\n","const isDev = (() => {\n try {\n return typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production';\n } catch {\n return false;\n }\n})();\n\n/**\n * Wrap JSXGraph operations that can throw on stale/missing state.\n * In dev mode: log to console. In prod: silent swallow + return fallback.\n *\n * @param label Short tag for grep-ability (vd \"removeObject\", \"board.update\").\n * @param fn Operation to execute.\n * @param fallback Value to return if fn throws (default: undefined).\n */\nexport function safeJsx<T>(label: string, fn: () => T): T | undefined;\nexport function safeJsx<T>(label: string, fn: () => T, fallback: T): T;\nexport function safeJsx<T>(label: string, fn: () => T, fallback?: T): T | undefined {\n try {\n return fn();\n } catch (err) {\n if (isDev) {\n \n console.warn('[whiteboard:jsxgraph]', label, err);\n }\n return fallback;\n }\n}\n","import { safeJsx } from './safeJsx';\n\n/**\n * Minimal JSXGraph board surface that wheel-zoom logic depends on.\n * Local shape to avoid importing the (untyped) jsxgraph default export here.\n */\nexport interface JxgBoardZoomable {\n zoomIn: (x?: number, y?: number) => void;\n zoomOut: (x?: number, y?: number) => void;\n // JSXGraph older versions may not expose this — wheel still works, just\n // without cursor-anchored zoom (falls back to board-center).\n getUsrCoordsOfMouse?: (e: WheelEvent) => unknown;\n}\n\n/**\n * Attach Excalidraw-style Ctrl/Cmd + wheel zoom to a container element.\n *\n * - Khi wheel kèm Ctrl/Cmd: preventDefault + zoom in/out với anchor tại\n * con trỏ (nếu board hỗ trợ `getUsrCoordsOfMouse`).\n * - Khi wheel không kèm modifier: bỏ qua → page scroll bình thường.\n *\n * Returns cleanup fn. Caller chịu trách nhiệm gọi trong cleanup của useEffect.\n *\n * @param target Container element (`HTMLDivElement` hoặc `SVGSVGElement`).\n * @param board JSXGraph board instance đã initBoard xong.\n * @param label Tag để log dev-mode khi safeJsx swallow lỗi (vd \"MiniBoard.2d\").\n */\nexport function attachJxgWheelZoom(\n target: HTMLElement,\n board: JxgBoardZoomable,\n label = 'wheelZoom',\n): () => void {\n const onWheel = (e: WheelEvent): void => {\n if (!e.ctrlKey && !e.metaKey) return;\n e.preventDefault();\n e.stopPropagation();\n let cx: number | undefined;\n let cy: number | undefined;\n safeJsx(`${label}.coords`, () => {\n const usr = board.getUsrCoordsOfMouse?.(e);\n if (Array.isArray(usr) && usr.length >= 2\n && Number.isFinite(usr[0]) && Number.isFinite(usr[1])) {\n cx = usr[0] as number;\n cy = usr[1] as number;\n }\n });\n if (e.deltaY < 0) safeJsx(`${label}.in`, () => board.zoomIn(cx, cy));\n else if (e.deltaY > 0) safeJsx(`${label}.out`, () => board.zoomOut(cx, cy));\n };\n target.addEventListener('wheel', onWheel, { passive: false });\n return () => { target.removeEventListener('wheel', onWheel); };\n}\n","import { safeJsx } from './safeJsx';\n\n/**\n * Toggles cho common JSXGraph options. Per-stamp callsite override mặc định\n * nếu cần preserve behavior cụ thể.\n *\n * Mặc định:\n * - text.display = 'internal' (true) — bắt buộc cho clone-SVG export.\n * - text.useASCII/MathJax/Katex = false (true) — JSXGraph default rendering,\n * không load extra text engine.\n * - label.display = 'internal' (true) — point label vào trong SVG.\n * - elements.highlight = false (FALSE mặc định) — opt-in vì graph-2d cần\n * JSXGraph's default hover-highlight để phân biệt object đang hover.\n */\nexport interface JxgInitDefaults {\n textDisplayInternal?: boolean;\n disableTextEngines?: boolean;\n labelDisplayInternal?: boolean;\n disableElementHighlight?: boolean;\n}\n\nexport interface InitJxgBoardConfig {\n /** Per-MiniBoard toggle cho common defaults. */\n defaults?: JxgInitDefaults;\n /** Options pass thẳng vào `JXG.JSXGraph.initBoard(target, opts)`. */\n boardOptions: Record<string, unknown>;\n /**\n * Hook để tweak options sau khi defaults được apply. Dùng cho per-stamp\n * customization (vd themeLabel color cho 2D, axesPosition cho 3D view3d).\n */\n \n extraOptionTweaks?: (opts: any) => void;\n /** Tag cho safeJsx log (vd \"MiniBoard.2d\"). Default: \"JxgBoard\". */\n label?: string;\n}\n\nexport interface InitJxgBoardResult {\n \n JXG: any;\n \n board: any;\n /** Gọi để free JSXGraph board (safe-wrapped). */\n cleanup: () => void;\n}\n\n/**\n * Async dynamic-import JSXGraph + apply common options + initBoard + trả\n * cleanup. Centralize boilerplate shared bởi 3 MiniBoard (2D/3D/graph-2d).\n *\n * Caller vẫn handle cancellation flag, refs, và post-init setup (renderer,\n * view3d, axes, etc.) ngoài helper này.\n *\n * @throws Nếu JSXGraph load hoặc initBoard throw. Caller nên try/catch nếu\n * cần tolerate mock environments.\n */\nexport async function initJxgBoard(\n target: string | HTMLElement,\n config: InitJxgBoardConfig,\n): Promise<InitJxgBoardResult> {\n const label = config.label ?? 'JxgBoard';\n \n const JXG = (await import('jsxgraph')).default as any;\n const {\n textDisplayInternal = true,\n disableTextEngines = true,\n labelDisplayInternal = true,\n disableElementHighlight = false,\n } = config.defaults ?? {};\n safeJsx(`${label}.applyOptions`, () => {\n const opts = JXG.Options;\n if (!opts) return;\n if (textDisplayInternal || disableTextEngines) {\n opts.text = opts.text ?? {};\n if (textDisplayInternal) opts.text.display = 'internal';\n if (disableTextEngines) {\n opts.text.useASCIIMathML = false;\n opts.text.useMathJax = false;\n opts.text.useKatex = false;\n }\n }\n if (labelDisplayInternal) {\n opts.label = opts.label ?? {};\n opts.label.display = 'internal';\n }\n if (disableElementHighlight) {\n opts.elements = opts.elements ?? {};\n opts.elements.highlight = false;\n }\n config.extraOptionTweaks?.(opts);\n });\n const board = JXG.JSXGraph.initBoard(target, config.boardOptions);\n const cleanup = (): void => {\n safeJsx(`${label}.freeBoard`, () => JXG.JSXGraph.freeBoard(board));\n };\n return { JXG, board, cleanup };\n}\n"]}
|
package/dist/chunk-QK6OVDLC.mjs
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
// src/stamps/geometry-2d/ai/vision/tesseract.ts
|
|
3
|
-
var DEFAULT_LANG = "vie+eng";
|
|
4
|
-
async function runTesseractOcr(image, opts = {}) {
|
|
5
|
-
if (opts.signal?.aborted) {
|
|
6
|
-
const err = new Error("Tesseract OCR aborted");
|
|
7
|
-
err.name = "AbortError";
|
|
8
|
-
throw err;
|
|
9
|
-
}
|
|
10
|
-
const { createWorker } = await import('tesseract.js');
|
|
11
|
-
const lang = opts.lang ?? DEFAULT_LANG;
|
|
12
|
-
const worker = await createWorker(lang);
|
|
13
|
-
try {
|
|
14
|
-
const dataUrl = `data:${image.mediaType};base64,${image.base64}`;
|
|
15
|
-
const { data } = await worker.recognize(dataUrl);
|
|
16
|
-
return { text: data.text, confidence: data.confidence };
|
|
17
|
-
} finally {
|
|
18
|
-
await worker.terminate();
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// src/stamps/geometry-2d/ai/vision/extractProblem.ts
|
|
23
|
-
var MIN_HIGH_CONFIDENCE_CHARS = 10;
|
|
24
|
-
var MAX_TEXT_CHARS = 2e3;
|
|
25
|
-
var TESSERACT_HIGH_CONFIDENCE_THRESHOLD = 70;
|
|
26
|
-
async function extractProblemFromImage(image, opts = {}) {
|
|
27
|
-
let raw;
|
|
28
|
-
try {
|
|
29
|
-
raw = await runTesseractOcr(image, {
|
|
30
|
-
...opts.tesseractLang ? { lang: opts.tesseractLang } : {},
|
|
31
|
-
...opts.signal ? { signal: opts.signal } : {}
|
|
32
|
-
});
|
|
33
|
-
} catch (e) {
|
|
34
|
-
const err = e;
|
|
35
|
-
return {
|
|
36
|
-
ok: false,
|
|
37
|
-
reason: "unreadable",
|
|
38
|
-
message: "Tesseract OCR fail: " + (err.message ?? "?")
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
const text = postProcess(raw.text);
|
|
42
|
-
if (text.length === 0) {
|
|
43
|
-
return { ok: false, reason: "empty", message: "Tesseract kh\xF4ng tr\xEDch \u0111\u01B0\u1EE3c text." };
|
|
44
|
-
}
|
|
45
|
-
const tooShort = text.length < MIN_HIGH_CONFIDENCE_CHARS;
|
|
46
|
-
const lowConf = raw.confidence < TESSERACT_HIGH_CONFIDENCE_THRESHOLD;
|
|
47
|
-
const confidence = tooShort || lowConf ? "low" : "high";
|
|
48
|
-
return {
|
|
49
|
-
ok: true,
|
|
50
|
-
text,
|
|
51
|
-
confidence,
|
|
52
|
-
usage: { inputTokens: 0, outputTokens: 0 }
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
function postProcess(raw) {
|
|
56
|
-
let t = raw.trim();
|
|
57
|
-
t = t.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
58
|
-
t = t.replace(/\*(.+?)\*/g, "$1");
|
|
59
|
-
t = t.replace(/_(.+?)_/g, "$1");
|
|
60
|
-
t = t.replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, "$1");
|
|
61
|
-
t = t.replace(/\s+/g, " ").trim();
|
|
62
|
-
t = t.normalize("NFC");
|
|
63
|
-
if (t.length > MAX_TEXT_CHARS) t = t.slice(0, MAX_TEXT_CHARS);
|
|
64
|
-
return t;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// src/stamps/geometry-2d/ai/handleExtractProblem.ts
|
|
68
|
-
async function handleExtractProblem(image, opts = {}) {
|
|
69
|
-
try {
|
|
70
|
-
const r = await extractProblemFromImage(image, opts);
|
|
71
|
-
if (r.ok) {
|
|
72
|
-
if (r.confidence === "low") {
|
|
73
|
-
return {
|
|
74
|
-
kind: "low-confidence",
|
|
75
|
-
text: r.text,
|
|
76
|
-
warning: "OCR c\xF3 th\u1EC3 kh\xF4ng ch\xEDnh x\xE1c, ki\u1EC3m tra tr\u01B0\u1EDBc khi v\u1EBD.",
|
|
77
|
-
usage: r.usage
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
return { kind: "success", text: r.text, usage: r.usage };
|
|
81
|
-
}
|
|
82
|
-
if (r.reason === "not-math") {
|
|
83
|
-
return { kind: "refused", reason: "not-math", message: r.message };
|
|
84
|
-
}
|
|
85
|
-
if (r.reason === "unsupported") {
|
|
86
|
-
return { kind: "error", code: "unsupported", message: r.message };
|
|
87
|
-
}
|
|
88
|
-
if (r.reason === "unreadable") {
|
|
89
|
-
return { kind: "error", code: "network", message: r.message };
|
|
90
|
-
}
|
|
91
|
-
return { kind: "error", code: "empty", message: r.message };
|
|
92
|
-
} catch (e) {
|
|
93
|
-
return {
|
|
94
|
-
kind: "error",
|
|
95
|
-
code: "unexpected",
|
|
96
|
-
message: e instanceof Error ? e.message : String(e)
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export { extractProblemFromImage, handleExtractProblem, runTesseractOcr };
|
|
102
|
-
//# sourceMappingURL=chunk-QK6OVDLC.mjs.map
|
|
103
|
-
//# sourceMappingURL=chunk-QK6OVDLC.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stamps/geometry-2d/ai/vision/tesseract.ts","../src/stamps/geometry-2d/ai/vision/extractProblem.ts","../src/stamps/geometry-2d/ai/handleExtractProblem.ts"],"names":[],"mappings":"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","file":"chunk-QK6OVDLC.mjs","sourcesContent":["// src/stamps/geometry-2d/ai/vision/tesseract.ts\n//\n// Tesseract.js wrapper cho client-side OCR đề toán. Default vie+eng, terminate\n// worker sau mỗi call (không cache v1). Lazy import giữ bundle slim.\n\nimport type { ImagePart } from './types';\n\nexport interface TesseractOcrOptions {\n /** Tesseract language code. Default 'vie+eng' cho đề toán VN. */\n lang?: string;\n signal?: AbortSignal;\n}\n\nexport interface TesseractOcrResult {\n text: string;\n /** Confidence 0–100 (Tesseract scale). */\n confidence: number;\n}\n\nconst DEFAULT_LANG = 'vie+eng';\n\nexport async function runTesseractOcr(\n image: ImagePart,\n opts: TesseractOcrOptions = {},\n): Promise<TesseractOcrResult> {\n if (opts.signal?.aborted) {\n const err = new Error('Tesseract OCR aborted');\n err.name = 'AbortError';\n throw err;\n }\n\n const { createWorker } = await import('tesseract.js');\n const lang = opts.lang ?? DEFAULT_LANG;\n const worker = await createWorker(lang);\n\n try {\n const dataUrl = `data:${image.mediaType};base64,${image.base64}`;\n const { data } = await worker.recognize(dataUrl);\n return { text: data.text, confidence: data.confidence };\n } finally {\n await worker.terminate();\n }\n}\n","// src/stamps/geometry-2d/ai/vision/extractProblem.ts\n//\n// Orchestrator vision → text. Engine duy nhất: Tesseract.js client-side OCR,\n// offline, không cần network / LLM / API key. (Engine 'llm' cũ đã gỡ.)\n\nimport { runTesseractOcr } from './tesseract';\nimport type { ImagePart } from './types';\n\n// Ngưỡng: text ngắn hơn thì force confidence=low bất kể engine report gì.\nconst MIN_HIGH_CONFIDENCE_CHARS = 10;\nconst MAX_TEXT_CHARS = 2000;\n// Tesseract confidence (0-100): ≥ ngưỡng này coi như high.\nconst TESSERACT_HIGH_CONFIDENCE_THRESHOLD = 70;\n\nexport interface ExtractProblemOptions {\n /** Tesseract language. Default 'vie+eng' cho đề toán VN. */\n tesseractLang?: string;\n signal?: AbortSignal;\n}\n\nexport interface ExtractProblemSuccess {\n ok: true;\n text: string;\n confidence: 'high' | 'low';\n usage: { inputTokens: number; outputTokens: number };\n}\n\nexport interface ExtractProblemFailure {\n ok: false;\n reason: 'not-math' | 'unreadable' | 'empty' | 'unsupported';\n message: string;\n}\n\nexport type ExtractProblemOutcome = ExtractProblemSuccess | ExtractProblemFailure;\n\nexport async function extractProblemFromImage(\n image: ImagePart,\n opts: ExtractProblemOptions = {},\n): Promise<ExtractProblemOutcome> {\n let raw: { text: string; confidence: number };\n try {\n raw = await runTesseractOcr(image, {\n ...(opts.tesseractLang ? { lang: opts.tesseractLang } : {}),\n ...(opts.signal ? { signal: opts.signal } : {}),\n });\n } catch (e) {\n const err = e as { message?: string };\n return {\n ok: false,\n reason: 'unreadable',\n message: 'Tesseract OCR fail: ' + (err.message ?? '?'),\n };\n }\n\n const text = postProcess(raw.text);\n if (text.length === 0) {\n return { ok: false, reason: 'empty', message: 'Tesseract không trích được text.' };\n }\n\n const tooShort = text.length < MIN_HIGH_CONFIDENCE_CHARS;\n const lowConf = raw.confidence < TESSERACT_HIGH_CONFIDENCE_THRESHOLD;\n const confidence: 'high' | 'low' = tooShort || lowConf ? 'low' : 'high';\n\n return {\n ok: true,\n text,\n confidence,\n usage: { inputTokens: 0, outputTokens: 0 },\n };\n}\n\nfunction postProcess(raw: string): string {\n let t = raw.trim();\n t = t.replace(/\\*\\*(.+?)\\*\\*/g, '$1');\n t = t.replace(/\\*(.+?)\\*/g, '$1');\n t = t.replace(/_(.+?)_/g, '$1');\n t = t.replace(/```[\\s\\S]*?```/g, '').replace(/`([^`]+)`/g, '$1');\n t = t.replace(/\\s+/g, ' ').trim();\n t = t.normalize('NFC');\n if (t.length > MAX_TEXT_CHARS) t = t.slice(0, MAX_TEXT_CHARS);\n return t;\n}\n","// src/stamps/geometry-2d/ai/handleExtractProblem.ts\n//\n// Façade UI-friendly cho extractProblemFromImage. Map outcome → UI result kind.\n// Engine = Tesseract client-side (offline, không LLM).\n\nimport {\n extractProblemFromImage,\n type ExtractProblemOptions,\n} from './vision/extractProblem';\nimport type { ImagePart } from './vision/types';\n\nexport interface HandleExtractProblemOptions extends ExtractProblemOptions {}\n\nexport type ExtractUiResult =\n | {\n kind: 'success';\n text: string;\n usage: { inputTokens: number; outputTokens: number };\n }\n | {\n kind: 'low-confidence';\n text: string;\n warning: string;\n usage: { inputTokens: number; outputTokens: number };\n }\n | {\n kind: 'refused';\n reason: 'not-math';\n message: string;\n }\n | {\n kind: 'error';\n code: 'network' | 'unsupported' | 'unexpected' | 'empty';\n message: string;\n };\n\nexport async function handleExtractProblem(\n image: ImagePart,\n opts: HandleExtractProblemOptions = {},\n): Promise<ExtractUiResult> {\n try {\n const r = await extractProblemFromImage(image, opts);\n if (r.ok) {\n if (r.confidence === 'low') {\n return {\n kind: 'low-confidence',\n text: r.text,\n warning: 'OCR có thể không chính xác, kiểm tra trước khi vẽ.',\n usage: r.usage,\n };\n }\n return { kind: 'success', text: r.text, usage: r.usage };\n }\n if (r.reason === 'not-math') {\n return { kind: 'refused', reason: 'not-math', message: r.message };\n }\n if (r.reason === 'unsupported') {\n return { kind: 'error', code: 'unsupported', message: r.message };\n }\n if (r.reason === 'unreadable') {\n return { kind: 'error', code: 'network', message: r.message };\n }\n return { kind: 'error', code: 'empty', message: r.message };\n } catch (e) {\n return {\n kind: 'error',\n code: 'unexpected',\n message: e instanceof Error ? e.message : String(e),\n };\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stamps/geometry-3d/serialize.ts","../src/core/scene/render/types.ts","../src/core/scene/render/JxgRenderer3D.ts","../src/stamps/geometry-3d/editor/theme.ts","../src/stamps/geometry-3d/render.ts"],"names":["paletteFor"],"mappings":"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","file":"chunk-T3N4BSJV.mjs","sourcesContent":["// src/stamps/geometry-3d/serialize.ts\n//\n// Sau Tier D PR 3: customData.jsonState chỉ chứa `JSON.stringify(state)`.\n// View info (bbox3D/azimuth/elevation) nằm trong `state.meta.view` (View3D shape).\n//\n// Type guard `isGeometry3DCustomData` giữ ở đây để index.tsx + host import từ\n// 1 chỗ.\n\nimport { serializeScene, deserializeScene } from '../shared/serializeScene';\nimport type { State, View3D } from '../../core/scene';\nimport type { BaseStampCustomData } from '../shared/types';\n\nexport interface Geometry3DCustomData extends BaseStampCustomData {\n kind: 'geometry3d';\n version: 2;\n jsonState: string;\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 (\n d.kind === 'geometry3d' &&\n d.version === 2 &&\n typeof d.jsonState === 'string'\n );\n}\n\nexport function serializeBoard3D(state: State, view: View3D): string {\n const withView: State = {\n ...state,\n meta: { domain: '3d', version: state.meta.version, view },\n };\n return serializeScene(withView);\n}\n\nexport function deserializeBoard3D(raw: string): State {\n return deserializeScene('3d', raw);\n}\n","// src/core/scene/render/types.ts\nimport type { RenderCtx } from '../types';\n\nexport type Theme3D = {\n point: { size: number; color: string };\n line: { strokeWidth: number; color: string };\n plane: { fillOpacity: number; color: string };\n};\n\nexport const DEFAULT_THEME_3D: Theme3D = {\n point: { size: 4, color: '#1e40af' },\n line: { strokeWidth: 2, color: '#0f172a' },\n plane: { fillOpacity: 0.15, color: '#60a5fa' },\n};\n\nexport type RenderCtx3D = RenderCtx & {\n theme: Theme3D;\n};\n","// src/core/scene/render/JxgRenderer3D.ts\nimport type { Store } from '../store';\nimport type { State, SceneObject, RenderCtx } from '../types';\nimport { getKind } from '../registry';\nimport { DEFAULT_THEME_3D, type Theme3D } from './types';\n\nexport type JxgRenderer3DOptions = { theme?: Theme3D };\n\nexport class JxgRenderer3D {\n private view: unknown;\n private store: Store;\n private theme: Theme3D;\n private elements = new Map<string, unknown>();\n private unsubscribe: () => void;\n private disposed = false;\n\n constructor(store: Store, view: unknown, options: JxgRenderer3DOptions = {}) {\n this.store = store;\n this.view = view;\n this.theme = options.theme ?? DEFAULT_THEME_3D;\n // Subscribe first, then render current state.\n this.unsubscribe = store.subscribe((next, prev) => this.applyDiff(prev, next));\n // Render initial state (e.g. if LOAD ran before subscribe).\n this.applyDiff(undefined, store.getState());\n }\n\n private ctx(): RenderCtx {\n return {\n jxg: this.view,\n resolveRef: (id: string) => {\n const el = this.elements.get(id);\n if (el === undefined) {\n throw new Error(`[scene] resolveRef: chưa render id=\"${id}\"`);\n }\n return el;\n },\n defaults: {},\n };\n }\n\n private create(obj: SceneObject): void {\n try {\n const def = getKind(obj.kind);\n const el = def.render(obj, this.ctx());\n this.elements.set(obj.id, el);\n } catch (err) {\n console.warn(`[scene/render] không render được ${obj.kind} id=\"${obj.id}\":`, err);\n }\n }\n\n private remove(id: string): void {\n // Selection halo phải bị xoá TRƯỚC element gốc (halo tham chiếu parent).\n this.removeHalo(id);\n this.selectedIds.delete(id);\n const el = this.elements.get(id);\n if (el === undefined) return;\n try {\n this.removeFromView(el);\n } catch (err) {\n console.warn(`[scene/render] không remove được id=\"${id}\":`, err);\n }\n this.elements.delete(id);\n }\n\n private removeFromView(el: unknown): void {\n const view = this.view as { removeObject?: (e: unknown) => void };\n if (el && typeof el === 'object') {\n const asObj = el as Record<string, unknown>;\n // Composite shape: { faces: [] } for polyhedron/cylinder/cone.\n if (Array.isArray(asObj['faces'])) {\n for (const face of asObj['faces'] as unknown[]) {\n view.removeObject?.(face);\n }\n // Also remove hidden vertex points if present (_verts).\n if (Array.isArray(asObj['_verts'])) {\n for (const v of asObj['_verts'] as unknown[]) {\n view.removeObject?.(v);\n }\n }\n return;\n }\n }\n view.removeObject?.(el);\n }\n\n private applyDiff(prev: State | undefined, next: State): void {\n if (this.disposed) return;\n const prevObjs = prev?.objects ?? {};\n const nextObjs = next.objects;\n\n // Remove ids that disappeared (iterate in order of prev to respect dependencies).\n for (const id of Object.keys(prevObjs)) {\n if (!(id in nextObjs)) {\n this.remove(id);\n }\n }\n\n // Add or update in next.order (preserves dependency order).\n for (const id of next.order) {\n const cur = nextObjs[id] as SceneObject | undefined;\n if (!cur) continue;\n const old = prevObjs[id] as SceneObject | undefined;\n if (!old) {\n // New object.\n this.create(cur);\n continue;\n }\n if (Object.is(old, cur)) {\n // Unchanged (same Immer reference).\n continue;\n }\n // Changed: try update hook, otherwise remove + recreate.\n let def;\n try { def = getKind(cur.kind); } catch { continue; }\n const existing = this.elements.get(id);\n if (def.update && existing !== undefined) {\n try {\n def.update(cur, old, this.ctx(), existing);\n continue;\n } catch (err) {\n console.warn(`[scene/render] update fail, recreate id=\"${id}\":`, err);\n }\n }\n this.remove(id);\n this.create(cur);\n }\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.unsubscribe();\n this.disposed = true;\n for (const id of Array.from(this.elements.keys())) {\n this.remove(id);\n }\n }\n\n listElements(): Map<string, unknown> {\n return this.elements;\n }\n\n // Selection halo overlay (3D): multi-select, halo phía sau element gốc cho\n // các kind đơn giản (point3d, segment/line/ray/vector). Các composite shape\n // (polyhedron/cone/cylinder/plane) chưa hỗ trợ halo overlay — bỏ qua.\n private selectedIds: Set<string> = new Set();\n private haloMap: Map<string, unknown[]> = new Map();\n\n highlight(ids: string | string[] | null): void {\n if (this.disposed) return;\n const newIds = new Set<string>(\n ids == null ? [] : Array.isArray(ids) ? ids : [ids],\n );\n for (const id of this.selectedIds) {\n if (!newIds.has(id)) this.removeHalo(id);\n }\n for (const id of newIds) {\n if (!this.selectedIds.has(id) && this.elements.has(id)) this.addHalo(id);\n }\n this.selectedIds = newIds;\n try {\n (this.view as { update?: () => void }).update?.();\n } catch { /* ignore */ }\n }\n\n private removeHalo(id: string): void {\n const halos = this.haloMap.get(id);\n if (!halos) return;\n const view = this.view as { removeObject?: (e: unknown) => void };\n for (const h of halos) {\n try { view.removeObject?.(h); } catch { /* ignore */ }\n }\n this.haloMap.delete(id);\n }\n\n private addHalo(id: string): void {\n const el = this.elements.get(id) as\n | {\n elType?: string;\n getAttribute?: (k: string) => unknown;\n point1?: unknown;\n point2?: unknown;\n element2D?: { X?: () => number; Y?: () => number };\n }\n | undefined;\n if (!el) return;\n const view = this.view as {\n create?: (kind: string, parents: unknown[], attrs?: unknown) => unknown;\n };\n if (!view.create) return;\n\n const SEL_STROKE = '#475569';\n const SEL_FILL = '#cbd5e1';\n const haloBase = {\n strokeColor: SEL_STROKE,\n strokeOpacity: 0.55,\n fillColor: SEL_FILL,\n fillOpacity: 0.3,\n fixed: true,\n withLabel: false,\n name: '',\n highlight: false,\n needsRegularUpdate: true,\n };\n const halos: unknown[] = [];\n try {\n switch (el.elType) {\n case 'point3d': {\n // 3D point: tạo point3d ở cùng coords với size to + gray. Lấy coords\n // qua element2D.X/Y (JSXGraph 3D nội bộ chiếu xuống 2D plane), nhưng\n // an toàn nhất là reference element gốc và đọc Z() / coords() runtime.\n const elAny = el as unknown as {\n X?: () => number; Y?: () => number; Z?: () => number;\n getAttribute?: (k: string) => unknown;\n };\n const baseSize = (elAny.getAttribute?.('size') as number | undefined) ?? 4;\n if (typeof elAny.X === 'function' && typeof elAny.Y === 'function' && typeof elAny.Z === 'function') {\n const halo = view.create('point3d', [\n () => elAny.X?.() ?? 0,\n () => elAny.Y?.() ?? 0,\n () => elAny.Z?.() ?? 0,\n ], {\n ...haloBase,\n size: baseSize + 6,\n face: 'o',\n strokeWidth: 2,\n strokeOpacity: 0.75,\n fillOpacity: 0.25,\n });\n halos.push(halo);\n }\n break;\n }\n case 'line3d': {\n if (el.point1 && el.point2) {\n const halo = view.create('line3d', [el.point1, el.point2], {\n ...haloBase,\n strokeWidth: 9,\n straightFirst: (el.getAttribute?.('straightFirst') as boolean | undefined) ?? false,\n straightLast: (el.getAttribute?.('straightLast') as boolean | undefined) ?? false,\n });\n halos.push(halo);\n }\n break;\n }\n default:\n // Composite/plane/sphere/cone/cylinder/polygon3d/polyhedron3d:\n // halo overlay khó (composite faces, depth ordering) — bỏ qua.\n // Selection visible qua ObjectListPanel row highlight thay vì\n // halo trên canvas.\n break;\n }\n } catch (err) {\n console.warn('[scene/render/3d] halo create fail:', err);\n }\n if (halos.length) this.haloMap.set(id, halos);\n }\n}\n","import {\n paletteFor as palette2D,\n} from '../../geometry-2d/editor/theme';\nimport type { Theme2D } from '../../../core/scene/render/types2d';\n\nexport type Geom3DPalette = Theme2D & {\n view3dBg: string;\n axisX: string;\n axisY: string;\n axisZ: string;\n};\n\nexport function paletteFor(isDark: boolean): Geom3DPalette {\n const base = palette2D(isDark);\n return {\n ...base,\n view3dBg: isDark ? '#1a1a1a' : '#ffffff',\n axisX: '#d63b3b',\n axisY: '#2d8a2d',\n axisZ: '#2d6dd6',\n };\n}\n\nexport const DEFAULT_VIEW3D: {\n azimuth: number;\n elevation: number;\n bbox3D: [number, number, number, number, number, number];\n} = {\n azimuth: 0.7,\n elevation: 0.4,\n bbox3D: [-3, -3, -3, 3, 3, 3],\n};\n\nexport const VIEW3D_ATTRS = (isDark: boolean) => {\n const p = paletteFor(isDark);\n const axisLabel = (color: string) => ({\n strokeColor: color,\n fontSize: 14,\n offset: [10, 0] as [number, number],\n });\n return {\n az: { slider: { visible: false }, point2: { visible: false } },\n el: { slider: { visible: false } },\n projection: 'central' as const,\n // GeoGebra-style: axes pass through origin (0,0,0) instead of bbox border.\n axesPosition: 'center' as const,\n xAxis: {\n strokeColor: p.axisX,\n strokeWidth: 2,\n lastArrow: { type: 2, size: 8 },\n name: 'x',\n withLabel: true,\n label: axisLabel(p.axisX),\n },\n yAxis: {\n strokeColor: p.axisY,\n strokeWidth: 2,\n lastArrow: { type: 2, size: 8 },\n name: 'y',\n withLabel: true,\n label: axisLabel(p.axisY),\n },\n zAxis: {\n strokeColor: p.axisZ,\n strokeWidth: 2,\n lastArrow: { type: 2, size: 8 },\n name: 'z',\n withLabel: true,\n label: axisLabel(p.axisZ),\n },\n // GeoGebra-style: hide ALL bbox wall planes; the XY ground plane is drawn\n // explicitly at z=0 via the helper below (so it coincides with Ox/Oy).\n xPlaneRear: { visible: false, mesh3d: { visible: false } },\n yPlaneRear: { visible: false, mesh3d: { visible: false } },\n zPlaneRear: { visible: false, mesh3d: { visible: false } },\n };\n};\n\nexport const GROUND_PLANE_ATTRS = (isDark: boolean) => ({\n fillColor: isDark ? '#2a2a2a' : '#e6e6e6',\n fillOpacity: isDark ? 0.5 : 0.55,\n strokeColor: isDark ? '#3a3a3a' : '#cfcfcf',\n strokeOpacity: 0.7,\n strokeWidth: 1,\n fixed: true,\n highlight: false,\n withLabel: false,\n layer: 0,\n});\n\n/** XY ground plane extent (square around origin in user units). */\nexport const GROUND_PLANE_RANGE: [number, number] = [-3, 3];\n","\"use client\";\n\nimport { deserializeBoard3D } from './serialize';\nimport { createStore } from '../../core/scene';\nimport { DEFAULT_VIEW_3D, type View3D } from '../../core/scene/types';\nimport { JxgRenderer3D } from '../../core/scene/render/JxgRenderer3D';\nimport { GROUND_PLANE_ATTRS, GROUND_PLANE_RANGE, VIEW3D_ATTRS } from './editor/theme';\nimport { renderJsxgOffscreen } from '../shared/jxgOffscreenRender';\n\nexport interface RenderResult {\n svgString: string;\n width: number;\n height: number;\n}\n\nconst OUTPUT_WIDTH = 1024;\nconst OUTPUT_HEIGHT = 768;\nconst BBOX_2D: [number, number, number, number] = [-6, 6, 6, -6];\n\n \ntype JxgObj = any;\n\nexport async function renderGeometry3DSvgFromState(jsonState: string): Promise<RenderResult> {\n const state = deserializeBoard3D(jsonState);\n const view3DInfo: View3D = state.meta.domain === '3d' ? state.meta.view : DEFAULT_VIEW_3D;\n\n const { svgString } = await renderJsxgOffscreen({\n bbox: BBOX_2D,\n dims: { width: OUTPUT_WIDTH, height: OUTPUT_HEIGHT },\n axis: false,\n grid: false,\n keepAspectRatio: true,\n applyOptions: (JXG) => {\n JXG.Options.text.display = 'internal';\n },\n setup: (board) => {\n const baseAttrs = VIEW3D_ATTRS(false);\n const view: JxgObj = (board as { create: (k: string, p: unknown[], a: unknown) => JxgObj }).create(\n 'view3d',\n [\n [-5, -5],\n [10, 10],\n [\n [view3DInfo.bbox3D[0], view3DInfo.bbox3D[3]],\n [view3DInfo.bbox3D[1], view3DInfo.bbox3D[4]],\n [view3DInfo.bbox3D[2], view3DInfo.bbox3D[5]],\n ],\n ],\n {\n ...baseAttrs,\n az: { ...baseAttrs.az, slider: { ...baseAttrs.az.slider, start: view3DInfo.azimuth } },\n el: { ...baseAttrs.el, slider: { ...baseAttrs.el.slider, start: view3DInfo.elevation } },\n },\n );\n\n try {\n \n const v = view as any;\n v?.az_slide?.setValue?.(view3DInfo.azimuth);\n v?.el_slide?.setValue?.(view3DInfo.elevation);\n v?.board?.update?.();\n } catch {\n /* older JSXGraph may not expose az_slide on view3d */\n }\n\n try {\n (view as { create: (k: string, p: unknown[], a: unknown) => JxgObj }).create(\n 'plane3d',\n [\n [0, 0, 0],\n [1, 0, 0],\n [0, 1, 0],\n GROUND_PLANE_RANGE,\n GROUND_PLANE_RANGE,\n ],\n GROUND_PLANE_ATTRS(false),\n );\n } catch {\n /* swallow */\n }\n\n const store = createStore(state);\n const renderer = new JxgRenderer3D(store, view);\n\n try {\n \n (view as any)?.board?.update?.();\n } catch {\n /* swallow */\n }\n\n return renderer;\n },\n postProcessSvg: (clone) => {\n clone.setAttribute('width', String(OUTPUT_WIDTH));\n clone.setAttribute('height', String(OUTPUT_HEIGHT));\n },\n });\n\n return { svgString, width: OUTPUT_WIDTH, height: OUTPUT_HEIGHT };\n}\n"]}
|