@xom11/whiteboard 0.7.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +51 -1
  2. package/dist/chunk-74VEEZBV.mjs +619 -0
  3. package/dist/chunk-74VEEZBV.mjs.map +1 -0
  4. package/dist/chunk-DU2NFHRR.mjs +103 -0
  5. package/dist/chunk-DU2NFHRR.mjs.map +1 -0
  6. package/dist/{chunk-SHFOGORM.mjs → chunk-DU3RHKT5.mjs} +4 -4
  7. package/dist/{chunk-SHFOGORM.mjs.map → chunk-DU3RHKT5.mjs.map} +1 -1
  8. package/dist/{chunk-HYXFHEDJ.mjs → chunk-IUVV52HO.mjs} +22 -7
  9. package/dist/chunk-IUVV52HO.mjs.map +1 -0
  10. package/dist/{chunk-BJX4YNA5.mjs → chunk-KEYZ5EZT.mjs} +26 -9
  11. package/dist/chunk-KEYZ5EZT.mjs.map +1 -0
  12. package/dist/{chunk-LPM4MM45.mjs → chunk-SBDMF4NQ.mjs} +3 -2
  13. package/dist/chunk-SBDMF4NQ.mjs.map +1 -0
  14. package/dist/{chunk-3SSQKRRO.mjs → chunk-ZVN356JZ.mjs} +4 -4
  15. package/dist/{chunk-3SSQKRRO.mjs.map → chunk-ZVN356JZ.mjs.map} +1 -1
  16. package/dist/geometry-2d.js +250 -218
  17. package/dist/geometry-2d.js.map +1 -1
  18. package/dist/geometry-2d.mjs +2 -2
  19. package/dist/geometry-3d.d.mts +1 -1
  20. package/dist/geometry-3d.d.ts +1 -1
  21. package/dist/geometry-3d.js +3276 -1201
  22. package/dist/geometry-3d.js.map +1 -1
  23. package/dist/geometry-3d.mjs +3 -2
  24. package/dist/graph-2d.js +360 -66
  25. package/dist/graph-2d.js.map +1 -1
  26. package/dist/graph-2d.mjs +2 -2
  27. package/dist/{host-2QGKMGCT.mjs → host-LZH2FZ2N.mjs} +3 -3
  28. package/dist/{host-2QGKMGCT.mjs.map → host-LZH2FZ2N.mjs.map} +1 -1
  29. package/dist/host-PIIDSMVE.mjs +3187 -0
  30. package/dist/host-PIIDSMVE.mjs.map +1 -0
  31. package/dist/{host-T2W6R6SO.mjs → host-VDNAJMLC.mjs} +221 -216
  32. package/dist/host-VDNAJMLC.mjs.map +1 -0
  33. package/dist/index.d.mts +6 -5
  34. package/dist/index.d.ts +6 -5
  35. package/dist/index.js +4365 -1821
  36. package/dist/index.js.map +1 -1
  37. package/dist/index.mjs +246 -102
  38. package/dist/index.mjs.map +1 -1
  39. package/package.json +6 -6
  40. package/dist/chunk-BJX4YNA5.mjs.map +0 -1
  41. package/dist/chunk-DJTBZEAR.mjs +0 -25
  42. package/dist/chunk-DJTBZEAR.mjs.map +0 -1
  43. package/dist/chunk-HM7RIXJE.mjs +0 -331
  44. package/dist/chunk-HM7RIXJE.mjs.map +0 -1
  45. package/dist/chunk-HYXFHEDJ.mjs.map +0 -1
  46. package/dist/chunk-LPM4MM45.mjs.map +0 -1
  47. package/dist/host-T2W6R6SO.mjs.map +0 -1
  48. package/dist/host-XUFON6CQ.mjs +0 -1422
  49. package/dist/host-XUFON6CQ.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stamps/graph-2d/serialize.ts","../src/stamps/graph-2d/evaluator.ts","../src/stamps/graph-2d/parser.ts","../src/stamps/graph-2d/editor/handlers.ts","../src/stamps/graph-2d/renderObjects.ts","../src/stamps/graph-2d/render.ts","../src/stamps/graph-2d/types.ts"],"names":["ALLOWED_FUNCTIONS"],"mappings":"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","file":"chunk-74VEEZBV.mjs","sourcesContent":["export interface SerializedGraph {\n version: 1;\n view: {\n xMin: number;\n xMax: number;\n yMin: number;\n yMax: number;\n showAxis: boolean;\n showGrid: boolean;\n };\n functions: SerializedFunction[];\n parameters: SerializedParameter[];\n points: SerializedPoint[];\n intersections: SerializedIntersection[];\n tangents: SerializedTangent[];\n}\n\nexport interface SerializedFunction {\n id: string;\n name: string;\n expression: string;\n color: string;\n visible: boolean;\n domain?: { min: number; max: number };\n}\n\nexport interface SerializedParameter {\n name: string;\n value: number;\n min: number;\n max: number;\n step: number;\n}\n\nexport interface SerializedPoint {\n id: string;\n functionId: string;\n x: number;\n label?: string;\n}\n\nexport interface SerializedIntersection {\n id: string;\n functionIdA: string;\n functionIdB: string;\n}\n\nexport interface SerializedTangent {\n id: string;\n pointId: string;\n}\n\nexport const EMPTY_GRAPH: SerializedGraph = {\n version: 1,\n view: { xMin: -10, xMax: 10, yMin: -10, yMax: 10, showAxis: true, showGrid: true },\n functions: [],\n parameters: [],\n points: [],\n intersections: [],\n tangents: [],\n};\n\nexport function stringifySerializedGraph(graph: SerializedGraph): string {\n return JSON.stringify(graph);\n}\n\nexport function parseSerializedGraph(jsonState: string): SerializedGraph | null {\n let raw: unknown;\n try {\n raw = JSON.parse(jsonState);\n } catch {\n return null;\n }\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const r = raw as Record<string, unknown>;\n if (r.version !== 1) return null;\n if (!r.view || typeof r.view !== 'object') return null;\n const v = r.view as Record<string, unknown>;\n if (\n typeof v.xMin !== 'number' ||\n typeof v.xMax !== 'number' ||\n typeof v.yMin !== 'number' ||\n typeof v.yMax !== 'number' ||\n typeof v.showAxis !== 'boolean' ||\n typeof v.showGrid !== 'boolean'\n ) {\n return null;\n }\n for (const key of ['functions', 'parameters', 'points', 'intersections', 'tangents']) {\n if (!Array.isArray(r[key])) return null;\n }\n return raw as SerializedGraph;\n}\n","/**\n * AST-based math expression evaluator — an toàn hơn `new Function`.\n *\n * Hỗ trợ:\n * - Toán tử: + - * / ^ và unary minus, dấu ngoặc ( )\n * - Số: integer, float, scientific notation (1e3, 1.2e-4)\n * - Hằng: pi → Math.PI, e → Math.E\n * - Biến: x (mặc định) + tham số đơn ký tự (a..z trừ x, e)\n * - Hàm: sin cos tan asin acos atan log ln exp sqrt abs floor ceil round\n *\n * KHÔNG dùng `new Function`, `eval`, `Function()`, hay bất kỳ dynamic code-gen nào.\n */\n\nconst ALLOWED_FUNCTIONS = {\n sin: Math.sin,\n cos: Math.cos,\n tan: Math.tan,\n asin: Math.asin,\n acos: Math.acos,\n atan: Math.atan,\n log: Math.log10, // log = log10 (khớp với rewriteToJs)\n ln: Math.log, // ln = log tự nhiên\n exp: Math.exp,\n sqrt: Math.sqrt,\n abs: Math.abs,\n floor: Math.floor,\n ceil: Math.ceil,\n round: Math.round,\n} as const;\n\nconst ALLOWED_CONSTANTS: Record<string, number> = {\n pi: Math.PI,\n e: Math.E,\n};\n\n// ----- Tokenizer ----------------------------------------------------------\n\ntype TokenType = 'NUMBER' | 'IDENT' | 'OP' | 'LPAREN' | 'RPAREN' | 'COMMA';\n\ninterface Token {\n type: TokenType;\n value: string;\n pos: number;\n}\n\nconst OPERATORS = new Set(['+', '-', '*', '/', '^']);\n\nexport function tokenize(src: string): Token[] {\n const tokens: Token[] = [];\n let i = 0;\n while (i < src.length) {\n const ch = src[i];\n\n // whitespace\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') {\n i++;\n continue;\n }\n\n // number (int, float, scientific)\n if ((ch >= '0' && ch <= '9') || ch === '.') {\n let j = i;\n let hasDot = false;\n let hasExp = false;\n while (j < src.length) {\n const c = src[j];\n if (c >= '0' && c <= '9') {\n j++;\n } else if (c === '.' && !hasDot && !hasExp) {\n hasDot = true;\n j++;\n } else if ((c === 'e' || c === 'E') && !hasExp) {\n hasExp = true;\n j++;\n if (src[j] === '+' || src[j] === '-') j++;\n } else {\n break;\n }\n }\n const raw = src.slice(i, j);\n // raw must contain ít nhất 1 digit\n if (!/[0-9]/.test(raw)) {\n throw new Error(`Số không hợp lệ tại vị trí ${i}: \"${raw}\"`);\n }\n tokens.push({ type: 'NUMBER', value: raw, pos: i });\n i = j;\n continue;\n }\n\n // identifier (a-zA-Z, sau đó a-zA-Z0-9_)\n if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {\n let j = i;\n while (j < src.length) {\n const c = src[j];\n if (\n (c >= 'a' && c <= 'z') ||\n (c >= 'A' && c <= 'Z') ||\n (c >= '0' && c <= '9') ||\n c === '_'\n ) {\n j++;\n } else {\n break;\n }\n }\n tokens.push({ type: 'IDENT', value: src.slice(i, j), pos: i });\n i = j;\n continue;\n }\n\n if (OPERATORS.has(ch)) {\n tokens.push({ type: 'OP', value: ch, pos: i });\n i++;\n continue;\n }\n if (ch === '(') {\n tokens.push({ type: 'LPAREN', value: ch, pos: i });\n i++;\n continue;\n }\n if (ch === ')') {\n tokens.push({ type: 'RPAREN', value: ch, pos: i });\n i++;\n continue;\n }\n if (ch === ',') {\n tokens.push({ type: 'COMMA', value: ch, pos: i });\n i++;\n continue;\n }\n\n throw new Error(`Ký tự không hợp lệ tại vị trí ${i}: \"${ch}\"`);\n }\n return tokens;\n}\n\n// ----- AST ----------------------------------------------------------------\n\nexport type AstNode =\n | { kind: 'num'; value: number }\n | { kind: 'ident'; name: string }\n | { kind: 'unary'; op: '-' | '+'; arg: AstNode }\n | { kind: 'binary'; op: '+' | '-' | '*' | '/' | '^'; lhs: AstNode; rhs: AstNode }\n | { kind: 'call'; name: string; args: AstNode[] };\n\n// ----- Parser (Pratt-style precedence climbing) ---------------------------\n\nclass Parser {\n private pos = 0;\n constructor(private tokens: Token[]) {}\n\n private peek(): Token | undefined {\n return this.tokens[this.pos];\n }\n\n private consume(): Token {\n const t = this.tokens[this.pos++];\n if (!t) throw new Error('Cú pháp: hết token sớm');\n return t;\n }\n\n parseExpression(): AstNode {\n const node = this.parseAddSub();\n if (this.pos < this.tokens.length) {\n const t = this.tokens[this.pos];\n throw new Error(`Cú pháp: token thừa \"${t.value}\" tại vị trí ${t.pos}`);\n }\n return node;\n }\n\n // + - (left assoc)\n private parseAddSub(): AstNode {\n let lhs = this.parseMulDiv();\n while (true) {\n const t = this.peek();\n if (t && t.type === 'OP' && (t.value === '+' || t.value === '-')) {\n this.consume();\n const rhs = this.parseMulDiv();\n lhs = { kind: 'binary', op: t.value as '+' | '-', lhs, rhs };\n } else {\n break;\n }\n }\n return lhs;\n }\n\n // * / (left assoc)\n private parseMulDiv(): AstNode {\n let lhs = this.parseUnary();\n while (true) {\n const t = this.peek();\n if (t && t.type === 'OP' && (t.value === '*' || t.value === '/')) {\n this.consume();\n const rhs = this.parseUnary();\n lhs = { kind: 'binary', op: t.value as '*' | '/', lhs, rhs };\n } else {\n break;\n }\n }\n return lhs;\n }\n\n // unary + - (right assoc) sau đó parsePow\n private parseUnary(): AstNode {\n const t = this.peek();\n if (t && t.type === 'OP' && (t.value === '+' || t.value === '-')) {\n this.consume();\n const arg = this.parseUnary();\n return { kind: 'unary', op: t.value as '+' | '-', arg };\n }\n return this.parsePow();\n }\n\n // ^ (right assoc)\n private parsePow(): AstNode {\n const lhs = this.parsePrimary();\n const t = this.peek();\n if (t && t.type === 'OP' && t.value === '^') {\n this.consume();\n // right-assoc: parseUnary để chấp nhận `2^-x`\n const rhs = this.parseUnary();\n return { kind: 'binary', op: '^', lhs, rhs };\n }\n return lhs;\n }\n\n private parsePrimary(): AstNode {\n const t = this.peek();\n if (!t) throw new Error('Cú pháp: thiếu biểu thức');\n\n if (t.type === 'NUMBER') {\n this.consume();\n const v = Number(t.value);\n if (!Number.isFinite(v) && !Number.isNaN(v)) {\n // Vẫn cho phép Infinity literal hiếm khi xảy ra; nhưng safe-guard NaN\n }\n return { kind: 'num', value: v };\n }\n\n if (t.type === 'IDENT') {\n this.consume();\n const next = this.peek();\n if (next && next.type === 'LPAREN') {\n // Function call\n this.consume(); // (\n const args: AstNode[] = [];\n const lookahead = this.peek();\n if (!lookahead || lookahead.type !== 'RPAREN') {\n args.push(this.parseAddSub());\n while (true) {\n const nx = this.peek();\n if (nx && nx.type === 'COMMA') {\n this.consume();\n args.push(this.parseAddSub());\n } else {\n break;\n }\n }\n }\n const close = this.peek();\n if (!close || close.type !== 'RPAREN') {\n throw new Error(`Cú pháp: thiếu \")\" sau hàm \"${t.value}\"`);\n }\n this.consume(); // )\n return { kind: 'call', name: t.value, args };\n }\n return { kind: 'ident', name: t.value };\n }\n\n if (t.type === 'LPAREN') {\n this.consume();\n const inner = this.parseAddSub();\n const close = this.peek();\n if (!close || close.type !== 'RPAREN') {\n throw new Error('Cú pháp: thiếu \")\"');\n }\n this.consume();\n return inner;\n }\n\n throw new Error(`Cú pháp: token bất ngờ \"${t.value}\" tại vị trí ${t.pos}`);\n }\n}\n\nexport function parseAst(src: string): AstNode {\n const tokens = tokenize(src);\n if (tokens.length === 0) throw new Error('Biểu thức rỗng');\n const p = new Parser(tokens);\n return p.parseExpression();\n}\n\n// ----- Evaluator ----------------------------------------------------------\n\nexport interface EvalEnv {\n /** Giá trị của biến `x`. */\n x: number;\n /** Tham số đơn ký tự (a, b, c, ...) từ caller. */\n params: Record<string, number>;\n}\n\nexport function evaluate(node: AstNode, env: EvalEnv): number {\n switch (node.kind) {\n case 'num':\n return node.value;\n case 'ident': {\n const name = node.name;\n if (name === 'x') return env.x;\n if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) {\n return ALLOWED_CONSTANTS[name];\n }\n // tham số 1 ký tự\n if (name.length === 1 && Object.prototype.hasOwnProperty.call(env.params, name)) {\n return env.params[name];\n }\n throw new Error(`Identifier không hợp lệ: \"${name}\"`);\n }\n case 'unary': {\n const v = evaluate(node.arg, env);\n return node.op === '-' ? -v : +v;\n }\n case 'binary': {\n const a = evaluate(node.lhs, env);\n const b = evaluate(node.rhs, env);\n switch (node.op) {\n case '+':\n return a + b;\n case '-':\n return a - b;\n case '*':\n return a * b;\n case '/':\n return a / b; // có thể trả Infinity/NaN — đúng theo IEEE 754\n case '^':\n return Math.pow(a, b);\n }\n // exhaustive — TS không bắt được vì op đã typed\n throw new Error(`Toán tử không hỗ trợ: \"${(node as { op: string }).op}\"`);\n }\n case 'call': {\n const fn = (ALLOWED_FUNCTIONS as Record<string, (...args: number[]) => number>)[node.name];\n if (typeof fn !== 'function') {\n throw new Error(`Hàm không hợp lệ: \"${node.name}\"`);\n }\n const args = node.args.map((a) => evaluate(a, env));\n return fn(...args);\n }\n }\n}\n\n/**\n * Lấy danh sách identifier đơn ký tự (ngoài `x`, `pi`, `e`) — đây là các tham số tự do.\n * Không tính identifier xuất hiện dưới dạng tên hàm (vd `sin` trong `sin(x)`).\n */\nexport function collectFreeVars(node: AstNode, out: Set<string> = new Set()): Set<string> {\n switch (node.kind) {\n case 'num':\n return out;\n case 'ident': {\n const name = node.name;\n if (name === 'x') return out;\n if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) return out;\n if (name.length === 1) out.add(name);\n // identifier dài > 1 không phải hằng → caller validate sẽ catch\n return out;\n }\n case 'unary':\n return collectFreeVars(node.arg, out);\n case 'binary':\n collectFreeVars(node.lhs, out);\n collectFreeVars(node.rhs, out);\n return out;\n case 'call':\n for (const a of node.args) collectFreeVars(a, out);\n return out;\n }\n}\n\n/**\n * Kiểm tra mọi identifier xuất hiện đều thuộc whitelist (functions/constants/x/single-char-param).\n * Trả về null nếu OK, hoặc message lỗi.\n */\nexport function checkIdentifiers(node: AstNode): string | null {\n switch (node.kind) {\n case 'num':\n return null;\n case 'ident': {\n const name = node.name;\n if (name === 'x') return null;\n if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) return null;\n if (name.length === 1) return null;\n return `Tên không hợp lệ: \"${name}\"`;\n }\n case 'unary':\n return checkIdentifiers(node.arg);\n case 'binary':\n return checkIdentifiers(node.lhs) ?? checkIdentifiers(node.rhs);\n case 'call': {\n if (!Object.prototype.hasOwnProperty.call(ALLOWED_FUNCTIONS, node.name)) {\n return `Tên hàm không hợp lệ: \"${node.name}\"`;\n }\n for (const a of node.args) {\n const e = checkIdentifiers(a);\n if (e) return e;\n }\n return null;\n }\n }\n}\n\nexport const ALLOWED_FUNCTION_NAMES: ReadonlySet<string> = new Set(Object.keys(ALLOWED_FUNCTIONS));\nexport const ALLOWED_CONSTANT_NAMES: ReadonlySet<string> = new Set(Object.keys(ALLOWED_CONSTANTS));\n","import {\n ALLOWED_FUNCTION_NAMES,\n checkIdentifiers,\n collectFreeVars,\n evaluate,\n parseAst,\n tokenize,\n type AstNode,\n} from './evaluator';\n\nconst ALLOWED_FUNCTIONS = ALLOWED_FUNCTION_NAMES;\n\n// Giữ whitelist ký tự để fail-closed sớm — không cho ; = [ ] ' \" ` { } \\ v.v.\nconst ALLOWED_CHARS = /^[a-zA-Z0-9_.+\\-*/^()\\s,]+$/;\n\nconst SUGGESTIONS: Record<string, string> = {\n tg: 'tan',\n arcsin: 'asin',\n arccos: 'acos',\n arctan: 'atan',\n};\n\nexport interface ParseResult {\n ok: boolean;\n error?: string;\n freeVars: Set<string>;\n}\n\nfunction errResult(message: string): ParseResult {\n return { ok: false, error: message, freeVars: new Set() };\n}\n\nexport function validate(expr: string): ParseResult {\n const trimmed = expr.trim();\n if (!trimmed) return errResult('Biểu thức rỗng');\n if (!ALLOWED_CHARS.test(trimmed)) return errResult('Ký tự không hợp lệ');\n\n // Tokenize trước để lấy danh sách IDENT thật sự (loại bỏ exponent `e` của số như `1e3`).\n // Pre-check identifier để cho ra error message thân thiện (suggestion tg→tan, ...).\n let tokens;\n try {\n tokens = tokenize(trimmed);\n } catch {\n return errResult('Lỗi cú pháp');\n }\n const earlyFree = new Set<string>();\n for (const tok of tokens) {\n if (tok.type !== 'IDENT') continue;\n const id = tok.value;\n if (id === 'x' || id === 'pi' || id === 'e') continue;\n if (ALLOWED_FUNCTIONS.has(id)) continue;\n if (id.length === 1) {\n earlyFree.add(id);\n continue;\n }\n const hint = SUGGESTIONS[id];\n return errResult(\n hint\n ? `Tên hàm không hợp lệ: \"${id}\". Bạn có ý là \"${hint}\" không?`\n : `Tên không hợp lệ: \"${id}\"`,\n );\n }\n\n let ast: AstNode;\n try {\n ast = parseAst(trimmed);\n } catch {\n return errResult('Lỗi cú pháp');\n }\n\n const idErr = checkIdentifiers(ast);\n if (idErr) return errResult(idErr);\n\n const freeVars = collectFreeVars(ast);\n // Hợp nhất với earlyFree (regex pass) — thường giống nhau, nhưng đảm bảo invariant.\n for (const v of earlyFree) freeVars.add(v);\n return { ok: true, freeVars };\n}\n\n// FUNCTION_REPLACEMENTS giữ longest-first để rewriteToJs không nhầm asin→a-sin.\nconst FUNCTION_REPLACEMENTS: Array<[string, string]> = [\n ['asin', 'Math.asin'],\n ['acos', 'Math.acos'],\n ['atan', 'Math.atan'],\n ['sqrt', 'Math.sqrt'],\n ['floor', 'Math.floor'],\n ['round', 'Math.round'],\n ['ceil', 'Math.ceil'],\n ['sin', 'Math.sin'],\n ['cos', 'Math.cos'],\n ['tan', 'Math.tan'],\n ['abs', 'Math.abs'],\n ['exp', 'Math.exp'],\n ['log', 'Math.log10'],\n ['ln', 'Math.log'],\n];\n\n/**\n * Chuyển expression user-input sang JS string tương đương.\n *\n * Function này chỉ dùng cho debug / hiển thị / tương thích ngược.\n * Runtime KHÔNG còn `eval` chuỗi này — `compile()` dùng AST evaluator.\n */\nexport function rewriteToJs(\n expr: string,\n params: Record<string, number>,\n): string {\n let s = expr.replace(/\\^/g, '**');\n s = s.replace(/\\bpi\\b/g, 'Math.PI');\n s = s.replace(/\\be\\b/g, 'Math.E');\n for (const [from, to] of FUNCTION_REPLACEMENTS) {\n s = s.replace(new RegExp(`\\\\b${from}\\\\b`, 'g'), to);\n }\n for (const [name, value] of Object.entries(params)) {\n if (name.length !== 1) continue;\n s = s.replace(new RegExp(`\\\\b${name}\\\\b`, 'g'), `(${value})`);\n }\n return s;\n}\n\nexport function compile(\n expr: string,\n paramValues: Record<string, number>,\n): ((x: number) => number) | { error: string } {\n const v = validate(expr);\n if (!v.ok) return { error: v.error ?? 'Invalid' };\n let ast: AstNode;\n try {\n ast = parseAst(expr.trim());\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) };\n }\n return (x: number) => {\n try {\n const y = evaluate(ast, { x, params: paramValues });\n return typeof y === 'number' ? y : NaN;\n } catch {\n return NaN;\n }\n };\n}\n","import type {\n SerializedGraph,\n SerializedPoint,\n SerializedIntersection,\n SerializedTangent,\n} from '../serialize';\nimport { compile } from '../parser';\n\nexport interface ClickContext {\n x: number;\n y: number;\n functionId?: string;\n}\n\nexport function addPointOnCurve(\n graph: SerializedGraph,\n ctx: ClickContext,\n idFactory: () => string,\n): SerializedGraph {\n if (!ctx.functionId) return graph;\n const point: SerializedPoint = {\n id: idFactory(),\n functionId: ctx.functionId,\n x: ctx.x,\n };\n return { ...graph, points: [...graph.points, point] };\n}\n\nexport function addIntersection(\n graph: SerializedGraph,\n functionIdA: string,\n functionIdB: string,\n idFactory: () => string,\n): SerializedGraph {\n if (functionIdA === functionIdB) return graph;\n const exists = graph.intersections.some(\n (i) =>\n (i.functionIdA === functionIdA && i.functionIdB === functionIdB) ||\n (i.functionIdA === functionIdB && i.functionIdB === functionIdA),\n );\n if (exists) return graph;\n const intersection: SerializedIntersection = {\n id: idFactory(),\n functionIdA,\n functionIdB,\n };\n return { ...graph, intersections: [...graph.intersections, intersection] };\n}\n\nexport function addTangent(\n graph: SerializedGraph,\n pointId: string,\n idFactory: () => string,\n): SerializedGraph {\n const exists = graph.tangents.some((t) => t.pointId === pointId);\n if (exists) return graph;\n const tangent: SerializedTangent = { id: idFactory(), pointId };\n return { ...graph, tangents: [...graph.tangents, tangent] };\n}\n\n/**\n * Numerical derivative via centered difference. Dùng cho tangent tool.\n */\nexport function numericalDerivative(\n expression: string,\n paramValues: Record<string, number>,\n x: number,\n h = 1e-4,\n): number {\n const fn = compile(expression, paramValues);\n if (typeof fn !== 'function') return NaN;\n const y1 = fn(x - h);\n const y2 = fn(x + h);\n return (y2 - y1) / (2 * h);\n}\n","import type { SerializedGraph } from './serialize';\nimport { compile } from './parser';\nimport { numericalDerivative } from './editor/handlers';\n\n/**\n * Render tất cả objects (functions, points, intersections, tangents) lên board JSXGraph.\n *\n * Pure function — không giữ state, dùng cho one-shot render (render.ts).\n * MiniBoard giữ syncObjects riêng để diff/track curves theo id.\n */\nexport function renderGraphObjects(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n board: any,\n graph: SerializedGraph,\n): void {\n const paramMap: Record<string, number> = {};\n for (const p of graph.parameters) paramMap[p.name] = p.value;\n\n // Functions\n for (const f of graph.functions) {\n if (!f.visible) continue;\n const compiled = compile(f.expression, paramMap);\n if (typeof compiled !== 'function') continue;\n const domain = f.domain ?? { min: graph.view.xMin, max: graph.view.xMax };\n board.create('functiongraph', [compiled, domain.min, domain.max], {\n strokeColor: f.color,\n strokeWidth: 2,\n name: f.name,\n withLabel: false,\n highlight: false,\n });\n }\n\n // Points\n for (const point of graph.points) {\n const fn = graph.functions.find((f) => f.id === point.functionId);\n if (!fn || !fn.visible) continue;\n const compiled = compile(fn.expression, paramMap);\n if (typeof compiled !== 'function') continue;\n const y = compiled(point.x);\n board.create('point', [point.x, y], {\n name: point.label ?? '',\n size: 3,\n fillColor: fn.color,\n strokeColor: fn.color,\n withLabel: !!point.label,\n });\n }\n\n // Intersections\n for (const inter of graph.intersections) {\n const fa = graph.functions.find((f) => f.id === inter.functionIdA);\n const fb = graph.functions.find((f) => f.id === inter.functionIdB);\n if (!fa || !fb || !fa.visible || !fb.visible) continue;\n const cfa = compile(fa.expression, paramMap);\n const cfb = compile(fb.expression, paramMap);\n if (typeof cfa !== 'function' || typeof cfb !== 'function') continue;\n const roots = scanRoots((x: number) => cfa(x) - cfb(x), graph.view.xMin, graph.view.xMax);\n for (const x of roots) {\n board.create('point', [x, cfa(x)], {\n size: 3,\n fillColor: '#000',\n strokeColor: '#000',\n });\n }\n }\n\n // Tangents\n for (const tan of graph.tangents) {\n const pt = graph.points.find((p) => p.id === tan.pointId);\n if (!pt) continue;\n const fn = graph.functions.find((f) => f.id === pt.functionId);\n if (!fn || !fn.visible) continue;\n const slope = numericalDerivative(fn.expression, paramMap, pt.x);\n const cfn = compile(fn.expression, paramMap);\n if (typeof cfn !== 'function' || !Number.isFinite(slope)) continue;\n const y0 = cfn(pt.x);\n const x1 = graph.view.xMin;\n const x2 = graph.view.xMax;\n board.create(\n 'line',\n [\n [x1, slope * (x1 - pt.x) + y0],\n [x2, slope * (x2 - pt.x) + y0],\n ],\n {\n strokeColor: fn.color,\n strokeWidth: 1,\n dash: 2,\n straightFirst: false,\n straightLast: false,\n },\n );\n }\n}\n\nfunction scanRoots(\n fn: (x: number) => number,\n xMin: number,\n xMax: number,\n samples = 200,\n): number[] {\n const roots: number[] = [];\n const step = (xMax - xMin) / samples;\n let prevX = xMin;\n let prevY = fn(prevX);\n for (let i = 1; i <= samples; i++) {\n const x = xMin + i * step;\n const y = fn(x);\n if (Number.isFinite(prevY) && Number.isFinite(y) && prevY * y < 0) {\n let a = prevX;\n let b = x;\n let ya = prevY;\n for (let j = 0; j < 30; j++) {\n const m = (a + b) / 2;\n const ym = fn(m);\n if (Math.abs(ym) < 1e-6) {\n a = b = m;\n break;\n }\n if (ya * ym < 0) {\n b = m;\n } else {\n a = m;\n ya = ym;\n }\n }\n roots.push((a + b) / 2);\n }\n prevX = x;\n prevY = y;\n }\n return roots;\n}\n","import { parseSerializedGraph } from './serialize';\nimport { renderGraphObjects } from './renderObjects';\n\n/**\n * Re-render SVG cho graph-2d stamp từ jsonState đã serialize.\n *\n * Dùng cho:\n * 1. Insert vào whiteboard (lúc user nhấn Chèn).\n * 2. Restore stamp file sau khi reload (Excalidraw không persist binary).\n *\n * Pattern mirror `geometry-2d/render.ts`:\n * - LUÔN dùng light palette. Excalidraw apply CSS invert filter trong dark mode.\n * - Đặt JXG.Options.text.display='internal' để label render dưới dạng SVG, không HTML overlay.\n * - Offscreen div 600×400 cố định; cleanup sau khi clone SVG outerHTML.\n */\nexport async function renderGraph2dSvgFromState(jsonState: string): Promise<string> {\n const parsed = parseSerializedGraph(jsonState);\n if (!parsed) throw new Error('renderGraph2dSvgFromState: jsonState corrupt');\n\n const JXG = (await import('jsxgraph')).default;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const opts = (JXG as any).Options;\n if (opts) {\n opts.text = opts.text || {};\n opts.text.display = 'internal';\n opts.text.useASCIIMathML = false;\n opts.text.useMathJax = false;\n opts.text.useKatex = false;\n opts.label = opts.label || {};\n opts.label.display = 'internal';\n }\n\n const container = document.createElement('div');\n container.id = `jxg_graph2d_off_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n container.style.cssText =\n 'position:absolute;top:-99999px;left:-99999px;width:600px;height:400px;visibility:hidden;pointer-events:none;';\n document.body.appendChild(container);\n\n let board: unknown = null;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n board = (JXG as any).JSXGraph.initBoard(container.id, {\n boundingbox: [parsed.view.xMin, parsed.view.yMax, parsed.view.xMax, parsed.view.yMin],\n axis: parsed.view.showAxis,\n grid: parsed.view.showGrid,\n showCopyright: false,\n showNavigation: false,\n keepAspectRatio: false,\n });\n renderGraphObjects(board, parsed);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (board as any).update();\n const svgEl = container.querySelector('svg');\n if (!svgEl) throw new Error('renderGraph2dSvgFromState: no svg generated');\n return svgEl.outerHTML;\n } finally {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (board) (JXG as any).JSXGraph.freeBoard(board);\n } catch {\n /* ignore */\n }\n if (container.parentNode) container.parentNode.removeChild(container);\n }\n}\n","import type { BaseStampCustomData } from '../shared/types';\n\nexport interface Graph2DCustomData extends BaseStampCustomData {\n kind: 'graph2d';\n version: 1;\n jsonState: string;\n svgWidth: number;\n svgHeight: number;\n}\n\nexport function isGraph2DCustomData(data: unknown): data is Graph2DCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<Graph2DCustomData>;\n return d.kind === 'graph2d' && d.version === 1 && typeof d.jsonState === 'string';\n}\n"]}
@@ -0,0 +1,103 @@
1
+ "use client";
2
+ import { paletteFor } from './chunk-HTBLO5JO.mjs';
3
+
4
+ // src/stamps/geometry-3d/serialize.ts
5
+ function isGeometry3DCustomData(data) {
6
+ if (!data || typeof data !== "object") return false;
7
+ const d = data;
8
+ return d.kind === "geometry3d" && (d.version === 1 || d.version === 2) && typeof d.jsonState === "string";
9
+ }
10
+ function serializeBoard3D(state) {
11
+ return JSON.stringify(state);
12
+ }
13
+ function parseSerializedBoard3D(json) {
14
+ const parsed = JSON.parse(json);
15
+ if (!parsed || typeof parsed !== "object") {
16
+ throw new Error("parseSerializedBoard3D: not an object");
17
+ }
18
+ const p = parsed;
19
+ if (p.version !== 1 && p.version !== 2) {
20
+ throw new Error(`parseSerializedBoard3D: unsupported version ${String(p.version)}`);
21
+ }
22
+ if (!Array.isArray(p.elements)) {
23
+ throw new Error("parseSerializedBoard3D: elements missing");
24
+ }
25
+ return parsed;
26
+ }
27
+
28
+ // src/stamps/geometry-3d/editor/theme.ts
29
+ function paletteFor2(isDark) {
30
+ const base = paletteFor(isDark);
31
+ return {
32
+ ...base,
33
+ view3dBg: isDark ? "#1a1a1a" : "#ffffff",
34
+ axisX: "#d63b3b",
35
+ axisY: "#2d8a2d",
36
+ axisZ: "#2d6dd6"
37
+ };
38
+ }
39
+ var DEFAULT_VIEW3D = {
40
+ azimuth: 0.7,
41
+ elevation: 0.4,
42
+ bbox3D: [-3, -3, -3, 3, 3, 3]
43
+ };
44
+ var VIEW3D_ATTRS = (isDark) => {
45
+ const p = paletteFor2(isDark);
46
+ const axisLabel = (color) => ({
47
+ strokeColor: color,
48
+ fontSize: 14,
49
+ offset: [10, 0]
50
+ });
51
+ return {
52
+ az: { slider: { visible: false }, point2: { visible: false } },
53
+ el: { slider: { visible: false } },
54
+ projection: "central",
55
+ // GeoGebra-style: axes pass through origin (0,0,0) instead of bbox border.
56
+ axesPosition: "center",
57
+ xAxis: {
58
+ strokeColor: p.axisX,
59
+ strokeWidth: 2,
60
+ lastArrow: { type: 2, size: 8 },
61
+ name: "x",
62
+ withLabel: true,
63
+ label: axisLabel(p.axisX)
64
+ },
65
+ yAxis: {
66
+ strokeColor: p.axisY,
67
+ strokeWidth: 2,
68
+ lastArrow: { type: 2, size: 8 },
69
+ name: "y",
70
+ withLabel: true,
71
+ label: axisLabel(p.axisY)
72
+ },
73
+ zAxis: {
74
+ strokeColor: p.axisZ,
75
+ strokeWidth: 2,
76
+ lastArrow: { type: 2, size: 8 },
77
+ name: "z",
78
+ withLabel: true,
79
+ label: axisLabel(p.axisZ)
80
+ },
81
+ // GeoGebra-style: hide ALL bbox wall planes; the XY ground plane is drawn
82
+ // explicitly at z=0 via the helper below (so it coincides with Ox/Oy).
83
+ xPlaneRear: { visible: false, mesh3d: { visible: false } },
84
+ yPlaneRear: { visible: false, mesh3d: { visible: false } },
85
+ zPlaneRear: { visible: false, mesh3d: { visible: false } }
86
+ };
87
+ };
88
+ var GROUND_PLANE_ATTRS = (isDark) => ({
89
+ fillColor: isDark ? "#2a2a2a" : "#e6e6e6",
90
+ fillOpacity: isDark ? 0.5 : 0.55,
91
+ strokeColor: isDark ? "#3a3a3a" : "#cfcfcf",
92
+ strokeOpacity: 0.7,
93
+ strokeWidth: 1,
94
+ fixed: true,
95
+ highlight: false,
96
+ withLabel: false,
97
+ layer: 0
98
+ });
99
+ var GROUND_PLANE_RANGE = [-3, 3];
100
+
101
+ export { DEFAULT_VIEW3D, GROUND_PLANE_ATTRS, GROUND_PLANE_RANGE, VIEW3D_ATTRS, isGeometry3DCustomData, paletteFor2 as paletteFor, parseSerializedBoard3D, serializeBoard3D };
102
+ //# sourceMappingURL=chunk-DU2NFHRR.mjs.map
103
+ //# sourceMappingURL=chunk-DU2NFHRR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stamps/geometry-3d/serialize.ts","../src/stamps/geometry-3d/editor/theme.ts"],"names":["paletteFor"],"mappings":";;;AAWO,SAAS,uBAAuB,IAAA,EAA6C;AAClF,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,KAAA;AAC9C,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,OACE,CAAA,CAAE,IAAA,KAAS,YAAA,KACV,CAAA,CAAE,OAAA,KAAY,CAAA,IAAK,CAAA,CAAE,OAAA,KAAY,CAAA,CAAA,IAClC,OAAO,CAAA,CAAE,SAAA,KAAc,QAAA;AAE3B;AAsCO,SAAS,iBAAiB,KAAA,EAAkC;AACjE,EAAA,OAAO,IAAA,CAAK,UAAU,KAAK,CAAA;AAC7B;AAEO,SAAS,uBAAuB,IAAA,EAAiC;AACtE,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC9B,EAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,EAAU;AACzC,IAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,EACzD;AACA,EAAA,MAAM,CAAA,GAAI,MAAA;AACV,EAAA,IAAI,CAAA,CAAE,OAAA,KAAY,CAAA,IAAK,CAAA,CAAE,YAAY,CAAA,EAAG;AACtC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4CAAA,EAA+C,OAAO,CAAA,CAAE,OAAO,CAAC,CAAA,CAAE,CAAA;AAAA,EACpF;AACA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,QAAQ,CAAA,EAAG;AAC9B,IAAA,MAAM,IAAI,MAAM,0CAA0C,CAAA;AAAA,EAC5D;AACA,EAAA,OAAO,MAAA;AACT;;;AC9DO,SAASA,YAAW,MAAA,EAAgC;AACzD,EAAA,MAAM,IAAA,GAAO,WAAU,MAAM,CAAA;AAC7B,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,QAAA,EAAU,SAAS,SAAA,GAAY,SAAA;AAAA,IAC/B,KAAA,EAAO,SAAA;AAAA,IACP,KAAA,EAAO,SAAA;AAAA,IACP,KAAA,EAAO;AAAA,GACT;AACF;AAEO,IAAM,cAAA,GAIT;AAAA,EACF,OAAA,EAAS,GAAA;AAAA,EACT,SAAA,EAAW,GAAA;AAAA,EACX,QAAQ,CAAC,EAAA,EAAI,IAAI,EAAA,EAAI,CAAA,EAAG,GAAG,CAAC;AAC9B;AAEO,IAAM,YAAA,GAAe,CAAC,MAAA,KAAoB;AAC/C,EAAA,MAAM,CAAA,GAAIA,YAAW,MAAM,CAAA;AAC3B,EAAA,MAAM,SAAA,GAAY,CAAC,KAAA,MAAmB;AAAA,IACpC,WAAA,EAAa,KAAA;AAAA,IACb,QAAA,EAAU,EAAA;AAAA,IACV,MAAA,EAAQ,CAAC,EAAA,EAAI,CAAC;AAAA,GAChB,CAAA;AACA,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,EAAE,MAAA,EAAQ,EAAE,OAAA,EAAS,KAAA,EAAM,EAAG,MAAA,EAAQ,EAAE,OAAA,EAAS,KAAA,EAAM,EAAE;AAAA,IAC7D,IAAI,EAAE,MAAA,EAAQ,EAAE,OAAA,EAAS,OAAM,EAAE;AAAA,IACjC,UAAA,EAAY,SAAA;AAAA;AAAA,IAEZ,YAAA,EAAc,QAAA;AAAA,IACd,KAAA,EAAO;AAAA,MACL,aAAa,CAAA,CAAE,KAAA;AAAA,MACf,WAAA,EAAa,CAAA;AAAA,MACb,SAAA,EAAW,EAAE,IAAA,EAAM,CAAA,EAAG,MAAM,CAAA,EAAE;AAAA,MAC9B,IAAA,EAAM,GAAA;AAAA,MACN,SAAA,EAAW,IAAA;AAAA,MACX,KAAA,EAAO,SAAA,CAAU,CAAA,CAAE,KAAK;AAAA,KAC1B;AAAA,IACA,KAAA,EAAO;AAAA,MACL,aAAa,CAAA,CAAE,KAAA;AAAA,MACf,WAAA,EAAa,CAAA;AAAA,MACb,SAAA,EAAW,EAAE,IAAA,EAAM,CAAA,EAAG,MAAM,CAAA,EAAE;AAAA,MAC9B,IAAA,EAAM,GAAA;AAAA,MACN,SAAA,EAAW,IAAA;AAAA,MACX,KAAA,EAAO,SAAA,CAAU,CAAA,CAAE,KAAK;AAAA,KAC1B;AAAA,IACA,KAAA,EAAO;AAAA,MACL,aAAa,CAAA,CAAE,KAAA;AAAA,MACf,WAAA,EAAa,CAAA;AAAA,MACb,SAAA,EAAW,EAAE,IAAA,EAAM,CAAA,EAAG,MAAM,CAAA,EAAE;AAAA,MAC9B,IAAA,EAAM,GAAA;AAAA,MACN,SAAA,EAAW,IAAA;AAAA,MACX,KAAA,EAAO,SAAA,CAAU,CAAA,CAAE,KAAK;AAAA,KAC1B;AAAA;AAAA;AAAA,IAGA,UAAA,EAAY,EAAE,OAAA,EAAS,KAAA,EAAO,QAAQ,EAAE,OAAA,EAAS,OAAM,EAAE;AAAA,IACzD,UAAA,EAAY,EAAE,OAAA,EAAS,KAAA,EAAO,QAAQ,EAAE,OAAA,EAAS,OAAM,EAAE;AAAA,IACzD,UAAA,EAAY,EAAE,OAAA,EAAS,KAAA,EAAO,QAAQ,EAAE,OAAA,EAAS,OAAM;AAAE,GAC3D;AACF;AAEO,IAAM,kBAAA,GAAqB,CAAC,MAAA,MAAqB;AAAA,EACtD,SAAA,EAAW,SAAS,SAAA,GAAY,SAAA;AAAA,EAChC,WAAA,EAAa,SAAS,GAAA,GAAM,IAAA;AAAA,EAC5B,WAAA,EAAa,SAAS,SAAA,GAAY,SAAA;AAAA,EAClC,aAAA,EAAe,GAAA;AAAA,EACf,WAAA,EAAa,CAAA;AAAA,EACb,KAAA,EAAO,IAAA;AAAA,EACP,SAAA,EAAW,KAAA;AAAA,EACX,SAAA,EAAW,KAAA;AAAA,EACX,KAAA,EAAO;AACT,CAAA;AAGO,IAAM,kBAAA,GAAuC,CAAC,EAAA,EAAI,CAAC","file":"chunk-DU2NFHRR.mjs","sourcesContent":["import type { BaseStampCustomData } from '../shared/types';\nimport type { Constraint } from './editor/scene/types';\n\nexport interface Geometry3DCustomData extends BaseStampCustomData {\n kind: 'geometry3d';\n version: 1 | 2;\n jsonState: string;\n svgWidth: number;\n svgHeight: number;\n}\n\nexport function isGeometry3DCustomData(data: unknown): data is Geometry3DCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<Geometry3DCustomData>;\n return (\n d.kind === 'geometry3d' &&\n (d.version === 1 || d.version === 2) &&\n typeof d.jsonState === 'string'\n );\n}\n\nexport type Element3DType =\n | 'point3d'\n | 'line3d'\n | 'plane3d'\n | 'polygon3d'\n | 'sphere3d'\n | 'text3d';\n\nexport interface SerializedElement3D {\n type: Element3DType;\n /**\n * Parents passed to JSXGraph view.create. Either literal values (numbers,\n * strings) or `\"@id:<id>\"` placeholder strings referencing earlier created\n * objects in the log (resolved at deserialize time).\n */\n parents: unknown[];\n attributes: Record<string, unknown>;\n id: string;\n label?: string;\n /** v2 only — present on point3d elements to encode the surface constraint. */\n constraint?: Constraint;\n}\n\nexport interface SerializedBoard3D {\n version: 1 | 2;\n bbox: [number, number, number, number];\n view: {\n azimuth: number;\n elevation: number;\n bbox3D: [number, number, number, number, number, number];\n };\n showAxes: boolean;\n showMesh: boolean;\n elements: SerializedElement3D[];\n}\n\nexport function serializeBoard3D(state: SerializedBoard3D): string {\n return JSON.stringify(state);\n}\n\nexport function parseSerializedBoard3D(json: string): SerializedBoard3D {\n const parsed = JSON.parse(json) as unknown;\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('parseSerializedBoard3D: not an object');\n }\n const p = parsed as Partial<SerializedBoard3D>;\n if (p.version !== 1 && p.version !== 2) {\n throw new Error(`parseSerializedBoard3D: unsupported version ${String(p.version)}`);\n }\n if (!Array.isArray(p.elements)) {\n throw new Error('parseSerializedBoard3D: elements missing');\n }\n return parsed as SerializedBoard3D;\n}\n","import {\n paletteFor as palette2D,\n type GeomPalette,\n} from '../../geometry-2d/editor/theme';\n\nexport type Geom3DPalette = GeomPalette & {\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"]}
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { isGeometryCustomData, renderGeometrySvgFromState } from './chunk-BJX4YNA5.mjs';
2
+ import { isGeometryCustomData, renderGeometrySvgFromState } from './chunk-KEYZ5EZT.mjs';
3
3
  import { lazy } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
6
6
  var GeometryStampHost = lazy(
7
- () => import('./host-T2W6R6SO.mjs').then((m) => ({ default: m.GeometryStampHost }))
7
+ () => import('./host-VDNAJMLC.mjs').then((m) => ({ default: m.GeometryStampHost }))
8
8
  );
9
9
  var GeometryIcon = /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
10
10
  /* @__PURE__ */ jsx("polygon", { points: "4,20 20,20 12,5" }),
@@ -40,5 +40,5 @@ var geometryStamp = {
40
40
  };
41
41
 
42
42
  export { geometryStamp };
43
- //# sourceMappingURL=chunk-SHFOGORM.mjs.map
44
- //# sourceMappingURL=chunk-SHFOGORM.mjs.map
43
+ //# sourceMappingURL=chunk-DU3RHKT5.mjs.map
44
+ //# sourceMappingURL=chunk-DU3RHKT5.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stamps/geometry-2d/index.tsx"],"names":[],"mappings":";;;;AAgBA,IAAM,iBAAA,GAAoB,IAAA;AAAA,EAAK,MAC7B,OAAO,qBAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,iBAAA,EAAkB,CAAE;AACjE,CAAA;AAEA,IAAM,YAAA,wBACH,KAAA,EAAA,EAAI,KAAA,EAAM,MAAK,MAAA,EAAO,IAAA,EAAK,SAAQ,WAAA,EAAY,IAAA,EAAK,QAAO,MAAA,EAAO,cAAA,EAAe,aAAY,KAAA,EAAM,aAAA,EAAc,SAAQ,cAAA,EAAe,OAAA,EAAQ,eAAY,MAAA,EAC3J,QAAA,EAAA;AAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAQ,QAAO,iBAAA,EAAkB,CAAA;AAAA,kBAClC,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,IAAA,EAAK,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO,CAAA;AAAA,kBACjE,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO,CAAA;AAAA,kBAClE,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,GAAA,EAAI,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO;AAAA,CAAA,EACnE,CAAA;AAGK,IAAM,aAAA,GAA2B;AAAA,EACtC,IAAA,EAAM,UAAA;AAAA,EACN,WAAA,EAAa,GAAA;AAAA,EACb,YAAA,EAAc,GAAA;AAAA,EACd,YAAA,EAAc,8BAAA;AAAA,EACd,WAAA,EAAa,YAAA;AAAA,EACb,aAAA,EAAe,wBAAA;AAAA,EACf,iBAAA,EAAmB,oBAAA;AAAA,EACnB,MAAM,wBAAwB,IAAA,EAAM;AAClC,IAAA,IAAI,CAAC,oBAAA,CAAqB,IAAI,CAAA,EAAG;AAC/B,MAAA,MAAM,IAAI,MAAM,+EAAuE,CAAA;AAAA,IACzF;AACA,IAAA,OAAO,0BAAA,CAA2B,KAAK,SAAS,CAAA;AAAA,EAClD,CAAA;AAAA,EACA,MAAM,0BAA0B,OAAA,EAA4C;AAC1E,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA;AACrB,IAAA,MAAM,SAAU,OAAA,CAAuC,MAAA;AACvD,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,MAAA,EAAQ,OAAO,IAAA;AAC7B,IAAA,IAAI,CAAC,oBAAA,CAAqB,IAAI,CAAA,EAAG,OAAO,IAAA;AACxC,IAAA,MAAM,SAAA,GAAY,MAAM,0BAAA,CAA2B,IAAA,CAAK,SAAS,CAAA;AACjE,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,kBAAA,CAAmB,SAAS,CAAC,CAAA;AACnD,IAAA,MAAM,OAAA,GAAU,4BAAA,IACd,OAAO,IAAA,KAAS,WAAA,GAAc,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,SAAS,QAAQ,CAAA,CAAA;AAEhF,IAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAU,eAAA,EAAgB;AAAA,EACtD,CAAA;AAAA,EACA,IAAA,EAAM;AACR","file":"chunk-SHFOGORM.mjs","sourcesContent":["'use client';\n\nimport { lazy, type ReactNode } from 'react';\nimport { renderGeometrySvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\nimport {\n isGeometryCustomData,\n type GeometryCustomData,\n} from './types';\n\nexport { isGeometryCustomData };\nexport type { GeometryCustomData };\n\nconst GeometryStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.GeometryStampHost })),\n);\n\nconst GeometryIcon: ReactNode = (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <polygon points=\"4,20 20,20 12,5\" />\n <circle cx=\"4\" cy=\"20\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"20\" cy=\"20\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"12\" cy=\"5\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n);\n\nexport const geometryStamp: StampType = {\n kind: 'geometry',\n shortcutKey: 'g',\n toolbarLabel: 'G',\n toolbarTitle: 'Chèn hình học (G)',\n toolbarIcon: GeometryIcon,\n toolbarTestId: 'stamp-toolbar-geometry',\n matchesCustomData: isGeometryCustomData,\n async renderSvgFromCustomData(data) {\n if (!isGeometryCustomData(data)) {\n throw new Error('geometryStamp.renderSvgFromCustomData: customData không phải geometry');\n }\n return renderGeometrySvgFromState(data.jsonState);\n },\n async restoreFileFromCustomData(element): Promise<RestoredStampFile | null> {\n const data = element.customData as GeometryCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId) return null;\n if (!isGeometryCustomData(data)) return null;\n const svgString = await renderGeometrySvgFromState(data.jsonState);\n const utf8 = unescape(encodeURIComponent(svgString));\n const dataURL = 'data:image/svg+xml;base64,' + (\n typeof btoa !== 'undefined' ? btoa(utf8) : Buffer.from(utf8).toString('base64')\n );\n return { fileId, dataURL, mimeType: 'image/svg+xml' };\n },\n Host: GeometryStampHost,\n};\n"]}
1
+ {"version":3,"sources":["../src/stamps/geometry-2d/index.tsx"],"names":[],"mappings":";;;;AAgBA,IAAM,iBAAA,GAAoB,IAAA;AAAA,EAAK,MAC7B,OAAO,qBAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,iBAAA,EAAkB,CAAE;AACjE,CAAA;AAEA,IAAM,YAAA,wBACH,KAAA,EAAA,EAAI,KAAA,EAAM,MAAK,MAAA,EAAO,IAAA,EAAK,SAAQ,WAAA,EAAY,IAAA,EAAK,QAAO,MAAA,EAAO,cAAA,EAAe,aAAY,KAAA,EAAM,aAAA,EAAc,SAAQ,cAAA,EAAe,OAAA,EAAQ,eAAY,MAAA,EAC3J,QAAA,EAAA;AAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAQ,QAAO,iBAAA,EAAkB,CAAA;AAAA,kBAClC,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,IAAA,EAAK,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO,CAAA;AAAA,kBACjE,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO,CAAA;AAAA,kBAClE,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,GAAA,EAAI,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO;AAAA,CAAA,EACnE,CAAA;AAGK,IAAM,aAAA,GAA2B;AAAA,EACtC,IAAA,EAAM,UAAA;AAAA,EACN,WAAA,EAAa,GAAA;AAAA,EACb,YAAA,EAAc,GAAA;AAAA,EACd,YAAA,EAAc,8BAAA;AAAA,EACd,WAAA,EAAa,YAAA;AAAA,EACb,aAAA,EAAe,wBAAA;AAAA,EACf,iBAAA,EAAmB,oBAAA;AAAA,EACnB,MAAM,wBAAwB,IAAA,EAAM;AAClC,IAAA,IAAI,CAAC,oBAAA,CAAqB,IAAI,CAAA,EAAG;AAC/B,MAAA,MAAM,IAAI,MAAM,+EAAuE,CAAA;AAAA,IACzF;AACA,IAAA,OAAO,0BAAA,CAA2B,KAAK,SAAS,CAAA;AAAA,EAClD,CAAA;AAAA,EACA,MAAM,0BAA0B,OAAA,EAA4C;AAC1E,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA;AACrB,IAAA,MAAM,SAAU,OAAA,CAAuC,MAAA;AACvD,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,MAAA,EAAQ,OAAO,IAAA;AAC7B,IAAA,IAAI,CAAC,oBAAA,CAAqB,IAAI,CAAA,EAAG,OAAO,IAAA;AACxC,IAAA,MAAM,SAAA,GAAY,MAAM,0BAAA,CAA2B,IAAA,CAAK,SAAS,CAAA;AACjE,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,kBAAA,CAAmB,SAAS,CAAC,CAAA;AACnD,IAAA,MAAM,OAAA,GAAU,4BAAA,IACd,OAAO,IAAA,KAAS,WAAA,GAAc,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,SAAS,QAAQ,CAAA,CAAA;AAEhF,IAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAU,eAAA,EAAgB;AAAA,EACtD,CAAA;AAAA,EACA,IAAA,EAAM;AACR","file":"chunk-DU3RHKT5.mjs","sourcesContent":["'use client';\n\nimport { lazy, type ReactNode } from 'react';\nimport { renderGeometrySvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\nimport {\n isGeometryCustomData,\n type GeometryCustomData,\n} from './types';\n\nexport { isGeometryCustomData };\nexport type { GeometryCustomData };\n\nconst GeometryStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.GeometryStampHost })),\n);\n\nconst GeometryIcon: ReactNode = (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <polygon points=\"4,20 20,20 12,5\" />\n <circle cx=\"4\" cy=\"20\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"20\" cy=\"20\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"12\" cy=\"5\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n);\n\nexport const geometryStamp: StampType = {\n kind: 'geometry',\n shortcutKey: 'g',\n toolbarLabel: 'G',\n toolbarTitle: 'Chèn hình học (G)',\n toolbarIcon: GeometryIcon,\n toolbarTestId: 'stamp-toolbar-geometry',\n matchesCustomData: isGeometryCustomData,\n async renderSvgFromCustomData(data) {\n if (!isGeometryCustomData(data)) {\n throw new Error('geometryStamp.renderSvgFromCustomData: customData không phải geometry');\n }\n return renderGeometrySvgFromState(data.jsonState);\n },\n async restoreFileFromCustomData(element): Promise<RestoredStampFile | null> {\n const data = element.customData as GeometryCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId) return null;\n if (!isGeometryCustomData(data)) return null;\n const svgString = await renderGeometrySvgFromState(data.jsonState);\n const utf8 = unescape(encodeURIComponent(svgString));\n const dataURL = 'data:image/svg+xml;base64,' + (\n typeof btoa !== 'undefined' ? btoa(utf8) : Buffer.from(utf8).toString('base64')\n );\n return { fileId, dataURL, mimeType: 'image/svg+xml' };\n },\n Host: GeometryStampHost,\n};\n"]}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { isGeometry3DCustomData, parseSerializedBoard3D } from './chunk-DJTBZEAR.mjs';
2
+ import { isGeometry3DCustomData, parseSerializedBoard3D, VIEW3D_ATTRS, GROUND_PLANE_RANGE, GROUND_PLANE_ATTRS } from './chunk-DU2NFHRR.mjs';
3
3
  import { lazy } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -21,6 +21,7 @@ async function renderGeometry3DSvgFromState(jsonState) {
21
21
  showNavigation: false,
22
22
  renderer: "svg"
23
23
  });
24
+ const baseAttrs = VIEW3D_ATTRS(false);
24
25
  const view = board.create(
25
26
  "view3d",
26
27
  [
@@ -33,14 +34,28 @@ async function renderGeometry3DSvgFromState(jsonState) {
33
34
  ]
34
35
  ],
35
36
  {
36
- az: { slider: { visible: false }, value: state.view.azimuth },
37
- el: { slider: { visible: false }, value: state.view.elevation },
38
- projection: "central"
37
+ ...baseAttrs,
38
+ az: { ...baseAttrs.az, value: state.view.azimuth },
39
+ el: { ...baseAttrs.el, value: state.view.elevation }
39
40
  }
40
41
  );
41
42
  if (!state.showAxes) {
42
43
  view.defaultAxes = [];
43
44
  }
45
+ try {
46
+ view.create(
47
+ "plane3d",
48
+ [
49
+ [0, 0, 0],
50
+ [1, 0, 0],
51
+ [0, 1, 0],
52
+ GROUND_PLANE_RANGE,
53
+ GROUND_PLANE_RANGE
54
+ ],
55
+ GROUND_PLANE_ATTRS(false)
56
+ );
57
+ } catch {
58
+ }
44
59
  const idMap = /* @__PURE__ */ new Map();
45
60
  for (const el of state.elements) {
46
61
  const parents = el.parents.map(
@@ -71,7 +86,7 @@ async function renderGeometry3DSvgFromState(jsonState) {
71
86
  }
72
87
  }
73
88
  var Geometry3DStampHost = lazy(
74
- () => import('./host-XUFON6CQ.mjs').then((m) => ({ default: m.Geometry3DStampHost }))
89
+ () => import('./host-PIIDSMVE.mjs').then((m) => ({ default: m.Geometry3DStampHost }))
75
90
  );
76
91
  var Geometry3DIcon = /* @__PURE__ */ jsxs(
77
92
  "svg",
@@ -125,5 +140,5 @@ var geometry3dStamp = {
125
140
  };
126
141
 
127
142
  export { geometry3dStamp };
128
- //# sourceMappingURL=chunk-HYXFHEDJ.mjs.map
129
- //# sourceMappingURL=chunk-HYXFHEDJ.mjs.map
143
+ //# sourceMappingURL=chunk-IUVV52HO.mjs.map
144
+ //# sourceMappingURL=chunk-IUVV52HO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stamps/geometry-3d/render.ts","../src/stamps/geometry-3d/index.tsx"],"names":[],"mappings":"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","file":"chunk-IUVV52HO.mjs","sourcesContent":["\"use client\";\n\nimport { parseSerializedBoard3D } from './serialize';\nimport { GROUND_PLANE_ATTRS, GROUND_PLANE_RANGE, VIEW3D_ATTRS } from './editor/theme';\n\nexport interface RenderResult {\n svgString: string;\n width: number;\n height: number;\n}\n\nconst OUTPUT_WIDTH = 1024;\nconst OUTPUT_HEIGHT = 768;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype JxgObj = any;\n\nexport async function renderGeometry3DSvgFromState(\n jsonState: string,\n): Promise<RenderResult> {\n const state = parseSerializedBoard3D(jsonState);\n const JXG = (await import('jsxgraph')).default;\n\n const div = document.createElement('div');\n div.style.cssText = `position:absolute;left:-9999px;top:-9999px;width:${OUTPUT_WIDTH}px;height:${OUTPUT_HEIGHT}px;`;\n document.body.appendChild(div);\n\n try {\n JXG.Options.text.display = 'internal';\n\n const board = JXG.JSXGraph.initBoard(div, {\n boundingbox: state.bbox,\n axis: false,\n showCopyright: false,\n showNavigation: false,\n renderer: 'svg',\n }) as { create: (k: string, p: unknown[], a: unknown) => JxgObj };\n\n const baseAttrs = VIEW3D_ATTRS(false);\n const view: JxgObj = board.create(\n 'view3d',\n [\n [-5, -5],\n [10, 10],\n [\n [state.view.bbox3D[0], state.view.bbox3D[3]],\n [state.view.bbox3D[1], state.view.bbox3D[4]],\n [state.view.bbox3D[2], state.view.bbox3D[5]],\n ],\n ],\n {\n ...baseAttrs,\n az: { ...baseAttrs.az, value: state.view.azimuth },\n el: { ...baseAttrs.el, value: state.view.elevation },\n },\n );\n\n if (!state.showAxes) {\n (view as { defaultAxes?: unknown[] }).defaultAxes = [];\n }\n\n // XY ground plane through origin (matches editor's MiniBoard3D).\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 idMap = new Map<string, JxgObj>();\n for (const el of state.elements) {\n const parents = el.parents.map((p) =>\n typeof p === 'string' && p.startsWith('@id:')\n ? idMap.get(p.slice(4))\n : p,\n );\n const obj = (\n view as { create: (k: string, p: unknown[], a: unknown) => JxgObj }\n ).create(el.type, parents, {\n ...el.attributes,\n id: el.id,\n name: el.label,\n });\n idMap.set(el.id, obj);\n }\n\n const svg = div.querySelector('svg');\n if (!svg) {\n throw new Error('renderGeometry3DSvgFromState: SVG not produced');\n }\n const clone = svg.cloneNode(true) as SVGElement;\n clone.setAttribute('width', String(OUTPUT_WIDTH));\n clone.setAttribute('height', String(OUTPUT_HEIGHT));\n const svgString = new XMLSerializer().serializeToString(clone);\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n JXG.JSXGraph.freeBoard(board as any);\n } catch {\n /* ignore teardown */\n }\n\n return { svgString, width: OUTPUT_WIDTH, height: OUTPUT_HEIGHT };\n } finally {\n document.body.removeChild(div);\n }\n}\n","'use client';\n\nimport { lazy, type ReactNode } from 'react';\nimport {\n isGeometry3DCustomData,\n type Geometry3DCustomData,\n} from './serialize';\nimport { renderGeometry3DSvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\n\nexport { isGeometry3DCustomData };\nexport type { Geometry3DCustomData };\n\nconst Geometry3DStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.Geometry3DStampHost })),\n);\n\nconst Geometry3DIcon: ReactNode = (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n {/* Mặt trước */}\n <path d=\"M4 9 L4 20 L14 20 L14 9 Z\" />\n {/* Mặt trên */}\n <path d=\"M4 9 L10 4 L20 4 L14 9 Z\" />\n {/* Mặt phải */}\n <path d=\"M14 9 L20 4 L20 15 L14 20 Z\" />\n </svg>\n);\n\nexport const geometry3dStamp: StampType = {\n kind: 'geometry3d',\n experimental: true,\n shortcutKey: 'd',\n toolbarLabel: 'D',\n toolbarTitle: 'Hình 3D (D)',\n toolbarIcon: Geometry3DIcon,\n toolbarTestId: 'stamp-toolbar-geometry3d',\n matchesCustomData: isGeometry3DCustomData,\n async renderSvgFromCustomData(data: unknown): Promise<string> {\n if (!isGeometry3DCustomData(data)) {\n throw new Error('geometry3dStamp.renderSvgFromCustomData: customData không phải geometry3d');\n }\n const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);\n return svgString;\n },\n restoreFileFromCustomData: async (element): Promise<RestoredStampFile | null> => {\n const data = element.customData as Geometry3DCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId) return null;\n if (!isGeometry3DCustomData(data)) return null;\n try {\n const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);\n const dataURL = `data:image/svg+xml;base64,${\n typeof btoa !== 'undefined'\n ? btoa(unescape(encodeURIComponent(svgString)))\n : Buffer.from(svgString).toString('base64')\n }`;\n return { fileId, dataURL, mimeType: 'image/svg+xml' };\n } catch {\n return null;\n }\n },\n Host: Geometry3DStampHost,\n};\n"]}
@@ -74,12 +74,31 @@ function deserializeIntoBoard(board, serialized, options = {}) {
74
74
  }
75
75
  }
76
76
 
77
+ // src/stamps/shared/safeJsx.ts
78
+ var isDev = (() => {
79
+ try {
80
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
81
+ } catch {
82
+ return false;
83
+ }
84
+ })();
85
+ function safeJsx(label, fn, fallback) {
86
+ try {
87
+ return fn();
88
+ } catch (err) {
89
+ if (isDev) {
90
+ console.warn("[whiteboard:jsxgraph]", label, err);
91
+ }
92
+ return fallback;
93
+ }
94
+ }
95
+
77
96
  // src/stamps/geometry-2d/render.ts
78
97
  async function renderGeometrySvgFromState(jsonState) {
79
98
  const parsed = JSON.parse(jsonState);
80
99
  const palette = paletteFor(false);
81
100
  const JXG = (await import('jsxgraph')).default;
82
- try {
101
+ safeJsx("render.applyOptions", () => {
83
102
  const opts = JXG.Options;
84
103
  if (opts) {
85
104
  opts.text = opts.text || {};
@@ -96,8 +115,7 @@ async function renderGeometrySvgFromState(jsonState) {
96
115
  opts.grid = opts.grid || {};
97
116
  opts.grid.strokeColor = palette.grid;
98
117
  }
99
- } catch {
100
- }
118
+ });
101
119
  const container = document.createElement("div");
102
120
  const containerId = "jxg_offscreen_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
103
121
  container.id = containerId;
@@ -117,10 +135,9 @@ async function renderGeometrySvgFromState(jsonState) {
117
135
  board.update();
118
136
  return renderGeometryToSvg(container);
119
137
  } finally {
120
- try {
138
+ safeJsx("render.freeBoard", () => {
121
139
  if (board) JXG.JSXGraph.freeBoard(board);
122
- } catch {
123
- }
140
+ });
124
141
  if (container.parentNode) container.parentNode.removeChild(container);
125
142
  }
126
143
  }
@@ -132,6 +149,6 @@ function isGeometryCustomData(data) {
132
149
  return d.kind === "geometry" && d.version === 1 && typeof d.jsonState === "string";
133
150
  }
134
151
 
135
- export { isGeometryCustomData, renderGeometrySvgFromState, serializeBoard };
136
- //# sourceMappingURL=chunk-BJX4YNA5.mjs.map
137
- //# sourceMappingURL=chunk-BJX4YNA5.mjs.map
152
+ export { isGeometryCustomData, renderGeometrySvgFromState, safeJsx, serializeBoard };
153
+ //# sourceMappingURL=chunk-KEYZ5EZT.mjs.map
154
+ //# sourceMappingURL=chunk-KEYZ5EZT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stamps/geometry-2d/renderInline.ts","../src/stamps/geometry-2d/serialize.ts","../src/stamps/shared/safeJsx.ts","../src/stamps/geometry-2d/render.ts","../src/stamps/geometry-2d/types.ts"],"names":[],"mappings":"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","file":"chunk-KEYZ5EZT.mjs","sourcesContent":["export function renderGeometryToSvg(boardContainer: HTMLElement): string {\n const svgEl = boardContainer.querySelector('svg');\n if (!svgEl) throw new Error('renderGeometryToSvg: no SVG found in board container');\n const clone = svgEl.cloneNode(true) as SVGElement;\n if (!clone.getAttribute('xmlns')) {\n clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');\n }\n return new XMLSerializer().serializeToString(clone);\n}\n","// JSXGraph không có built-in getJSON. Component giữ MỘT LOG riêng của các create() call\n// do user trigger, pass log đó vào serializeBoard. Replay = gọi board.create() theo thứ tự log.\n//\n// type === 'transform': args là [refs đến điểm/đường/scalar], attrs là { type: 'translate'|'rotate'|'reflect'|'scale', ... }.\n// Object trả về (kết quả board.create('transform', ...)) được đăng ký vào idMap như mọi element khác\n// để point/line phụ thuộc reference được bằng id ('j5' → JSXGraph transform object).\n//\n// Log lưu màu dưới dạng sentinel ('@stroke', '@axis', '@grid', '@label') để\n// theme-neutral. Khi replay, palette resolve thành màu thực theo `isDark` hiện\n// tại (truyền qua options.palette).\n\nimport { paletteFor, resolveAttrColors, type GeomPalette } from './editor/theme';\n\nexport interface SerializedElement {\n type: string;\n args: unknown[];\n attrs: Record<string, unknown>;\n id: string;\n}\n\nexport interface SerializedBoard {\n bbox: [number, number, number, number];\n elements: SerializedElement[];\n showAxis?: boolean;\n showGrid?: boolean;\n}\n\ninterface BoardLike {\n getBoundingBox(): [number, number, number, number];\n create(type: string, args: unknown[], attrs: Record<string, unknown>): unknown;\n}\n\nexport function serializeBoard(\n board: BoardLike,\n log: SerializedElement[],\n options: { showAxis?: boolean; showGrid?: boolean } = {},\n): SerializedBoard {\n return {\n bbox: board.getBoundingBox(),\n elements: log.map(e => ({ type: e.type, args: e.args, attrs: e.attrs, id: e.id })),\n showAxis: !!options.showAxis,\n showGrid: !!options.showGrid,\n };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction createValueLabel(board: any, target: any): unknown {\n if (!board || !target) return null;\n const e = (target.elType ?? target.type ?? '').toString().toLowerCase();\n if (e === 'segment' || e === 'line' || e === 'arrow') {\n const p1 = target.point1, p2 = target.point2;\n if (!p1 || !p2) return null;\n return board.create('text', [\n () => (p1.X() + p2.X()) / 2 + 0.15,\n () => (p1.Y() + p2.Y()) / 2 + 0.25,\n () => {\n const len = Math.hypot(p2.X() - p1.X(), p2.Y() - p1.Y());\n const name = typeof target.name === 'string' && target.name ? target.name : 'd';\n return `${name} = ${len.toFixed(2)}`;\n },\n ], { fontSize: 12, color: '#dc2626', fixed: true, highlight: false });\n }\n if (e === 'circle' || e === 'circumcircle') {\n const center = target.center ?? target.midpoint ?? target.point1;\n if (!center) return null;\n return board.create('text', [\n () => center.X() + 0.3,\n () => center.Y() + 0.3,\n () => {\n const r = typeof target.Radius === 'function' ? target.Radius() : 0;\n const name = typeof target.name === 'string' && target.name ? target.name : 'r';\n return `${name} = ${r.toFixed(2)}`;\n },\n ], { fontSize: 12, color: '#dc2626', fixed: true, highlight: false });\n }\n return null;\n}\n\nexport interface DeserializeOptions {\n /** Theme-aware palette để resolve sentinel attrs. Mặc định = light. */\n palette?: GeomPalette;\n}\n\nexport function deserializeIntoBoard(\n board: BoardLike,\n serialized: SerializedBoard,\n options: DeserializeOptions = {},\n): void {\n // Replay: args may contain references to earlier elements by our serialized id (\"j0\", \"j1\"…).\n // We resolve those to actual JSXGraph objects via a local id→object map. Nested\n // arrays are also resolved recursively — needed for dilate, which logs the\n // transform parent of a transformed point as [\"j2\",\"j3\",\"j4\"] (a chain of 3\n // transforms passed to `board.create('point', [src, [t1,t2,t3]])`).\n const palette = options.palette ?? paletteFor(false);\n const idMap = new Map<string, unknown>();\n const resolve = (a: unknown): unknown => {\n if (typeof a === 'string' && idMap.has(a)) return idMap.get(a);\n if (Array.isArray(a)) return a.map(resolve);\n return a;\n };\n for (const el of serialized.elements) {\n const resolvedArgs = el.args.map(resolve);\n if (el.type === 'valueLabel') {\n const target = resolvedArgs[0];\n const txt = createValueLabel(board, target);\n if (txt) idMap.set(el.id, txt);\n continue;\n }\n const themedAttrs = resolveAttrColors({ ...el.attrs }, palette);\n const created = board.create(el.type, resolvedArgs, themedAttrs);\n idMap.set(el.id, created);\n }\n}\n","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 // eslint-disable-next-line no-console\n console.warn('[whiteboard:jsxgraph]', label, err);\n }\n return fallback;\n }\n}\n","import { renderGeometryToSvg } from './renderInline';\nimport { deserializeIntoBoard, type SerializedBoard } from './serialize';\nimport { paletteFor } from './editor/theme';\nimport { safeJsx } from '../shared/safeJsx';\n\n/**\n * Re-render geometry SVG từ jsonState đã serialize. Dùng cho:\n * 1. Restore math-stamp file sau khi reload page (Excalidraw mất binary files).\n * 2. Generate SVG lúc INSERT (thay vì clone DOM với màu theo theme editor).\n *\n * LƯU Ý quan trọng — luôn dùng LIGHT palette (nét đậm). Excalidraw apply CSS\n * `filter: invert(93%) hue-rotate(180deg)` lên canvas trong dark mode → nét\n * đậm tự đảo thành sáng. Nếu ta bake nét sáng vào SVG cho dark mode, filter\n * sẽ đảo thành đậm → chìm vào nền tối. Giải pháp: luôn dùng nét đậm + để\n * Excalidraw tự lo invert.\n *\n * Implementation: tạo 1 div ẩn (off-screen, real dimensions để JSXGraph render\n * chuẩn), initBoard, replay creation log từ jsonState, dump SVG, dọn dẹp.\n *\n * Lý do JXG.Options.text.display = 'internal': JSXGraph mặc định render\n * label bằng HTML <div> overlay → clone SVG export sẽ thiếu label.\n */\nexport async function renderGeometrySvgFromState(jsonState: string): Promise<string> {\n const parsed = JSON.parse(jsonState) as SerializedBoard;\n // Stamps inserted vào Excalidraw canvas → luôn dùng light palette.\n // Excalidraw's THEME_FILTER tự đảo nét trong dark mode.\n const palette = paletteFor(false);\n const JXG = (await import('jsxgraph')).default;\n safeJsx('render.applyOptions', () => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const opts = (JXG as any).Options;\n if (opts) {\n opts.text = opts.text || {};\n opts.text.display = 'internal';\n opts.text.useASCIIMathML = false;\n opts.text.useMathJax = false;\n opts.text.useKatex = false;\n opts.text.strokeColor = palette.label;\n opts.label = opts.label || {};\n opts.label.display = 'internal';\n opts.label.strokeColor = palette.label;\n opts.axis = opts.axis || {};\n opts.axis.strokeColor = palette.axis;\n opts.grid = opts.grid || {};\n opts.grid.strokeColor = palette.grid;\n }\n });\n const container = document.createElement('div');\n const containerId = 'jxg_offscreen_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8);\n container.id = containerId;\n container.style.cssText = 'position:absolute;top:-99999px;left:-99999px;width:400px;height:300px;visibility:hidden;pointer-events:none;';\n document.body.appendChild(container);\n let board: unknown = null;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n board = (JXG as any).JSXGraph.initBoard(containerId, {\n boundingbox: parsed.bbox,\n axis: !!parsed.showAxis,\n grid: !!parsed.showGrid,\n showCopyright: false,\n showNavigation: false,\n keepAspectRatio: false,\n });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n deserializeIntoBoard(board as any, parsed, { palette });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (board as any).update();\n return renderGeometryToSvg(container);\n } finally {\n safeJsx('render.freeBoard', () => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (board) (JXG as any).JSXGraph.freeBoard(board);\n });\n if (container.parentNode) container.parentNode.removeChild(container);\n }\n}\n","import type { BaseStampCustomData } from '../shared/types';\n\nexport interface GeometryCustomData extends BaseStampCustomData {\n kind: 'geometry';\n version: 1;\n jsonState: string;\n svgWidth: number;\n svgHeight: number;\n}\n\nexport function isGeometryCustomData(data: unknown): data is GeometryCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<GeometryCustomData>;\n return d.kind === 'geometry' && d.version === 1 && typeof d.jsonState === 'string';\n}\n"]}
@@ -156,6 +156,7 @@ function MobileToolDrawer({
156
156
  disabled: a.disabled,
157
157
  "aria-label": a.label,
158
158
  title: a.title ?? a.label,
159
+ "data-testid": a.testId,
159
160
  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",
160
161
  children: a.icon
161
162
  },
@@ -207,5 +208,5 @@ function MobileToolDrawer({
207
208
  }
208
209
 
209
210
  export { MobileToolDrawer, useChordShortcut };
210
- //# sourceMappingURL=chunk-LPM4MM45.mjs.map
211
- //# sourceMappingURL=chunk-LPM4MM45.mjs.map
211
+ //# sourceMappingURL=chunk-SBDMF4NQ.mjs.map
212
+ //# sourceMappingURL=chunk-SBDMF4NQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stamps/shared/useChordShortcut.ts","../src/stamps/shared/MobileToolDrawer.tsx"],"names":[],"mappings":"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","file":"chunk-SBDMF4NQ.mjs","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface UseChordShortcutArgs<G extends string> {\n groupOrder: readonly G[];\n tools: ReadonlyArray<{ key: string; group: G }>;\n onSelect: (toolKey: string) => void;\n enabled: boolean;\n}\n\ninterface UseChordShortcutResult<G extends string> {\n chordGroup: G | null;\n cancel: () => void;\n}\n\nconst A_CODE = 'a'.charCodeAt(0);\n\nfunction isFieldFocused(): boolean {\n const ae = (typeof document !== 'undefined'\n ? (document.activeElement as HTMLElement | null)\n : null);\n return !!(\n ae &&\n (ae.tagName === 'INPUT' ||\n ae.tagName === 'TEXTAREA' ||\n ae.isContentEditable)\n );\n}\n\nexport function useChordShortcut<G extends string>(\n args: UseChordShortcutArgs<G>,\n): UseChordShortcutResult<G> {\n const { groupOrder, tools, onSelect, enabled } = args;\n\n const [chordGroup, setChordGroup] = useState<G | null>(null);\n\n const groupOrderRef = useRef(groupOrder);\n const toolsRef = useRef(tools);\n const onSelectRef = useRef(onSelect);\n const chordGroupRef = useRef<G | null>(null);\n\n groupOrderRef.current = groupOrder;\n toolsRef.current = tools;\n onSelectRef.current = onSelect;\n // chordGroupRef được sync ngay trong handler (xem `setChord` dưới đây)\n // thay vì ghi từ render body — nếu ghi ở body sẽ bị React batch hoá khi\n // hai event xảy ra trong cùng một act() (event sau đọc giá trị cũ).\n\n const cancel = useCallback(() => {\n chordGroupRef.current = null;\n setChordGroup(null);\n }, []);\n\n useEffect(() => {\n if (!enabled) return;\n\n const setChord = (next: G | null) => {\n chordGroupRef.current = next;\n setChordGroup(next);\n };\n\n const onKey = (e: KeyboardEvent) => {\n if (e.metaKey || e.ctrlKey || e.altKey) return;\n if (isFieldFocused()) return;\n\n const key = e.key;\n const lower = key.length === 1 ? key.toLowerCase() : key;\n\n if (key === 'Escape') {\n if (chordGroupRef.current !== null) {\n e.preventDefault();\n e.stopPropagation();\n setChord(null);\n }\n return;\n }\n\n if (lower.length === 1 && lower >= 'a' && lower <= 'z') {\n const idx = lower.charCodeAt(0) - A_CODE;\n if (idx < groupOrderRef.current.length) {\n e.preventDefault();\n e.stopPropagation();\n setChord(groupOrderRef.current[idx]);\n }\n return;\n }\n\n if (key >= '1' && key <= '9') {\n const active = chordGroupRef.current;\n if (active === null) return;\n const n = key.charCodeAt(0) - '1'.charCodeAt(0); // 0-indexed\n const toolsInGroup = toolsRef.current.filter(\n (t) => t.group === active,\n );\n e.preventDefault();\n e.stopPropagation();\n if (n < toolsInGroup.length) {\n onSelectRef.current(toolsInGroup[n].key);\n }\n setChord(null);\n return;\n }\n };\n\n window.addEventListener('keydown', onKey, { capture: true });\n return () => {\n window.removeEventListener('keydown', onKey, { capture: true });\n };\n }, [enabled]);\n\n return { chordGroup, cancel };\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}\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}: MobileToolDrawerProps<TKey, TGroup>) {\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 {/* 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 {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-6 w-6 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 </div>\n </aside>\n </>\n );\n}\n"]}
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { isGraph2DCustomData, renderGraph2dSvgFromState } from './chunk-HM7RIXJE.mjs';
2
+ import { isGraph2DCustomData, renderGraph2dSvgFromState } from './chunk-74VEEZBV.mjs';
3
3
  import { lazy } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
6
6
  var Graph2DStampHost = lazy(
7
- () => import('./host-2QGKMGCT.mjs').then((m) => ({ default: m.Graph2DStampHost }))
7
+ () => import('./host-LZH2FZ2N.mjs').then((m) => ({ default: m.Graph2DStampHost }))
8
8
  );
9
9
  var Graph2DIcon = /* @__PURE__ */ jsxs(
10
10
  "svg",
@@ -54,5 +54,5 @@ var graph2dStamp = {
54
54
  };
55
55
 
56
56
  export { graph2dStamp };
57
- //# sourceMappingURL=chunk-3SSQKRRO.mjs.map
58
- //# sourceMappingURL=chunk-3SSQKRRO.mjs.map
57
+ //# sourceMappingURL=chunk-ZVN356JZ.mjs.map
58
+ //# sourceMappingURL=chunk-ZVN356JZ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stamps/graph-2d/index.tsx"],"names":[],"mappings":";;;;AAaA,IAAM,gBAAA,GAAmB,IAAA;AAAA,EAAK,MAC5B,OAAO,qBAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,gBAAA,EAAiB,CAAE;AAChE,CAAA;AAEA,IAAM,WAAA,mBACJ,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,IAAA;AAAA,IACN,MAAA,EAAO,IAAA;AAAA,IACP,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAY,KAAA;AAAA,IACZ,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,aAAA,EAAY,MAAA;AAAA,IAEZ,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,GAAE,UAAA,EAAW,CAAA;AAAA,sBACnB,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,WAAA,EAAY,CAAA;AAAA,sBACpB,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,yBAAA,EAA0B;AAAA;AAAA;AACpC,CAAA;AAGK,IAAM,YAAA,GAA0B;AAAA,EACrC,IAAA,EAAM,SAAA;AAAA,EACN,YAAA,EAAc,IAAA;AAAA,EACd,WAAA,EAAa,GAAA;AAAA,EACb,YAAA,EAAc,GAAA;AAAA,EACd,YAAA,EAAc,sCAAA;AAAA,EACd,WAAA,EAAa,WAAA;AAAA,EACb,aAAA,EAAe,uBAAA;AAAA,EACf,iBAAA,EAAmB,mBAAA;AAAA,EACnB,MAAM,wBAAwB,IAAA,EAAM;AAClC,IAAA,IAAI,CAAC,mBAAA,CAAoB,IAAI,CAAA,EAAG;AAC9B,MAAA,MAAM,IAAI,MAAM,6EAAqE,CAAA;AAAA,IACvF;AACA,IAAA,OAAO,yBAAA,CAA0B,KAAK,SAAS,CAAA;AAAA,EACjD,CAAA;AAAA,EACA,MAAM,0BAA0B,OAAA,EAA4C;AAC1E,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA;AACrB,IAAA,MAAM,SAAU,OAAA,CAAuC,MAAA;AACvD,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,MAAA,EAAQ,OAAO,IAAA;AAC7B,IAAA,IAAI,CAAC,mBAAA,CAAoB,IAAI,CAAA,EAAG,OAAO,IAAA;AACvC,IAAA,MAAM,SAAA,GAAY,MAAM,yBAAA,CAA0B,IAAA,CAAK,SAAS,CAAA;AAChE,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,kBAAA,CAAmB,SAAS,CAAC,CAAA;AACnD,IAAA,MAAM,OAAA,GACJ,4BAAA,IACC,OAAO,IAAA,KAAS,WAAA,GAAc,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,SAAS,QAAQ,CAAA,CAAA;AACjF,IAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAU,eAAA,EAAgB;AAAA,EACtD,CAAA;AAAA,EACA,IAAA,EAAM;AACR","file":"chunk-3SSQKRRO.mjs","sourcesContent":["'use client';\n\nimport { lazy } from 'react';\nimport { renderGraph2dSvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\nimport { isGraph2DCustomData, type Graph2DCustomData } from './types';\n\nexport { isGraph2DCustomData };\nexport type { Graph2DCustomData };\n\nconst Graph2DStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.Graph2DStampHost })),\n);\n\nconst Graph2DIcon = (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M3 21 V3\" />\n <path d=\"M3 21 H21\" />\n <path d=\"M5 19 C8 5, 14 5, 19 17\" />\n </svg>\n);\n\nexport const graph2dStamp: StampType = {\n kind: 'graph2d',\n experimental: true,\n shortcutKey: 'h',\n toolbarLabel: 'H',\n toolbarTitle: 'Chèn đồ thị 2D (H)',\n toolbarIcon: Graph2DIcon,\n toolbarTestId: 'stamp-toolbar-graph2d',\n matchesCustomData: isGraph2DCustomData,\n async renderSvgFromCustomData(data) {\n if (!isGraph2DCustomData(data)) {\n throw new Error('graph2dStamp.renderSvgFromCustomData: customData không phải graph2d');\n }\n return renderGraph2dSvgFromState(data.jsonState);\n },\n async restoreFileFromCustomData(element): Promise<RestoredStampFile | null> {\n const data = element.customData as Graph2DCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId) return null;\n if (!isGraph2DCustomData(data)) return null;\n const svgString = await renderGraph2dSvgFromState(data.jsonState);\n const utf8 = unescape(encodeURIComponent(svgString));\n const dataURL =\n 'data:image/svg+xml;base64,' +\n (typeof btoa !== 'undefined' ? btoa(utf8) : Buffer.from(utf8).toString('base64'));\n return { fileId, dataURL, mimeType: 'image/svg+xml' };\n },\n Host: Graph2DStampHost,\n};\n"]}
1
+ {"version":3,"sources":["../src/stamps/graph-2d/index.tsx"],"names":[],"mappings":";;;;AAaA,IAAM,gBAAA,GAAmB,IAAA;AAAA,EAAK,MAC5B,OAAO,qBAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,gBAAA,EAAiB,CAAE;AAChE,CAAA;AAEA,IAAM,WAAA,mBACJ,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,KAAA,EAAM,IAAA;AAAA,IACN,MAAA,EAAO,IAAA;AAAA,IACP,OAAA,EAAQ,WAAA;AAAA,IACR,IAAA,EAAK,MAAA;AAAA,IACL,MAAA,EAAO,cAAA;AAAA,IACP,WAAA,EAAY,KAAA;AAAA,IACZ,aAAA,EAAc,OAAA;AAAA,IACd,cAAA,EAAe,OAAA;AAAA,IACf,aAAA,EAAY,MAAA;AAAA,IAEZ,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,GAAE,UAAA,EAAW,CAAA;AAAA,sBACnB,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,WAAA,EAAY,CAAA;AAAA,sBACpB,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,yBAAA,EAA0B;AAAA;AAAA;AACpC,CAAA;AAGK,IAAM,YAAA,GAA0B;AAAA,EACrC,IAAA,EAAM,SAAA;AAAA,EACN,YAAA,EAAc,IAAA;AAAA,EACd,WAAA,EAAa,GAAA;AAAA,EACb,YAAA,EAAc,GAAA;AAAA,EACd,YAAA,EAAc,sCAAA;AAAA,EACd,WAAA,EAAa,WAAA;AAAA,EACb,aAAA,EAAe,uBAAA;AAAA,EACf,iBAAA,EAAmB,mBAAA;AAAA,EACnB,MAAM,wBAAwB,IAAA,EAAM;AAClC,IAAA,IAAI,CAAC,mBAAA,CAAoB,IAAI,CAAA,EAAG;AAC9B,MAAA,MAAM,IAAI,MAAM,6EAAqE,CAAA;AAAA,IACvF;AACA,IAAA,OAAO,yBAAA,CAA0B,KAAK,SAAS,CAAA;AAAA,EACjD,CAAA;AAAA,EACA,MAAM,0BAA0B,OAAA,EAA4C;AAC1E,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA;AACrB,IAAA,MAAM,SAAU,OAAA,CAAuC,MAAA;AACvD,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,MAAA,EAAQ,OAAO,IAAA;AAC7B,IAAA,IAAI,CAAC,mBAAA,CAAoB,IAAI,CAAA,EAAG,OAAO,IAAA;AACvC,IAAA,MAAM,SAAA,GAAY,MAAM,yBAAA,CAA0B,IAAA,CAAK,SAAS,CAAA;AAChE,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,kBAAA,CAAmB,SAAS,CAAC,CAAA;AACnD,IAAA,MAAM,OAAA,GACJ,4BAAA,IACC,OAAO,IAAA,KAAS,WAAA,GAAc,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,SAAS,QAAQ,CAAA,CAAA;AACjF,IAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAU,eAAA,EAAgB;AAAA,EACtD,CAAA;AAAA,EACA,IAAA,EAAM;AACR","file":"chunk-ZVN356JZ.mjs","sourcesContent":["'use client';\n\nimport { lazy } from 'react';\nimport { renderGraph2dSvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\nimport { isGraph2DCustomData, type Graph2DCustomData } from './types';\n\nexport { isGraph2DCustomData };\nexport type { Graph2DCustomData };\n\nconst Graph2DStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.Graph2DStampHost })),\n);\n\nconst Graph2DIcon = (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M3 21 V3\" />\n <path d=\"M3 21 H21\" />\n <path d=\"M5 19 C8 5, 14 5, 19 17\" />\n </svg>\n);\n\nexport const graph2dStamp: StampType = {\n kind: 'graph2d',\n experimental: true,\n shortcutKey: 'h',\n toolbarLabel: 'H',\n toolbarTitle: 'Chèn đồ thị 2D (H)',\n toolbarIcon: Graph2DIcon,\n toolbarTestId: 'stamp-toolbar-graph2d',\n matchesCustomData: isGraph2DCustomData,\n async renderSvgFromCustomData(data) {\n if (!isGraph2DCustomData(data)) {\n throw new Error('graph2dStamp.renderSvgFromCustomData: customData không phải graph2d');\n }\n return renderGraph2dSvgFromState(data.jsonState);\n },\n async restoreFileFromCustomData(element): Promise<RestoredStampFile | null> {\n const data = element.customData as Graph2DCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId) return null;\n if (!isGraph2DCustomData(data)) return null;\n const svgString = await renderGraph2dSvgFromState(data.jsonState);\n const utf8 = unescape(encodeURIComponent(svgString));\n const dataURL =\n 'data:image/svg+xml;base64,' +\n (typeof btoa !== 'undefined' ? btoa(utf8) : Buffer.from(utf8).toString('base64'));\n return { fileId, dataURL, mimeType: 'image/svg+xml' };\n },\n Host: Graph2DStampHost,\n};\n"]}