@xom11/whiteboard 0.6.5 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/chunk-3SSQKRRO.mjs +58 -0
- package/dist/chunk-3SSQKRRO.mjs.map +1 -0
- package/dist/chunk-7P7SQFOW.mjs +39 -0
- package/dist/chunk-7P7SQFOW.mjs.map +1 -0
- package/dist/chunk-BJX4YNA5.mjs +137 -0
- package/dist/chunk-BJX4YNA5.mjs.map +1 -0
- package/dist/chunk-C6SCVOMC.mjs +111 -0
- package/dist/chunk-C6SCVOMC.mjs.map +1 -0
- package/dist/chunk-DJTBZEAR.mjs +25 -0
- package/dist/chunk-DJTBZEAR.mjs.map +1 -0
- package/dist/chunk-HM7RIXJE.mjs +331 -0
- package/dist/chunk-HM7RIXJE.mjs.map +1 -0
- package/dist/chunk-HTBLO5JO.mjs +41 -0
- package/dist/chunk-HTBLO5JO.mjs.map +1 -0
- package/dist/chunk-HYXFHEDJ.mjs +129 -0
- package/dist/chunk-HYXFHEDJ.mjs.map +1 -0
- package/dist/chunk-LPM4MM45.mjs +211 -0
- package/dist/chunk-LPM4MM45.mjs.map +1 -0
- package/dist/chunk-P2AOIF7S.mjs +40 -0
- package/dist/chunk-P2AOIF7S.mjs.map +1 -0
- package/dist/chunk-SHFOGORM.mjs +44 -0
- package/dist/chunk-SHFOGORM.mjs.map +1 -0
- package/dist/chunk-X5R72SSJ.mjs +52 -0
- package/dist/chunk-X5R72SSJ.mjs.map +1 -0
- package/dist/geometry-2d.d.mts +16 -0
- package/dist/geometry-2d.d.ts +16 -0
- package/dist/geometry-2d.js +3549 -0
- package/dist/geometry-2d.js.map +1 -0
- package/dist/geometry-2d.mjs +7 -0
- package/dist/geometry-2d.mjs.map +1 -0
- package/dist/geometry-3d.d.mts +16 -0
- package/dist/geometry-3d.d.ts +16 -0
- package/dist/geometry-3d.js +2030 -0
- package/dist/geometry-3d.js.map +1 -0
- package/dist/geometry-3d.mjs +6 -0
- package/dist/geometry-3d.mjs.map +1 -0
- package/dist/graph-2d.d.mts +16 -0
- package/dist/graph-2d.d.ts +16 -0
- package/dist/graph-2d.js +1725 -0
- package/dist/graph-2d.js.map +1 -0
- package/dist/graph-2d.mjs +6 -0
- package/dist/graph-2d.mjs.map +1 -0
- package/dist/host-2QGKMGCT.mjs +1066 -0
- package/dist/host-2QGKMGCT.mjs.map +1 -0
- package/dist/host-T2W6R6SO.mjs +2859 -0
- package/dist/host-T2W6R6SO.mjs.map +1 -0
- package/dist/host-XUFON6CQ.mjs +1422 -0
- package/dist/host-XUFON6CQ.mjs.map +1 -0
- package/dist/host-Z3TEJKZA.mjs +466 -0
- package/dist/host-Z3TEJKZA.mjs.map +1 -0
- package/dist/index.d.mts +27 -146
- package/dist/index.d.ts +27 -146
- package/dist/index.js +4694 -4482
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +136 -7179
- package/dist/index.mjs.map +1 -1
- package/dist/latex.d.mts +15 -0
- package/dist/latex.d.ts +15 -0
- package/dist/latex.js +750 -0
- package/dist/latex.js.map +1 -0
- package/dist/latex.mjs +6 -0
- package/dist/latex.mjs.map +1 -0
- package/dist/types-CinstD7T.d.mts +110 -0
- package/dist/types-CinstD7T.d.ts +110 -0
- package/package.json +21 -2
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/stamps/graph-2d/serialize.ts
|
|
3
|
+
var EMPTY_GRAPH = {
|
|
4
|
+
version: 1,
|
|
5
|
+
view: { xMin: -10, xMax: 10, yMin: -10, yMax: 10, showAxis: true, showGrid: true },
|
|
6
|
+
functions: [],
|
|
7
|
+
parameters: [],
|
|
8
|
+
points: [],
|
|
9
|
+
intersections: [],
|
|
10
|
+
tangents: []
|
|
11
|
+
};
|
|
12
|
+
function stringifySerializedGraph(graph) {
|
|
13
|
+
return JSON.stringify(graph);
|
|
14
|
+
}
|
|
15
|
+
function parseSerializedGraph(jsonState) {
|
|
16
|
+
let raw;
|
|
17
|
+
try {
|
|
18
|
+
raw = JSON.parse(jsonState);
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
23
|
+
const r = raw;
|
|
24
|
+
if (r.version !== 1) return null;
|
|
25
|
+
if (!r.view || typeof r.view !== "object") return null;
|
|
26
|
+
const v = r.view;
|
|
27
|
+
if (typeof v.xMin !== "number" || typeof v.xMax !== "number" || typeof v.yMin !== "number" || typeof v.yMax !== "number" || typeof v.showAxis !== "boolean" || typeof v.showGrid !== "boolean") {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
for (const key of ["functions", "parameters", "points", "intersections", "tangents"]) {
|
|
31
|
+
if (!Array.isArray(r[key])) return null;
|
|
32
|
+
}
|
|
33
|
+
return raw;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/stamps/graph-2d/parser.ts
|
|
37
|
+
var ALLOWED_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
38
|
+
"sin",
|
|
39
|
+
"cos",
|
|
40
|
+
"tan",
|
|
41
|
+
"asin",
|
|
42
|
+
"acos",
|
|
43
|
+
"atan",
|
|
44
|
+
"log",
|
|
45
|
+
"ln",
|
|
46
|
+
"exp",
|
|
47
|
+
"sqrt",
|
|
48
|
+
"abs",
|
|
49
|
+
"floor",
|
|
50
|
+
"ceil",
|
|
51
|
+
"round"
|
|
52
|
+
]);
|
|
53
|
+
var ALLOWED_CHARS = /^[a-zA-Z0-9_.+\-*/^()\s,]+$/;
|
|
54
|
+
var IDENTIFIER_RE = /[a-zA-Z][a-zA-Z0-9_]*/g;
|
|
55
|
+
var SUGGESTIONS = {
|
|
56
|
+
tg: "tan",
|
|
57
|
+
arcsin: "asin",
|
|
58
|
+
arccos: "acos",
|
|
59
|
+
arctan: "atan"
|
|
60
|
+
};
|
|
61
|
+
function errResult(message) {
|
|
62
|
+
return { ok: false, error: message, freeVars: /* @__PURE__ */ new Set() };
|
|
63
|
+
}
|
|
64
|
+
function validate(expr) {
|
|
65
|
+
const trimmed = expr.trim();
|
|
66
|
+
if (!trimmed) return errResult("Bi\u1EC3u th\u1EE9c r\u1ED7ng");
|
|
67
|
+
if (!ALLOWED_CHARS.test(trimmed)) return errResult("K\xFD t\u1EF1 kh\xF4ng h\u1EE3p l\u1EC7");
|
|
68
|
+
const ids = trimmed.match(IDENTIFIER_RE) ?? [];
|
|
69
|
+
const freeVars = /* @__PURE__ */ new Set();
|
|
70
|
+
for (const id of ids) {
|
|
71
|
+
if (id === "x" || id === "pi" || id === "e") continue;
|
|
72
|
+
if (ALLOWED_FUNCTIONS.has(id)) continue;
|
|
73
|
+
if (id.length === 1) {
|
|
74
|
+
freeVars.add(id);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const hint = SUGGESTIONS[id];
|
|
78
|
+
return errResult(
|
|
79
|
+
hint ? `T\xEAn h\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${id}". B\u1EA1n c\xF3 \xFD l\xE0 "${hint}" kh\xF4ng?` : `T\xEAn kh\xF4ng h\u1EE3p l\u1EC7: "${id}"`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const paramSubs = Object.fromEntries([...freeVars].map((v) => [v, 1]));
|
|
84
|
+
const rewritten = rewriteToJs(trimmed, paramSubs);
|
|
85
|
+
new Function("x", `return (${rewritten})`);
|
|
86
|
+
} catch {
|
|
87
|
+
return errResult("L\u1ED7i c\xFA ph\xE1p");
|
|
88
|
+
}
|
|
89
|
+
return { ok: true, freeVars };
|
|
90
|
+
}
|
|
91
|
+
var FUNCTION_REPLACEMENTS = [
|
|
92
|
+
// longest first để tránh substring conflict (asin trước sin)
|
|
93
|
+
["asin", "Math.asin"],
|
|
94
|
+
["acos", "Math.acos"],
|
|
95
|
+
["atan", "Math.atan"],
|
|
96
|
+
["sqrt", "Math.sqrt"],
|
|
97
|
+
["floor", "Math.floor"],
|
|
98
|
+
["round", "Math.round"],
|
|
99
|
+
["ceil", "Math.ceil"],
|
|
100
|
+
["sin", "Math.sin"],
|
|
101
|
+
["cos", "Math.cos"],
|
|
102
|
+
["tan", "Math.tan"],
|
|
103
|
+
["abs", "Math.abs"],
|
|
104
|
+
["exp", "Math.exp"],
|
|
105
|
+
["log", "Math.log10"],
|
|
106
|
+
["ln", "Math.log"]
|
|
107
|
+
];
|
|
108
|
+
function rewriteToJs(expr, params) {
|
|
109
|
+
let s = expr.replace(/\^/g, "**");
|
|
110
|
+
s = s.replace(/\bpi\b/g, "Math.PI");
|
|
111
|
+
s = s.replace(/\be\b/g, "Math.E");
|
|
112
|
+
for (const [from, to] of FUNCTION_REPLACEMENTS) {
|
|
113
|
+
s = s.replace(new RegExp(`\\b${from}\\b`, "g"), to);
|
|
114
|
+
}
|
|
115
|
+
for (const [name, value] of Object.entries(params)) {
|
|
116
|
+
if (name.length !== 1) continue;
|
|
117
|
+
s = s.replace(new RegExp(`\\b${name}\\b`, "g"), `(${value})`);
|
|
118
|
+
}
|
|
119
|
+
return s;
|
|
120
|
+
}
|
|
121
|
+
function compile(expr, paramValues) {
|
|
122
|
+
const v = validate(expr);
|
|
123
|
+
if (!v.ok) return { error: v.error ?? "Invalid" };
|
|
124
|
+
try {
|
|
125
|
+
const rewritten = rewriteToJs(expr, paramValues);
|
|
126
|
+
const raw = new Function("x", `return (${rewritten})`);
|
|
127
|
+
return (x) => {
|
|
128
|
+
try {
|
|
129
|
+
const y = raw(x);
|
|
130
|
+
return typeof y === "number" ? y : NaN;
|
|
131
|
+
} catch {
|
|
132
|
+
return NaN;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
} catch (err) {
|
|
136
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/stamps/graph-2d/editor/handlers.ts
|
|
141
|
+
function addPointOnCurve(graph, ctx, idFactory) {
|
|
142
|
+
if (!ctx.functionId) return graph;
|
|
143
|
+
const point = {
|
|
144
|
+
id: idFactory(),
|
|
145
|
+
functionId: ctx.functionId,
|
|
146
|
+
x: ctx.x
|
|
147
|
+
};
|
|
148
|
+
return { ...graph, points: [...graph.points, point] };
|
|
149
|
+
}
|
|
150
|
+
function addIntersection(graph, functionIdA, functionIdB, idFactory) {
|
|
151
|
+
if (functionIdA === functionIdB) return graph;
|
|
152
|
+
const exists = graph.intersections.some(
|
|
153
|
+
(i) => i.functionIdA === functionIdA && i.functionIdB === functionIdB || i.functionIdA === functionIdB && i.functionIdB === functionIdA
|
|
154
|
+
);
|
|
155
|
+
if (exists) return graph;
|
|
156
|
+
const intersection = {
|
|
157
|
+
id: idFactory(),
|
|
158
|
+
functionIdA,
|
|
159
|
+
functionIdB
|
|
160
|
+
};
|
|
161
|
+
return { ...graph, intersections: [...graph.intersections, intersection] };
|
|
162
|
+
}
|
|
163
|
+
function numericalDerivative(expression, paramValues, x, h = 1e-4) {
|
|
164
|
+
const fn = compile(expression, paramValues);
|
|
165
|
+
if (typeof fn !== "function") return NaN;
|
|
166
|
+
const y1 = fn(x - h);
|
|
167
|
+
const y2 = fn(x + h);
|
|
168
|
+
return (y2 - y1) / (2 * h);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/stamps/graph-2d/renderObjects.ts
|
|
172
|
+
function renderGraphObjects(board, graph) {
|
|
173
|
+
const paramMap = {};
|
|
174
|
+
for (const p of graph.parameters) paramMap[p.name] = p.value;
|
|
175
|
+
for (const f of graph.functions) {
|
|
176
|
+
if (!f.visible) continue;
|
|
177
|
+
const compiled = compile(f.expression, paramMap);
|
|
178
|
+
if (typeof compiled !== "function") continue;
|
|
179
|
+
const domain = f.domain ?? { min: graph.view.xMin, max: graph.view.xMax };
|
|
180
|
+
board.create("functiongraph", [compiled, domain.min, domain.max], {
|
|
181
|
+
strokeColor: f.color,
|
|
182
|
+
strokeWidth: 2,
|
|
183
|
+
name: f.name,
|
|
184
|
+
withLabel: false,
|
|
185
|
+
highlight: false
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
for (const point of graph.points) {
|
|
189
|
+
const fn = graph.functions.find((f) => f.id === point.functionId);
|
|
190
|
+
if (!fn || !fn.visible) continue;
|
|
191
|
+
const compiled = compile(fn.expression, paramMap);
|
|
192
|
+
if (typeof compiled !== "function") continue;
|
|
193
|
+
const y = compiled(point.x);
|
|
194
|
+
board.create("point", [point.x, y], {
|
|
195
|
+
name: point.label ?? "",
|
|
196
|
+
size: 3,
|
|
197
|
+
fillColor: fn.color,
|
|
198
|
+
strokeColor: fn.color,
|
|
199
|
+
withLabel: !!point.label
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
for (const inter of graph.intersections) {
|
|
203
|
+
const fa = graph.functions.find((f) => f.id === inter.functionIdA);
|
|
204
|
+
const fb = graph.functions.find((f) => f.id === inter.functionIdB);
|
|
205
|
+
if (!fa || !fb || !fa.visible || !fb.visible) continue;
|
|
206
|
+
const cfa = compile(fa.expression, paramMap);
|
|
207
|
+
const cfb = compile(fb.expression, paramMap);
|
|
208
|
+
if (typeof cfa !== "function" || typeof cfb !== "function") continue;
|
|
209
|
+
const roots = scanRoots((x) => cfa(x) - cfb(x), graph.view.xMin, graph.view.xMax);
|
|
210
|
+
for (const x of roots) {
|
|
211
|
+
board.create("point", [x, cfa(x)], {
|
|
212
|
+
size: 3,
|
|
213
|
+
fillColor: "#000",
|
|
214
|
+
strokeColor: "#000"
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
for (const tan of graph.tangents) {
|
|
219
|
+
const pt = graph.points.find((p) => p.id === tan.pointId);
|
|
220
|
+
if (!pt) continue;
|
|
221
|
+
const fn = graph.functions.find((f) => f.id === pt.functionId);
|
|
222
|
+
if (!fn || !fn.visible) continue;
|
|
223
|
+
const slope = numericalDerivative(fn.expression, paramMap, pt.x);
|
|
224
|
+
const cfn = compile(fn.expression, paramMap);
|
|
225
|
+
if (typeof cfn !== "function" || !Number.isFinite(slope)) continue;
|
|
226
|
+
const y0 = cfn(pt.x);
|
|
227
|
+
const x1 = graph.view.xMin;
|
|
228
|
+
const x2 = graph.view.xMax;
|
|
229
|
+
board.create(
|
|
230
|
+
"line",
|
|
231
|
+
[
|
|
232
|
+
[x1, slope * (x1 - pt.x) + y0],
|
|
233
|
+
[x2, slope * (x2 - pt.x) + y0]
|
|
234
|
+
],
|
|
235
|
+
{
|
|
236
|
+
strokeColor: fn.color,
|
|
237
|
+
strokeWidth: 1,
|
|
238
|
+
dash: 2,
|
|
239
|
+
straightFirst: false,
|
|
240
|
+
straightLast: false
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function scanRoots(fn, xMin, xMax, samples = 200) {
|
|
246
|
+
const roots = [];
|
|
247
|
+
const step = (xMax - xMin) / samples;
|
|
248
|
+
let prevX = xMin;
|
|
249
|
+
let prevY = fn(prevX);
|
|
250
|
+
for (let i = 1; i <= samples; i++) {
|
|
251
|
+
const x = xMin + i * step;
|
|
252
|
+
const y = fn(x);
|
|
253
|
+
if (Number.isFinite(prevY) && Number.isFinite(y) && prevY * y < 0) {
|
|
254
|
+
let a = prevX;
|
|
255
|
+
let b = x;
|
|
256
|
+
let ya = prevY;
|
|
257
|
+
for (let j = 0; j < 30; j++) {
|
|
258
|
+
const m = (a + b) / 2;
|
|
259
|
+
const ym = fn(m);
|
|
260
|
+
if (Math.abs(ym) < 1e-6) {
|
|
261
|
+
a = b = m;
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
if (ya * ym < 0) {
|
|
265
|
+
b = m;
|
|
266
|
+
} else {
|
|
267
|
+
a = m;
|
|
268
|
+
ya = ym;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
roots.push((a + b) / 2);
|
|
272
|
+
}
|
|
273
|
+
prevX = x;
|
|
274
|
+
prevY = y;
|
|
275
|
+
}
|
|
276
|
+
return roots;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/stamps/graph-2d/render.ts
|
|
280
|
+
async function renderGraph2dSvgFromState(jsonState) {
|
|
281
|
+
const parsed = parseSerializedGraph(jsonState);
|
|
282
|
+
if (!parsed) throw new Error("renderGraph2dSvgFromState: jsonState corrupt");
|
|
283
|
+
const JXG = (await import('jsxgraph')).default;
|
|
284
|
+
const opts = JXG.Options;
|
|
285
|
+
if (opts) {
|
|
286
|
+
opts.text = opts.text || {};
|
|
287
|
+
opts.text.display = "internal";
|
|
288
|
+
opts.text.useASCIIMathML = false;
|
|
289
|
+
opts.text.useMathJax = false;
|
|
290
|
+
opts.text.useKatex = false;
|
|
291
|
+
opts.label = opts.label || {};
|
|
292
|
+
opts.label.display = "internal";
|
|
293
|
+
}
|
|
294
|
+
const container = document.createElement("div");
|
|
295
|
+
container.id = `jxg_graph2d_off_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
296
|
+
container.style.cssText = "position:absolute;top:-99999px;left:-99999px;width:600px;height:400px;visibility:hidden;pointer-events:none;";
|
|
297
|
+
document.body.appendChild(container);
|
|
298
|
+
let board = null;
|
|
299
|
+
try {
|
|
300
|
+
board = JXG.JSXGraph.initBoard(container.id, {
|
|
301
|
+
boundingbox: [parsed.view.xMin, parsed.view.yMax, parsed.view.xMax, parsed.view.yMin],
|
|
302
|
+
axis: parsed.view.showAxis,
|
|
303
|
+
grid: parsed.view.showGrid,
|
|
304
|
+
showCopyright: false,
|
|
305
|
+
showNavigation: false,
|
|
306
|
+
keepAspectRatio: false
|
|
307
|
+
});
|
|
308
|
+
renderGraphObjects(board, parsed);
|
|
309
|
+
board.update();
|
|
310
|
+
const svgEl = container.querySelector("svg");
|
|
311
|
+
if (!svgEl) throw new Error("renderGraph2dSvgFromState: no svg generated");
|
|
312
|
+
return svgEl.outerHTML;
|
|
313
|
+
} finally {
|
|
314
|
+
try {
|
|
315
|
+
if (board) JXG.JSXGraph.freeBoard(board);
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
if (container.parentNode) container.parentNode.removeChild(container);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/stamps/graph-2d/types.ts
|
|
323
|
+
function isGraph2DCustomData(data) {
|
|
324
|
+
if (!data || typeof data !== "object") return false;
|
|
325
|
+
const d = data;
|
|
326
|
+
return d.kind === "graph2d" && d.version === 1 && typeof d.jsonState === "string";
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export { EMPTY_GRAPH, addIntersection, addPointOnCurve, compile, isGraph2DCustomData, numericalDerivative, parseSerializedGraph, renderGraph2dSvgFromState, stringifySerializedGraph, validate };
|
|
330
|
+
//# sourceMappingURL=chunk-HM7RIXJE.mjs.map
|
|
331
|
+
//# sourceMappingURL=chunk-HM7RIXJE.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stamps/graph-2d/serialize.ts","../src/stamps/graph-2d/parser.ts","../src/stamps/graph-2d/editor/handlers.ts","../src/stamps/graph-2d/renderObjects.ts","../src/stamps/graph-2d/render.ts","../src/stamps/graph-2d/types.ts"],"names":[],"mappings":";AAoDO,IAAM,WAAA,GAA+B;AAAA,EAC1C,OAAA,EAAS,CAAA;AAAA,EACT,IAAA,EAAM,EAAE,IAAA,EAAM,GAAA,EAAK,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,GAAA,EAAK,IAAA,EAAM,EAAA,EAAI,QAAA,EAAU,IAAA,EAAM,UAAU,IAAA,EAAK;AAAA,EACjF,WAAW,EAAC;AAAA,EACZ,YAAY,EAAC;AAAA,EACb,QAAQ,EAAC;AAAA,EACT,eAAe,EAAC;AAAA,EAChB,UAAU;AACZ;AAEO,SAAS,yBAAyB,KAAA,EAAgC;AACvE,EAAA,OAAO,IAAA,CAAK,UAAU,KAAK,CAAA;AAC7B;AAEO,SAAS,qBAAqB,SAAA,EAA2C;AAC9E,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,IAAA,CAAK,MAAM,SAAS,CAAA;AAAA,EAC5B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI,CAAC,OAAO,OAAO,GAAA,KAAQ,YAAY,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG,OAAO,IAAA;AAClE,EAAA,MAAM,CAAA,GAAI,GAAA;AACV,EAAA,IAAI,CAAA,CAAE,OAAA,KAAY,CAAA,EAAG,OAAO,IAAA;AAC5B,EAAA,IAAI,CAAC,CAAA,CAAE,IAAA,IAAQ,OAAO,CAAA,CAAE,IAAA,KAAS,UAAU,OAAO,IAAA;AAClD,EAAA,MAAM,IAAI,CAAA,CAAE,IAAA;AACZ,EAAA,IACE,OAAO,EAAE,IAAA,KAAS,QAAA,IAClB,OAAO,CAAA,CAAE,IAAA,KAAS,QAAA,IAClB,OAAO,CAAA,CAAE,IAAA,KAAS,YAClB,OAAO,CAAA,CAAE,IAAA,KAAS,QAAA,IAClB,OAAO,CAAA,CAAE,aAAa,SAAA,IACtB,OAAO,CAAA,CAAE,QAAA,KAAa,SAAA,EACtB;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,KAAA,MAAW,OAAO,CAAC,WAAA,EAAa,cAAc,QAAA,EAAU,eAAA,EAAiB,UAAU,CAAA,EAAG;AACpF,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,EAAE,GAAG,CAAC,GAAG,OAAO,IAAA;AAAA,EACrC;AACA,EAAA,OAAO,GAAA;AACT;;;AC5FA,IAAM,iBAAA,uBAAwB,GAAA,CAAI;AAAA,EAChC,KAAA;AAAA,EAAO,KAAA;AAAA,EAAO,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,MAAA;AAAA,EACrC,KAAA;AAAA,EAAO,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,KAAA;AAAA,EAC5B,OAAA;AAAA,EAAS,MAAA;AAAA,EAAQ;AACnB,CAAC,CAAA;AAED,IAAM,aAAA,GAAgB,6BAAA;AACtB,IAAM,aAAA,GAAgB,wBAAA;AAEtB,IAAM,WAAA,GAAsC;AAAA,EAC1C,EAAA,EAAI,KAAA;AAAA,EACJ,MAAA,EAAQ,MAAA;AAAA,EACR,MAAA,EAAQ,MAAA;AAAA,EACR,MAAA,EAAQ;AACV,CAAA;AAQA,SAAS,UAAU,OAAA,EAA8B;AAC/C,EAAA,OAAO,EAAE,IAAI,KAAA,EAAO,KAAA,EAAO,SAAS,QAAA,kBAAU,IAAI,KAAI,EAAE;AAC1D;AAEO,SAAS,SAAS,IAAA,EAA2B;AAClD,EAAA,MAAM,OAAA,GAAU,KAAK,IAAA,EAAK;AAC1B,EAAA,IAAI,CAAC,OAAA,EAAS,OAAO,SAAA,CAAU,+BAAgB,CAAA;AAC/C,EAAA,IAAI,CAAC,aAAA,CAAc,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,UAAU,yCAAoB,CAAA;AAEvE,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,KAAA,CAAM,aAAa,KAAK,EAAC;AAC7C,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAY;AACjC,EAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,IAAA,IAAI,EAAA,KAAO,GAAA,IAAO,EAAA,KAAO,IAAA,IAAQ,OAAO,GAAA,EAAK;AAC7C,IAAA,IAAI,iBAAA,CAAkB,GAAA,CAAI,EAAE,CAAA,EAAG;AAC/B,IAAA,IAAI,EAAA,CAAG,WAAW,CAAA,EAAG;AACnB,MAAA,QAAA,CAAS,IAAI,EAAE,CAAA;AACf,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,GAAO,YAAY,EAAE,CAAA;AAC3B,IAAA,OAAO,SAAA;AAAA,MACL,OACI,CAAA,0CAAA,EAA0B,EAAE,iCAAmB,IAAI,CAAA,WAAA,CAAA,GACnD,sCAAsB,EAAE,CAAA,CAAA;AAAA,KAC9B;AAAA,EACF;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,WAAA,CAAY,CAAC,GAAG,QAAQ,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,EAAG,CAAC,CAAC,CAAC,CAAA;AACrE,IAAA,MAAM,SAAA,GAAY,WAAA,CAAY,OAAA,EAAS,SAAS,CAAA;AAChD,IAAA,IAAI,QAAA,CAAS,GAAA,EAAK,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA,EAC3C,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,UAAU,wBAAa,CAAA;AAAA,EAChC;AAEA,EAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,QAAA,EAAS;AAC9B;AAEA,IAAM,qBAAA,GAAiD;AAAA;AAAA,EAErD,CAAC,QAAQ,WAAW,CAAA;AAAA,EACpB,CAAC,QAAQ,WAAW,CAAA;AAAA,EACpB,CAAC,QAAQ,WAAW,CAAA;AAAA,EACpB,CAAC,QAAQ,WAAW,CAAA;AAAA,EACpB,CAAC,SAAS,YAAY,CAAA;AAAA,EACtB,CAAC,SAAS,YAAY,CAAA;AAAA,EACtB,CAAC,QAAQ,WAAW,CAAA;AAAA,EACpB,CAAC,OAAO,UAAU,CAAA;AAAA,EAClB,CAAC,OAAO,UAAU,CAAA;AAAA,EAClB,CAAC,OAAO,UAAU,CAAA;AAAA,EAClB,CAAC,OAAO,UAAU,CAAA;AAAA,EAClB,CAAC,OAAO,UAAU,CAAA;AAAA,EAClB,CAAC,OAAO,YAAY,CAAA;AAAA,EACpB,CAAC,MAAM,UAAU;AACnB,CAAA;AAEO,SAAS,WAAA,CACd,MACA,MAAA,EACQ;AACR,EAAA,IAAI,CAAA,GAAI,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,IAAI,CAAA;AAChC,EAAA,CAAA,GAAI,CAAA,CAAE,OAAA,CAAQ,SAAA,EAAW,SAAS,CAAA;AAClC,EAAA,CAAA,GAAI,CAAA,CAAE,OAAA,CAAQ,QAAA,EAAU,QAAQ,CAAA;AAChC,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,EAAE,CAAA,IAAK,qBAAA,EAAuB;AAC9C,IAAA,CAAA,GAAI,CAAA,CAAE,QAAQ,IAAI,MAAA,CAAO,MAAM,IAAI,CAAA,GAAA,CAAA,EAAO,GAAG,CAAA,EAAG,EAAE,CAAA;AAAA,EACpD;AACA,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAClD,IAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACvB,IAAA,CAAA,GAAI,CAAA,CAAE,OAAA,CAAQ,IAAI,MAAA,CAAO,CAAA,GAAA,EAAM,IAAI,CAAA,GAAA,CAAA,EAAO,GAAG,CAAA,EAAG,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,CAAG,CAAA;AAAA,EAC9D;AACA,EAAA,OAAO,CAAA;AACT;AAEO,SAAS,OAAA,CACd,MACA,WAAA,EAC6C;AAC7C,EAAA,MAAM,CAAA,GAAI,SAAS,IAAI,CAAA;AACvB,EAAA,IAAI,CAAC,EAAE,EAAA,EAAI,OAAO,EAAE,KAAA,EAAO,CAAA,CAAE,SAAS,SAAA,EAAU;AAChD,EAAA,IAAI;AACF,IAAA,MAAM,SAAA,GAAY,WAAA,CAAY,IAAA,EAAM,WAAW,CAAA;AAC/C,IAAA,MAAM,MAAM,IAAI,QAAA,CAAS,GAAA,EAAK,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAG,CAAA;AACrD,IAAA,OAAO,CAAC,CAAA,KAAc;AACpB,MAAA,IAAI;AACF,QAAA,MAAM,CAAA,GAAI,IAAI,CAAC,CAAA;AACf,QAAA,OAAO,OAAO,CAAA,KAAM,QAAA,GAAW,CAAA,GAAI,GAAA;AAAA,MACrC,CAAA,CAAA,MAAQ;AACN,QAAA,OAAO,GAAA;AAAA,MACT;AAAA,IACF,CAAA;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,OAAO,EAAE,OAAO,GAAA,YAAe,KAAA,GAAQ,IAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAA,EAAE;AAAA,EACnE;AACF;;;ACpGO,SAAS,eAAA,CACd,KAAA,EACA,GAAA,EACA,SAAA,EACiB;AACjB,EAAA,IAAI,CAAC,GAAA,CAAI,UAAA,EAAY,OAAO,KAAA;AAC5B,EAAA,MAAM,KAAA,GAAyB;AAAA,IAC7B,IAAI,SAAA,EAAU;AAAA,IACd,YAAY,GAAA,CAAI,UAAA;AAAA,IAChB,GAAG,GAAA,CAAI;AAAA,GACT;AACA,EAAA,OAAO,EAAE,GAAG,KAAA,EAAO,MAAA,EAAQ,CAAC,GAAG,KAAA,CAAM,MAAA,EAAQ,KAAK,CAAA,EAAE;AACtD;AAEO,SAAS,eAAA,CACd,KAAA,EACA,WAAA,EACA,WAAA,EACA,SAAA,EACiB;AACjB,EAAA,IAAI,WAAA,KAAgB,aAAa,OAAO,KAAA;AACxC,EAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,IAAA;AAAA,IACjC,CAAC,CAAA,KACE,CAAA,CAAE,WAAA,KAAgB,WAAA,IAAe,CAAA,CAAE,WAAA,KAAgB,WAAA,IACnD,CAAA,CAAE,WAAA,KAAgB,WAAA,IAAe,CAAA,CAAE,WAAA,KAAgB;AAAA,GACxD;AACA,EAAA,IAAI,QAAQ,OAAO,KAAA;AACnB,EAAA,MAAM,YAAA,GAAuC;AAAA,IAC3C,IAAI,SAAA,EAAU;AAAA,IACd,WAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,OAAO,EAAE,GAAG,KAAA,EAAO,aAAA,EAAe,CAAC,GAAG,KAAA,CAAM,aAAA,EAAe,YAAY,CAAA,EAAE;AAC3E;AAgBO,SAAS,mBAAA,CACd,UAAA,EACA,WAAA,EACA,CAAA,EACA,IAAI,IAAA,EACI;AACR,EAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,UAAA,EAAY,WAAW,CAAA;AAC1C,EAAA,IAAI,OAAO,EAAA,KAAO,UAAA,EAAY,OAAO,GAAA;AACrC,EAAA,MAAM,EAAA,GAAK,EAAA,CAAG,CAAA,GAAI,CAAC,CAAA;AACnB,EAAA,MAAM,EAAA,GAAK,EAAA,CAAG,CAAA,GAAI,CAAC,CAAA;AACnB,EAAA,OAAA,CAAQ,EAAA,GAAK,OAAO,CAAA,GAAI,CAAA,CAAA;AAC1B;;;AChEO,SAAS,kBAAA,CAEd,OACA,KAAA,EACM;AACN,EAAA,MAAM,WAAmC,EAAC;AAC1C,EAAA,KAAA,MAAW,KAAK,KAAA,CAAM,UAAA,WAAqB,CAAA,CAAE,IAAI,IAAI,CAAA,CAAE,KAAA;AAGvD,EAAA,KAAA,MAAW,CAAA,IAAK,MAAM,SAAA,EAAW;AAC/B,IAAA,IAAI,CAAC,EAAE,OAAA,EAAS;AAChB,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,CAAA,CAAE,UAAA,EAAY,QAAQ,CAAA;AAC/C,IAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AACpC,IAAA,MAAM,MAAA,GAAS,CAAA,CAAE,MAAA,IAAU,EAAE,GAAA,EAAK,KAAA,CAAM,IAAA,CAAK,IAAA,EAAM,GAAA,EAAK,KAAA,CAAM,IAAA,CAAK,IAAA,EAAK;AACxE,IAAA,KAAA,CAAM,MAAA,CAAO,iBAAiB,CAAC,QAAA,EAAU,OAAO,GAAA,EAAK,MAAA,CAAO,GAAG,CAAA,EAAG;AAAA,MAChE,aAAa,CAAA,CAAE,KAAA;AAAA,MACf,WAAA,EAAa,CAAA;AAAA,MACb,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,SAAA,EAAW,KAAA;AAAA,MACX,SAAA,EAAW;AAAA,KACZ,CAAA;AAAA,EACH;AAGA,EAAA,KAAA,MAAW,KAAA,IAAS,MAAM,MAAA,EAAQ;AAChC,IAAA,MAAM,EAAA,GAAK,MAAM,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,KAAA,CAAM,UAAU,CAAA;AAChE,IAAA,IAAI,CAAC,EAAA,IAAM,CAAC,EAAA,CAAG,OAAA,EAAS;AACxB,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,EAAA,CAAG,UAAA,EAAY,QAAQ,CAAA;AAChD,IAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AACpC,IAAA,MAAM,CAAA,GAAI,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA;AAC1B,IAAA,KAAA,CAAM,OAAO,OAAA,EAAS,CAAC,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,EAAG;AAAA,MAClC,IAAA,EAAM,MAAM,KAAA,IAAS,EAAA;AAAA,MACrB,IAAA,EAAM,CAAA;AAAA,MACN,WAAW,EAAA,CAAG,KAAA;AAAA,MACd,aAAa,EAAA,CAAG,KAAA;AAAA,MAChB,SAAA,EAAW,CAAC,CAAC,KAAA,CAAM;AAAA,KACpB,CAAA;AAAA,EACH;AAGA,EAAA,KAAA,MAAW,KAAA,IAAS,MAAM,aAAA,EAAe;AACvC,IAAA,MAAM,EAAA,GAAK,MAAM,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,KAAA,CAAM,WAAW,CAAA;AACjE,IAAA,MAAM,EAAA,GAAK,MAAM,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,KAAA,CAAM,WAAW,CAAA;AACjE,IAAA,IAAI,CAAC,MAAM,CAAC,EAAA,IAAM,CAAC,EAAA,CAAG,OAAA,IAAW,CAAC,EAAA,CAAG,OAAA,EAAS;AAC9C,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,EAAA,CAAG,UAAA,EAAY,QAAQ,CAAA;AAC3C,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,EAAA,CAAG,UAAA,EAAY,QAAQ,CAAA;AAC3C,IAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,IAAc,OAAO,QAAQ,UAAA,EAAY;AAC5D,IAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,CAAC,CAAA,KAAc,IAAI,CAAC,CAAA,GAAI,GAAA,CAAI,CAAC,GAAG,KAAA,CAAM,IAAA,CAAK,IAAA,EAAM,KAAA,CAAM,KAAK,IAAI,CAAA;AACxF,IAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,MAAA,KAAA,CAAM,OAAO,OAAA,EAAS,CAAC,GAAG,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG;AAAA,QACjC,IAAA,EAAM,CAAA;AAAA,QACN,SAAA,EAAW,MAAA;AAAA,QACX,WAAA,EAAa;AAAA,OACd,CAAA;AAAA,IACH;AAAA,EACF;AAGA,EAAA,KAAA,MAAW,GAAA,IAAO,MAAM,QAAA,EAAU;AAChC,IAAA,MAAM,EAAA,GAAK,MAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,GAAA,CAAI,OAAO,CAAA;AACxD,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,MAAM,EAAA,GAAK,MAAM,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,EAAA,CAAG,UAAU,CAAA;AAC7D,IAAA,IAAI,CAAC,EAAA,IAAM,CAAC,EAAA,CAAG,OAAA,EAAS;AACxB,IAAA,MAAM,QAAQ,mBAAA,CAAoB,EAAA,CAAG,UAAA,EAAY,QAAA,EAAU,GAAG,CAAC,CAAA;AAC/D,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,EAAA,CAAG,UAAA,EAAY,QAAQ,CAAA;AAC3C,IAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,IAAc,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,EAAG;AAC1D,IAAA,MAAM,EAAA,GAAK,GAAA,CAAI,EAAA,CAAG,CAAC,CAAA;AACnB,IAAA,MAAM,EAAA,GAAK,MAAM,IAAA,CAAK,IAAA;AACtB,IAAA,MAAM,EAAA,GAAK,MAAM,IAAA,CAAK,IAAA;AACtB,IAAA,KAAA,CAAM,MAAA;AAAA,MACJ,MAAA;AAAA,MACA;AAAA,QACE,CAAC,EAAA,EAAI,KAAA,IAAS,EAAA,GAAK,EAAA,CAAG,KAAK,EAAE,CAAA;AAAA,QAC7B,CAAC,EAAA,EAAI,KAAA,IAAS,EAAA,GAAK,EAAA,CAAG,KAAK,EAAE;AAAA,OAC/B;AAAA,MACA;AAAA,QACE,aAAa,EAAA,CAAG,KAAA;AAAA,QAChB,WAAA,EAAa,CAAA;AAAA,QACb,IAAA,EAAM,CAAA;AAAA,QACN,aAAA,EAAe,KAAA;AAAA,QACf,YAAA,EAAc;AAAA;AAChB,KACF;AAAA,EACF;AACF;AAEA,SAAS,SAAA,CACP,EAAA,EACA,IAAA,EACA,IAAA,EACA,UAAU,GAAA,EACA;AACV,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,MAAM,IAAA,GAAA,CAAQ,OAAO,IAAA,IAAQ,OAAA;AAC7B,EAAA,IAAI,KAAA,GAAQ,IAAA;AACZ,EAAA,IAAI,KAAA,GAAQ,GAAG,KAAK,CAAA;AACpB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,OAAA,EAAS,CAAA,EAAA,EAAK;AACjC,IAAA,MAAM,CAAA,GAAI,OAAO,CAAA,GAAI,IAAA;AACrB,IAAA,MAAM,CAAA,GAAI,GAAG,CAAC,CAAA;AACd,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,MAAA,CAAO,SAAS,CAAC,CAAA,IAAK,KAAA,GAAQ,CAAA,GAAI,CAAA,EAAG;AACjE,MAAA,IAAI,CAAA,GAAI,KAAA;AACR,MAAA,IAAI,CAAA,GAAI,CAAA;AACR,MAAA,IAAI,EAAA,GAAK,KAAA;AACT,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,EAAA,EAAI,CAAA,EAAA,EAAK;AAC3B,QAAA,MAAM,CAAA,GAAA,CAAK,IAAI,CAAA,IAAK,CAAA;AACpB,QAAA,MAAM,EAAA,GAAK,GAAG,CAAC,CAAA;AACf,QAAA,IAAI,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,GAAI,IAAA,EAAM;AACvB,UAAA,CAAA,GAAI,CAAA,GAAI,CAAA;AACR,UAAA;AAAA,QACF;AACA,QAAA,IAAI,EAAA,GAAK,KAAK,CAAA,EAAG;AACf,UAAA,CAAA,GAAI,CAAA;AAAA,QACN,CAAA,MAAO;AACL,UAAA,CAAA,GAAI,CAAA;AACJ,UAAA,EAAA,GAAK,EAAA;AAAA,QACP;AAAA,MACF;AACA,MAAA,KAAA,CAAM,IAAA,CAAA,CAAM,CAAA,GAAI,CAAA,IAAK,CAAC,CAAA;AAAA,IACxB;AACA,IAAA,KAAA,GAAQ,CAAA;AACR,IAAA,KAAA,GAAQ,CAAA;AAAA,EACV;AACA,EAAA,OAAO,KAAA;AACT;;;ACtHA,eAAsB,0BAA0B,SAAA,EAAoC;AAClF,EAAA,MAAM,MAAA,GAAS,qBAAqB,SAAS,CAAA;AAC7C,EAAA,IAAI,CAAC,MAAA,EAAQ,MAAM,IAAI,MAAM,8CAA8C,CAAA;AAE3E,EAAA,MAAM,GAAA,GAAA,CAAO,MAAM,OAAO,UAAU,CAAA,EAAG,OAAA;AAEvC,EAAA,MAAM,OAAQ,GAAA,CAAY,OAAA;AAC1B,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA,CAAK,IAAA,IAAQ,EAAC;AAC1B,IAAA,IAAA,CAAK,KAAK,OAAA,GAAU,UAAA;AACpB,IAAA,IAAA,CAAK,KAAK,cAAA,GAAiB,KAAA;AAC3B,IAAA,IAAA,CAAK,KAAK,UAAA,GAAa,KAAA;AACvB,IAAA,IAAA,CAAK,KAAK,QAAA,GAAW,KAAA;AACrB,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA,CAAK,KAAA,IAAS,EAAC;AAC5B,IAAA,IAAA,CAAK,MAAM,OAAA,GAAU,UAAA;AAAA,EACvB;AAEA,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC9C,EAAA,SAAA,CAAU,EAAA,GAAK,CAAA,gBAAA,EAAmB,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,EAAO,CAAE,SAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AACtF,EAAA,SAAA,CAAU,MAAM,OAAA,GACd,8GAAA;AACF,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,SAAS,CAAA;AAEnC,EAAA,IAAI,KAAA,GAAiB,IAAA;AACrB,EAAA,IAAI;AAEF,IAAA,KAAA,GAAS,GAAA,CAAY,QAAA,CAAS,SAAA,CAAU,SAAA,CAAU,EAAA,EAAI;AAAA,MACpD,WAAA,EAAa,CAAC,MAAA,CAAO,IAAA,CAAK,IAAA,EAAM,MAAA,CAAO,IAAA,CAAK,IAAA,EAAM,MAAA,CAAO,IAAA,CAAK,IAAA,EAAM,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,MACpF,IAAA,EAAM,OAAO,IAAA,CAAK,QAAA;AAAA,MAClB,IAAA,EAAM,OAAO,IAAA,CAAK,QAAA;AAAA,MAClB,aAAA,EAAe,KAAA;AAAA,MACf,cAAA,EAAgB,KAAA;AAAA,MAChB,eAAA,EAAiB;AAAA,KAClB,CAAA;AACD,IAAA,kBAAA,CAAmB,OAAO,MAAM,CAAA;AAEhC,IAAC,MAAc,MAAA,EAAO;AACtB,IAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,aAAA,CAAc,KAAK,CAAA;AAC3C,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,MAAM,6CAA6C,CAAA;AACzE,IAAA,OAAO,KAAA,CAAM,SAAA;AAAA,EACf,CAAA,SAAE;AACA,IAAA,IAAI;AAEF,MAAA,IAAI,KAAA,EAAQ,GAAA,CAAY,QAAA,CAAS,UAAU,KAAK,CAAA;AAAA,IAClD,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,IAAI,SAAA,CAAU,UAAA,EAAY,SAAA,CAAU,UAAA,CAAW,YAAY,SAAS,CAAA;AAAA,EACtE;AACF;;;ACtDO,SAAS,oBAAoB,IAAA,EAA0C;AAC5E,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,KAAA;AAC9C,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,OAAO,CAAA,CAAE,SAAS,SAAA,IAAa,CAAA,CAAE,YAAY,CAAA,IAAK,OAAO,EAAE,SAAA,KAAc,QAAA;AAC3E","file":"chunk-HM7RIXJE.mjs","sourcesContent":["export interface SerializedGraph {\n version: 1;\n view: {\n xMin: number;\n xMax: number;\n yMin: number;\n yMax: number;\n showAxis: boolean;\n showGrid: boolean;\n };\n functions: SerializedFunction[];\n parameters: SerializedParameter[];\n points: SerializedPoint[];\n intersections: SerializedIntersection[];\n tangents: SerializedTangent[];\n}\n\nexport interface SerializedFunction {\n id: string;\n name: string;\n expression: string;\n color: string;\n visible: boolean;\n domain?: { min: number; max: number };\n}\n\nexport interface SerializedParameter {\n name: string;\n value: number;\n min: number;\n max: number;\n step: number;\n}\n\nexport interface SerializedPoint {\n id: string;\n functionId: string;\n x: number;\n label?: string;\n}\n\nexport interface SerializedIntersection {\n id: string;\n functionIdA: string;\n functionIdB: string;\n}\n\nexport interface SerializedTangent {\n id: string;\n pointId: string;\n}\n\nexport const EMPTY_GRAPH: SerializedGraph = {\n version: 1,\n view: { xMin: -10, xMax: 10, yMin: -10, yMax: 10, showAxis: true, showGrid: true },\n functions: [],\n parameters: [],\n points: [],\n intersections: [],\n tangents: [],\n};\n\nexport function stringifySerializedGraph(graph: SerializedGraph): string {\n return JSON.stringify(graph);\n}\n\nexport function parseSerializedGraph(jsonState: string): SerializedGraph | null {\n let raw: unknown;\n try {\n raw = JSON.parse(jsonState);\n } catch {\n return null;\n }\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const r = raw as Record<string, unknown>;\n if (r.version !== 1) return null;\n if (!r.view || typeof r.view !== 'object') return null;\n const v = r.view as Record<string, unknown>;\n if (\n typeof v.xMin !== 'number' ||\n typeof v.xMax !== 'number' ||\n typeof v.yMin !== 'number' ||\n typeof v.yMax !== 'number' ||\n typeof v.showAxis !== 'boolean' ||\n typeof v.showGrid !== 'boolean'\n ) {\n return null;\n }\n for (const key of ['functions', 'parameters', 'points', 'intersections', 'tangents']) {\n if (!Array.isArray(r[key])) return null;\n }\n return raw as SerializedGraph;\n}\n","const ALLOWED_FUNCTIONS = new Set([\n 'sin', 'cos', 'tan', 'asin', 'acos', 'atan',\n 'log', 'ln', 'exp', 'sqrt', 'abs',\n 'floor', 'ceil', 'round',\n]);\n\nconst ALLOWED_CHARS = /^[a-zA-Z0-9_.+\\-*/^()\\s,]+$/;\nconst IDENTIFIER_RE = /[a-zA-Z][a-zA-Z0-9_]*/g;\n\nconst SUGGESTIONS: Record<string, string> = {\n tg: 'tan',\n arcsin: 'asin',\n arccos: 'acos',\n arctan: 'atan',\n};\n\nexport interface ParseResult {\n ok: boolean;\n error?: string;\n freeVars: Set<string>;\n}\n\nfunction errResult(message: string): ParseResult {\n return { ok: false, error: message, freeVars: new Set() };\n}\n\nexport function validate(expr: string): ParseResult {\n const trimmed = expr.trim();\n if (!trimmed) return errResult('Biểu thức rỗng');\n if (!ALLOWED_CHARS.test(trimmed)) return errResult('Ký tự không hợp lệ');\n\n const ids = trimmed.match(IDENTIFIER_RE) ?? [];\n const freeVars = new Set<string>();\n for (const id of ids) {\n if (id === 'x' || id === 'pi' || id === 'e') continue;\n if (ALLOWED_FUNCTIONS.has(id)) continue;\n if (id.length === 1) {\n freeVars.add(id);\n continue;\n }\n const hint = SUGGESTIONS[id];\n return errResult(\n hint\n ? `Tên hàm không hợp lệ: \"${id}\". Bạn có ý là \"${hint}\" không?`\n : `Tên không hợp lệ: \"${id}\"`,\n );\n }\n\n try {\n const paramSubs = Object.fromEntries([...freeVars].map((v) => [v, 1]));\n const rewritten = rewriteToJs(trimmed, paramSubs);\n new Function('x', `return (${rewritten})`);\n } catch {\n return errResult('Lỗi cú pháp');\n }\n\n return { ok: true, freeVars };\n}\n\nconst FUNCTION_REPLACEMENTS: Array<[string, string]> = [\n // longest first để tránh substring conflict (asin trước sin)\n ['asin', 'Math.asin'],\n ['acos', 'Math.acos'],\n ['atan', 'Math.atan'],\n ['sqrt', 'Math.sqrt'],\n ['floor', 'Math.floor'],\n ['round', 'Math.round'],\n ['ceil', 'Math.ceil'],\n ['sin', 'Math.sin'],\n ['cos', 'Math.cos'],\n ['tan', 'Math.tan'],\n ['abs', 'Math.abs'],\n ['exp', 'Math.exp'],\n ['log', 'Math.log10'],\n ['ln', 'Math.log'],\n];\n\nexport function rewriteToJs(\n expr: string,\n params: Record<string, number>,\n): string {\n let s = expr.replace(/\\^/g, '**');\n s = s.replace(/\\bpi\\b/g, 'Math.PI');\n s = s.replace(/\\be\\b/g, 'Math.E');\n for (const [from, to] of FUNCTION_REPLACEMENTS) {\n s = s.replace(new RegExp(`\\\\b${from}\\\\b`, 'g'), to);\n }\n for (const [name, value] of Object.entries(params)) {\n if (name.length !== 1) continue;\n s = s.replace(new RegExp(`\\\\b${name}\\\\b`, 'g'), `(${value})`);\n }\n return s;\n}\n\nexport function compile(\n expr: string,\n paramValues: Record<string, number>,\n): ((x: number) => number) | { error: string } {\n const v = validate(expr);\n if (!v.ok) return { error: v.error ?? 'Invalid' };\n try {\n const rewritten = rewriteToJs(expr, paramValues);\n const raw = new Function('x', `return (${rewritten})`) as (x: number) => number;\n return (x: number) => {\n try {\n const y = raw(x);\n return typeof y === 'number' ? y : NaN;\n } catch {\n return NaN;\n }\n };\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) };\n }\n}\n","import type {\n SerializedGraph,\n SerializedPoint,\n SerializedIntersection,\n SerializedTangent,\n} from '../serialize';\nimport { compile } from '../parser';\n\nexport interface ClickContext {\n x: number;\n y: number;\n functionId?: string;\n}\n\nexport function addPointOnCurve(\n graph: SerializedGraph,\n ctx: ClickContext,\n idFactory: () => string,\n): SerializedGraph {\n if (!ctx.functionId) return graph;\n const point: SerializedPoint = {\n id: idFactory(),\n functionId: ctx.functionId,\n x: ctx.x,\n };\n return { ...graph, points: [...graph.points, point] };\n}\n\nexport function addIntersection(\n graph: SerializedGraph,\n functionIdA: string,\n functionIdB: string,\n idFactory: () => string,\n): SerializedGraph {\n if (functionIdA === functionIdB) return graph;\n const exists = graph.intersections.some(\n (i) =>\n (i.functionIdA === functionIdA && i.functionIdB === functionIdB) ||\n (i.functionIdA === functionIdB && i.functionIdB === functionIdA),\n );\n if (exists) return graph;\n const intersection: SerializedIntersection = {\n id: idFactory(),\n functionIdA,\n functionIdB,\n };\n return { ...graph, intersections: [...graph.intersections, intersection] };\n}\n\nexport function addTangent(\n graph: SerializedGraph,\n pointId: string,\n idFactory: () => string,\n): SerializedGraph {\n const exists = graph.tangents.some((t) => t.pointId === pointId);\n if (exists) return graph;\n const tangent: SerializedTangent = { id: idFactory(), pointId };\n return { ...graph, tangents: [...graph.tangents, tangent] };\n}\n\n/**\n * Numerical derivative via centered difference. Dùng cho tangent tool.\n */\nexport function numericalDerivative(\n expression: string,\n paramValues: Record<string, number>,\n x: number,\n h = 1e-4,\n): number {\n const fn = compile(expression, paramValues);\n if (typeof fn !== 'function') return NaN;\n const y1 = fn(x - h);\n const y2 = fn(x + h);\n return (y2 - y1) / (2 * h);\n}\n","import type { SerializedGraph } from './serialize';\nimport { compile } from './parser';\nimport { numericalDerivative } from './editor/handlers';\n\n/**\n * Render tất cả objects (functions, points, intersections, tangents) lên board JSXGraph.\n *\n * Pure function — không giữ state, dùng cho one-shot render (render.ts).\n * MiniBoard giữ syncObjects riêng để diff/track curves theo id.\n */\nexport function renderGraphObjects(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n board: any,\n graph: SerializedGraph,\n): void {\n const paramMap: Record<string, number> = {};\n for (const p of graph.parameters) paramMap[p.name] = p.value;\n\n // Functions\n for (const f of graph.functions) {\n if (!f.visible) continue;\n const compiled = compile(f.expression, paramMap);\n if (typeof compiled !== 'function') continue;\n const domain = f.domain ?? { min: graph.view.xMin, max: graph.view.xMax };\n board.create('functiongraph', [compiled, domain.min, domain.max], {\n strokeColor: f.color,\n strokeWidth: 2,\n name: f.name,\n withLabel: false,\n highlight: false,\n });\n }\n\n // Points\n for (const point of graph.points) {\n const fn = graph.functions.find((f) => f.id === point.functionId);\n if (!fn || !fn.visible) continue;\n const compiled = compile(fn.expression, paramMap);\n if (typeof compiled !== 'function') continue;\n const y = compiled(point.x);\n board.create('point', [point.x, y], {\n name: point.label ?? '',\n size: 3,\n fillColor: fn.color,\n strokeColor: fn.color,\n withLabel: !!point.label,\n });\n }\n\n // Intersections\n for (const inter of graph.intersections) {\n const fa = graph.functions.find((f) => f.id === inter.functionIdA);\n const fb = graph.functions.find((f) => f.id === inter.functionIdB);\n if (!fa || !fb || !fa.visible || !fb.visible) continue;\n const cfa = compile(fa.expression, paramMap);\n const cfb = compile(fb.expression, paramMap);\n if (typeof cfa !== 'function' || typeof cfb !== 'function') continue;\n const roots = scanRoots((x: number) => cfa(x) - cfb(x), graph.view.xMin, graph.view.xMax);\n for (const x of roots) {\n board.create('point', [x, cfa(x)], {\n size: 3,\n fillColor: '#000',\n strokeColor: '#000',\n });\n }\n }\n\n // Tangents\n for (const tan of graph.tangents) {\n const pt = graph.points.find((p) => p.id === tan.pointId);\n if (!pt) continue;\n const fn = graph.functions.find((f) => f.id === pt.functionId);\n if (!fn || !fn.visible) continue;\n const slope = numericalDerivative(fn.expression, paramMap, pt.x);\n const cfn = compile(fn.expression, paramMap);\n if (typeof cfn !== 'function' || !Number.isFinite(slope)) continue;\n const y0 = cfn(pt.x);\n const x1 = graph.view.xMin;\n const x2 = graph.view.xMax;\n board.create(\n 'line',\n [\n [x1, slope * (x1 - pt.x) + y0],\n [x2, slope * (x2 - pt.x) + y0],\n ],\n {\n strokeColor: fn.color,\n strokeWidth: 1,\n dash: 2,\n straightFirst: false,\n straightLast: false,\n },\n );\n }\n}\n\nfunction scanRoots(\n fn: (x: number) => number,\n xMin: number,\n xMax: number,\n samples = 200,\n): number[] {\n const roots: number[] = [];\n const step = (xMax - xMin) / samples;\n let prevX = xMin;\n let prevY = fn(prevX);\n for (let i = 1; i <= samples; i++) {\n const x = xMin + i * step;\n const y = fn(x);\n if (Number.isFinite(prevY) && Number.isFinite(y) && prevY * y < 0) {\n let a = prevX;\n let b = x;\n let ya = prevY;\n for (let j = 0; j < 30; j++) {\n const m = (a + b) / 2;\n const ym = fn(m);\n if (Math.abs(ym) < 1e-6) {\n a = b = m;\n break;\n }\n if (ya * ym < 0) {\n b = m;\n } else {\n a = m;\n ya = ym;\n }\n }\n roots.push((a + b) / 2);\n }\n prevX = x;\n prevY = y;\n }\n return roots;\n}\n","import { parseSerializedGraph } from './serialize';\nimport { renderGraphObjects } from './renderObjects';\n\n/**\n * Re-render SVG cho graph-2d stamp từ jsonState đã serialize.\n *\n * Dùng cho:\n * 1. Insert vào whiteboard (lúc user nhấn Chèn).\n * 2. Restore stamp file sau khi reload (Excalidraw không persist binary).\n *\n * Pattern mirror `geometry-2d/render.ts`:\n * - LUÔN dùng light palette. Excalidraw apply CSS invert filter trong dark mode.\n * - Đặt JXG.Options.text.display='internal' để label render dưới dạng SVG, không HTML overlay.\n * - Offscreen div 600×400 cố định; cleanup sau khi clone SVG outerHTML.\n */\nexport async function renderGraph2dSvgFromState(jsonState: string): Promise<string> {\n const parsed = parseSerializedGraph(jsonState);\n if (!parsed) throw new Error('renderGraph2dSvgFromState: jsonState corrupt');\n\n const JXG = (await import('jsxgraph')).default;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const opts = (JXG as any).Options;\n if (opts) {\n opts.text = opts.text || {};\n opts.text.display = 'internal';\n opts.text.useASCIIMathML = false;\n opts.text.useMathJax = false;\n opts.text.useKatex = false;\n opts.label = opts.label || {};\n opts.label.display = 'internal';\n }\n\n const container = document.createElement('div');\n container.id = `jxg_graph2d_off_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n container.style.cssText =\n 'position:absolute;top:-99999px;left:-99999px;width:600px;height:400px;visibility:hidden;pointer-events:none;';\n document.body.appendChild(container);\n\n let board: unknown = null;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n board = (JXG as any).JSXGraph.initBoard(container.id, {\n boundingbox: [parsed.view.xMin, parsed.view.yMax, parsed.view.xMax, parsed.view.yMin],\n axis: parsed.view.showAxis,\n grid: parsed.view.showGrid,\n showCopyright: false,\n showNavigation: false,\n keepAspectRatio: false,\n });\n renderGraphObjects(board, parsed);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (board as any).update();\n const svgEl = container.querySelector('svg');\n if (!svgEl) throw new Error('renderGraph2dSvgFromState: no svg generated');\n return svgEl.outerHTML;\n } finally {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (board) (JXG as any).JSXGraph.freeBoard(board);\n } catch {\n /* ignore */\n }\n if (container.parentNode) container.parentNode.removeChild(container);\n }\n}\n","import type { BaseStampCustomData } from '../shared/types';\n\nexport interface Graph2DCustomData extends BaseStampCustomData {\n kind: 'graph2d';\n version: 1;\n jsonState: string;\n svgWidth: number;\n svgHeight: number;\n}\n\nexport function isGraph2DCustomData(data: unknown): data is Graph2DCustomData {\n if (!data || typeof data !== 'object') return false;\n const d = data as Partial<Graph2DCustomData>;\n return d.kind === 'graph2d' && d.version === 1 && typeof d.jsonState === 'string';\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// src/stamps/geometry-2d/editor/theme.ts
|
|
3
|
+
var themeStroke = (dark) => dark ? "#e2e8f0" : "#0f172a";
|
|
4
|
+
var themeAxis = (dark) => dark ? "#cbd5e1" : "#94a3b8";
|
|
5
|
+
var themeGrid = (dark) => dark ? "#475569" : "#e2e8f0";
|
|
6
|
+
var themeLabel = (dark) => dark ? "#e2e8f0" : "#000000";
|
|
7
|
+
function paletteFor(isDark) {
|
|
8
|
+
return {
|
|
9
|
+
stroke: themeStroke(isDark),
|
|
10
|
+
axis: themeAxis(isDark),
|
|
11
|
+
grid: themeGrid(isDark),
|
|
12
|
+
label: themeLabel(isDark)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
var SENTINEL_MAP = {
|
|
16
|
+
"@stroke": "stroke",
|
|
17
|
+
"@axis": "axis",
|
|
18
|
+
"@grid": "grid",
|
|
19
|
+
"@label": "label"
|
|
20
|
+
};
|
|
21
|
+
function resolveAttrColors(attrs, palette) {
|
|
22
|
+
if (typeof attrs === "string") {
|
|
23
|
+
const key = SENTINEL_MAP[attrs];
|
|
24
|
+
return key ? palette[key] : attrs;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(attrs)) {
|
|
27
|
+
return attrs.map((a) => resolveAttrColors(a, palette));
|
|
28
|
+
}
|
|
29
|
+
if (attrs && typeof attrs === "object") {
|
|
30
|
+
const out = {};
|
|
31
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
32
|
+
out[k] = resolveAttrColors(v, palette);
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
return attrs;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { paletteFor, resolveAttrColors, themeAxis, themeGrid, themeLabel };
|
|
40
|
+
//# sourceMappingURL=chunk-HTBLO5JO.mjs.map
|
|
41
|
+
//# sourceMappingURL=chunk-HTBLO5JO.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stamps/geometry-2d/editor/theme.ts"],"names":[],"mappings":";AAWO,IAAM,WAAA,GAAc,CAAC,IAAA,KAAmB,IAAA,GAAO,SAAA,GAAY,SAAA;AAC3D,IAAM,SAAA,GAAY,CAAC,IAAA,KAAmB,IAAA,GAAO,SAAA,GAAY;AACzD,IAAM,SAAA,GAAY,CAAC,IAAA,KAAmB,IAAA,GAAO,SAAA,GAAY;AACzD,IAAM,UAAA,GAAa,CAAC,IAAA,KAAmB,IAAA,GAAO,SAAA,GAAY;AAS1D,SAAS,WAAW,MAAA,EAA8B;AACvD,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,YAAY,MAAM,CAAA;AAAA,IAC1B,IAAA,EAAM,UAAU,MAAM,CAAA;AAAA,IACtB,IAAA,EAAM,UAAU,MAAM,CAAA;AAAA,IACtB,KAAA,EAAO,WAAW,MAAM;AAAA,GAC1B;AACF;AAEA,IAAM,YAAA,GAAkD;AAAA,EACtD,SAAA,EAAW,QAAA;AAAA,EACX,OAAA,EAAS,MAAA;AAAA,EACT,OAAA,EAAS,MAAA;AAAA,EACT,QAAA,EAAU;AACZ,CAAA;AAOO,SAAS,iBAAA,CAAqB,OAAU,OAAA,EAAyB;AACtE,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,MAAM,GAAA,GAAM,aAAa,KAAK,CAAA;AAC9B,IAAA,OAAQ,GAAA,GAAM,OAAA,CAAQ,GAAG,CAAA,GAAI,KAAA;AAAA,EAC/B;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,IAAA,OAAO,MAAM,GAAA,CAAI,CAAC,MAAM,iBAAA,CAAkB,CAAA,EAAG,OAAO,CAAC,CAAA;AAAA,EACvD;AACA,EAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACtC,IAAA,MAAM,MAA+B,EAAC;AACtC,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAgC,CAAA,EAAG;AACrE,MAAA,GAAA,CAAI,CAAC,CAAA,GAAI,iBAAA,CAAkB,CAAA,EAAG,OAAO,CAAA;AAAA,IACvC;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT","file":"chunk-HTBLO5JO.mjs","sourcesContent":["// Theme-aware palette cho geometry stamps.\n//\n// Creation log của JSXGraphMiniBoard lưu attrs dưới dạng \"sentinel\" string\n// (`@stroke`, `@axis`, `@grid`, `@label`) thay vì màu cụ thể. Khi:\n// 1. Editor render real-time → resolve theo `isDark` của editor.\n// 2. Offscreen re-render (sau reload / sau khi switch theme) → resolve theo\n// `isDark` của canvas Excalidraw hiện tại.\n//\n// Nhờ vậy stamp tự đổi màu khi user switch dark/light, giống các nét vẽ\n// native của Excalidraw.\n\nexport const themeStroke = (dark: boolean) => (dark ? '#e2e8f0' : '#0f172a');\nexport const themeAxis = (dark: boolean) => (dark ? '#cbd5e1' : '#94a3b8');\nexport const themeGrid = (dark: boolean) => (dark ? '#475569' : '#e2e8f0');\nexport const themeLabel = (dark: boolean) => (dark ? '#e2e8f0' : '#000000');\n\nexport interface GeomPalette {\n stroke: string;\n axis: string;\n grid: string;\n label: string;\n}\n\nexport function paletteFor(isDark: boolean): GeomPalette {\n return {\n stroke: themeStroke(isDark),\n axis: themeAxis(isDark),\n grid: themeGrid(isDark),\n label: themeLabel(isDark),\n };\n}\n\nconst SENTINEL_MAP: Record<string, keyof GeomPalette> = {\n '@stroke': 'stroke',\n '@axis': 'axis',\n '@grid': 'grid',\n '@label': 'label',\n};\n\n/**\n * Walk attrs object/array/string và thay sentinel bằng màu thực từ palette.\n * Đệ quy để hỗ trợ nested attrs (ví dụ polygon's `borders.strokeColor`).\n * Trả về object mới — input giữ nguyên (immutable, an toàn cho log).\n */\nexport function resolveAttrColors<T>(attrs: T, palette: GeomPalette): T {\n if (typeof attrs === 'string') {\n const key = SENTINEL_MAP[attrs];\n return (key ? palette[key] : attrs) as T;\n }\n if (Array.isArray(attrs)) {\n return attrs.map((a) => resolveAttrColors(a, palette)) as unknown as T;\n }\n if (attrs && typeof attrs === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(attrs as Record<string, unknown>)) {\n out[k] = resolveAttrColors(v, palette);\n }\n return out as T;\n }\n return attrs;\n}\n"]}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { isGeometry3DCustomData, parseSerializedBoard3D } from './chunk-DJTBZEAR.mjs';
|
|
3
|
+
import { lazy } from 'react';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/stamps/geometry-3d/render.ts
|
|
7
|
+
var OUTPUT_WIDTH = 1024;
|
|
8
|
+
var OUTPUT_HEIGHT = 768;
|
|
9
|
+
async function renderGeometry3DSvgFromState(jsonState) {
|
|
10
|
+
const state = parseSerializedBoard3D(jsonState);
|
|
11
|
+
const JXG = (await import('jsxgraph')).default;
|
|
12
|
+
const div = document.createElement("div");
|
|
13
|
+
div.style.cssText = `position:absolute;left:-9999px;top:-9999px;width:${OUTPUT_WIDTH}px;height:${OUTPUT_HEIGHT}px;`;
|
|
14
|
+
document.body.appendChild(div);
|
|
15
|
+
try {
|
|
16
|
+
JXG.Options.text.display = "internal";
|
|
17
|
+
const board = JXG.JSXGraph.initBoard(div, {
|
|
18
|
+
boundingbox: state.bbox,
|
|
19
|
+
axis: false,
|
|
20
|
+
showCopyright: false,
|
|
21
|
+
showNavigation: false,
|
|
22
|
+
renderer: "svg"
|
|
23
|
+
});
|
|
24
|
+
const view = board.create(
|
|
25
|
+
"view3d",
|
|
26
|
+
[
|
|
27
|
+
[-5, -5],
|
|
28
|
+
[10, 10],
|
|
29
|
+
[
|
|
30
|
+
[state.view.bbox3D[0], state.view.bbox3D[3]],
|
|
31
|
+
[state.view.bbox3D[1], state.view.bbox3D[4]],
|
|
32
|
+
[state.view.bbox3D[2], state.view.bbox3D[5]]
|
|
33
|
+
]
|
|
34
|
+
],
|
|
35
|
+
{
|
|
36
|
+
az: { slider: { visible: false }, value: state.view.azimuth },
|
|
37
|
+
el: { slider: { visible: false }, value: state.view.elevation },
|
|
38
|
+
projection: "central"
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
if (!state.showAxes) {
|
|
42
|
+
view.defaultAxes = [];
|
|
43
|
+
}
|
|
44
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
45
|
+
for (const el of state.elements) {
|
|
46
|
+
const parents = el.parents.map(
|
|
47
|
+
(p) => typeof p === "string" && p.startsWith("@id:") ? idMap.get(p.slice(4)) : p
|
|
48
|
+
);
|
|
49
|
+
const obj = view.create(el.type, parents, {
|
|
50
|
+
...el.attributes,
|
|
51
|
+
id: el.id,
|
|
52
|
+
name: el.label
|
|
53
|
+
});
|
|
54
|
+
idMap.set(el.id, obj);
|
|
55
|
+
}
|
|
56
|
+
const svg = div.querySelector("svg");
|
|
57
|
+
if (!svg) {
|
|
58
|
+
throw new Error("renderGeometry3DSvgFromState: SVG not produced");
|
|
59
|
+
}
|
|
60
|
+
const clone = svg.cloneNode(true);
|
|
61
|
+
clone.setAttribute("width", String(OUTPUT_WIDTH));
|
|
62
|
+
clone.setAttribute("height", String(OUTPUT_HEIGHT));
|
|
63
|
+
const svgString = new XMLSerializer().serializeToString(clone);
|
|
64
|
+
try {
|
|
65
|
+
JXG.JSXGraph.freeBoard(board);
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
return { svgString, width: OUTPUT_WIDTH, height: OUTPUT_HEIGHT };
|
|
69
|
+
} finally {
|
|
70
|
+
document.body.removeChild(div);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
var Geometry3DStampHost = lazy(
|
|
74
|
+
() => import('./host-XUFON6CQ.mjs').then((m) => ({ default: m.Geometry3DStampHost }))
|
|
75
|
+
);
|
|
76
|
+
var Geometry3DIcon = /* @__PURE__ */ jsxs(
|
|
77
|
+
"svg",
|
|
78
|
+
{
|
|
79
|
+
width: "20",
|
|
80
|
+
height: "20",
|
|
81
|
+
viewBox: "0 0 24 24",
|
|
82
|
+
fill: "none",
|
|
83
|
+
stroke: "currentColor",
|
|
84
|
+
strokeWidth: "1.6",
|
|
85
|
+
strokeLinecap: "round",
|
|
86
|
+
strokeLinejoin: "round",
|
|
87
|
+
"aria-hidden": "true",
|
|
88
|
+
children: [
|
|
89
|
+
/* @__PURE__ */ jsx("path", { d: "M4 9 L4 20 L14 20 L14 9 Z" }),
|
|
90
|
+
/* @__PURE__ */ jsx("path", { d: "M4 9 L10 4 L20 4 L14 9 Z" }),
|
|
91
|
+
/* @__PURE__ */ jsx("path", { d: "M14 9 L20 4 L20 15 L14 20 Z" })
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
var geometry3dStamp = {
|
|
96
|
+
kind: "geometry3d",
|
|
97
|
+
experimental: true,
|
|
98
|
+
shortcutKey: "d",
|
|
99
|
+
toolbarLabel: "D",
|
|
100
|
+
toolbarTitle: "H\xECnh 3D (D)",
|
|
101
|
+
toolbarIcon: Geometry3DIcon,
|
|
102
|
+
toolbarTestId: "stamp-toolbar-geometry3d",
|
|
103
|
+
matchesCustomData: isGeometry3DCustomData,
|
|
104
|
+
async renderSvgFromCustomData(data) {
|
|
105
|
+
if (!isGeometry3DCustomData(data)) {
|
|
106
|
+
throw new Error("geometry3dStamp.renderSvgFromCustomData: customData kh\xF4ng ph\u1EA3i geometry3d");
|
|
107
|
+
}
|
|
108
|
+
const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);
|
|
109
|
+
return svgString;
|
|
110
|
+
},
|
|
111
|
+
restoreFileFromCustomData: async (element) => {
|
|
112
|
+
const data = element.customData;
|
|
113
|
+
const fileId = element.fileId;
|
|
114
|
+
if (!data || !fileId) return null;
|
|
115
|
+
if (!isGeometry3DCustomData(data)) return null;
|
|
116
|
+
try {
|
|
117
|
+
const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);
|
|
118
|
+
const dataURL = `data:image/svg+xml;base64,${typeof btoa !== "undefined" ? btoa(unescape(encodeURIComponent(svgString))) : Buffer.from(svgString).toString("base64")}`;
|
|
119
|
+
return { fileId, dataURL, mimeType: "image/svg+xml" };
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
Host: Geometry3DStampHost
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export { geometry3dStamp };
|
|
128
|
+
//# sourceMappingURL=chunk-HYXFHEDJ.mjs.map
|
|
129
|
+
//# sourceMappingURL=chunk-HYXFHEDJ.mjs.map
|
|
@@ -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-HYXFHEDJ.mjs","sourcesContent":["\"use client\";\n\nimport { parseSerializedBoard3D } from './serialize';\n\nexport interface RenderResult {\n svgString: string;\n width: number;\n height: number;\n}\n\nconst OUTPUT_WIDTH = 1024;\nconst OUTPUT_HEIGHT = 768;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype JxgObj = any;\n\nexport async function renderGeometry3DSvgFromState(\n jsonState: string,\n): Promise<RenderResult> {\n const state = parseSerializedBoard3D(jsonState);\n const JXG = (await import('jsxgraph')).default;\n\n const div = document.createElement('div');\n div.style.cssText = `position:absolute;left:-9999px;top:-9999px;width:${OUTPUT_WIDTH}px;height:${OUTPUT_HEIGHT}px;`;\n document.body.appendChild(div);\n\n try {\n JXG.Options.text.display = 'internal';\n\n const board = JXG.JSXGraph.initBoard(div, {\n boundingbox: state.bbox,\n axis: false,\n showCopyright: false,\n showNavigation: false,\n renderer: 'svg',\n }) as { create: (k: string, p: unknown[], a: unknown) => JxgObj };\n\n const view: JxgObj = board.create(\n 'view3d',\n [\n [-5, -5],\n [10, 10],\n [\n [state.view.bbox3D[0], state.view.bbox3D[3]],\n [state.view.bbox3D[1], state.view.bbox3D[4]],\n [state.view.bbox3D[2], state.view.bbox3D[5]],\n ],\n ],\n {\n az: { slider: { visible: false }, value: state.view.azimuth },\n el: { slider: { visible: false }, value: state.view.elevation },\n projection: 'central',\n },\n );\n\n if (!state.showAxes) {\n (view as { defaultAxes?: unknown[] }).defaultAxes = [];\n }\n\n const idMap = new Map<string, JxgObj>();\n for (const el of state.elements) {\n const parents = el.parents.map((p) =>\n typeof p === 'string' && p.startsWith('@id:')\n ? idMap.get(p.slice(4))\n : p,\n );\n const obj = (\n view as { create: (k: string, p: unknown[], a: unknown) => JxgObj }\n ).create(el.type, parents, {\n ...el.attributes,\n id: el.id,\n name: el.label,\n });\n idMap.set(el.id, obj);\n }\n\n const svg = div.querySelector('svg');\n if (!svg) {\n throw new Error('renderGeometry3DSvgFromState: SVG not produced');\n }\n const clone = svg.cloneNode(true) as SVGElement;\n clone.setAttribute('width', String(OUTPUT_WIDTH));\n clone.setAttribute('height', String(OUTPUT_HEIGHT));\n const svgString = new XMLSerializer().serializeToString(clone);\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n JXG.JSXGraph.freeBoard(board as any);\n } catch {\n /* ignore teardown */\n }\n\n return { svgString, width: OUTPUT_WIDTH, height: OUTPUT_HEIGHT };\n } finally {\n document.body.removeChild(div);\n }\n}\n","'use client';\n\nimport { lazy, type ReactNode } from 'react';\nimport {\n isGeometry3DCustomData,\n type Geometry3DCustomData,\n} from './serialize';\nimport { renderGeometry3DSvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\n\nexport { isGeometry3DCustomData };\nexport type { Geometry3DCustomData };\n\nconst Geometry3DStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.Geometry3DStampHost })),\n);\n\nconst Geometry3DIcon: ReactNode = (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n {/* Mặt trước */}\n <path d=\"M4 9 L4 20 L14 20 L14 9 Z\" />\n {/* Mặt trên */}\n <path d=\"M4 9 L10 4 L20 4 L14 9 Z\" />\n {/* Mặt phải */}\n <path d=\"M14 9 L20 4 L20 15 L14 20 Z\" />\n </svg>\n);\n\nexport const geometry3dStamp: StampType = {\n kind: 'geometry3d',\n experimental: true,\n shortcutKey: 'd',\n toolbarLabel: 'D',\n toolbarTitle: 'Hình 3D (D)',\n toolbarIcon: Geometry3DIcon,\n toolbarTestId: 'stamp-toolbar-geometry3d',\n matchesCustomData: isGeometry3DCustomData,\n async renderSvgFromCustomData(data: unknown): Promise<string> {\n if (!isGeometry3DCustomData(data)) {\n throw new Error('geometry3dStamp.renderSvgFromCustomData: customData không phải geometry3d');\n }\n const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);\n return svgString;\n },\n restoreFileFromCustomData: async (element): Promise<RestoredStampFile | null> => {\n const data = element.customData as Geometry3DCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId) return null;\n if (!isGeometry3DCustomData(data)) return null;\n try {\n const { svgString } = await renderGeometry3DSvgFromState(data.jsonState);\n const dataURL = `data:image/svg+xml;base64,${\n typeof btoa !== 'undefined'\n ? btoa(unescape(encodeURIComponent(svgString)))\n : Buffer.from(svgString).toString('base64')\n }`;\n return { fileId, dataURL, mimeType: 'image/svg+xml' };\n } catch {\n return null;\n }\n },\n Host: Geometry3DStampHost,\n};\n"]}
|