@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
@@ -1,6 +1,7 @@
1
1
  "use client";
2
- export { geometry3dStamp } from './chunk-HYXFHEDJ.mjs';
3
- export { isGeometry3DCustomData } from './chunk-DJTBZEAR.mjs';
2
+ export { geometry3dStamp } from './chunk-IUVV52HO.mjs';
3
+ export { isGeometry3DCustomData } from './chunk-DU2NFHRR.mjs';
4
+ import './chunk-HTBLO5JO.mjs';
4
5
  import './chunk-BJTO5JO5.mjs';
5
6
  //# sourceMappingURL=geometry-3d.mjs.map
6
7
  //# sourceMappingURL=geometry-3d.mjs.map
package/dist/graph-2d.js CHANGED
@@ -59,6 +59,335 @@ var init_serialize = __esm({
59
59
  }
60
60
  });
61
61
 
62
+ // src/stamps/graph-2d/evaluator.ts
63
+ function tokenize(src) {
64
+ const tokens = [];
65
+ let i = 0;
66
+ while (i < src.length) {
67
+ const ch = src[i];
68
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
69
+ i++;
70
+ continue;
71
+ }
72
+ if (ch >= "0" && ch <= "9" || ch === ".") {
73
+ let j = i;
74
+ let hasDot = false;
75
+ let hasExp = false;
76
+ while (j < src.length) {
77
+ const c = src[j];
78
+ if (c >= "0" && c <= "9") {
79
+ j++;
80
+ } else if (c === "." && !hasDot && !hasExp) {
81
+ hasDot = true;
82
+ j++;
83
+ } else if ((c === "e" || c === "E") && !hasExp) {
84
+ hasExp = true;
85
+ j++;
86
+ if (src[j] === "+" || src[j] === "-") j++;
87
+ } else {
88
+ break;
89
+ }
90
+ }
91
+ const raw = src.slice(i, j);
92
+ if (!/[0-9]/.test(raw)) {
93
+ throw new Error(`S\u1ED1 kh\xF4ng h\u1EE3p l\u1EC7 t\u1EA1i v\u1ECB tr\xED ${i}: "${raw}"`);
94
+ }
95
+ tokens.push({ type: "NUMBER", value: raw, pos: i });
96
+ i = j;
97
+ continue;
98
+ }
99
+ if (ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z") {
100
+ let j = i;
101
+ while (j < src.length) {
102
+ const c = src[j];
103
+ if (c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c >= "0" && c <= "9" || c === "_") {
104
+ j++;
105
+ } else {
106
+ break;
107
+ }
108
+ }
109
+ tokens.push({ type: "IDENT", value: src.slice(i, j), pos: i });
110
+ i = j;
111
+ continue;
112
+ }
113
+ if (OPERATORS.has(ch)) {
114
+ tokens.push({ type: "OP", value: ch, pos: i });
115
+ i++;
116
+ continue;
117
+ }
118
+ if (ch === "(") {
119
+ tokens.push({ type: "LPAREN", value: ch, pos: i });
120
+ i++;
121
+ continue;
122
+ }
123
+ if (ch === ")") {
124
+ tokens.push({ type: "RPAREN", value: ch, pos: i });
125
+ i++;
126
+ continue;
127
+ }
128
+ if (ch === ",") {
129
+ tokens.push({ type: "COMMA", value: ch, pos: i });
130
+ i++;
131
+ continue;
132
+ }
133
+ throw new Error(`K\xFD t\u1EF1 kh\xF4ng h\u1EE3p l\u1EC7 t\u1EA1i v\u1ECB tr\xED ${i}: "${ch}"`);
134
+ }
135
+ return tokens;
136
+ }
137
+ function parseAst(src) {
138
+ const tokens = tokenize(src);
139
+ if (tokens.length === 0) throw new Error("Bi\u1EC3u th\u1EE9c r\u1ED7ng");
140
+ const p = new Parser(tokens);
141
+ return p.parseExpression();
142
+ }
143
+ function evaluate(node, env) {
144
+ switch (node.kind) {
145
+ case "num":
146
+ return node.value;
147
+ case "ident": {
148
+ const name = node.name;
149
+ if (name === "x") return env.x;
150
+ if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) {
151
+ return ALLOWED_CONSTANTS[name];
152
+ }
153
+ if (name.length === 1 && Object.prototype.hasOwnProperty.call(env.params, name)) {
154
+ return env.params[name];
155
+ }
156
+ throw new Error(`Identifier kh\xF4ng h\u1EE3p l\u1EC7: "${name}"`);
157
+ }
158
+ case "unary": {
159
+ const v = evaluate(node.arg, env);
160
+ return node.op === "-" ? -v : +v;
161
+ }
162
+ case "binary": {
163
+ const a = evaluate(node.lhs, env);
164
+ const b = evaluate(node.rhs, env);
165
+ switch (node.op) {
166
+ case "+":
167
+ return a + b;
168
+ case "-":
169
+ return a - b;
170
+ case "*":
171
+ return a * b;
172
+ case "/":
173
+ return a / b;
174
+ // có thể trả Infinity/NaN — đúng theo IEEE 754
175
+ case "^":
176
+ return Math.pow(a, b);
177
+ }
178
+ throw new Error(`To\xE1n t\u1EED kh\xF4ng h\u1ED7 tr\u1EE3: "${node.op}"`);
179
+ }
180
+ case "call": {
181
+ const fn = ALLOWED_FUNCTIONS[node.name];
182
+ if (typeof fn !== "function") {
183
+ throw new Error(`H\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${node.name}"`);
184
+ }
185
+ const args = node.args.map((a) => evaluate(a, env));
186
+ return fn(...args);
187
+ }
188
+ }
189
+ }
190
+ function collectFreeVars(node, out = /* @__PURE__ */ new Set()) {
191
+ switch (node.kind) {
192
+ case "num":
193
+ return out;
194
+ case "ident": {
195
+ const name = node.name;
196
+ if (name === "x") return out;
197
+ if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) return out;
198
+ if (name.length === 1) out.add(name);
199
+ return out;
200
+ }
201
+ case "unary":
202
+ return collectFreeVars(node.arg, out);
203
+ case "binary":
204
+ collectFreeVars(node.lhs, out);
205
+ collectFreeVars(node.rhs, out);
206
+ return out;
207
+ case "call":
208
+ for (const a of node.args) collectFreeVars(a, out);
209
+ return out;
210
+ }
211
+ }
212
+ function checkIdentifiers(node) {
213
+ switch (node.kind) {
214
+ case "num":
215
+ return null;
216
+ case "ident": {
217
+ const name = node.name;
218
+ if (name === "x") return null;
219
+ if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) return null;
220
+ if (name.length === 1) return null;
221
+ return `T\xEAn kh\xF4ng h\u1EE3p l\u1EC7: "${name}"`;
222
+ }
223
+ case "unary":
224
+ return checkIdentifiers(node.arg);
225
+ case "binary":
226
+ return checkIdentifiers(node.lhs) ?? checkIdentifiers(node.rhs);
227
+ case "call": {
228
+ if (!Object.prototype.hasOwnProperty.call(ALLOWED_FUNCTIONS, node.name)) {
229
+ return `T\xEAn h\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${node.name}"`;
230
+ }
231
+ for (const a of node.args) {
232
+ const e = checkIdentifiers(a);
233
+ if (e) return e;
234
+ }
235
+ return null;
236
+ }
237
+ }
238
+ }
239
+ var ALLOWED_FUNCTIONS, ALLOWED_CONSTANTS, OPERATORS, Parser, ALLOWED_FUNCTION_NAMES;
240
+ var init_evaluator = __esm({
241
+ "src/stamps/graph-2d/evaluator.ts"() {
242
+ ALLOWED_FUNCTIONS = {
243
+ sin: Math.sin,
244
+ cos: Math.cos,
245
+ tan: Math.tan,
246
+ asin: Math.asin,
247
+ acos: Math.acos,
248
+ atan: Math.atan,
249
+ log: Math.log10,
250
+ // log = log10 (khớp với rewriteToJs)
251
+ ln: Math.log,
252
+ // ln = log tự nhiên
253
+ exp: Math.exp,
254
+ sqrt: Math.sqrt,
255
+ abs: Math.abs,
256
+ floor: Math.floor,
257
+ ceil: Math.ceil,
258
+ round: Math.round
259
+ };
260
+ ALLOWED_CONSTANTS = {
261
+ pi: Math.PI,
262
+ e: Math.E
263
+ };
264
+ OPERATORS = /* @__PURE__ */ new Set(["+", "-", "*", "/", "^"]);
265
+ Parser = class {
266
+ constructor(tokens) {
267
+ this.tokens = tokens;
268
+ this.pos = 0;
269
+ }
270
+ peek() {
271
+ return this.tokens[this.pos];
272
+ }
273
+ consume() {
274
+ const t = this.tokens[this.pos++];
275
+ if (!t) throw new Error("C\xFA ph\xE1p: h\u1EBFt token s\u1EDBm");
276
+ return t;
277
+ }
278
+ parseExpression() {
279
+ const node = this.parseAddSub();
280
+ if (this.pos < this.tokens.length) {
281
+ const t = this.tokens[this.pos];
282
+ throw new Error(`C\xFA ph\xE1p: token th\u1EEBa "${t.value}" t\u1EA1i v\u1ECB tr\xED ${t.pos}`);
283
+ }
284
+ return node;
285
+ }
286
+ // + - (left assoc)
287
+ parseAddSub() {
288
+ let lhs = this.parseMulDiv();
289
+ while (true) {
290
+ const t = this.peek();
291
+ if (t && t.type === "OP" && (t.value === "+" || t.value === "-")) {
292
+ this.consume();
293
+ const rhs = this.parseMulDiv();
294
+ lhs = { kind: "binary", op: t.value, lhs, rhs };
295
+ } else {
296
+ break;
297
+ }
298
+ }
299
+ return lhs;
300
+ }
301
+ // * / (left assoc)
302
+ parseMulDiv() {
303
+ let lhs = this.parseUnary();
304
+ while (true) {
305
+ const t = this.peek();
306
+ if (t && t.type === "OP" && (t.value === "*" || t.value === "/")) {
307
+ this.consume();
308
+ const rhs = this.parseUnary();
309
+ lhs = { kind: "binary", op: t.value, lhs, rhs };
310
+ } else {
311
+ break;
312
+ }
313
+ }
314
+ return lhs;
315
+ }
316
+ // unary + - (right assoc) sau đó parsePow
317
+ parseUnary() {
318
+ const t = this.peek();
319
+ if (t && t.type === "OP" && (t.value === "+" || t.value === "-")) {
320
+ this.consume();
321
+ const arg = this.parseUnary();
322
+ return { kind: "unary", op: t.value, arg };
323
+ }
324
+ return this.parsePow();
325
+ }
326
+ // ^ (right assoc)
327
+ parsePow() {
328
+ const lhs = this.parsePrimary();
329
+ const t = this.peek();
330
+ if (t && t.type === "OP" && t.value === "^") {
331
+ this.consume();
332
+ const rhs = this.parseUnary();
333
+ return { kind: "binary", op: "^", lhs, rhs };
334
+ }
335
+ return lhs;
336
+ }
337
+ parsePrimary() {
338
+ const t = this.peek();
339
+ if (!t) throw new Error("C\xFA ph\xE1p: thi\u1EBFu bi\u1EC3u th\u1EE9c");
340
+ if (t.type === "NUMBER") {
341
+ this.consume();
342
+ const v = Number(t.value);
343
+ return { kind: "num", value: v };
344
+ }
345
+ if (t.type === "IDENT") {
346
+ this.consume();
347
+ const next = this.peek();
348
+ if (next && next.type === "LPAREN") {
349
+ this.consume();
350
+ const args = [];
351
+ const lookahead = this.peek();
352
+ if (!lookahead || lookahead.type !== "RPAREN") {
353
+ args.push(this.parseAddSub());
354
+ while (true) {
355
+ const nx = this.peek();
356
+ if (nx && nx.type === "COMMA") {
357
+ this.consume();
358
+ args.push(this.parseAddSub());
359
+ } else {
360
+ break;
361
+ }
362
+ }
363
+ }
364
+ const close = this.peek();
365
+ if (!close || close.type !== "RPAREN") {
366
+ throw new Error(`C\xFA ph\xE1p: thi\u1EBFu ")" sau h\xE0m "${t.value}"`);
367
+ }
368
+ this.consume();
369
+ return { kind: "call", name: t.value, args };
370
+ }
371
+ return { kind: "ident", name: t.value };
372
+ }
373
+ if (t.type === "LPAREN") {
374
+ this.consume();
375
+ const inner = this.parseAddSub();
376
+ const close = this.peek();
377
+ if (!close || close.type !== "RPAREN") {
378
+ throw new Error('C\xFA ph\xE1p: thi\u1EBFu ")"');
379
+ }
380
+ this.consume();
381
+ return inner;
382
+ }
383
+ throw new Error(`C\xFA ph\xE1p: token b\u1EA5t ng\u1EDD "${t.value}" t\u1EA1i v\u1ECB tr\xED ${t.pos}`);
384
+ }
385
+ };
386
+ ALLOWED_FUNCTION_NAMES = new Set(Object.keys(ALLOWED_FUNCTIONS));
387
+ new Set(Object.keys(ALLOWED_CONSTANTS));
388
+ }
389
+ });
390
+
62
391
  // src/stamps/graph-2d/parser.ts
63
392
  function errResult(message) {
64
393
  return { ok: false, error: message, freeVars: /* @__PURE__ */ new Set() };
@@ -67,13 +396,20 @@ function validate(expr) {
67
396
  const trimmed = expr.trim();
68
397
  if (!trimmed) return errResult("Bi\u1EC3u th\u1EE9c r\u1ED7ng");
69
398
  if (!ALLOWED_CHARS.test(trimmed)) return errResult("K\xFD t\u1EF1 kh\xF4ng h\u1EE3p l\u1EC7");
70
- const ids = trimmed.match(IDENTIFIER_RE) ?? [];
71
- const freeVars = /* @__PURE__ */ new Set();
72
- for (const id of ids) {
399
+ let tokens;
400
+ try {
401
+ tokens = tokenize(trimmed);
402
+ } catch {
403
+ return errResult("L\u1ED7i c\xFA ph\xE1p");
404
+ }
405
+ const earlyFree = /* @__PURE__ */ new Set();
406
+ for (const tok of tokens) {
407
+ if (tok.type !== "IDENT") continue;
408
+ const id = tok.value;
73
409
  if (id === "x" || id === "pi" || id === "e") continue;
74
- if (ALLOWED_FUNCTIONS.has(id)) continue;
410
+ if (ALLOWED_FUNCTIONS2.has(id)) continue;
75
411
  if (id.length === 1) {
76
- freeVars.add(id);
412
+ earlyFree.add(id);
77
413
  continue;
78
414
  }
79
415
  const hint = SUGGESTIONS[id];
@@ -81,90 +417,48 @@ function validate(expr) {
81
417
  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}"`
82
418
  );
83
419
  }
420
+ let ast;
84
421
  try {
85
- const paramSubs = Object.fromEntries([...freeVars].map((v) => [v, 1]));
86
- const rewritten = rewriteToJs(trimmed, paramSubs);
87
- new Function("x", `return (${rewritten})`);
422
+ ast = parseAst(trimmed);
88
423
  } catch {
89
424
  return errResult("L\u1ED7i c\xFA ph\xE1p");
90
425
  }
426
+ const idErr = checkIdentifiers(ast);
427
+ if (idErr) return errResult(idErr);
428
+ const freeVars = collectFreeVars(ast);
429
+ for (const v of earlyFree) freeVars.add(v);
91
430
  return { ok: true, freeVars };
92
431
  }
93
- function rewriteToJs(expr, params) {
94
- let s = expr.replace(/\^/g, "**");
95
- s = s.replace(/\bpi\b/g, "Math.PI");
96
- s = s.replace(/\be\b/g, "Math.E");
97
- for (const [from, to] of FUNCTION_REPLACEMENTS) {
98
- s = s.replace(new RegExp(`\\b${from}\\b`, "g"), to);
99
- }
100
- for (const [name, value] of Object.entries(params)) {
101
- if (name.length !== 1) continue;
102
- s = s.replace(new RegExp(`\\b${name}\\b`, "g"), `(${value})`);
103
- }
104
- return s;
105
- }
106
432
  function compile(expr, paramValues) {
107
433
  const v = validate(expr);
108
434
  if (!v.ok) return { error: v.error ?? "Invalid" };
435
+ let ast;
109
436
  try {
110
- const rewritten = rewriteToJs(expr, paramValues);
111
- const raw = new Function("x", `return (${rewritten})`);
112
- return (x) => {
113
- try {
114
- const y = raw(x);
115
- return typeof y === "number" ? y : NaN;
116
- } catch {
117
- return NaN;
118
- }
119
- };
437
+ ast = parseAst(expr.trim());
120
438
  } catch (err) {
121
439
  return { error: err instanceof Error ? err.message : String(err) };
122
440
  }
441
+ return (x) => {
442
+ try {
443
+ const y = evaluate(ast, { x, params: paramValues });
444
+ return typeof y === "number" ? y : NaN;
445
+ } catch {
446
+ return NaN;
447
+ }
448
+ };
123
449
  }
124
- var ALLOWED_FUNCTIONS, ALLOWED_CHARS, IDENTIFIER_RE, SUGGESTIONS, FUNCTION_REPLACEMENTS;
450
+ var ALLOWED_FUNCTIONS2, ALLOWED_CHARS, SUGGESTIONS;
125
451
  var init_parser = __esm({
126
452
  "src/stamps/graph-2d/parser.ts"() {
127
- ALLOWED_FUNCTIONS = /* @__PURE__ */ new Set([
128
- "sin",
129
- "cos",
130
- "tan",
131
- "asin",
132
- "acos",
133
- "atan",
134
- "log",
135
- "ln",
136
- "exp",
137
- "sqrt",
138
- "abs",
139
- "floor",
140
- "ceil",
141
- "round"
142
- ]);
453
+ init_evaluator();
454
+ ALLOWED_FUNCTIONS2 = ALLOWED_FUNCTION_NAMES;
143
455
  ALLOWED_CHARS = /^[a-zA-Z0-9_.+\-*/^()\s,]+$/;
144
- IDENTIFIER_RE = /[a-zA-Z][a-zA-Z0-9_]*/g;
145
456
  SUGGESTIONS = {
146
457
  tg: "tan",
147
458
  arcsin: "asin",
148
459
  arccos: "acos",
149
460
  arctan: "atan"
150
461
  };
151
- FUNCTION_REPLACEMENTS = [
152
- // longest first để tránh substring conflict (asin trước sin)
153
- ["asin", "Math.asin"],
154
- ["acos", "Math.acos"],
155
- ["atan", "Math.atan"],
156
- ["sqrt", "Math.sqrt"],
157
- ["floor", "Math.floor"],
158
- ["round", "Math.round"],
159
- ["ceil", "Math.ceil"],
160
- ["sin", "Math.sin"],
161
- ["cos", "Math.cos"],
162
- ["tan", "Math.tan"],
163
- ["abs", "Math.abs"],
164
- ["exp", "Math.exp"],
165
- ["log", "Math.log10"],
166
- ["ln", "Math.log"]
167
- ];
168
462
  }
169
463
  });
170
464