@cortex-js/compute-engine 0.55.5 → 0.56.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.
Files changed (259) hide show
  1. package/dist/compile.esm.js +912 -770
  2. package/dist/compile.min.esm.js +300 -523
  3. package/dist/compile.min.umd.cjs +301 -524
  4. package/dist/compile.umd.cjs +912 -770
  5. package/dist/compute-engine.esm.js +1517 -902
  6. package/dist/compute-engine.min.esm.js +300 -522
  7. package/dist/compute-engine.min.umd.cjs +300 -522
  8. package/dist/compute-engine.umd.cjs +1517 -902
  9. package/dist/core.esm.js +1516 -901
  10. package/dist/core.min.esm.js +299 -521
  11. package/dist/core.min.umd.cjs +299 -521
  12. package/dist/core.umd.cjs +1516 -901
  13. package/dist/interval.esm.js +268 -63
  14. package/dist/interval.min.esm.js +7 -7
  15. package/dist/interval.min.umd.cjs +7 -7
  16. package/dist/interval.umd.cjs +268 -63
  17. package/dist/latex-syntax.esm.js +371 -77
  18. package/dist/latex-syntax.min.esm.js +7 -6
  19. package/dist/latex-syntax.min.umd.cjs +7 -6
  20. package/dist/latex-syntax.umd.cjs +371 -77
  21. package/dist/math-json.esm.js +2 -2
  22. package/dist/math-json.min.esm.js +2 -2
  23. package/dist/math-json.min.umd.cjs +2 -2
  24. package/dist/math-json.umd.cjs +2 -2
  25. package/dist/numerics.esm.js +4 -2
  26. package/dist/numerics.min.esm.js +3 -3
  27. package/dist/numerics.min.umd.cjs +3 -3
  28. package/dist/numerics.umd.cjs +4 -2
  29. package/dist/types/big-decimal/big-decimal.d.ts +1 -1
  30. package/dist/types/big-decimal/index.d.ts +1 -1
  31. package/dist/types/big-decimal/transcendentals.d.ts +1 -1
  32. package/dist/types/big-decimal/utils.d.ts +1 -1
  33. package/dist/types/common/ansi-codes.d.ts +1 -1
  34. package/dist/types/common/configuration-change.d.ts +1 -1
  35. package/dist/types/common/fuzzy-string-match.d.ts +1 -1
  36. package/dist/types/common/grapheme-splitter.d.ts +1 -1
  37. package/dist/types/common/interruptible.d.ts +1 -1
  38. package/dist/types/common/one-of.d.ts +1 -1
  39. package/dist/types/common/signals.d.ts +1 -1
  40. package/dist/types/common/type/ast-nodes.d.ts +1 -1
  41. package/dist/types/common/type/boxed-type.d.ts +1 -1
  42. package/dist/types/common/type/lexer.d.ts +1 -1
  43. package/dist/types/common/type/parse.d.ts +1 -1
  44. package/dist/types/common/type/parser.d.ts +1 -1
  45. package/dist/types/common/type/primitive.d.ts +1 -1
  46. package/dist/types/common/type/reduce.d.ts +1 -1
  47. package/dist/types/common/type/serialize.d.ts +1 -1
  48. package/dist/types/common/type/subtype.d.ts +1 -1
  49. package/dist/types/common/type/type-builder.d.ts +1 -1
  50. package/dist/types/common/type/types.d.ts +2 -2
  51. package/dist/types/common/type/utils.d.ts +1 -1
  52. package/dist/types/common/utils.d.ts +1 -1
  53. package/dist/types/compile.d.ts +1 -1
  54. package/dist/types/compute-engine/assume.d.ts +1 -1
  55. package/dist/types/compute-engine/boxed-expression/abstract-boxed-expression.d.ts +1 -1
  56. package/dist/types/compute-engine/boxed-expression/apply.d.ts +1 -1
  57. package/dist/types/compute-engine/boxed-expression/arithmetic-add.d.ts +1 -1
  58. package/dist/types/compute-engine/boxed-expression/arithmetic-mul-div.d.ts +1 -1
  59. package/dist/types/compute-engine/boxed-expression/arithmetic-power.d.ts +1 -1
  60. package/dist/types/compute-engine/boxed-expression/ascii-math.d.ts +1 -1
  61. package/dist/types/compute-engine/boxed-expression/box.d.ts +1 -1
  62. package/dist/types/compute-engine/boxed-expression/boxed-dictionary.d.ts +1 -1
  63. package/dist/types/compute-engine/boxed-expression/boxed-function.d.ts +1 -1
  64. package/dist/types/compute-engine/boxed-expression/boxed-number.d.ts +1 -1
  65. package/dist/types/compute-engine/boxed-expression/boxed-operator-definition.d.ts +1 -1
  66. package/dist/types/compute-engine/boxed-expression/boxed-patterns.d.ts +1 -1
  67. package/dist/types/compute-engine/boxed-expression/boxed-string.d.ts +1 -1
  68. package/dist/types/compute-engine/boxed-expression/boxed-symbol.d.ts +1 -1
  69. package/dist/types/compute-engine/boxed-expression/boxed-tensor.d.ts +1 -1
  70. package/dist/types/compute-engine/boxed-expression/boxed-value-definition.d.ts +1 -1
  71. package/dist/types/compute-engine/boxed-expression/cache.d.ts +1 -1
  72. package/dist/types/compute-engine/boxed-expression/canonical-utils.d.ts +1 -1
  73. package/dist/types/compute-engine/boxed-expression/canonical.d.ts +1 -1
  74. package/dist/types/compute-engine/boxed-expression/compare.d.ts +1 -1
  75. package/dist/types/compute-engine/boxed-expression/constants.d.ts +1 -1
  76. package/dist/types/compute-engine/boxed-expression/expand.d.ts +1 -1
  77. package/dist/types/compute-engine/boxed-expression/expression-map.d.ts +1 -1
  78. package/dist/types/compute-engine/boxed-expression/factor.d.ts +1 -1
  79. package/dist/types/compute-engine/boxed-expression/flatten.d.ts +1 -1
  80. package/dist/types/compute-engine/boxed-expression/hold.d.ts +1 -1
  81. package/dist/types/compute-engine/boxed-expression/inequality-bounds.d.ts +1 -1
  82. package/dist/types/compute-engine/boxed-expression/init-lazy-refs.d.ts +1 -1
  83. package/dist/types/compute-engine/boxed-expression/invisible-operator.d.ts +1 -1
  84. package/dist/types/compute-engine/boxed-expression/match.d.ts +1 -1
  85. package/dist/types/compute-engine/boxed-expression/negate.d.ts +1 -1
  86. package/dist/types/compute-engine/boxed-expression/numerics.d.ts +1 -1
  87. package/dist/types/compute-engine/boxed-expression/order.d.ts +1 -1
  88. package/dist/types/compute-engine/boxed-expression/pattern-utils.d.ts +1 -1
  89. package/dist/types/compute-engine/boxed-expression/polynomial-degree.d.ts +1 -1
  90. package/dist/types/compute-engine/boxed-expression/polynomials.d.ts +1 -1
  91. package/dist/types/compute-engine/boxed-expression/predicates.d.ts +1 -1
  92. package/dist/types/compute-engine/boxed-expression/rules.d.ts +1 -1
  93. package/dist/types/compute-engine/boxed-expression/serialize.d.ts +1 -1
  94. package/dist/types/compute-engine/boxed-expression/sgn.d.ts +1 -1
  95. package/dist/types/compute-engine/boxed-expression/simplify.d.ts +1 -1
  96. package/dist/types/compute-engine/boxed-expression/solve-linear-system.d.ts +1 -1
  97. package/dist/types/compute-engine/boxed-expression/solve.d.ts +1 -1
  98. package/dist/types/compute-engine/boxed-expression/stochastic-equal.d.ts +1 -1
  99. package/dist/types/compute-engine/boxed-expression/trigonometry.d.ts +1 -1
  100. package/dist/types/compute-engine/boxed-expression/type-guards.d.ts +1 -1
  101. package/dist/types/compute-engine/boxed-expression/utils.d.ts +1 -1
  102. package/dist/types/compute-engine/boxed-expression/validate.d.ts +1 -1
  103. package/dist/types/compute-engine/collection-utils.d.ts +1 -1
  104. package/dist/types/compute-engine/compilation/base-compiler.d.ts +1 -1
  105. package/dist/types/compute-engine/compilation/compile-expression.d.ts +2 -3
  106. package/dist/types/compute-engine/compilation/constant-folding.d.ts +1 -1
  107. package/dist/types/compute-engine/compilation/glsl-target.d.ts +1 -1
  108. package/dist/types/compute-engine/compilation/gpu-target.d.ts +15 -58
  109. package/dist/types/compute-engine/compilation/interval-javascript-target.d.ts +1 -1
  110. package/dist/types/compute-engine/compilation/javascript-target.d.ts +25 -3
  111. package/dist/types/compute-engine/compilation/python-target.d.ts +1 -1
  112. package/dist/types/compute-engine/compilation/types.d.ts +1 -67
  113. package/dist/types/compute-engine/compilation/wgsl-target.d.ts +1 -1
  114. package/dist/types/compute-engine/cost-function.d.ts +1 -1
  115. package/dist/types/compute-engine/engine-assumptions.d.ts +1 -1
  116. package/dist/types/compute-engine/engine-cache.d.ts +1 -1
  117. package/dist/types/compute-engine/engine-common-symbols.d.ts +1 -1
  118. package/dist/types/compute-engine/engine-compilation-targets.d.ts +1 -1
  119. package/dist/types/compute-engine/engine-configuration-lifecycle.d.ts +1 -1
  120. package/dist/types/compute-engine/engine-declarations.d.ts +1 -1
  121. package/dist/types/compute-engine/engine-expression-entrypoints.d.ts +1 -1
  122. package/dist/types/compute-engine/engine-extension-contracts.d.ts +1 -1
  123. package/dist/types/compute-engine/engine-library-bootstrap.d.ts +1 -1
  124. package/dist/types/compute-engine/engine-numeric-configuration.d.ts +1 -1
  125. package/dist/types/compute-engine/engine-runtime-state.d.ts +1 -1
  126. package/dist/types/compute-engine/engine-scope.d.ts +1 -1
  127. package/dist/types/compute-engine/engine-sequences.d.ts +1 -1
  128. package/dist/types/compute-engine/engine-simplification-rules.d.ts +1 -1
  129. package/dist/types/compute-engine/engine-startup-coordinator.d.ts +1 -1
  130. package/dist/types/compute-engine/engine-type-resolver.d.ts +1 -1
  131. package/dist/types/compute-engine/engine-validation-entrypoints.d.ts +1 -1
  132. package/dist/types/compute-engine/free-functions.d.ts +1 -1
  133. package/dist/types/compute-engine/function-utils.d.ts +1 -1
  134. package/dist/types/compute-engine/global-types.d.ts +1 -1
  135. package/dist/types/compute-engine/index.d.ts +23 -3
  136. package/dist/types/compute-engine/interval/arithmetic.d.ts +1 -1
  137. package/dist/types/compute-engine/interval/comparison.d.ts +1 -1
  138. package/dist/types/compute-engine/interval/elementary.d.ts +1 -1
  139. package/dist/types/compute-engine/interval/index.d.ts +1 -1
  140. package/dist/types/compute-engine/interval/trigonometric.d.ts +1 -1
  141. package/dist/types/compute-engine/interval/types.d.ts +1 -1
  142. package/dist/types/compute-engine/interval/util.d.ts +1 -1
  143. package/dist/types/compute-engine/latex-syntax/dictionary/default-dictionary.d.ts +4 -3
  144. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-algebra.d.ts +1 -1
  145. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-arithmetic.d.ts +1 -1
  146. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-calculus.d.ts +1 -1
  147. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-colors.d.ts +10 -0
  148. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-complex.d.ts +1 -1
  149. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-core.d.ts +1 -1
  150. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-linear-algebra.d.ts +1 -1
  151. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-logic.d.ts +1 -1
  152. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-other.d.ts +1 -1
  153. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-relational-operators.d.ts +1 -1
  154. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-sets.d.ts +1 -1
  155. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-statistics.d.ts +1 -1
  156. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-symbols.d.ts +1 -1
  157. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-trigonometry.d.ts +1 -1
  158. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-units.d.ts +1 -1
  159. package/dist/types/compute-engine/latex-syntax/dictionary/definitions.d.ts +1 -1
  160. package/dist/types/compute-engine/latex-syntax/dictionary/indexed-types.d.ts +9 -1
  161. package/dist/types/compute-engine/latex-syntax/latex-syntax.d.ts +1 -1
  162. package/dist/types/compute-engine/latex-syntax/parse-number.d.ts +1 -1
  163. package/dist/types/compute-engine/latex-syntax/parse-symbol.d.ts +1 -1
  164. package/dist/types/compute-engine/latex-syntax/parse.d.ts +1 -1
  165. package/dist/types/compute-engine/latex-syntax/serialize-dms.d.ts +1 -1
  166. package/dist/types/compute-engine/latex-syntax/serialize-number.d.ts +1 -1
  167. package/dist/types/compute-engine/latex-syntax/serializer-style.d.ts +1 -1
  168. package/dist/types/compute-engine/latex-syntax/serializer.d.ts +4 -2
  169. package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
  170. package/dist/types/compute-engine/latex-syntax/types.d.ts +1 -1
  171. package/dist/types/compute-engine/latex-syntax/utils.d.ts +1 -1
  172. package/dist/types/compute-engine/library/arithmetic.d.ts +1 -1
  173. package/dist/types/compute-engine/library/calculus.d.ts +1 -1
  174. package/dist/types/compute-engine/library/collections.d.ts +1 -1
  175. package/dist/types/compute-engine/library/colors.d.ts +1 -1
  176. package/dist/types/compute-engine/library/combinatorics.d.ts +1 -1
  177. package/dist/types/compute-engine/library/complex.d.ts +1 -1
  178. package/dist/types/compute-engine/library/control-structures.d.ts +1 -1
  179. package/dist/types/compute-engine/library/core.d.ts +1 -1
  180. package/dist/types/compute-engine/library/fractals.d.ts +1 -1
  181. package/dist/types/compute-engine/library/library.d.ts +1 -1
  182. package/dist/types/compute-engine/library/linear-algebra.d.ts +1 -1
  183. package/dist/types/compute-engine/library/logic-analysis.d.ts +1 -1
  184. package/dist/types/compute-engine/library/logic.d.ts +1 -1
  185. package/dist/types/compute-engine/library/number-theory.d.ts +1 -1
  186. package/dist/types/compute-engine/library/polynomials.d.ts +1 -1
  187. package/dist/types/compute-engine/library/quantity-arithmetic.d.ts +1 -1
  188. package/dist/types/compute-engine/library/random-expression.d.ts +1 -1
  189. package/dist/types/compute-engine/library/relational-operator.d.ts +1 -1
  190. package/dist/types/compute-engine/library/sets.d.ts +1 -1
  191. package/dist/types/compute-engine/library/statistics.d.ts +1 -1
  192. package/dist/types/compute-engine/library/trigonometry.d.ts +1 -1
  193. package/dist/types/compute-engine/library/type-handlers.d.ts +1 -1
  194. package/dist/types/compute-engine/library/unit-data.d.ts +1 -1
  195. package/dist/types/compute-engine/library/units.d.ts +1 -1
  196. package/dist/types/compute-engine/library/utils.d.ts +1 -1
  197. package/dist/types/compute-engine/numeric-value/big-numeric-value.d.ts +1 -1
  198. package/dist/types/compute-engine/numeric-value/exact-numeric-value.d.ts +1 -1
  199. package/dist/types/compute-engine/numeric-value/machine-numeric-value.d.ts +1 -1
  200. package/dist/types/compute-engine/numeric-value/types.d.ts +1 -1
  201. package/dist/types/compute-engine/numerics/bigint.d.ts +1 -1
  202. package/dist/types/compute-engine/numerics/expression.d.ts +1 -1
  203. package/dist/types/compute-engine/numerics/interval.d.ts +1 -1
  204. package/dist/types/compute-engine/numerics/linear-algebra.d.ts +1 -1
  205. package/dist/types/compute-engine/numerics/monte-carlo.d.ts +1 -1
  206. package/dist/types/compute-engine/numerics/numeric-bigint.d.ts +1 -1
  207. package/dist/types/compute-engine/numerics/numeric-bignum.d.ts +1 -1
  208. package/dist/types/compute-engine/numerics/numeric-complex.d.ts +1 -1
  209. package/dist/types/compute-engine/numerics/numeric.d.ts +1 -1
  210. package/dist/types/compute-engine/numerics/primes.d.ts +1 -1
  211. package/dist/types/compute-engine/numerics/rationals.d.ts +1 -1
  212. package/dist/types/compute-engine/numerics/richardson.d.ts +1 -1
  213. package/dist/types/compute-engine/numerics/special-functions.d.ts +1 -1
  214. package/dist/types/compute-engine/numerics/statistics.d.ts +1 -1
  215. package/dist/types/compute-engine/numerics/strings.d.ts +1 -1
  216. package/dist/types/compute-engine/numerics/types.d.ts +1 -1
  217. package/dist/types/compute-engine/numerics/unit-data.d.ts +1 -1
  218. package/dist/types/compute-engine/oeis.d.ts +1 -1
  219. package/dist/types/compute-engine/sequence.d.ts +1 -1
  220. package/dist/types/compute-engine/symbolic/antiderivative.d.ts +1 -1
  221. package/dist/types/compute-engine/symbolic/derivative.d.ts +1 -1
  222. package/dist/types/compute-engine/symbolic/distribute.d.ts +1 -1
  223. package/dist/types/compute-engine/symbolic/fu-cost.d.ts +1 -1
  224. package/dist/types/compute-engine/symbolic/fu-transforms.d.ts +1 -1
  225. package/dist/types/compute-engine/symbolic/fu.d.ts +1 -1
  226. package/dist/types/compute-engine/symbolic/logic-utils.d.ts +1 -1
  227. package/dist/types/compute-engine/symbolic/simplify-abs.d.ts +1 -1
  228. package/dist/types/compute-engine/symbolic/simplify-divide.d.ts +1 -1
  229. package/dist/types/compute-engine/symbolic/simplify-factorial.d.ts +1 -1
  230. package/dist/types/compute-engine/symbolic/simplify-hyperbolic.d.ts +1 -1
  231. package/dist/types/compute-engine/symbolic/simplify-infinity.d.ts +1 -1
  232. package/dist/types/compute-engine/symbolic/simplify-log.d.ts +1 -1
  233. package/dist/types/compute-engine/symbolic/simplify-logic.d.ts +1 -1
  234. package/dist/types/compute-engine/symbolic/simplify-power.d.ts +1 -1
  235. package/dist/types/compute-engine/symbolic/simplify-product.d.ts +1 -1
  236. package/dist/types/compute-engine/symbolic/simplify-rules.d.ts +1 -1
  237. package/dist/types/compute-engine/symbolic/simplify-sum.d.ts +1 -1
  238. package/dist/types/compute-engine/symbolic/simplify-trig.d.ts +1 -1
  239. package/dist/types/compute-engine/tensor/tensor-fields.d.ts +1 -1
  240. package/dist/types/compute-engine/tensor/tensors.d.ts +1 -1
  241. package/dist/types/compute-engine/types-definitions.d.ts +1 -1
  242. package/dist/types/compute-engine/types-engine.d.ts +6 -2
  243. package/dist/types/compute-engine/types-evaluation.d.ts +1 -1
  244. package/dist/types/compute-engine/types-expression.d.ts +1 -1
  245. package/dist/types/compute-engine/types-kernel-evaluation.d.ts +1 -1
  246. package/dist/types/compute-engine/types-kernel-serialization.d.ts +1 -1
  247. package/dist/types/compute-engine/types-serialization.d.ts +1 -1
  248. package/dist/types/compute-engine/types.d.ts +1 -1
  249. package/dist/types/compute-engine.d.ts +2 -3
  250. package/dist/types/core.d.ts +1 -1
  251. package/dist/types/interval.d.ts +1 -1
  252. package/dist/types/latex-syntax.d.ts +2 -2
  253. package/dist/types/math-json/symbols.d.ts +1 -1
  254. package/dist/types/math-json/types.d.ts +1 -1
  255. package/dist/types/math-json/utils.d.ts +1 -1
  256. package/dist/types/math-json.d.ts +2 -2
  257. package/dist/types/numerics.d.ts +1 -1
  258. package/package.json +2 -2
  259. package/dist/types/compute-engine/compilation/fractal-orbit.d.ts +0 -19
package/dist/core.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- /** Compute Engine 0.55.5 */
1
+ /** Compute Engine 0.56.0 */
2
2
 
3
3
  // node_modules/complex-esm/dist/src/complex.js
4
4
  var cosh = Math.cosh || function(x) {
@@ -2240,6 +2240,7 @@ var SCALAR_TYPES = [
2240
2240
  ];
2241
2241
  var VALUE_TYPES = [
2242
2242
  "value",
2243
+ "color",
2243
2244
  ...COLLECTION_TYPES,
2244
2245
  ...SCALAR_TYPES
2245
2246
  ];
@@ -3939,6 +3940,7 @@ var PRIMITIVE_SUBTYPES = {
3939
3940
  symbol: [],
3940
3941
  boolean: [],
3941
3942
  string: [],
3943
+ color: [],
3942
3944
  expression: EXPRESSION_TYPES
3943
3945
  };
3944
3946
  function isPrimitiveSubtype(lhs, rhs) {
@@ -7822,9 +7824,10 @@ var _BoxedExpression = class __BoxedExpression {
7822
7824
  if (!materialized.isLazyCollection) return materialized.latex;
7823
7825
  }
7824
7826
  const syntax = this.engine._requireLatexSyntax();
7825
- return syntax.serialize(
7826
- this.toMathJson({ prettify: true, fractionalDigits: "auto" })
7827
- );
7827
+ const json = this.toMathJson({ prettify: true, fractionalDigits: "auto" });
7828
+ const latexOpts = this.engine.latexOptions;
7829
+ if (Object.keys(latexOpts).length === 0) return syntax.serialize(json);
7830
+ return syntax.serialize(json, { ...latexOpts });
7828
7831
  }
7829
7832
  /**
7830
7833
  * Return a LaTeX representation of this expression with custom
@@ -7848,9 +7851,13 @@ var _BoxedExpression = class __BoxedExpression {
7848
7851
  fractionalDigits: "auto"
7849
7852
  });
7850
7853
  const syntax = this.engine._requireLatexSyntax();
7851
- if (!options || Object.keys(options).length === 0)
7852
- return syntax.serialize(json);
7853
- return syntax.serialize(json, options);
7854
+ const latexOpts = this.engine.latexOptions;
7855
+ const haveEngineOpts = Object.keys(latexOpts).length > 0;
7856
+ const haveCallOpts = options && Object.keys(options).length > 0;
7857
+ if (!haveEngineOpts && !haveCallOpts) return syntax.serialize(json);
7858
+ if (!haveEngineOpts) return syntax.serialize(json, options);
7859
+ if (!haveCallOpts) return syntax.serialize(json, { ...latexOpts });
7860
+ return syntax.serialize(json, { ...latexOpts, ...options });
7854
7861
  }
7855
7862
  /** Called by `JSON.stringify()` when serializing to json.
7856
7863
  *
@@ -7894,11 +7901,13 @@ var _BoxedExpression = class __BoxedExpression {
7894
7901
  "number",
7895
7902
  "dictionary"
7896
7903
  ];
7897
- }
7898
- if (Array.isArray(options.shorthands))
7904
+ } else if (Array.isArray(options.shorthands)) {
7899
7905
  defaultOptions.shorthands = options.shorthands;
7906
+ }
7900
7907
  if (typeof options.metadata === "string" && options.metadata === "all" || options.metadata?.includes("all")) {
7901
7908
  defaultOptions.metadata = ["latex", "wikidata"];
7909
+ } else if (Array.isArray(options.metadata)) {
7910
+ defaultOptions.metadata = options.metadata;
7902
7911
  }
7903
7912
  if (options.fractionalDigits === "auto")
7904
7913
  defaultOptions.fractionalDigits = -this.engine.precision;
@@ -11037,10 +11046,6 @@ var DEFINITIONS_CORE = [
11037
11046
  // Lagrange notation
11038
11047
  {
11039
11048
  name: "Derivative",
11040
- // @todo: Leibniz notation: {% latex " \\frac{d^n}{dx^n} f(x)" %}
11041
- // @todo: Euler modified notation: This notation is used by Mathematica. The Euler notation uses `D` instead of
11042
- // `\partial`: `\partial_{x} f`, `\partial_{x,y} f`
11043
- // Newton notation (\dot{v}, \ddot{v}) is implemented below
11044
11049
  serialize: (serializer, expr2) => {
11045
11050
  const degree = machineValue(operand(expr2, 2)) ?? 1;
11046
11051
  const base = serializer.serialize(operand(expr2, 1));
@@ -12078,14 +12083,18 @@ var DEFINITIONS_SETS = [
12078
12083
  return ["Complement", lhs];
12079
12084
  }
12080
12085
  // precedence: 240,
12081
- // @todo: serialize for the multiple argument case
12082
12086
  },
12083
12087
  {
12084
12088
  name: "Complement",
12085
12089
  latexTrigger: ["^", "<{>", "\\complement", "<}>"],
12086
- kind: "postfix"
12090
+ kind: "postfix",
12087
12091
  // precedence: 240,
12088
- // @todo: serialize for the multiple argument case
12092
+ serialize: (serializer, expr2) => {
12093
+ return joinLatex([
12094
+ serializer.serialize(operand(expr2, 1)),
12095
+ "^\\complement"
12096
+ ]);
12097
+ }
12089
12098
  },
12090
12099
  {
12091
12100
  name: "Intersection",
@@ -12172,7 +12181,6 @@ var DEFINITIONS_SETS = [
12172
12181
  // commands like \rbrack a, b \rbrack which are unambiguous.
12173
12182
  {
12174
12183
  name: "Multiple",
12175
- // @todo: parse
12176
12184
  serialize: serializeSet
12177
12185
  },
12178
12186
  {
@@ -12181,14 +12189,28 @@ var DEFINITIONS_SETS = [
12181
12189
  kind: "infix",
12182
12190
  precedence: 350
12183
12191
  },
12192
+ // \mid as a separator/operator (used in set-builder notation: {x \mid x > 0})
12193
+ // Low precedence so it binds loosely — everything on each side is parsed first
12194
+ {
12195
+ name: "Divides",
12196
+ latexTrigger: ["\\mid"],
12197
+ kind: "infix",
12198
+ precedence: 160
12199
+ },
12184
12200
  {
12185
12201
  name: "Set",
12186
12202
  kind: "matchfix",
12187
12203
  openTrigger: "{",
12188
12204
  closeTrigger: "}",
12189
- // @todo: the set syntax can also include conditions...
12190
12205
  parse: (_parser, body) => {
12191
12206
  if (isEmptySequence(body)) return "EmptySet";
12207
+ const h = operator(body);
12208
+ if (h === "Divides" || h === "Colon") {
12209
+ const expr2 = operand(body, 1);
12210
+ const condition = operand(body, 2);
12211
+ if (expr2 !== null && condition !== null)
12212
+ return ["Set", expr2, ["Condition", condition]];
12213
+ }
12192
12214
  if (operator(body) == "Delimiter" && stringValue(operand(body, 2)) === ",") {
12193
12215
  body = operand(body, 1);
12194
12216
  }
@@ -12196,6 +12218,17 @@ var DEFINITIONS_SETS = [
12196
12218
  return ["Set", ...operands(body)];
12197
12219
  },
12198
12220
  serialize: (serializer, expr2) => {
12221
+ if (nops(expr2) === 2 && operator(operand(expr2, 2)) === "Condition") {
12222
+ const condition = operand(expr2, 2);
12223
+ return joinLatex([
12224
+ "\\lbrace",
12225
+ serializer.serialize(operand(expr2, 1)),
12226
+ "\\mid",
12227
+ // Serialize the inner expression of the Condition wrapper
12228
+ serializer.serialize(operand(condition, 1)),
12229
+ "\\rbrace"
12230
+ ]);
12231
+ }
12199
12232
  return joinLatex([
12200
12233
  "\\lbrace",
12201
12234
  operands(expr2).map((x) => serializer.serialize(x)).join(", "),
@@ -12362,23 +12395,6 @@ function serializeSet(serializer, expr2) {
12362
12395
  if (expr2 === null) return "";
12363
12396
  const h = operator(expr2);
12364
12397
  if (!h) return "";
12365
- if (h === "Set") {
12366
- if (nops(expr2) === 0) return "\\emptyset";
12367
- if (nops(expr2) === 2 && operator(operand(expr2, 2)) === "Condition") {
12368
- return joinLatex([
12369
- "\\left\\lbrace",
12370
- serializer.serialize(operand(expr2, 1)),
12371
- "\\middle\\mid",
12372
- serializer.serialize(operand(expr2, 2)),
12373
- "\\right\\rbrace"
12374
- ]);
12375
- }
12376
- return joinLatex([
12377
- "\\left\\lbrace",
12378
- ...operands(expr2).map((x) => serializer.serialize(x) + " ,"),
12379
- "\\right\\rbrace"
12380
- ]);
12381
- }
12382
12398
  if (h === "Multiple") {
12383
12399
  }
12384
12400
  if (h === "Range") {
@@ -13526,11 +13542,13 @@ var DEFINITIONS_ARITHMETIC = [
13526
13542
  if (!parser.match("_")) return null;
13527
13543
  const base = parser.parseGroup();
13528
13544
  if (operator(base) !== "To") return null;
13529
- const expr2 = parser.parseArguments("implicit");
13545
+ const expr2 = parser.parseExpression({
13546
+ minPrec: MULTIPLICATION_PRECEDENCE
13547
+ });
13530
13548
  if (!expr2) return null;
13531
13549
  return [
13532
13550
  "Limit",
13533
- ["Function", expr2[0], operand(base, 1)],
13551
+ ["Function", expr2, operand(base, 1)],
13534
13552
  operand(base, 2)
13535
13553
  ];
13536
13554
  },
@@ -13611,6 +13629,8 @@ var DEFINITIONS_ARITHMETIC = [
13611
13629
  precedence: DIVISION_PRECEDENCE,
13612
13630
  parse: "Mod"
13613
13631
  },
13632
+ // Function-style alias: `\operatorname{mod}(a, b)`
13633
+ { latexTrigger: "\\operatorname{mod}", parse: "Mod" },
13614
13634
  {
13615
13635
  latexTrigger: "\\pmod",
13616
13636
  kind: "prefix",
@@ -13851,6 +13871,13 @@ var DEFINITIONS_ARITHMETIC = [
13851
13871
  const rhs = serializer.wrap(operand(expr2, 2), ADDITION_PRECEDENCE + 3);
13852
13872
  return joinLatex([lhs, "-", rhs]);
13853
13873
  }
13874
+ },
13875
+ // Euclidean distance between two points (tuples of numbers).
13876
+ {
13877
+ name: "Distance",
13878
+ latexTrigger: ["\\operatorname{distance}"],
13879
+ kind: "function",
13880
+ serialize: (serializer, expr2) => "\\operatorname{distance}" + serializer.wrapArguments(expr2)
13854
13881
  }
13855
13882
  ];
13856
13883
  function getIndexAssignment(expr2, upper) {
@@ -15275,7 +15302,9 @@ var DEFINITIONS_STATISTICS = [
15275
15302
  if (!expr2 || !symbol(expr2)) return null;
15276
15303
  return ["Mean", expr2];
15277
15304
  }
15278
- }
15305
+ },
15306
+ // Function-style alias: `\operatorname{var}(...)`
15307
+ { latexTrigger: "\\operatorname{var}", parse: "Variance" }
15279
15308
  ];
15280
15309
 
15281
15310
  // src/compute-engine/numerics/unit-data.ts
@@ -15827,12 +15856,52 @@ var DEFINITIONS_UNITS = [
15827
15856
  ];
15828
15857
 
15829
15858
  // src/compute-engine/latex-syntax/dictionary/definitions-other.ts
15859
+ var TEX_UNITS = [
15860
+ "pt",
15861
+ "em",
15862
+ "mu",
15863
+ "ex",
15864
+ "mm",
15865
+ "cm",
15866
+ "in",
15867
+ "bp",
15868
+ "sp",
15869
+ "dd",
15870
+ "cc",
15871
+ "pc",
15872
+ "nc",
15873
+ "nd"
15874
+ ];
15875
+ function skipTexDimension(parser) {
15876
+ parser.skipSpace();
15877
+ if (parser.peek === "-" || parser.peek === "+") parser.nextToken();
15878
+ while (/^[\d.]$/.test(parser.peek)) parser.nextToken();
15879
+ for (const unit of TEX_UNITS) {
15880
+ if (parser.matchAll([...unit])) return;
15881
+ }
15882
+ }
15830
15883
  function parseSingleArg(cmd) {
15831
15884
  return (parser) => {
15832
15885
  const arg = parser.parseGroup();
15833
15886
  return arg === null ? [cmd] : [cmd, arg];
15834
15887
  };
15835
15888
  }
15889
+ function parseMathStyleSwitch(mathStyle) {
15890
+ return (parser) => {
15891
+ const body = parser.parseExpression();
15892
+ if (body !== null && !isEmptySequence(body))
15893
+ return ["Annotated", body, { dict: { mathStyle } }];
15894
+ return "Nothing";
15895
+ };
15896
+ }
15897
+ function parseSizeSwitch(size) {
15898
+ return (parser) => {
15899
+ const body = parser.parseExpression();
15900
+ if (body !== null && !isEmptySequence(body))
15901
+ return ["Annotated", body, { dict: { size } }];
15902
+ return "Nothing";
15903
+ };
15904
+ }
15836
15905
  var DEFINITIONS_OTHERS = [
15837
15906
  {
15838
15907
  name: "Overscript",
@@ -16072,80 +16141,71 @@ var DEFINITIONS_OTHERS = [
16072
16141
  },
16073
16142
  {
16074
16143
  latexTrigger: ["\\displaystyle"],
16075
- parse: () => "Nothing"
16076
- // @todo: parse as ['Annotated'...]
16144
+ parse: parseMathStyleSwitch("normal")
16077
16145
  },
16078
16146
  {
16079
16147
  latexTrigger: ["\\textstyle"],
16080
- parse: () => "Nothing"
16081
- // @todo: parse as ['Annotated'...]
16148
+ parse: parseMathStyleSwitch("compact")
16082
16149
  },
16083
16150
  {
16084
16151
  latexTrigger: ["\\scriptstyle"],
16085
- parse: () => "Nothing"
16086
- // @todo: parse as ['Annotated'...]
16152
+ parse: parseMathStyleSwitch("script")
16087
16153
  },
16088
16154
  {
16089
16155
  latexTrigger: ["\\scriptscriptstyle"],
16090
- parse: () => "Nothing"
16091
- // @todo: parse as ['Annotated'...]
16156
+ parse: parseMathStyleSwitch("scriptscript")
16092
16157
  },
16093
16158
  {
16094
16159
  latexTrigger: ["\\color"],
16095
16160
  parse: (parser) => {
16096
- parser.parseGroup();
16161
+ const color = parser.parseStringGroup();
16162
+ if (color !== null) {
16163
+ const body = parser.parseExpression();
16164
+ if (body !== null && !isEmptySequence(body))
16165
+ return ["Annotated", body, { dict: { color } }];
16166
+ }
16097
16167
  return "Nothing";
16098
16168
  }
16099
16169
  },
16100
16170
  {
16101
16171
  latexTrigger: ["\\tiny"],
16102
- parse: () => "Nothing"
16103
- // @todo: parse as ['Annotated'...]
16172
+ parse: parseSizeSwitch(1)
16104
16173
  },
16105
16174
  {
16106
16175
  latexTrigger: ["\\scriptsize"],
16107
- parse: () => "Nothing"
16108
- // @todo: parse as ['Annotated'...]
16176
+ parse: parseSizeSwitch(2)
16109
16177
  },
16110
16178
  {
16111
16179
  latexTrigger: ["\\footnotesize"],
16112
- parse: () => "Nothing"
16113
- // @todo: parse as ['Annotated'...]
16180
+ parse: parseSizeSwitch(3)
16114
16181
  },
16115
16182
  {
16116
16183
  latexTrigger: ["\\small"],
16117
- parse: () => "Nothing"
16118
- // @todo: parse as ['Annotated'...]
16184
+ parse: parseSizeSwitch(4)
16119
16185
  },
16120
16186
  {
16121
16187
  latexTrigger: ["\\normalsize"],
16122
- parse: () => "Nothing"
16123
- // @todo: parse as ['Annotated'...]
16188
+ parse: parseSizeSwitch(5)
16124
16189
  },
16125
16190
  {
16126
16191
  latexTrigger: ["\\large"],
16127
- parse: () => "Nothing"
16128
- // @todo: parse as ['Annotated'...]
16192
+ parse: parseSizeSwitch(6)
16129
16193
  },
16130
16194
  {
16131
16195
  latexTrigger: ["\\Large"],
16132
- parse: () => "Nothing"
16133
- // @todo: parse as ['Annotated'...]
16196
+ parse: parseSizeSwitch(7)
16134
16197
  },
16135
16198
  {
16136
16199
  latexTrigger: ["\\LARGE"],
16137
- parse: () => "Nothing"
16138
- // @todo: parse as ['Annotated'...]
16200
+ parse: parseSizeSwitch(8)
16139
16201
  },
16140
16202
  {
16141
16203
  latexTrigger: ["\\huge"],
16142
- parse: () => "Nothing"
16143
- // @todo: parse as ['Annotated'...]
16204
+ parse: parseSizeSwitch(9)
16144
16205
  },
16145
16206
  {
16146
16207
  latexTrigger: ["\\Huge"],
16147
- parse: () => "Nothing"
16148
- // @todo: parse as ['Annotated'...]
16208
+ parse: parseSizeSwitch(10)
16149
16209
  },
16150
16210
  {
16151
16211
  name: "Annotated",
@@ -16157,6 +16217,10 @@ var DEFINITIONS_OTHERS = [
16157
16217
  result = joinLatex(["{\\displaystyle", result, "}"]);
16158
16218
  else if (dict.dict.mathStyle === "compact")
16159
16219
  result = joinLatex(["{\\textstyle", result, "}"]);
16220
+ else if (dict.dict.mathStyle === "script")
16221
+ result = joinLatex(["{\\scriptstyle", result, "}"]);
16222
+ else if (dict.dict.mathStyle === "scriptscript")
16223
+ result = joinLatex(["{\\scriptscriptstyle", result, "}"]);
16160
16224
  const v = dict.dict.size;
16161
16225
  if (v !== null && v >= 1 && v <= 10) {
16162
16226
  result = joinLatex([
@@ -16244,6 +16308,28 @@ var DEFINITIONS_OTHERS = [
16244
16308
  latexTrigger: ["\\enspace"],
16245
16309
  parse: () => ["HorizontalSpacing", 9]
16246
16310
  },
16311
+ {
16312
+ latexTrigger: ["\\hspace"],
16313
+ parse: (parser) => {
16314
+ if (parser.peek === "*") parser.nextToken();
16315
+ parser.parseStringGroup();
16316
+ return ["HorizontalSpacing", 0];
16317
+ }
16318
+ },
16319
+ {
16320
+ latexTrigger: ["\\hskip"],
16321
+ parse: (parser) => {
16322
+ skipTexDimension(parser);
16323
+ return ["HorizontalSpacing", 0];
16324
+ }
16325
+ },
16326
+ {
16327
+ latexTrigger: ["\\kern"],
16328
+ parse: (parser) => {
16329
+ skipTexDimension(parser);
16330
+ return ["HorizontalSpacing", 0];
16331
+ }
16332
+ },
16247
16333
  {
16248
16334
  latexTrigger: ["\\phantom"],
16249
16335
  parse: (parser) => {
@@ -16294,7 +16380,17 @@ var DEFINITIONS_OTHERS = [
16294
16380
  // `["HorizontalSpacing", expr, 'op'|'bin'|rel]` -> indicate a spacing around and expression, i.e. `\mathbin{x}`, etc...
16295
16381
  serialize: (serializer, expr2) => {
16296
16382
  if (operand(expr2, 2) !== null) {
16297
- return serializer.serialize(operand(expr2, 1));
16383
+ const cls = stringValue(operand(expr2, 2));
16384
+ const inner = serializer.serialize(operand(expr2, 1));
16385
+ if (cls === "bin") return `\\mathbin{${inner}}`;
16386
+ if (cls === "op") return `\\mathop{${inner}}`;
16387
+ if (cls === "rel") return `\\mathrel{${inner}}`;
16388
+ if (cls === "ord") return `\\mathord{${inner}}`;
16389
+ if (cls === "open") return `\\mathopen{${inner}}`;
16390
+ if (cls === "close") return `\\mathclose{${inner}}`;
16391
+ if (cls === "punct") return `\\mathpunct{${inner}}`;
16392
+ if (cls === "inner") return `\\mathinner{${inner}}`;
16393
+ return inner;
16298
16394
  }
16299
16395
  const v = machineValue(operand(expr2, 1));
16300
16396
  if (v === null) return "";
@@ -16309,7 +16405,7 @@ var DEFINITIONS_OTHERS = [
16309
16405
  36: "\\qquad"
16310
16406
  }[v] ?? "";
16311
16407
  }
16312
- }
16408
+ },
16313
16409
  // if (
16314
16410
  // [
16315
16411
  // '\\!',
@@ -16333,6 +16429,121 @@ var DEFINITIONS_OTHERS = [
16333
16429
  // name: '',
16334
16430
  // trigger: '\\check',
16335
16431
  // },
16432
+ // ---------------------------------------------------------------------------
16433
+ // Function-style aliases for collection / random operators that some
16434
+ // notations write in lowercase (e.g. `\operatorname{shuffle}(L)`).
16435
+ // The capitalized library entries already exist; these are pure parse
16436
+ // aliases so the lowercase names don't land in `unsupported-operator`.
16437
+ // ---------------------------------------------------------------------------
16438
+ { latexTrigger: "\\operatorname{random}", parse: "Random" },
16439
+ { latexTrigger: "\\operatorname{shuffle}", parse: "Shuffle" },
16440
+ { latexTrigger: "\\operatorname{repeat}", parse: "Repeat" },
16441
+ { latexTrigger: "\\operatorname{join}", parse: "Join" },
16442
+ // ---------------------------------------------------------------------------
16443
+ // Geometric primitive heads. Registered as known typed heads so consumers
16444
+ // can branch on the operator name; CE itself doesn't render them. The
16445
+ // library entries (with no evaluator) live in `library/core.ts`.
16446
+ // ---------------------------------------------------------------------------
16447
+ {
16448
+ name: "Triangle",
16449
+ latexTrigger: ["\\operatorname{triangle}"],
16450
+ kind: "function",
16451
+ serialize: (serializer, expr2) => "\\operatorname{triangle}" + serializer.wrapArguments(expr2)
16452
+ },
16453
+ // Desmos's geometric `vector(p1, p2)` — a directed segment between two
16454
+ // points. Routed to a dedicated head (not the existing column-vector
16455
+ // `Vector`, which has a narrower `(number+) -> vector` signature).
16456
+ {
16457
+ name: "GeometricVector",
16458
+ latexTrigger: ["\\operatorname{vector}"],
16459
+ kind: "function",
16460
+ serialize: (serializer, expr2) => "\\operatorname{vector}" + serializer.wrapArguments(expr2)
16461
+ },
16462
+ {
16463
+ name: "Sphere",
16464
+ latexTrigger: ["\\operatorname{sphere}"],
16465
+ kind: "function",
16466
+ serialize: (serializer, expr2) => "\\operatorname{sphere}" + serializer.wrapArguments(expr2)
16467
+ },
16468
+ {
16469
+ name: "Segment",
16470
+ latexTrigger: ["\\operatorname{segment}"],
16471
+ kind: "function",
16472
+ serialize: (serializer, expr2) => "\\operatorname{segment}" + serializer.wrapArguments(expr2)
16473
+ }
16474
+ ];
16475
+
16476
+ // src/compute-engine/latex-syntax/dictionary/definitions-colors.ts
16477
+ var DEFINITIONS_COLORS = [
16478
+ // Color constructors (one per colorspace, preserves space on evaluation)
16479
+ {
16480
+ name: "Rgb",
16481
+ latexTrigger: ["\\operatorname{rgb}"],
16482
+ kind: "function",
16483
+ serialize: (serializer, expr2) => "\\operatorname{rgb}" + serializer.wrapArguments(expr2)
16484
+ },
16485
+ {
16486
+ name: "Hsv",
16487
+ latexTrigger: ["\\operatorname{hsv}"],
16488
+ kind: "function",
16489
+ serialize: (serializer, expr2) => "\\operatorname{hsv}" + serializer.wrapArguments(expr2)
16490
+ },
16491
+ {
16492
+ name: "Hsl",
16493
+ latexTrigger: ["\\operatorname{hsl}"],
16494
+ kind: "function",
16495
+ serialize: (serializer, expr2) => "\\operatorname{hsl}" + serializer.wrapArguments(expr2)
16496
+ },
16497
+ {
16498
+ name: "Oklab",
16499
+ latexTrigger: ["\\operatorname{oklab}"],
16500
+ kind: "function",
16501
+ serialize: (serializer, expr2) => "\\operatorname{oklab}" + serializer.wrapArguments(expr2)
16502
+ },
16503
+ {
16504
+ name: "Oklch",
16505
+ latexTrigger: ["\\operatorname{oklch}"],
16506
+ kind: "function",
16507
+ serialize: (serializer, expr2) => "\\operatorname{oklch}" + serializer.wrapArguments(expr2)
16508
+ },
16509
+ // Conversion functions (color → color in the named space)
16510
+ {
16511
+ name: "AsRgb",
16512
+ latexTrigger: ["\\operatorname{asRgb}"],
16513
+ kind: "function",
16514
+ serialize: (serializer, expr2) => "\\operatorname{asRgb}" + serializer.wrapArguments(expr2)
16515
+ },
16516
+ {
16517
+ name: "AsHsv",
16518
+ latexTrigger: ["\\operatorname{asHsv}"],
16519
+ kind: "function",
16520
+ serialize: (serializer, expr2) => "\\operatorname{asHsv}" + serializer.wrapArguments(expr2)
16521
+ },
16522
+ {
16523
+ name: "AsHsl",
16524
+ latexTrigger: ["\\operatorname{asHsl}"],
16525
+ kind: "function",
16526
+ serialize: (serializer, expr2) => "\\operatorname{asHsl}" + serializer.wrapArguments(expr2)
16527
+ },
16528
+ {
16529
+ name: "AsOklab",
16530
+ latexTrigger: ["\\operatorname{asOklab}"],
16531
+ kind: "function",
16532
+ serialize: (serializer, expr2) => "\\operatorname{asOklab}" + serializer.wrapArguments(expr2)
16533
+ },
16534
+ {
16535
+ name: "AsOklch",
16536
+ latexTrigger: ["\\operatorname{asOklch}"],
16537
+ kind: "function",
16538
+ serialize: (serializer, expr2) => "\\operatorname{asOklch}" + serializer.wrapArguments(expr2)
16539
+ },
16540
+ // Perceptual difference (returns a scalar in [0, ~1])
16541
+ {
16542
+ name: "ColorDelta",
16543
+ latexTrigger: ["\\operatorname{colorDelta}"],
16544
+ kind: "function",
16545
+ serialize: (serializer, expr2) => "\\operatorname{colorDelta}" + serializer.wrapArguments(expr2)
16546
+ }
16336
16547
  ];
16337
16548
 
16338
16549
  // src/compute-engine/latex-syntax/dictionary/default-dictionary.ts
@@ -16363,7 +16574,8 @@ var LATEX_DICTIONARY = [
16363
16574
  ...DEFINITIONS_STATISTICS,
16364
16575
  ...DEFINITIONS_UNITS,
16365
16576
  ...DEFINITIONS_OTHERS,
16366
- ...DEFINITIONS_PHYSICS
16577
+ ...DEFINITIONS_PHYSICS,
16578
+ ...DEFINITIONS_COLORS
16367
16579
  ];
16368
16580
 
16369
16581
  // src/math-json/symbols.ts
@@ -16528,6 +16740,17 @@ function addEntry(result, entry, onError) {
16528
16740
  } else if (Array.isArray(openTrigger) && openTrigger.length > 0) {
16529
16741
  openTokens.push(openTrigger[0]);
16530
16742
  }
16743
+ const closeTrigger = indexedEntry.closeTrigger;
16744
+ const closeTokens = /* @__PURE__ */ new Set();
16745
+ if (typeof closeTrigger === "string") {
16746
+ const variants = DELIMITER_SHORTHAND[closeTrigger];
16747
+ if (variants) for (const v of variants) closeTokens.add(v);
16748
+ else closeTokens.add(closeTrigger);
16749
+ if (closeTrigger === "||") closeTokens.add("|");
16750
+ } else if (Array.isArray(closeTrigger) && closeTrigger.length > 0) {
16751
+ closeTokens.add(closeTrigger[0]);
16752
+ }
16753
+ indexedEntry.closeTokens = closeTokens;
16531
16754
  for (const token of openTokens) {
16532
16755
  const existing = result.matchfixByOpen.get(token);
16533
16756
  if (existing) {
@@ -16685,25 +16908,43 @@ function makeIndexedEntry(entry, onError) {
16685
16908
  result.arguments = entry.arguments;
16686
16909
  return result;
16687
16910
  }
16911
+ function serializeTabularBody(serializer, body) {
16912
+ if (!body) return "";
16913
+ if (operator(body) !== "List") return serializer.serialize(body);
16914
+ const rows = operands(body);
16915
+ if (rows.length === 0) return "";
16916
+ if (!rows.every((row) => operator(row) === "List"))
16917
+ return serializer.serialize(body);
16918
+ return rows.map(
16919
+ (row) => operands(row).map((cell) => serializer.serialize(cell)).join(" & ")
16920
+ ).join(" \\\\\n");
16921
+ }
16688
16922
  function makeSerializeHandler(entry, latexTrigger, idTrigger) {
16689
16923
  if (typeof entry.serialize === "function") return entry.serialize;
16690
16924
  const kind = entry["kind"] ?? "expression";
16691
16925
  if (kind === "environment") {
16692
16926
  const envName = entry["symbolTrigger"] ?? entry.name ?? "unknown";
16693
- return (serializer, expr2) => joinLatex([
16694
- `\\begin{${envName}}`,
16695
- serializer.serialize(operand(expr2, 1)),
16696
- `\\end{${envName}}`
16697
- ]);
16927
+ return (serializer, expr2) => {
16928
+ const body = operand(expr2, 1);
16929
+ return joinLatex([
16930
+ `\\begin{${envName}}`,
16931
+ serializeTabularBody(serializer, body),
16932
+ `\\end{${envName}}`
16933
+ ]);
16934
+ };
16698
16935
  }
16699
16936
  if (isMatchfixEntry(entry)) {
16700
16937
  const openDelim = typeof entry.openTrigger === "string" ? DEFAULT_DELIMITER[entry.openTrigger] : tokensToString(entry.openTrigger);
16701
16938
  const closeDelim = typeof entry.closeTrigger === "string" ? DEFAULT_DELIMITER[entry.closeTrigger] : tokensToString(entry.closeTrigger);
16702
- return (serializer, expr2) => joinLatex([
16703
- openDelim,
16704
- serializer.serialize(operand(expr2, 1)),
16705
- closeDelim
16706
- ]);
16939
+ return (serializer, expr2) => {
16940
+ const style = serializer.groupStyle(expr2, serializer.level + 1);
16941
+ const inner = serializer.serialize(operand(expr2, 1));
16942
+ if (style === "scaled")
16943
+ return joinLatex([`\\left${openDelim}`, inner, `\\right${closeDelim}`]);
16944
+ if (style === "big")
16945
+ return joinLatex([`\\Bigl${openDelim}`, inner, `\\Bigr${closeDelim}`]);
16946
+ return joinLatex([openDelim, inner, closeDelim]);
16947
+ };
16707
16948
  }
16708
16949
  let latex = entry.serialize;
16709
16950
  if (latex === void 0 && latexTrigger) latex = tokensToString(latexTrigger);
@@ -17588,6 +17829,16 @@ function parseNumber(parser, fmt) {
17588
17829
  }
17589
17830
 
17590
17831
  // src/compute-engine/latex-syntax/parse.ts
17832
+ var _symbolToUnicode = null;
17833
+ function getSymbolToUnicode() {
17834
+ if (!_symbolToUnicode) {
17835
+ _symbolToUnicode = /* @__PURE__ */ new Map();
17836
+ for (const [, latex, codepoint] of SYMBOLS2) {
17837
+ _symbolToUnicode.set(latex, String.fromCodePoint(codepoint));
17838
+ }
17839
+ }
17840
+ return _symbolToUnicode;
17841
+ }
17591
17842
  var DELIMITER_SHORTHAND2 = {
17592
17843
  "(": ["\\lparen", "("],
17593
17844
  ")": ["\\rparen", ")"],
@@ -18035,6 +18286,35 @@ var _Parser = class __Parser {
18035
18286
  this.nextToken();
18036
18287
  this.skipVisualSpace();
18037
18288
  }
18289
+ if (this.match("\\hspace")) {
18290
+ this.match("*");
18291
+ this.parseStringGroup();
18292
+ this.skipVisualSpace();
18293
+ }
18294
+ if (this.match("\\hskip") || this.match("\\kern")) {
18295
+ this.skipSpace();
18296
+ if (!this.match("-")) this.match("+");
18297
+ while (/^[\d.]$/.test(this.peek)) this.nextToken();
18298
+ for (const unit of [
18299
+ "pt",
18300
+ "em",
18301
+ "mu",
18302
+ "ex",
18303
+ "mm",
18304
+ "cm",
18305
+ "in",
18306
+ "bp",
18307
+ "sp",
18308
+ "dd",
18309
+ "cc",
18310
+ "pc",
18311
+ "nc",
18312
+ "nd"
18313
+ ]) {
18314
+ if (this.matchAll([...unit])) break;
18315
+ }
18316
+ this.skipVisualSpace();
18317
+ }
18038
18318
  this.skipSpace();
18039
18319
  }
18040
18320
  match(token) {
@@ -18372,7 +18652,8 @@ var _Parser = class __Parser {
18372
18652
  } else if (token === "<space>") {
18373
18653
  result += " ";
18374
18654
  } else if (token[0] === "\\") {
18375
- result += token;
18655
+ const unicode = getSymbolToUnicode().get(token);
18656
+ result += unicode ?? token;
18376
18657
  } else {
18377
18658
  result += token;
18378
18659
  }
@@ -18526,6 +18807,19 @@ var _Parser = class __Parser {
18526
18807
  }
18527
18808
  for (const def of defs) {
18528
18809
  this.index = start;
18810
+ if (def.closeTokens.size > 0) {
18811
+ let found = false;
18812
+ const tokens = this._tokens;
18813
+ for (let i = start; i < tokens.length; i++) {
18814
+ if (def.closeTokens.has(tokens[i])) {
18815
+ found = true;
18816
+ break;
18817
+ }
18818
+ }
18819
+ if (!found) continue;
18820
+ }
18821
+ if (typeof def.openTrigger === "string" && def.openTrigger === "." && !OPEN_DELIMITER_PREFIX[currentToken])
18822
+ continue;
18529
18823
  const matched = this.matchDelimiter(def.openTrigger, def.closeTrigger);
18530
18824
  if (!matched) continue;
18531
18825
  const bodyStart = this.index;
@@ -19732,7 +20026,7 @@ var STYLE_MODIFIERS = {
19732
20026
  sansserif: (s) => `\\mathsf{${s}}`,
19733
20027
  monospace: (s) => `\\mathtt{${s}}`
19734
20028
  };
19735
- var Serializer4 = class {
20029
+ var Serializer5 = class {
19736
20030
  options;
19737
20031
  dictionary;
19738
20032
  level = -1;
@@ -19747,11 +20041,18 @@ var Serializer4 = class {
19747
20041
  /**
19748
20042
  * Serialize the expression, and if the expression is an operator
19749
20043
  * of precedence less than or equal to prec, wrap it in some parens.
19750
- * @todo: don't wrap Abs, Floor, Ceil, Delimiter
20044
+ *
20045
+ * Skip wrapping for matchfix operators (Abs, Floor, Ceil, Norm, etc.)
20046
+ * and Delimiter since they already have visible delimiters.
19751
20047
  */
19752
20048
  wrap(expr2, prec) {
19753
20049
  if (expr2 === null || expr2 === void 0) return "";
19754
20050
  if (prec === void 0) {
20051
+ const name2 = operator(expr2);
20052
+ if (name2) {
20053
+ const def = this.dictionary.ids.get(name2);
20054
+ if (def?.kind === "matchfix") return this.serialize(expr2);
20055
+ }
19755
20056
  return this.wrapString(
19756
20057
  this.serialize(expr2),
19757
20058
  this.options.groupStyle(expr2, this.level + 1)
@@ -20045,7 +20346,7 @@ function serializeSymbol2(s, style = "auto") {
20045
20346
  return body;
20046
20347
  }
20047
20348
  function serializeLatex(expr2, dict, options) {
20048
- const serializer = new Serializer4(dict, options);
20349
+ const serializer = new Serializer5(dict, options);
20049
20350
  return serializer.serialize(expr2);
20050
20351
  }
20051
20352
 
@@ -32081,6 +32382,29 @@ var ARITHMETIC_LIBRARY = [
32081
32382
  signature: "(value*) -> number | list",
32082
32383
  evaluate: (xs, { engine }) => evaluateMinMax(engine, xs, "Infimum")
32083
32384
  },
32385
+ Distance: {
32386
+ description: "Euclidean distance between two points (tuples of numbers).",
32387
+ complexity: 6e3,
32388
+ signature: "(tuple, tuple) -> number",
32389
+ evaluate: ([a, b], { engine: ce }) => {
32390
+ if (!isFunction2(a) || !isFunction2(b))
32391
+ return ce.error("incompatible-type");
32392
+ if (a.operator !== "Tuple" || b.operator !== "Tuple")
32393
+ return ce.error("incompatible-type");
32394
+ if (a.ops.length !== b.ops.length || a.ops.length === 0)
32395
+ return ce.error("incompatible-type");
32396
+ let sumSq = 0;
32397
+ for (let i = 0; i < a.ops.length; i++) {
32398
+ const ai = a.ops[i].re;
32399
+ const bi = b.ops[i].re;
32400
+ if (!Number.isFinite(ai) || !Number.isFinite(bi))
32401
+ return ce.error("expected-value");
32402
+ const d = ai - bi;
32403
+ sumSq += d * d;
32404
+ }
32405
+ return ce.number(Math.sqrt(sumSq));
32406
+ }
32407
+ },
32084
32408
  Product: {
32085
32409
  description: "`Product(f, a, b)` computes the product of `f` from `a` to `b`",
32086
32410
  wikidata: "Q901718",
@@ -32613,16 +32937,11 @@ function boxRule(ce, rule, options) {
32613
32937
  );
32614
32938
  let condFn;
32615
32939
  if (typeof condition === "string") {
32616
- const latex = asLatexString(condition);
32617
- if (latex) {
32618
- const condPattern = ce.parse(latex, {
32619
- form: options?.canonical ? "canonical" : "raw"
32620
- }) ?? ce.expr("Nothing");
32621
- condFn = (x, _ce) => {
32622
- const evaluated = condPattern.subs(x).evaluate();
32623
- return isSymbol2(evaluated, "True");
32624
- };
32625
- }
32940
+ const condPattern = ce.parse(condition) ?? ce.expr("Nothing");
32941
+ condFn = (x, _ce) => {
32942
+ const evaluated = condPattern.subs(x).evaluate();
32943
+ return isSymbol2(evaluated, "True");
32944
+ };
32626
32945
  } else {
32627
32946
  if (condition !== void 0 && typeof condition !== "function")
32628
32947
  throw new Error(
@@ -32724,6 +33043,15 @@ ${e.message}
32724
33043
  function applyRule(rule, expr2, substitution, options) {
32725
33044
  if (!rule) return null;
32726
33045
  let canonical2 = options?.canonical ?? (expr2.isCanonical || expr2.isStructural);
33046
+ let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
33047
+ const because = id ?? "";
33048
+ const ce = expr2.engine;
33049
+ if (canonical2 && match2) {
33050
+ const awc = getWildcards(match2);
33051
+ const canonicalMatch = match2.canonical;
33052
+ const bwc = getWildcards(canonicalMatch);
33053
+ if (!awc.every((x) => bwc.includes(x))) return null;
33054
+ }
32727
33055
  let operandsMatched = false;
32728
33056
  if (isFunction2(expr2) && options?.recursive) {
32729
33057
  const newOps = expr2.ops.map((op) => {
@@ -32735,20 +33063,11 @@ function applyRule(rule, expr2, substitution, options) {
32735
33063
  if (operandsMatched) {
32736
33064
  if (!canonical2 && options?.canonical === void 0 && newOps.every((x) => x.isCanonical))
32737
33065
  canonical2 = true;
32738
- expr2 = expr2.engine.function(expr2.operator, newOps, {
33066
+ expr2 = ce.function(expr2.operator, newOps, {
32739
33067
  form: canonical2 ? "canonical" : "raw"
32740
33068
  });
32741
33069
  }
32742
33070
  }
32743
- let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
32744
- const because = id ?? "";
32745
- if (canonical2 && match2) {
32746
- const awc = getWildcards(match2);
32747
- const canonicalMatch = match2.canonical;
32748
- const bwc = getWildcards(canonicalMatch);
32749
- if (!awc.every((x) => bwc.includes(x)))
32750
- return operandsMatched ? { value: expr2, because } : null;
32751
- }
32752
33071
  const useVariations = rule.useVariations ?? options?.useVariations ?? false;
32753
33072
  const matchPermutations = options?.matchPermutations ?? true;
32754
33073
  onBeforeMatch?.(rule, expr2);
@@ -32767,7 +33086,7 @@ function applyRule(rule, expr2, substitution, options) {
32767
33086
  ...sub2
32768
33087
  };
32769
33088
  try {
32770
- if (!condition(conditionSub, expr2.engine))
33089
+ if (!condition(conditionSub, ce))
32771
33090
  return operandsMatched ? { value: expr2, because } : null;
32772
33091
  } catch (e) {
32773
33092
  console.error(
@@ -32782,7 +33101,8 @@ function applyRule(rule, expr2, substitution, options) {
32782
33101
  if (!canonical2 && options?.canonical === void 0 && replace2 instanceof _BoxedExpression && replace2.isCanonical)
32783
33102
  canonical2 = true;
32784
33103
  const result = typeof replace2 === "function" ? replace2(expr2, sub2) : replace2.subs(sub2, { canonical: canonical2 });
32785
- if (!result) return null;
33104
+ if (!result)
33105
+ return operandsMatched ? { value: canonical2 ? expr2.canonical : expr2, because } : null;
32786
33106
  onMatch?.(rule, expr2, result);
32787
33107
  if (isRuleStep(result))
32788
33108
  return canonical2 ? { ...result, value: result.value.canonical } : result;
@@ -38356,6 +38676,40 @@ function rgbToHsl(r, g, b) {
38356
38676
  else h = ((r - g) / d + 4) / 6;
38357
38677
  return { h: h * 360, s, l };
38358
38678
  }
38679
+ function hsvToRgb(h, s, v) {
38680
+ h = (h % 360 + 360) % 360;
38681
+ s = Math.max(0, Math.min(1, s));
38682
+ v = Math.max(0, Math.min(1, v));
38683
+ const c = v * s;
38684
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
38685
+ const m = v - c;
38686
+ let r = 0, g = 0, b = 0;
38687
+ if (h < 60) [r, g, b] = [c, x, 0];
38688
+ else if (h < 120) [r, g, b] = [x, c, 0];
38689
+ else if (h < 180) [r, g, b] = [0, c, x];
38690
+ else if (h < 240) [r, g, b] = [0, x, c];
38691
+ else if (h < 300) [r, g, b] = [x, 0, c];
38692
+ else [r, g, b] = [c, 0, x];
38693
+ return { r: (r + m) * 255, g: (g + m) * 255, b: (b + m) * 255 };
38694
+ }
38695
+ function rgbToHsv(r, g, b) {
38696
+ r /= 255;
38697
+ g /= 255;
38698
+ b /= 255;
38699
+ const max2 = Math.max(r, g, b);
38700
+ const min2 = Math.min(r, g, b);
38701
+ const d = max2 - min2;
38702
+ let h = 0;
38703
+ if (d > 0) {
38704
+ if (max2 === r) h = (g - b) / d % 6;
38705
+ else if (max2 === g) h = (b - r) / d + 2;
38706
+ else h = (r - g) / d + 4;
38707
+ h *= 60;
38708
+ if (h < 0) h += 360;
38709
+ }
38710
+ const s = max2 === 0 ? 0 : d / max2;
38711
+ return { h, s, v: max2 };
38712
+ }
38359
38713
  function parseHexColor(s) {
38360
38714
  const hex = s.startsWith("#") ? s.substring(1) : s;
38361
38715
  let r, g, b;
@@ -38380,6 +38734,12 @@ function parseHexColor(s) {
38380
38734
  if (alpha !== void 0) result.alpha = alpha;
38381
38735
  return result;
38382
38736
  }
38737
+ function asOklch(color) {
38738
+ if (typeof color === "string") return rgbToOklch(parseHexColor(color));
38739
+ if ("C" in color) return color;
38740
+ if ("a" in color && "b" in color) return oklabToOklch(color);
38741
+ return rgbToOklch(color);
38742
+ }
38383
38743
  function asRgb(color) {
38384
38744
  if (typeof color === "number") {
38385
38745
  return {
@@ -38811,6 +39171,13 @@ var NAMED_COLORS = {
38811
39171
  };
38812
39172
  function parseColor(s, darkMode) {
38813
39173
  const str = s.trim().toLowerCase();
39174
+ const opacityMatch = str.match(/^(.+?)\s*\/\s*(\d+(?:\.\d+)?)%?\s*$/);
39175
+ if (opacityMatch) {
39176
+ const base = parseColor(opacityMatch[1].trim(), darkMode);
39177
+ const opacity = Math.max(0, Math.min(100, parseFloat(opacityMatch[2])));
39178
+ const alpha = Math.round(opacity / 100 * 255);
39179
+ return base & 4294967040 | alpha;
39180
+ }
38814
39181
  if (str.startsWith("#")) {
38815
39182
  const hex = str.substring(1);
38816
39183
  let r, g, b, a = 255;
@@ -38943,14 +39310,6 @@ function parseColor(s, darkMode) {
38943
39310
  console.warn(`parseColor: unrecognized color "${s}"`);
38944
39311
  return 0;
38945
39312
  }
38946
- function parseColorToRgb01(s, darkMode) {
38947
- const color = parseColor(s, darkMode);
38948
- return [
38949
- (color >>> 24 & 255) / 255,
38950
- (color >>> 16 & 255) / 255,
38951
- (color >>> 8 & 255) / 255
38952
- ];
38953
- }
38954
39313
  function apca(bgColor, fgColor) {
38955
39314
  const bgRgb = asRgb(bgColor);
38956
39315
  const fgRgb = asRgb(fgColor);
@@ -39009,6 +39368,12 @@ function contrastingColor(arg) {
39009
39368
  const contrast2 = Math.abs(apca(fg2, bg));
39010
39369
  return contrast1 >= contrast2 ? asColorNumber(fg1) : asColorNumber(fg2);
39011
39370
  }
39371
+ function oklabDeltaE(a, b) {
39372
+ const dL = a.L - b.L;
39373
+ const da = a.a - b.a;
39374
+ const db = a.b - b.b;
39375
+ return Math.sqrt(dL * dL + da * da + db * db);
39376
+ }
39012
39377
  function lerpOklch(c1, c2, f) {
39013
39378
  const L = c1.L + (c2.L - c1.L) * f;
39014
39379
  const C = c1.C + (c2.C - c1.C) * f;
@@ -41770,14 +42135,30 @@ var SEQUENTIAL_PALETTES = {
41770
42135
  };
41771
42136
 
41772
42137
  // src/compute-engine/library/colors.ts
41773
- function colorNumberToTuple(ce, color) {
41774
- const r = (color >>> 24 & 255) / 255;
41775
- const g = (color >>> 16 & 255) / 255;
41776
- const b = (color >>> 8 & 255) / 255;
41777
- const a = (color & 255) / 255;
41778
- if (Math.abs(a - 1) < 1e-4)
41779
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
41780
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b), ce.number(a));
42138
+ function normalizeAlpha(a) {
42139
+ if (a === void 0) return void 0;
42140
+ if (!Number.isFinite(a)) return void 0;
42141
+ if (Math.abs(a - 1) < 1e-9) return void 0;
42142
+ return a;
42143
+ }
42144
+ function normalizeColorHead(ce, expr2) {
42145
+ if (!isFunction2(expr2) || !expr2.ops || expr2.ops.length < 4) return expr2;
42146
+ const alphaExpr = expr2.ops[3];
42147
+ if (!isNumber(alphaExpr)) return expr2;
42148
+ if (normalizeAlpha(alphaExpr.re) === void 0) {
42149
+ return ce.function(expr2.operator, [expr2.ops[0], expr2.ops[1], expr2.ops[2]]);
42150
+ }
42151
+ return expr2;
42152
+ }
42153
+ function colorNumberToOklch(ce, color) {
42154
+ const r = color >>> 24 & 255;
42155
+ const g = color >>> 16 & 255;
42156
+ const b = color >>> 8 & 255;
42157
+ const a = normalizeAlpha((color & 255) / 255);
42158
+ const c = rgbToOklch({ r, g, b });
42159
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42160
+ if (a !== void 0) args.push(ce.number(a));
42161
+ return ce.function("Oklch", args);
41781
42162
  }
41782
42163
  var ALL_PALETTES = {
41783
42164
  ...SEQUENTIAL_PALETTES,
@@ -41788,45 +42169,139 @@ function samplePalette(ce, palette, t) {
41788
42169
  t = Math.max(0, Math.min(1, t));
41789
42170
  const n = palette.length;
41790
42171
  if (n === 0) return ce.error("expected-value");
41791
- if (n === 1) return colorNumberToTuple(ce, parseColor(palette[0]));
42172
+ if (n === 1) return colorNumberToOklch(ce, parseColor(palette[0]));
41792
42173
  const pos = t * (n - 1);
41793
42174
  const i = Math.floor(pos);
41794
42175
  const frac = pos - i;
41795
- if (i >= n - 1) return colorNumberToTuple(ce, parseColor(palette[n - 1]));
41796
- if (frac < 1e-9) return colorNumberToTuple(ce, parseColor(palette[i]));
41797
- const rgb = interpolateOklch(palette[i], palette[i + 1], frac);
41798
- const r = rgb.r / 255;
41799
- const g = rgb.g / 255;
41800
- const b = rgb.b / 255;
41801
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42176
+ if (i >= n - 1) return colorNumberToOklch(ce, parseColor(palette[n - 1]));
42177
+ if (frac < 1e-9) return colorNumberToOklch(ce, parseColor(palette[i]));
42178
+ return oklchToExpr(
42179
+ ce,
42180
+ asOklch(interpolateOklch(palette[i], palette[i + 1], frac))
42181
+ );
42182
+ }
42183
+ var COLOR_OPERATORS = /* @__PURE__ */ new Set(["Rgb", "Hsv", "Hsl", "Oklab", "Oklch"]);
42184
+ function readColorExpr(arg) {
42185
+ if (!isFunction2(arg)) return null;
42186
+ if (!COLOR_OPERATORS.has(arg.operator)) return null;
42187
+ if (!arg.ops || arg.ops.length < 3) return null;
42188
+ const c0 = arg.ops[0].re;
42189
+ const c1 = arg.ops[1].re;
42190
+ const c2 = arg.ops[2].re;
42191
+ if (!Number.isFinite(c0) || !Number.isFinite(c1) || !Number.isFinite(c2))
42192
+ return null;
42193
+ const alpha = arg.ops.length >= 4 ? normalizeAlpha(arg.ops[3].re) : void 0;
42194
+ return { space: arg.operator, c0, c1, c2, alpha };
42195
+ }
42196
+ function colorExprToRgb(arg) {
42197
+ const c = readColorExpr(arg);
42198
+ if (!c) return null;
42199
+ const withAlpha = (rgb) => c.alpha !== void 0 ? { ...rgb, alpha: c.alpha } : rgb;
42200
+ switch (c.space) {
42201
+ case "Rgb":
42202
+ return withAlpha({ r: c.c0 * 255, g: c.c1 * 255, b: c.c2 * 255 });
42203
+ case "Hsv":
42204
+ return withAlpha(hsvToRgb(c.c0, c.c1, c.c2));
42205
+ case "Hsl":
42206
+ return withAlpha(hslToRgb(c.c0, c.c1, c.c2));
42207
+ case "Oklab":
42208
+ return withAlpha(oklabToRgb({ L: c.c0, a: c.c1, b: c.c2 }));
42209
+ case "Oklch":
42210
+ return withAlpha(oklchToRgb({ L: c.c0, C: c.c1, H: c.c2 }));
42211
+ }
42212
+ return null;
42213
+ }
42214
+ function colorExprToOklch(arg) {
42215
+ const c = readColorExpr(arg);
42216
+ if (!c) return null;
42217
+ switch (c.space) {
42218
+ case "Oklch":
42219
+ return asOklch({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42220
+ case "Oklab":
42221
+ return asOklch({ L: c.c0, a: c.c1, b: c.c2, alpha: c.alpha });
42222
+ case "Rgb":
42223
+ return asOklch({
42224
+ r: c.c0 * 255,
42225
+ g: c.c1 * 255,
42226
+ b: c.c2 * 255,
42227
+ alpha: c.alpha
42228
+ });
42229
+ case "Hsv": {
42230
+ const rgb = hsvToRgb(c.c0, c.c1, c.c2);
42231
+ return asOklch({ ...rgb, alpha: c.alpha });
42232
+ }
42233
+ case "Hsl": {
42234
+ const rgb = hslToRgb(c.c0, c.c1, c.c2);
42235
+ return asOklch({ r: rgb.r, g: rgb.g, b: rgb.b, alpha: c.alpha });
42236
+ }
42237
+ }
42238
+ return null;
42239
+ }
42240
+ function toOklch(ce, arg) {
42241
+ const direct = colorExprToOklch(arg);
42242
+ if (direct) return direct;
42243
+ const rgb = extractRgb(ce, arg);
42244
+ return rgb ? asOklch(rgb) : null;
42245
+ }
42246
+ function lerpOklchColor(a, b, t) {
42247
+ const L = a.L + (b.L - a.L) * t;
42248
+ const C = a.C + (b.C - a.C) * t;
42249
+ const aAchromatic = a.C < 1e-6;
42250
+ const bAchromatic = b.C < 1e-6;
42251
+ let H;
42252
+ if (aAchromatic && bAchromatic) H = a.H;
42253
+ else if (aAchromatic) H = b.H;
42254
+ else if (bAchromatic) H = a.H;
42255
+ else {
42256
+ let dH = b.H - a.H;
42257
+ if (dH > 180) dH -= 360;
42258
+ if (dH < -180) dH += 360;
42259
+ H = a.H + dH * t;
42260
+ if (H < 0) H += 360;
42261
+ if (H >= 360) H -= 360;
42262
+ }
42263
+ const alphaA = a.alpha ?? 1;
42264
+ const alphaB = b.alpha ?? 1;
42265
+ return { L, C, H, alpha: normalizeAlpha(alphaA + (alphaB - alphaA) * t) };
42266
+ }
42267
+ function oklchToExpr(ce, c) {
42268
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42269
+ if (c.alpha !== void 0) args.push(ce.number(c.alpha));
42270
+ return ce.function("Oklch", args);
41802
42271
  }
41803
42272
  function extractRgb(ce, arg) {
41804
42273
  if (isString(arg)) {
41805
42274
  const s = arg.string;
41806
42275
  if (!s) return void 0;
41807
42276
  const color = parseColor(s);
41808
- return {
42277
+ const rgb = {
41809
42278
  r: color >>> 24 & 255,
41810
42279
  g: color >>> 16 & 255,
41811
- b: color >>> 8 & 255,
41812
- alpha: (color & 255) / 255
42280
+ b: color >>> 8 & 255
41813
42281
  };
42282
+ const alpha = normalizeAlpha((color & 255) / 255);
42283
+ if (alpha !== void 0) rgb.alpha = alpha;
42284
+ return rgb;
41814
42285
  }
42286
+ const fromTyped = colorExprToRgb(arg);
42287
+ if (fromTyped) return fromTyped;
41815
42288
  if (arg.operator === "Tuple" && arg.ops && arg.ops.length >= 3) {
41816
42289
  const rgb = {
41817
42290
  r: arg.ops[0].re * 255,
41818
42291
  g: arg.ops[1].re * 255,
41819
42292
  b: arg.ops[2].re * 255
41820
42293
  };
41821
- if (arg.ops.length >= 4) rgb.alpha = arg.ops[3].re;
42294
+ if (arg.ops.length >= 4) {
42295
+ const alpha = normalizeAlpha(arg.ops[3].re);
42296
+ if (alpha !== void 0) rgb.alpha = alpha;
42297
+ }
41822
42298
  return rgb;
41823
42299
  }
41824
42300
  return void 0;
41825
42301
  }
41826
42302
  function componentsTuple(ce, components, alpha) {
41827
42303
  const args = components.map((v) => ce.number(v));
41828
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4)
41829
- args.push(ce.number(alpha));
42304
+ if (alpha !== void 0) args.push(ce.number(alpha));
41830
42305
  return ce.tuple(...args);
41831
42306
  }
41832
42307
  function rgbToHex(rgb) {
@@ -41834,7 +42309,7 @@ function rgbToHex(rgb) {
41834
42309
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
41835
42310
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
41836
42311
  const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
41837
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
42312
+ if (rgb.alpha !== void 0) {
41838
42313
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
41839
42314
  return hex + a.toString(16).padStart(2, "0");
41840
42315
  }
@@ -41842,22 +42317,29 @@ function rgbToHex(rgb) {
41842
42317
  }
41843
42318
  var COLORS_LIBRARY = {
41844
42319
  Color: {
41845
- description: "Convert a color string to a canonical sRGB tuple",
42320
+ description: "Parse a CSS-style color string to an Oklch color",
41846
42321
  complexity: 8e3,
41847
- signature: "(string) -> tuple",
42322
+ signature: "(string) -> color",
41848
42323
  evaluate: (ops, { engine: ce }) => {
41849
42324
  const input = isString(ops[0]) ? ops[0].string : void 0;
41850
42325
  if (!input) return ce.error("incompatible-type");
41851
42326
  const color = parseColor(input);
41852
42327
  if (color === 0 && input.trim().toLowerCase() !== "transparent")
41853
42328
  return ce.error("incompatible-type");
41854
- return colorNumberToTuple(ce, color);
42329
+ const r = color >>> 24 & 255;
42330
+ const g = color >>> 16 & 255;
42331
+ const b = color >>> 8 & 255;
42332
+ const a = normalizeAlpha((color & 255) / 255);
42333
+ const c = rgbToOklch({ r, g, b });
42334
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42335
+ if (a !== void 0) args.push(ce.number(a));
42336
+ return ce.function("Oklch", args);
41855
42337
  }
41856
42338
  },
41857
42339
  ColorToString: {
41858
42340
  description: "Convert a color to a string in the specified format",
41859
42341
  complexity: 8e3,
41860
- signature: "(any, string?) -> string",
42342
+ signature: "(color | string | tuple, string?) -> string",
41861
42343
  evaluate: (ops, { engine: ce }) => {
41862
42344
  const rgb = extractRgb(ce, ops[0]);
41863
42345
  if (!rgb) return ce.error("incompatible-type");
@@ -41869,7 +42351,7 @@ var COLORS_LIBRARY = {
41869
42351
  const r = Math.round(rgb.r);
41870
42352
  const g = Math.round(rgb.g);
41871
42353
  const b = Math.round(rgb.b);
41872
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42354
+ if (rgb.alpha !== void 0)
41873
42355
  return ce.string(`rgb(${r} ${g} ${b} / ${rgb.alpha})`);
41874
42356
  return ce.string(`rgb(${r} ${g} ${b})`);
41875
42357
  }
@@ -41878,17 +42360,17 @@ var COLORS_LIBRARY = {
41878
42360
  const h = Math.round(hsl.h * 10) / 10;
41879
42361
  const s = Math.round(hsl.s * 1e3) / 10;
41880
42362
  const l = Math.round(hsl.l * 1e3) / 10;
41881
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42363
+ if (rgb.alpha !== void 0)
41882
42364
  return ce.string(`hsl(${h} ${s}% ${l}% / ${rgb.alpha})`);
41883
42365
  return ce.string(`hsl(${h} ${s}% ${l}%)`);
41884
42366
  }
41885
42367
  case "oklch": {
41886
- const c = rgbToOklch(rgb);
42368
+ const c = colorExprToOklch(ops[0]) ?? asOklch(rgb);
41887
42369
  const L = Math.round(c.L * 1e3) / 1e3;
41888
42370
  const C = Math.round(c.C * 1e3) / 1e3;
41889
42371
  const H = Math.round(c.H * 10) / 10;
41890
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
41891
- return ce.string(`oklch(${L} ${C} ${H} / ${rgb.alpha})`);
42372
+ if (c.alpha !== void 0)
42373
+ return ce.string(`oklch(${L} ${C} ${H} / ${c.alpha})`);
41892
42374
  return ce.string(`oklch(${L} ${C} ${H})`);
41893
42375
  }
41894
42376
  default:
@@ -41899,60 +42381,44 @@ var COLORS_LIBRARY = {
41899
42381
  ColorMix: {
41900
42382
  description: "Mix two colors in OKLCh space",
41901
42383
  complexity: 8e3,
41902
- signature: "(any, any, number?) -> tuple",
42384
+ signature: "(color | string | tuple, color | string | tuple, number?) -> color",
41903
42385
  evaluate: (ops, { engine: ce }) => {
41904
- const rgb1 = extractRgb(ce, ops[0]);
41905
- const rgb2 = extractRgb(ce, ops[1]);
41906
- if (!rgb1 || !rgb2) return ce.error("incompatible-type");
41907
42386
  let ratio = 0.5;
41908
42387
  if (ops.length >= 3 && ops[2] !== void 0) {
41909
42388
  ratio = ops[2].re;
41910
42389
  if (!Number.isFinite(ratio)) return ce.error("expected-value");
41911
42390
  ratio = Math.max(0, Math.min(1, ratio));
41912
42391
  }
41913
- const c1 = rgbToOklch(rgb1);
41914
- const c2 = rgbToOklch(rgb2);
41915
- const mixed = oklchToRgb(lerpOklch(c1, c2, ratio));
41916
- const r = mixed.r / 255;
41917
- const g = mixed.g / 255;
41918
- const b = mixed.b / 255;
41919
- const a1 = rgb1.alpha ?? 1;
41920
- const a2 = rgb2.alpha ?? 1;
41921
- const alpha = a1 + (a2 - a1) * ratio;
41922
- if (Math.abs(alpha - 1) > 1e-4)
41923
- return ce.tuple(
41924
- ce.number(r),
41925
- ce.number(g),
41926
- ce.number(b),
41927
- ce.number(alpha)
41928
- );
41929
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42392
+ const oklch1 = toOklch(ce, ops[0]);
42393
+ const oklch2 = toOklch(ce, ops[1]);
42394
+ if (!oklch1 || !oklch2) return ce.error("incompatible-type");
42395
+ return oklchToExpr(ce, lerpOklchColor(oklch1, oklch2, ratio));
41930
42396
  }
41931
42397
  },
41932
42398
  Colormap: {
41933
42399
  description: "Sample colors from a named palette",
41934
42400
  complexity: 8e3,
41935
- signature: "(string, number?) -> any",
42401
+ signature: "(string, number?) -> color | list<color>",
41936
42402
  evaluate: (ops, { engine: ce }) => {
41937
42403
  const name = isString(ops[0]) ? ops[0].string : void 0;
41938
42404
  if (!name) return ce.error("incompatible-type");
41939
42405
  const palette = ALL_PALETTES[name];
41940
42406
  if (!palette) return ce.error("expected-value", name);
41941
42407
  if (ops.length < 2 || ops[1] === void 0) {
41942
- const tuples = palette.map(
41943
- (hex) => colorNumberToTuple(ce, parseColor(hex))
42408
+ const colors = palette.map(
42409
+ (hex) => colorNumberToOklch(ce, parseColor(hex))
41944
42410
  );
41945
- return ce.function("List", tuples);
42411
+ return ce.function("List", colors);
41946
42412
  }
41947
42413
  const val = ops[1].re;
41948
42414
  if (!Number.isFinite(val)) return ce.error("expected-value");
41949
42415
  if (Number.isInteger(val) && val >= 2) {
41950
42416
  const n = val;
41951
- const tuples = [];
42417
+ const colors = [];
41952
42418
  for (let i = 0; i < n; i++) {
41953
- tuples.push(samplePalette(ce, palette, i / (n - 1)));
42419
+ colors.push(samplePalette(ce, palette, i / (n - 1)));
41954
42420
  }
41955
- return ce.function("List", tuples);
42421
+ return ce.function("List", colors);
41956
42422
  }
41957
42423
  return samplePalette(ce, palette, val);
41958
42424
  }
@@ -41960,12 +42426,25 @@ var COLORS_LIBRARY = {
41960
42426
  ColorToColorspace: {
41961
42427
  description: "Convert a color to components in a target color space",
41962
42428
  complexity: 8e3,
41963
- signature: "(any, string) -> tuple",
42429
+ signature: "(color | string | tuple, string) -> tuple",
41964
42430
  evaluate: (ops, { engine: ce }) => {
41965
- const rgb = extractRgb(ce, ops[0]);
41966
- if (!rgb) return ce.error("incompatible-type");
41967
42431
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
41968
42432
  if (!space) return ce.error("incompatible-type");
42433
+ if (space === "oklch" || space === "oklab" || space === "lab") {
42434
+ const oklch2 = colorExprToOklch(ops[0]);
42435
+ if (oklch2) {
42436
+ if (space === "oklch")
42437
+ return componentsTuple(
42438
+ ce,
42439
+ [oklch2.L, oklch2.C, oklch2.H],
42440
+ oklch2.alpha
42441
+ );
42442
+ const lab = oklchToOklab(oklch2);
42443
+ return componentsTuple(ce, [lab.L, lab.a, lab.b], lab.alpha);
42444
+ }
42445
+ }
42446
+ const rgb = extractRgb(ce, ops[0]);
42447
+ if (!rgb) return ce.error("incompatible-type");
41969
42448
  const alpha = rgb.alpha;
41970
42449
  switch (space) {
41971
42450
  case "rgb":
@@ -41995,17 +42474,27 @@ var COLORS_LIBRARY = {
41995
42474
  ColorFromColorspace: {
41996
42475
  description: "Convert color space components to a canonical sRGB tuple",
41997
42476
  complexity: 8e3,
41998
- signature: "(tuple, string) -> tuple",
42477
+ signature: "(color | tuple, string) -> tuple",
41999
42478
  evaluate: (ops, { engine: ce }) => {
42000
- const tuple = ops[0];
42001
- if (!isFunction2(tuple) || tuple.operator !== "Tuple" || tuple.ops.length < 3)
42002
- return ce.error("incompatible-type");
42003
- const c0 = tuple.ops[0].re;
42004
- const c1 = tuple.ops[1].re;
42005
- const c2 = tuple.ops[2].re;
42006
- const alpha = tuple.ops.length >= 4 ? tuple.ops[3].re : void 0;
42007
42479
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42008
42480
  if (!space) return ce.error("incompatible-type");
42481
+ let c0, c1, c2;
42482
+ let alpha;
42483
+ const arg = ops[0];
42484
+ const typed = readColorExpr(arg);
42485
+ if (typed) {
42486
+ c0 = typed.c0;
42487
+ c1 = typed.c1;
42488
+ c2 = typed.c2;
42489
+ alpha = typed.alpha;
42490
+ } else if (isFunction2(arg) && arg.operator === "Tuple" && arg.ops.length >= 3) {
42491
+ c0 = arg.ops[0].re;
42492
+ c1 = arg.ops[1].re;
42493
+ c2 = arg.ops[2].re;
42494
+ alpha = arg.ops.length >= 4 ? arg.ops[3].re : void 0;
42495
+ } else {
42496
+ return ce.error("incompatible-type");
42497
+ }
42009
42498
  let rgb;
42010
42499
  switch (space) {
42011
42500
  case "rgb":
@@ -42041,7 +42530,7 @@ var COLORS_LIBRARY = {
42041
42530
  ColorContrast: {
42042
42531
  description: "APCA contrast ratio between two colors",
42043
42532
  complexity: 8e3,
42044
- signature: "(any, any) -> number",
42533
+ signature: "(color | string | tuple, color | string | tuple) -> number",
42045
42534
  evaluate: (ops, { engine: ce }) => {
42046
42535
  const bgRgb = extractRgb(ce, ops[0]);
42047
42536
  const fgRgb = extractRgb(ce, ops[1]);
@@ -42052,19 +42541,186 @@ var COLORS_LIBRARY = {
42052
42541
  ContrastingColor: {
42053
42542
  description: "Choose the foreground color with better APCA contrast against a background",
42054
42543
  complexity: 8e3,
42055
- signature: "(any, any?, any?) -> tuple",
42544
+ signature: "(color | string | tuple, (color | string | tuple)?, (color | string | tuple)?) -> color",
42056
42545
  evaluate: (ops, { engine: ce }) => {
42057
42546
  const bgRgb = extractRgb(ce, ops[0]);
42058
42547
  if (!bgRgb) return ce.error("incompatible-type");
42548
+ let packed;
42059
42549
  if (ops.length >= 3 && ops[1] !== void 0 && ops[2] !== void 0) {
42060
42550
  const fg1 = extractRgb(ce, ops[1]);
42061
42551
  const fg2 = extractRgb(ce, ops[2]);
42062
42552
  if (!fg1 || !fg2) return ce.error("incompatible-type");
42063
- const result2 = contrastingColor({ bg: bgRgb, fg1, fg2 });
42064
- return colorNumberToTuple(ce, result2);
42553
+ packed = contrastingColor({ bg: bgRgb, fg1, fg2 });
42554
+ } else {
42555
+ packed = contrastingColor(bgRgb);
42556
+ }
42557
+ const r = (packed >>> 24 & 255) / 255;
42558
+ const g = (packed >>> 16 & 255) / 255;
42559
+ const b = (packed >>> 8 & 255) / 255;
42560
+ const alpha = normalizeAlpha((packed & 255) / 255);
42561
+ const args = [ce.number(r), ce.number(g), ce.number(b)];
42562
+ if (alpha !== void 0) args.push(ce.number(alpha));
42563
+ return ce.function("Rgb", args);
42564
+ }
42565
+ },
42566
+ // ---------------------------------------------------------------------------
42567
+ // Color constructors. Each preserves its colorspace on evaluation; the
42568
+ // operator name is the discriminator. Components are interpreted per
42569
+ // colorspace conventions (Rgb channels 0-1, Hsv/Hsl hue in degrees with
42570
+ // sat/value 0-1, Oklab/Oklch L 0-1 with standard a/b/C/H ranges). The
42571
+ // optional 4th argument is alpha in [0, 1]. No clamping at evaluation time.
42572
+ // ---------------------------------------------------------------------------
42573
+ Rgb: {
42574
+ description: "sRGB color (channels 0-1, optional alpha 0-1)",
42575
+ complexity: 8e3,
42576
+ signature: "(number, number, number, number?) -> color"
42577
+ },
42578
+ Hsv: {
42579
+ description: "HSV color (hue degrees, saturation/value 0-1, optional alpha)",
42580
+ complexity: 8e3,
42581
+ signature: "(number, number, number, number?) -> color"
42582
+ },
42583
+ Hsl: {
42584
+ description: "HSL color (hue degrees, saturation/lightness 0-1, optional alpha)",
42585
+ complexity: 8e3,
42586
+ signature: "(number, number, number, number?) -> color"
42587
+ },
42588
+ Oklab: {
42589
+ description: "OKLab color (L 0-1, a/b ~ -0.4..0.4, optional alpha)",
42590
+ complexity: 8e3,
42591
+ signature: "(number, number, number, number?) -> color"
42592
+ },
42593
+ Oklch: {
42594
+ description: "OKLCh color (L 0-1, C 0-~0.4, hue degrees, optional alpha)",
42595
+ complexity: 8e3,
42596
+ signature: "(number, number, number, number?) -> color"
42597
+ },
42598
+ // ---------------------------------------------------------------------------
42599
+ // Color-space conversions. Each accepts any of the five color heads and
42600
+ // returns the same color in the named space. If the input is already in
42601
+ // the target space, returns the input unchanged.
42602
+ // ---------------------------------------------------------------------------
42603
+ AsRgb: {
42604
+ description: "Convert any color to sRGB (channels 0-1)",
42605
+ complexity: 8e3,
42606
+ signature: "(color) -> color",
42607
+ evaluate: (ops, { engine: ce }) => {
42608
+ const arg = ops[0];
42609
+ if (isFunction2(arg) && arg.operator === "Rgb")
42610
+ return normalizeColorHead(ce, arg);
42611
+ const rgb = colorExprToRgb(arg);
42612
+ if (!rgb) return ce.error("incompatible-type");
42613
+ const args = [
42614
+ ce.number(rgb.r / 255),
42615
+ ce.number(rgb.g / 255),
42616
+ ce.number(rgb.b / 255)
42617
+ ];
42618
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42619
+ return ce.function("Rgb", args);
42620
+ }
42621
+ },
42622
+ AsHsv: {
42623
+ description: "Convert any color to HSV (hue degrees, s/v 0-1)",
42624
+ complexity: 8e3,
42625
+ signature: "(color) -> color",
42626
+ evaluate: (ops, { engine: ce }) => {
42627
+ const arg = ops[0];
42628
+ if (isFunction2(arg) && arg.operator === "Hsv")
42629
+ return normalizeColorHead(ce, arg);
42630
+ const rgb = colorExprToRgb(arg);
42631
+ if (!rgb) return ce.error("incompatible-type");
42632
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
42633
+ const args = [ce.number(hsv.h), ce.number(hsv.s), ce.number(hsv.v)];
42634
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42635
+ return ce.function("Hsv", args);
42636
+ }
42637
+ },
42638
+ AsHsl: {
42639
+ description: "Convert any color to HSL (hue degrees, s/l 0-1)",
42640
+ complexity: 8e3,
42641
+ signature: "(color) -> color",
42642
+ evaluate: (ops, { engine: ce }) => {
42643
+ const arg = ops[0];
42644
+ if (isFunction2(arg) && arg.operator === "Hsl")
42645
+ return normalizeColorHead(ce, arg);
42646
+ const rgb = colorExprToRgb(arg);
42647
+ if (!rgb) return ce.error("incompatible-type");
42648
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
42649
+ const args = [ce.number(hsl.h), ce.number(hsl.s), ce.number(hsl.l)];
42650
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42651
+ return ce.function("Hsl", args);
42652
+ }
42653
+ },
42654
+ AsOklab: {
42655
+ description: "Convert any color to OKLab",
42656
+ complexity: 8e3,
42657
+ signature: "(color) -> color",
42658
+ evaluate: (ops, { engine: ce }) => {
42659
+ const arg = ops[0];
42660
+ if (isFunction2(arg) && arg.operator === "Oklab")
42661
+ return normalizeColorHead(ce, arg);
42662
+ if (isFunction2(arg) && arg.operator === "Oklch") {
42663
+ const c = readColorExpr(arg);
42664
+ if (!c) return ce.error("incompatible-type");
42665
+ const lab2 = oklchToOklab({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42666
+ const args2 = [ce.number(lab2.L), ce.number(lab2.a), ce.number(lab2.b)];
42667
+ if (lab2.alpha !== void 0) args2.push(ce.number(lab2.alpha));
42668
+ return ce.function("Oklab", args2);
42669
+ }
42670
+ const rgb = colorExprToRgb(arg);
42671
+ if (!rgb) return ce.error("incompatible-type");
42672
+ const lab = rgbToOklab(rgb);
42673
+ const args = [ce.number(lab.L), ce.number(lab.a), ce.number(lab.b)];
42674
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42675
+ return ce.function("Oklab", args);
42676
+ }
42677
+ },
42678
+ AsOklch: {
42679
+ description: "Convert any color to OKLCh",
42680
+ complexity: 8e3,
42681
+ signature: "(color) -> color",
42682
+ evaluate: (ops, { engine: ce }) => {
42683
+ const arg = ops[0];
42684
+ if (isFunction2(arg) && arg.operator === "Oklch")
42685
+ return normalizeColorHead(ce, arg);
42686
+ if (isFunction2(arg) && arg.operator === "Oklab") {
42687
+ const c2 = readColorExpr(arg);
42688
+ if (!c2) return ce.error("incompatible-type");
42689
+ const oklch2 = oklabToOklch({
42690
+ L: c2.c0,
42691
+ a: c2.c1,
42692
+ b: c2.c2,
42693
+ alpha: c2.alpha
42694
+ });
42695
+ const args2 = [
42696
+ ce.number(oklch2.L),
42697
+ ce.number(oklch2.C),
42698
+ ce.number(oklch2.H)
42699
+ ];
42700
+ if (oklch2.alpha !== void 0) args2.push(ce.number(oklch2.alpha));
42701
+ return ce.function("Oklch", args2);
42065
42702
  }
42066
- const result = contrastingColor(bgRgb);
42067
- return colorNumberToTuple(ce, result);
42703
+ const rgb = colorExprToRgb(arg);
42704
+ if (!rgb) return ce.error("incompatible-type");
42705
+ const c = rgbToOklch(rgb);
42706
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42707
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42708
+ return ce.function("Oklch", args);
42709
+ }
42710
+ },
42711
+ // ---------------------------------------------------------------------------
42712
+ // Perceptual difference. Returns ΔE_OK (Euclidean distance in OKLab),
42713
+ // an approximately perceptually uniform scalar.
42714
+ // ---------------------------------------------------------------------------
42715
+ ColorDelta: {
42716
+ description: "Perceptual color difference (\u0394E_OK) between two colors",
42717
+ complexity: 8e3,
42718
+ signature: "(color | string | tuple, color | string | tuple) -> number",
42719
+ evaluate: (ops, { engine: ce }) => {
42720
+ const a = toOklch(ce, ops[0]);
42721
+ const b = toOklch(ce, ops[1]);
42722
+ if (!a || !b) return ce.error("incompatible-type");
42723
+ return ce.number(oklabDeltaE(oklchToOklab(a), oklchToOklab(b)));
42068
42724
  }
42069
42725
  }
42070
42726
  };
@@ -42084,12 +42740,14 @@ var CONTROL_STRUCTURES_LIBRARY = [
42084
42740
  canonical: canonicalBlock,
42085
42741
  evaluate: evaluateBlock
42086
42742
  },
42087
- // A condition expression tests for one or more conditions of an expression
42088
- // ['Condition', value, "positive"]
42743
+ // A condition expression tests for one or more conditions of an expression.
42744
+ // Two forms:
42745
+ // ['Condition', value, "positive"] — tests value against named condition(s)
42746
+ // ['Condition', predicate] — set-builder predicate (e.g. x > 0)
42089
42747
  Condition: {
42090
42748
  description: "Test whether a value satisfies one or more conditions.",
42091
42749
  lazy: true,
42092
- signature: "(value, symbol) -> boolean",
42750
+ signature: "(expression, symbol?) -> boolean",
42093
42751
  evaluate: ([value, conds], { engine }) => {
42094
42752
  let conditions = [];
42095
42753
  if (isSymbol2(conds)) {
@@ -44542,6 +45200,34 @@ var CORE_LIBRARY = [
44542
45200
  signature: "() -> expression",
44543
45201
  evaluate: (_ops, { engine }) => engine.expr(randomExpression())
44544
45202
  }
45203
+ },
45204
+ // ---------------------------------------------------------------------------
45205
+ // Opaque typed heads — registered so the names are in the standard set
45206
+ // (consumers can branch on the operator name); CE itself does not evaluate
45207
+ // them. Geometric primitives `Triangle`/`Sphere`/`Segment` and the action
45208
+ // arrow `To` (`a \to b`).
45209
+ // ---------------------------------------------------------------------------
45210
+ {
45211
+ Triangle: {
45212
+ description: "Triangle primitive \u2014 opaque typed head.",
45213
+ signature: "(any+) -> expression"
45214
+ },
45215
+ GeometricVector: {
45216
+ description: "Geometric vector (directed segment between two points) \u2014 opaque typed head. Distinct from the column-vector `Vector` operator.",
45217
+ signature: "(any, any) -> expression"
45218
+ },
45219
+ Sphere: {
45220
+ description: "Sphere primitive \u2014 opaque typed head.",
45221
+ signature: "(any+) -> expression"
45222
+ },
45223
+ Segment: {
45224
+ description: "Segment primitive \u2014 opaque typed head.",
45225
+ signature: "(any+) -> expression"
45226
+ },
45227
+ To: {
45228
+ description: "Action arrow / mapping (`a \\to b`) \u2014 opaque typed head.",
45229
+ signature: "(any, any) -> nothing"
45230
+ }
44545
45231
  }
44546
45232
  ];
44547
45233
 
@@ -51678,6 +52364,7 @@ function orderConvexHull(points) {
51678
52364
 
51679
52365
  // src/compute-engine/boxed-expression/cache.ts
51680
52366
  function cachedValue(v, generation, fn) {
52367
+ if (v.generation === generation && v.value !== null) return v.value;
51681
52368
  v.generation = generation;
51682
52369
  v.value = fn();
51683
52370
  return v.value;
@@ -53455,6 +54142,12 @@ function _setProduct(fn) {
53455
54142
  function _escapeJsonString(s) {
53456
54143
  return s;
53457
54144
  }
54145
+ function _serializeLatexMetadata(ce, expr2) {
54146
+ const syntax = ce.latexSyntax;
54147
+ const opts = ce.latexOptions;
54148
+ if (Object.keys(opts).length === 0) return syntax.serialize(expr2);
54149
+ return syntax.serialize(expr2, { ...opts });
54150
+ }
53458
54151
  function serializeSubtract(ce, a, b, options, metadata) {
53459
54152
  if (isNumber(a) && a.isNegative) {
53460
54153
  const v = a.numericValue;
@@ -53759,7 +54452,7 @@ function serializeJsonFunction(ce, name, args, options, metadata) {
53759
54452
  ];
53760
54453
  const md = { ...metadata ?? {} };
53761
54454
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53762
- md.latex = _escapeJsonString(md.latex ?? ce.latexSyntax.serialize(fn));
54455
+ md.latex = _escapeJsonString(md.latex ?? _serializeLatexMetadata(ce, fn));
53763
54456
  } else md.latex = "";
53764
54457
  if (!options.metadata.includes("wikidata")) md.wikidata = "";
53765
54458
  if (!md.latex && !md.wikidata && options.shorthands.includes("function"))
@@ -53784,7 +54477,7 @@ function serializeJsonSymbol(ce, sym2, options, metadata) {
53784
54477
  }
53785
54478
  metadata = { ...metadata };
53786
54479
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53787
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(sym2);
54480
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, sym2);
53788
54481
  if (metadata.latex !== void 0)
53789
54482
  metadata.latex = _escapeJsonString(metadata.latex);
53790
54483
  } else metadata.latex = void 0;
@@ -53946,7 +54639,7 @@ function serializeJsonNumber(ce, value, options, metadata) {
53946
54639
  }
53947
54640
  }
53948
54641
  if (options.metadata.includes("latex") && ce.latexSyntax)
53949
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(result2 ?? { num });
54642
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, result2 ?? { num });
53950
54643
  if (result2) {
53951
54644
  if (metadata.latex !== void 0)
53952
54645
  return { sym: result2, latex: metadata.latex };
@@ -53962,7 +54655,7 @@ function serializeJsonNumber(ce, value, options, metadata) {
53962
54655
  if (value.isNaN()) {
53963
54656
  num = "NaN";
53964
54657
  if (options.metadata.includes("latex") && ce.latexSyntax)
53965
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
54658
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
53966
54659
  return metadata.latex !== void 0 ? { num, latex: metadata.latex } : { num };
53967
54660
  }
53968
54661
  return serializeJsonFunction(
@@ -53996,7 +54689,7 @@ function serializeJsonNumber(ce, value, options, metadata) {
53996
54689
  value = Number(value);
53997
54690
  } else {
53998
54691
  if (options.metadata.includes("latex") && ce.latexSyntax)
53999
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({
54692
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, {
54000
54693
  num: value.toString()
54001
54694
  });
54002
54695
  if (metadata.latex !== void 0)
@@ -54010,7 +54703,7 @@ function serializeJsonNumber(ce, value, options, metadata) {
54010
54703
  result = value > 0 ? "PositiveInfinity" : "NegativeInfinity";
54011
54704
  else num = serializeRepeatingDecimals(value.toString(), options);
54012
54705
  if (options.metadata.includes("latex") && ce.latexSyntax)
54013
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
54706
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54014
54707
  if (result) {
54015
54708
  if (metadata.latex !== void 0)
54016
54709
  return { sym: result, latex: metadata.latex };
@@ -54763,8 +55456,7 @@ function compile(expr2, options) {
54763
55456
  vars: options?.vars,
54764
55457
  imports: options?.imports,
54765
55458
  preamble: options?.preamble,
54766
- realOnly: options?.realOnly,
54767
- hints: options?.hints
55459
+ realOnly: options?.realOnly
54768
55460
  });
54769
55461
  } catch (e) {
54770
55462
  if (options?.fallback ?? true) {
@@ -54776,8 +55468,7 @@ function compile(expr2, options) {
54776
55468
  ce.pushScope();
54777
55469
  try {
54778
55470
  if (vars && typeof vars === "object") {
54779
- for (const [k, v] of Object.entries(vars))
54780
- ce.assign(k, v);
55471
+ for (const [k, v] of Object.entries(vars)) ce.assign(k, v);
54781
55472
  }
54782
55473
  return expr2.evaluate().re;
54783
55474
  } finally {
@@ -60007,8 +60698,7 @@ function tryGetComplexParts(expr2, compile3) {
60007
60698
  return { re: null, im: formatFloat(iScale) };
60008
60699
  }
60009
60700
  const compiledFactors = remaining.map((r) => compile3(r));
60010
- if (iScale !== 1)
60011
- compiledFactors.unshift(formatFloat(iScale));
60701
+ if (iScale !== 1) compiledFactors.unshift(formatFloat(iScale));
60012
60702
  const imCode = foldTerms(compiledFactors, "1.0", "*");
60013
60703
  return { re: null, im: imCode };
60014
60704
  }
@@ -60612,39 +61302,130 @@ var JAVASCRIPT_FUNCTIONS = {
60612
61302
  if (args.length >= 2)
60613
61303
  return `_SYS.colormap(${compile3(args[0])}, ${compile3(args[1])})`;
60614
61304
  return `_SYS.colormap(${compile3(args[0])})`;
61305
+ },
61306
+ // -----------------------------------------------------------------------
61307
+ // Color constructor heads. All compile to OKLCh arrays at runtime — the
61308
+ // canonical color representation in this target. The constructors take
61309
+ // their own colorspace's components and convert internally.
61310
+ // (Mirrors the GPU target's design: color values are vec3 OKLCh.)
61311
+ // -----------------------------------------------------------------------
61312
+ Rgb: (args, compile3) => {
61313
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
61314
+ return `_SYS.rgb(${args.map(compile3).join(", ")})`;
61315
+ },
61316
+ Hsv: (args, compile3) => {
61317
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
61318
+ return `_SYS.hsv(${args.map(compile3).join(", ")})`;
61319
+ },
61320
+ Hsl: (args, compile3) => {
61321
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
61322
+ return `_SYS.hsl(${args.map(compile3).join(", ")})`;
61323
+ },
61324
+ Oklab: (args, compile3) => {
61325
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
61326
+ return `_SYS.oklab(${args.map(compile3).join(", ")})`;
61327
+ },
61328
+ Oklch: (args, compile3) => {
61329
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
61330
+ return `_SYS.oklch(${args.map(compile3).join(", ")})`;
61331
+ },
61332
+ // -----------------------------------------------------------------------
61333
+ // As* converters. Compile-time output convention matches the engine and
61334
+ // the GPU target: each returns components in the named space as a 3- or
61335
+ // 4-element array. `AsRgb` uses 0-1 sRGB channels (consistent across all
61336
+ // layers). `AsOklch` is the identity (canonical form).
61337
+ // -----------------------------------------------------------------------
61338
+ AsRgb: ([c], compile3) => {
61339
+ if (c === null) throw new Error("AsRgb: no argument");
61340
+ return `_SYS.asRgb(${compile3(c)})`;
61341
+ },
61342
+ AsHsv: ([c], compile3) => {
61343
+ if (c === null) throw new Error("AsHsv: no argument");
61344
+ return `_SYS.asHsv(${compile3(c)})`;
61345
+ },
61346
+ AsHsl: ([c], compile3) => {
61347
+ if (c === null) throw new Error("AsHsl: no argument");
61348
+ return `_SYS.asHsl(${compile3(c)})`;
61349
+ },
61350
+ AsOklab: ([c], compile3) => {
61351
+ if (c === null) throw new Error("AsOklab: no argument");
61352
+ return `_SYS.asOklab(${compile3(c)})`;
61353
+ },
61354
+ AsOklch: ([c], compile3) => {
61355
+ if (c === null) throw new Error("AsOklch: no argument");
61356
+ return compile3(c);
61357
+ },
61358
+ // Perceptual color difference (ΔE_OK).
61359
+ ColorDelta: ([a, b], compile3) => {
61360
+ if (a === null || b === null)
61361
+ throw new Error("ColorDelta: need two colors");
61362
+ return `_SYS.colorDelta(${compile3(a)}, ${compile3(b)})`;
61363
+ },
61364
+ // Euclidean distance between two tuples (any positive dimension).
61365
+ // The GPU target maps `Distance` to the GLSL/WGSL `distance()` builtin
61366
+ // (vec-only); this JS handler works on plain arrays of any length.
61367
+ Distance: ([a, b], compile3) => {
61368
+ if (a === null || b === null) throw new Error("Distance: need two points");
61369
+ return `_SYS.distance(${compile3(a)}, ${compile3(b)})`;
60615
61370
  }
60616
61371
  };
60617
61372
  function toRI(c) {
60618
61373
  return { re: c.re, im: c.im };
60619
61374
  }
61375
+ function normalizeAlpha2(a) {
61376
+ if (a === void 0) return void 0;
61377
+ if (!Number.isFinite(a)) return void 0;
61378
+ if (Math.abs(a - 1) < 1e-9) return void 0;
61379
+ return a;
61380
+ }
60620
61381
  function toRgb255(input) {
60621
61382
  if (typeof input === "string") {
60622
61383
  const c = parseColor(input);
60623
- return {
61384
+ const rgb2 = {
60624
61385
  r: c >>> 24 & 255,
60625
61386
  g: c >>> 16 & 255,
60626
- b: c >>> 8 & 255,
60627
- alpha: (c & 255) / 255
61387
+ b: c >>> 8 & 255
60628
61388
  };
61389
+ const alpha = normalizeAlpha2((c & 255) / 255);
61390
+ if (alpha !== void 0) rgb2.alpha = alpha;
61391
+ return rgb2;
61392
+ }
61393
+ const rgb = oklchToRgb({ L: input[0], C: input[1], H: input[2] });
61394
+ if (input.length >= 4) {
61395
+ const alpha = normalizeAlpha2(input[3]);
61396
+ if (alpha !== void 0) rgb.alpha = alpha;
60629
61397
  }
60630
- const rgb = {
60631
- r: input[0] * 255,
60632
- g: input[1] * 255,
60633
- b: input[2] * 255
60634
- };
60635
- if (input.length >= 4) rgb.alpha = input[3];
60636
61398
  return rgb;
60637
61399
  }
60638
- function packedToArray(c) {
60639
- const r = (c >>> 24 & 255) / 255;
60640
- const g = (c >>> 16 & 255) / 255;
60641
- const b = (c >>> 8 & 255) / 255;
60642
- const a = (c & 255) / 255;
60643
- return Math.abs(a - 1) < 1e-4 ? [r, g, b] : [r, g, b, a];
61400
+ function toOklch2(input) {
61401
+ if (typeof input === "string") {
61402
+ const c = parseColor(input);
61403
+ const r = c >>> 24 & 255;
61404
+ const g = c >>> 16 & 255;
61405
+ const b = c >>> 8 & 255;
61406
+ const oklch2 = rgbToOklch({ r, g, b });
61407
+ const alpha = normalizeAlpha2((c & 255) / 255);
61408
+ if (alpha !== void 0) oklch2.alpha = alpha;
61409
+ return oklch2;
61410
+ }
61411
+ return {
61412
+ L: input[0],
61413
+ C: input[1],
61414
+ H: input[2],
61415
+ alpha: input.length >= 4 ? normalizeAlpha2(input[3]) : void 0
61416
+ };
61417
+ }
61418
+ function packedToOklch(c) {
61419
+ const r = c >>> 24 & 255;
61420
+ const g = c >>> 16 & 255;
61421
+ const b = c >>> 8 & 255;
61422
+ const oklch2 = rgbToOklch({ r, g, b });
61423
+ const alpha = normalizeAlpha2((c & 255) / 255);
61424
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
60644
61425
  }
60645
61426
  var colorHelpers = {
60646
61427
  color(input) {
60647
- return packedToArray(parseColor(input));
61428
+ return packedToOklch(parseColor(input));
60648
61429
  },
60649
61430
  colorToString(input, format) {
60650
61431
  const rgb = toRgb255(input);
@@ -60655,7 +61436,7 @@ var colorHelpers = {
60655
61436
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
60656
61437
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
60657
61438
  let hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
60658
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
61439
+ if (rgb.alpha !== void 0) {
60659
61440
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
60660
61441
  hex += a.toString(16).padStart(2, "0");
60661
61442
  }
@@ -60665,7 +61446,7 @@ var colorHelpers = {
60665
61446
  const r = Math.round(rgb.r);
60666
61447
  const g = Math.round(rgb.g);
60667
61448
  const b = Math.round(rgb.b);
60668
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
61449
+ if (rgb.alpha !== void 0)
60669
61450
  return `rgb(${r} ${g} ${b} / ${rgb.alpha})`;
60670
61451
  return `rgb(${r} ${g} ${b})`;
60671
61452
  }
@@ -60674,7 +61455,7 @@ var colorHelpers = {
60674
61455
  const h = Math.round(hsl.h * 10) / 10;
60675
61456
  const s = Math.round(hsl.s * 1e3) / 10;
60676
61457
  const l = Math.round(hsl.l * 1e3) / 10;
60677
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
61458
+ if (rgb.alpha !== void 0)
60678
61459
  return `hsl(${h} ${s}% ${l}% / ${rgb.alpha})`;
60679
61460
  return `hsl(${h} ${s}% ${l}%)`;
60680
61461
  }
@@ -60683,7 +61464,7 @@ var colorHelpers = {
60683
61464
  const L = Math.round(c.L * 1e3) / 1e3;
60684
61465
  const C = Math.round(c.C * 1e3) / 1e3;
60685
61466
  const H = Math.round(c.H * 10) / 10;
60686
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
61467
+ if (rgb.alpha !== void 0)
60687
61468
  return `oklch(${L} ${C} ${H} / ${rgb.alpha})`;
60688
61469
  return `oklch(${L} ${C} ${H})`;
60689
61470
  }
@@ -60692,29 +61473,29 @@ var colorHelpers = {
60692
61473
  }
60693
61474
  },
60694
61475
  colorMix(input1, input2, ratio = 0.5) {
60695
- const rgb1 = toRgb255(input1);
60696
- const rgb2 = toRgb255(input2);
61476
+ const c1 = toOklch2(input1);
61477
+ const c2 = toOklch2(input2);
60697
61478
  ratio = Math.max(0, Math.min(1, ratio));
60698
- const c1 = rgbToOklch(rgb1);
60699
- const c2 = rgbToOklch(rgb2);
60700
- let dh = c2.H - c1.H;
60701
- if (dh > 180) dh -= 360;
60702
- if (dh < -180) dh += 360;
60703
- let H = c1.H + dh * ratio;
60704
- if (H < 0) H += 360;
60705
- if (H >= 360) H -= 360;
60706
- const mixed = oklchToRgb({
60707
- L: c1.L + (c2.L - c1.L) * ratio,
60708
- C: c1.C + (c2.C - c1.C) * ratio,
60709
- H
60710
- });
60711
- const r = mixed.r / 255;
60712
- const g = mixed.g / 255;
60713
- const b = mixed.b / 255;
60714
- const a1 = rgb1.alpha ?? 1;
60715
- const a2 = rgb2.alpha ?? 1;
60716
- const alpha = a1 + (a2 - a1) * ratio;
60717
- return Math.abs(alpha - 1) > 1e-4 ? [r, g, b, alpha] : [r, g, b];
61479
+ const c1Achromatic = c1.C < 1e-6;
61480
+ const c2Achromatic = c2.C < 1e-6;
61481
+ let H;
61482
+ if (c1Achromatic && c2Achromatic) H = c1.H;
61483
+ else if (c1Achromatic) H = c2.H;
61484
+ else if (c2Achromatic) H = c1.H;
61485
+ else {
61486
+ let dh = c2.H - c1.H;
61487
+ if (dh > 180) dh -= 360;
61488
+ if (dh < -180) dh += 360;
61489
+ H = c1.H + dh * ratio;
61490
+ if (H < 0) H += 360;
61491
+ if (H >= 360) H -= 360;
61492
+ }
61493
+ const L = c1.L + (c2.L - c1.L) * ratio;
61494
+ const C = c1.C + (c2.C - c1.C) * ratio;
61495
+ const a1 = c1.alpha ?? 1;
61496
+ const a2 = c2.alpha ?? 1;
61497
+ const alpha = normalizeAlpha2(a1 + (a2 - a1) * ratio);
61498
+ return alpha !== void 0 ? [L, C, H, alpha] : [L, C, H];
60718
61499
  },
60719
61500
  colorContrast(bg, fg) {
60720
61501
  return apca(toRgb255(bg), toRgb255(fg));
@@ -60722,11 +61503,11 @@ var colorHelpers = {
60722
61503
  contrastingColor(bg, fg1, fg2) {
60723
61504
  const bgRgb = toRgb255(bg);
60724
61505
  if (fg1 !== void 0 && fg2 !== void 0) {
60725
- return packedToArray(
61506
+ return packedToOklch(
60726
61507
  contrastingColor({ bg: bgRgb, fg1: toRgb255(fg1), fg2: toRgb255(fg2) })
60727
61508
  );
60728
61509
  }
60729
- return packedToArray(contrastingColor(bgRgb));
61510
+ return packedToOklch(contrastingColor(bgRgb));
60730
61511
  },
60731
61512
  colorToColorspace(input, space) {
60732
61513
  const rgb = toRgb255(input);
@@ -60755,7 +61536,7 @@ var colorHelpers = {
60755
61536
  default:
60756
61537
  throw new Error(`Unknown color space: ${space}`);
60757
61538
  }
60758
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
61539
+ if (alpha !== void 0) result.push(alpha);
60759
61540
  return result;
60760
61541
  },
60761
61542
  colormap(name, arg) {
@@ -60767,7 +61548,7 @@ var colorHelpers = {
60767
61548
  const palette = allPalettes[name];
60768
61549
  if (!palette) throw new Error(`Unknown palette: ${name}`);
60769
61550
  const colors = palette.map(
60770
- (hex) => parseColorToRgb01(hex)
61551
+ (hex) => packedToOklch(parseColor(hex))
60771
61552
  );
60772
61553
  if (arg === void 0) return colors;
60773
61554
  if (Number.isInteger(arg) && arg >= 2) {
@@ -60791,62 +61572,128 @@ var colorHelpers = {
60791
61572
  const frac = pos - i;
60792
61573
  if (frac === 0 || i >= colors.length - 1)
60793
61574
  return [...colors[Math.min(i, colors.length - 1)]];
60794
- const rgb1 = {
60795
- r: colors[i][0] * 255,
60796
- g: colors[i][1] * 255,
60797
- b: colors[i][2] * 255
60798
- };
60799
- const rgb2 = {
60800
- r: colors[i + 1][0] * 255,
60801
- g: colors[i + 1][1] * 255,
60802
- b: colors[i + 1][2] * 255
60803
- };
60804
- const c1 = rgbToOklch(rgb1);
60805
- const c2 = rgbToOklch(rgb2);
60806
- let dh = c2.H - c1.H;
60807
- if (dh > 180) dh -= 360;
60808
- if (dh < -180) dh += 360;
60809
- let H = c1.H + dh * frac;
60810
- if (H < 0) H += 360;
60811
- if (H >= 360) H -= 360;
60812
- const mixed = oklchToRgb({
60813
- L: c1.L + (c2.L - c1.L) * frac,
60814
- C: c1.C + (c2.C - c1.C) * frac,
60815
- H
60816
- });
60817
- return [mixed.r / 255, mixed.g / 255, mixed.b / 255];
61575
+ const [L1, C1, H1] = colors[i];
61576
+ const [L2, C2, H2] = colors[i + 1];
61577
+ const c1Achromatic = C1 < 1e-6;
61578
+ const c2Achromatic = C2 < 1e-6;
61579
+ let H;
61580
+ if (c1Achromatic && c2Achromatic) H = H1;
61581
+ else if (c1Achromatic) H = H2;
61582
+ else if (c2Achromatic) H = H1;
61583
+ else {
61584
+ let dh = H2 - H1;
61585
+ if (dh > 180) dh -= 360;
61586
+ if (dh < -180) dh += 360;
61587
+ H = H1 + dh * frac;
61588
+ if (H < 0) H += 360;
61589
+ if (H >= 360) H -= 360;
61590
+ }
61591
+ return [L1 + (L2 - L1) * frac, C1 + (C2 - C1) * frac, H];
60818
61592
  },
60819
61593
  colorFromColorspace(components, space) {
60820
61594
  const c0 = components[0];
60821
61595
  const c1 = components[1];
60822
61596
  const c2 = components[2];
60823
61597
  const alpha = components.length >= 4 ? components[3] : void 0;
60824
- let result;
61598
+ let oklch2;
60825
61599
  switch (space.toLowerCase()) {
60826
61600
  case "rgb":
60827
- result = [c0, c1, c2];
61601
+ oklch2 = rgbToOklch({ r: c0 * 255, g: c1 * 255, b: c2 * 255 });
60828
61602
  break;
60829
61603
  case "hsl": {
60830
- const r = hslToRgb(c0, c1, c2);
60831
- result = [r.r / 255, r.g / 255, r.b / 255];
61604
+ const rgb = hslToRgb(c0, c1, c2);
61605
+ oklch2 = rgbToOklch(rgb);
60832
61606
  break;
60833
61607
  }
60834
- case "oklch": {
60835
- const r = oklchToRgb({ L: c0, C: c1, H: c2 });
60836
- result = [r.r / 255, r.g / 255, r.b / 255];
61608
+ case "oklch":
61609
+ oklch2 = { L: c0, C: c1, H: c2 };
60837
61610
  break;
60838
- }
60839
61611
  case "oklab":
60840
- case "lab": {
60841
- const r = oklabToRgb({ L: c0, a: c1, b: c2 });
60842
- result = [r.r / 255, r.g / 255, r.b / 255];
61612
+ case "lab":
61613
+ oklch2 = oklabToOklch({ L: c0, a: c1, b: c2 });
60843
61614
  break;
60844
- }
60845
61615
  default:
60846
61616
  throw new Error(`Unknown color space: ${space}`);
60847
61617
  }
60848
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
60849
- return result;
61618
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
61619
+ },
61620
+ // -----------------------------------------------------------------------
61621
+ // Color constructors. Each accepts components in its colorspace's natural
61622
+ // units and returns the canonical OKLCh array `[L, C, H]` (or with alpha).
61623
+ // -----------------------------------------------------------------------
61624
+ rgb(r, g, b, alpha) {
61625
+ const c = rgbToOklch({ r: r * 255, g: g * 255, b: b * 255 });
61626
+ const a = normalizeAlpha2(alpha);
61627
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
61628
+ },
61629
+ hsv(h, s, v, alpha) {
61630
+ const rgb = hsvToRgb(h, s, v);
61631
+ const c = rgbToOklch(rgb);
61632
+ const a = normalizeAlpha2(alpha);
61633
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
61634
+ },
61635
+ hsl(h, s, l, alpha) {
61636
+ const rgb = hslToRgb(h, s, l);
61637
+ const c = rgbToOklch({ r: rgb.r, g: rgb.g, b: rgb.b });
61638
+ const a = normalizeAlpha2(alpha);
61639
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
61640
+ },
61641
+ oklab(L, a, b, alpha) {
61642
+ const c = oklabToOklch({ L, a, b });
61643
+ const al = normalizeAlpha2(alpha);
61644
+ return al !== void 0 ? [c.L, c.C, c.H, al] : [c.L, c.C, c.H];
61645
+ },
61646
+ oklch(L, C, H, alpha) {
61647
+ const a = normalizeAlpha2(alpha);
61648
+ return a !== void 0 ? [L, C, H, a] : [L, C, H];
61649
+ },
61650
+ // -----------------------------------------------------------------------
61651
+ // As* converters. Inputs are anything `toOklch` accepts (string, packed
61652
+ // int, or OKLCh array). Outputs are 3- or 4-element arrays in the named
61653
+ // space. sRGB-based outputs (asRgb/asHsv/asHsl) use 0-1 channels for
61654
+ // consistency with the GPU target's shader convention.
61655
+ // -----------------------------------------------------------------------
61656
+ asRgb(input) {
61657
+ const rgb = toRgb255(input);
61658
+ const r = rgb.r / 255;
61659
+ const g = rgb.g / 255;
61660
+ const b = rgb.b / 255;
61661
+ return rgb.alpha !== void 0 ? [r, g, b, rgb.alpha] : [r, g, b];
61662
+ },
61663
+ asHsv(input) {
61664
+ const rgb = toRgb255(input);
61665
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
61666
+ return rgb.alpha !== void 0 ? [hsv.h, hsv.s, hsv.v, rgb.alpha] : [hsv.h, hsv.s, hsv.v];
61667
+ },
61668
+ asHsl(input) {
61669
+ const rgb = toRgb255(input);
61670
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
61671
+ return rgb.alpha !== void 0 ? [hsl.h, hsl.s, hsl.l, rgb.alpha] : [hsl.h, hsl.s, hsl.l];
61672
+ },
61673
+ asOklab(input) {
61674
+ const c = toOklch2(input);
61675
+ const lab = oklchToOklab({ L: c.L, C: c.C, H: c.H });
61676
+ return c.alpha !== void 0 ? [lab.L, lab.a, lab.b, c.alpha] : [lab.L, lab.a, lab.b];
61677
+ },
61678
+ // asOklch is identity — handled at compile time as a pass-through
61679
+ // Perceptual color difference (ΔE_OK).
61680
+ colorDelta(a, b) {
61681
+ const labA = oklchToOklab(toOklch2(a));
61682
+ const labB = oklchToOklab(toOklch2(b));
61683
+ return oklabDeltaE(labA, labB);
61684
+ },
61685
+ // Euclidean distance between two tuples. Plain numeric — not a color
61686
+ // operation despite living in the same helpers block.
61687
+ distance(a, b) {
61688
+ if (!Array.isArray(a) || !Array.isArray(b))
61689
+ throw new Error("Distance: expected two arrays");
61690
+ if (a.length !== b.length) throw new Error("Distance: dimension mismatch");
61691
+ let sumSq = 0;
61692
+ for (let i = 0; i < a.length; i++) {
61693
+ const d = a[i] - b[i];
61694
+ sumSq += d * d;
61695
+ }
61696
+ return Math.sqrt(sumSq);
60850
61697
  }
60851
61698
  };
60852
61699
  var SYS_HELPERS = {
@@ -61264,43 +62111,6 @@ function fibonacci(n) {
61264
62111
  return b;
61265
62112
  }
61266
62113
 
61267
- // src/compute-engine/compilation/fractal-orbit.ts
61268
- function toBigDecimal(v) {
61269
- if (typeof v === "object" && "hi" in v)
61270
- return new BigDecimal(v.hi).add(new BigDecimal(v.lo));
61271
- return new BigDecimal(v);
61272
- }
61273
- function hpToNumber(v) {
61274
- if (typeof v === "number") return v;
61275
- if (typeof v === "string") return Number(v);
61276
- return v.hi + v.lo;
61277
- }
61278
- function computeReferenceOrbit(center, maxIter, precision) {
61279
- const prevPrecision = BigDecimal.precision;
61280
- BigDecimal.precision = precision;
61281
- try {
61282
- const cr = toBigDecimal(center[0]);
61283
- const ci = toBigDecimal(center[1]);
61284
- let zr = BigDecimal.ZERO;
61285
- let zi = BigDecimal.ZERO;
61286
- const ESCAPE = new BigDecimal(256);
61287
- const points = [];
61288
- for (let i = 0; i < maxIter; i++) {
61289
- points.push(zr.toNumber(), zi.toNumber());
61290
- const zr2 = zr.mul(zr).toPrecision(precision);
61291
- const zi2 = zi.mul(zi).toPrecision(precision);
61292
- const mag2 = zr2.add(zi2);
61293
- if (mag2.cmp(ESCAPE) > 0) break;
61294
- const new_zi = zr.mul(zi).toPrecision(precision).mul(2).add(ci);
61295
- zr = zr2.sub(zi2).add(cr);
61296
- zi = new_zi;
61297
- }
61298
- return new Float32Array(points);
61299
- } finally {
61300
- BigDecimal.precision = prevPrecision;
61301
- }
61302
- }
61303
-
61304
62114
  // src/compute-engine/compilation/gpu-target.ts
61305
62115
  var GPU_OPERATORS = {
61306
62116
  Add: ["+", 11],
@@ -61322,6 +62132,13 @@ var GPU_OPERATORS = {
61322
62132
  function gpuVec2(target) {
61323
62133
  return target?.language === "wgsl" ? "vec2f" : "vec2";
61324
62134
  }
62135
+ function gpuVec3(target) {
62136
+ return target?.language === "wgsl" ? "vec3f" : "vec3";
62137
+ }
62138
+ function readStringLiteral(expr2) {
62139
+ if (!isString(expr2)) return null;
62140
+ return expr2.string?.toLowerCase() ?? null;
62141
+ }
61325
62142
  function compileIntArg(expr2, compile3, target) {
61326
62143
  const c = tryGetConstant(expr2);
61327
62144
  if (c !== void 0 && Number.isInteger(c)) return c.toString();
@@ -61380,17 +62197,10 @@ function compileGPUSumProduct(kind, args, _compile2, target) {
61380
62197
  `for (${indexDecl} = ${lowerStr}; ${index} <= ${upperStr}; ${index}++) {`,
61381
62198
  ` ${acc} ${op}= ${body};`,
61382
62199
  `}`,
61383
- `return ${acc}`
62200
+ `return ${acc};`
61384
62201
  ];
61385
62202
  return lines.join("\n");
61386
62203
  }
61387
- function selectFractalStrategy(target) {
61388
- const radius = target.hints?.viewport?.radius;
61389
- if (radius === void 0) return "single";
61390
- if (radius > 1e-6) return "single";
61391
- if (radius > 1e-14) return "double";
61392
- return "perturbation";
61393
- }
61394
62204
  var GPU_FUNCTIONS = {
61395
62205
  // Variadic arithmetic (for function-call form, e.g., with vectors)
61396
62206
  Add: (args, compile3, target) => {
@@ -61441,8 +62251,7 @@ var GPU_FUNCTIONS = {
61441
62251
  const iScale = isSymbol2(iFactor, "ImaginaryUnit") ? 1 : iFactor.im;
61442
62252
  const realFactors = args.filter((_, i) => i !== iIndex);
61443
62253
  const v2 = gpuVec2(target);
61444
- if (realFactors.length === 0)
61445
- return `${v2}(0.0, ${formatFloat(iScale)})`;
62254
+ if (realFactors.length === 0) return `${v2}(0.0, ${formatFloat(iScale)})`;
61446
62255
  const factors = realFactors.map((f) => compile3(f));
61447
62256
  if (iScale !== 1) factors.unshift(formatFloat(iScale));
61448
62257
  const imCode = foldTerms(factors, "1.0", "*");
@@ -61495,8 +62304,7 @@ var GPU_FUNCTIONS = {
61495
62304
  if (isNumber(x) && x.im !== 0) {
61496
62305
  return `${gpuVec2(target)}(${formatFloat(-x.re)}, ${formatFloat(-x.im)})`;
61497
62306
  }
61498
- if (isSymbol2(x, "ImaginaryUnit"))
61499
- return `${gpuVec2(target)}(0.0, -1.0)`;
62307
+ if (isSymbol2(x, "ImaginaryUnit")) return `${gpuVec2(target)}(0.0, -1.0)`;
61500
62308
  return `(-${compile3(x)})`;
61501
62309
  },
61502
62310
  // Standard math functions with complex dispatch
@@ -61869,49 +62677,139 @@ var GPU_FUNCTIONS = {
61869
62677
  }
61870
62678
  const isWGSL = target?.language === "wgsl";
61871
62679
  const v3 = isWGSL ? "vec3f" : "vec3";
61872
- return `((_gpu_apca(${bg}, ${v3}(0.0)) > 50.0) ? ${v3}(0.0) : ${v3}(1.0))`;
62680
+ const black = `${v3}(0.0)`;
62681
+ const white = `${v3}(1.0, 0.0, 0.0)`;
62682
+ return `((_gpu_apca(${bg}, ${black}) > 50.0) ? ${black} : ${white})`;
61873
62683
  },
61874
62684
  ColorToColorspace: ([color, space], compile3) => {
61875
62685
  if (color === null || space === null)
61876
62686
  throw new Error("ColorToColorspace: need color and space");
61877
- return `_gpu_srgb_to_oklab(${compile3(color)})`;
62687
+ const spaceName = readStringLiteral(space);
62688
+ if (spaceName === null)
62689
+ throw new Error("ColorToColorspace: space must be a string literal");
62690
+ const c = compile3(color);
62691
+ switch (spaceName) {
62692
+ case "oklch":
62693
+ return c;
62694
+ case "oklab":
62695
+ case "lab":
62696
+ return `_gpu_oklch_to_oklab(${c})`;
62697
+ case "rgb":
62698
+ return `_gpu_oklch_to_srgb(${c})`;
62699
+ case "hsl":
62700
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${c}))`;
62701
+ case "hsv":
62702
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${c}))`;
62703
+ default:
62704
+ throw new Error(
62705
+ `ColorToColorspace: unsupported space "${spaceName}" on GPU target`
62706
+ );
62707
+ }
61878
62708
  },
61879
62709
  ColorFromColorspace: ([components, space], compile3) => {
61880
62710
  if (components === null || space === null)
61881
62711
  throw new Error("ColorFromColorspace: need components and space");
61882
- return `_gpu_oklab_to_srgb(${compile3(components)})`;
62712
+ const spaceName = readStringLiteral(space);
62713
+ if (spaceName === null)
62714
+ throw new Error("ColorFromColorspace: space must be a string literal");
62715
+ const c = compile3(components);
62716
+ switch (spaceName) {
62717
+ case "oklch":
62718
+ return c;
62719
+ case "oklab":
62720
+ case "lab":
62721
+ return `_gpu_oklab_to_oklch(${c})`;
62722
+ case "rgb":
62723
+ return `_gpu_srgb_to_oklch(${c})`;
62724
+ case "hsl":
62725
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${c}))`;
62726
+ case "hsv":
62727
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${c}))`;
62728
+ default:
62729
+ throw new Error(
62730
+ `ColorFromColorspace: unsupported space "${spaceName}" on GPU target`
62731
+ );
62732
+ }
62733
+ },
62734
+ // ---------------------------------------------------------------------------
62735
+ // Color literals. Each typed head compiles to a canonical OKLCh vec3.
62736
+ // Alpha (4th argument) is dropped — GPU color values are vec3 only. Pass
62737
+ // alpha as a separate uniform if it's needed at the framebuffer boundary.
62738
+ // ---------------------------------------------------------------------------
62739
+ Color: ([s], _compile2, target) => {
62740
+ if (s === null) throw new Error("Color: no argument");
62741
+ const str = readStringLiteral(s);
62742
+ if (str === null)
62743
+ throw new Error("Color: argument must be a string literal on GPU target");
62744
+ const packed = parseColor(str);
62745
+ if (packed === 0 && str.trim().toLowerCase() !== "transparent")
62746
+ throw new Error(`Color: invalid color string "${str}"`);
62747
+ const r = packed >>> 24 & 255;
62748
+ const g = packed >>> 16 & 255;
62749
+ const b = packed >>> 8 & 255;
62750
+ const oklch2 = rgbToOklch({ r, g, b });
62751
+ return `${gpuVec3(target)}(${formatFloat(oklch2.L)}, ${formatFloat(oklch2.C)}, ${formatFloat(oklch2.H)})`;
62752
+ },
62753
+ Rgb: (args, compile3, target) => {
62754
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
62755
+ const v3 = gpuVec3(target);
62756
+ return `_gpu_srgb_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
62757
+ },
62758
+ Hsv: (args, compile3, target) => {
62759
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
62760
+ const v3 = gpuVec3(target);
62761
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
62762
+ },
62763
+ Hsl: (args, compile3, target) => {
62764
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
62765
+ const v3 = gpuVec3(target);
62766
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
62767
+ },
62768
+ Oklab: (args, compile3, target) => {
62769
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
62770
+ const v3 = gpuVec3(target);
62771
+ return `_gpu_oklab_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
62772
+ },
62773
+ Oklch: (args, compile3, target) => {
62774
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
62775
+ const v3 = gpuVec3(target);
62776
+ return `${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})`;
62777
+ },
62778
+ // ---------------------------------------------------------------------------
62779
+ // As* operators. AsOklch is identity (canonical). The other As* return
62780
+ // components in the named space, equivalent to ColorToColorspace(c, 'x').
62781
+ // ---------------------------------------------------------------------------
62782
+ AsOklch: ([c], compile3) => {
62783
+ if (c === null) throw new Error("AsOklch: no argument");
62784
+ return compile3(c);
62785
+ },
62786
+ AsOklab: ([c], compile3) => {
62787
+ if (c === null) throw new Error("AsOklab: no argument");
62788
+ return `_gpu_oklch_to_oklab(${compile3(c)})`;
62789
+ },
62790
+ AsRgb: ([c], compile3) => {
62791
+ if (c === null) throw new Error("AsRgb: no argument");
62792
+ return `_gpu_oklch_to_srgb(${compile3(c)})`;
62793
+ },
62794
+ AsHsv: ([c], compile3) => {
62795
+ if (c === null) throw new Error("AsHsv: no argument");
62796
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${compile3(c)}))`;
62797
+ },
62798
+ AsHsl: ([c], compile3) => {
62799
+ if (c === null) throw new Error("AsHsl: no argument");
62800
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${compile3(c)}))`;
61883
62801
  },
61884
62802
  // Fractal functions
61885
62803
  Mandelbrot: ([c, maxIter], compile3, target) => {
61886
62804
  if (c === null || maxIter === null)
61887
62805
  throw new Error("Mandelbrot: missing arguments");
61888
62806
  const iterCode = compileIntArg(maxIter, compile3, target);
61889
- const strategy = selectFractalStrategy(target);
61890
- if (strategy === "double") {
61891
- const dpCoord = target?.language === "wgsl" ? "_dp_coord(v_uv)" : "_dp_coord()";
61892
- return `_fractal_mandelbrot_dp(${dpCoord}, ${iterCode})`;
61893
- }
61894
- if (strategy === "perturbation") {
61895
- const ptDelta = target?.language === "wgsl" ? "_pt_delta(v_uv)" : "_pt_delta()";
61896
- return `_fractal_mandelbrot_pt(${ptDelta}, ${iterCode})`;
61897
- }
61898
62807
  return `_fractal_mandelbrot(${compile3(c)}, ${iterCode})`;
61899
62808
  },
61900
62809
  Julia: ([z, c, maxIter], compile3, target) => {
61901
62810
  if (z === null || c === null || maxIter === null)
61902
62811
  throw new Error("Julia: missing arguments");
61903
62812
  const iterCode = compileIntArg(maxIter, compile3, target);
61904
- const strategy = selectFractalStrategy(target);
61905
- if (strategy === "double") {
61906
- const dpCoord = target?.language === "wgsl" ? "_dp_coord(v_uv)" : "_dp_coord()";
61907
- const cCode = compile3(c);
61908
- return `_fractal_julia_dp(${dpCoord}, vec4(${cCode}, vec2(0.0)), ${iterCode})`;
61909
- }
61910
- if (strategy === "perturbation") {
61911
- const ptDelta = target?.language === "wgsl" ? "_pt_delta(v_uv)" : "_pt_delta()";
61912
- const cCode = compile3(c);
61913
- return `_fractal_julia_pt(${ptDelta}, ${cCode}, ${iterCode})`;
61914
- }
61915
62813
  return `_fractal_julia(${compile3(z)}, ${compile3(c)}, ${iterCode})`;
61916
62814
  },
61917
62815
  // Vector/Matrix operations
@@ -62509,232 +63407,6 @@ fn _gpu_besselJ(n_in: i32, x_in: f32) -> f32 {
62509
63407
  return sgn * vals[n] / norm;
62510
63408
  }
62511
63409
  `;
62512
- var GPU_DS_ARITHMETIC_PREAMBLE_GLSL = `
62513
- // Split a float into high and low parts for exact multiplication
62514
- vec2 ds_split(float a) {
62515
- const float SPLIT = 4097.0; // 2^12 + 1
62516
- float t = SPLIT * a;
62517
- float hi = t - (t - a);
62518
- float lo = a - hi;
62519
- return vec2(hi, lo);
62520
- }
62521
-
62522
- // Create a double-single from a single float
62523
- vec2 ds_from(float a) {
62524
- return vec2(a, 0.0);
62525
- }
62526
-
62527
- // Error-free addition (Knuth TwoSum)
62528
- vec2 ds_add(vec2 a, vec2 b) {
62529
- float s = a.x + b.x;
62530
- float v = s - a.x;
62531
- float e = (a.x - (s - v)) + (b.x - v);
62532
- float lo = (a.y + b.y) + e;
62533
- float hi = s + lo;
62534
- lo = lo - (hi - s);
62535
- return vec2(hi, lo);
62536
- }
62537
-
62538
- // Double-single subtraction
62539
- vec2 ds_sub(vec2 a, vec2 b) {
62540
- return ds_add(a, vec2(-b.x, -b.y));
62541
- }
62542
-
62543
- // Error-free multiplication (Dekker TwoProduct)
62544
- vec2 ds_mul(vec2 a, vec2 b) {
62545
- float p = a.x * b.x;
62546
- vec2 sa = ds_split(a.x);
62547
- vec2 sb = ds_split(b.x);
62548
- float err = ((sa.x * sb.x - p) + sa.x * sb.y + sa.y * sb.x) + sa.y * sb.y;
62549
- err += a.x * b.y + a.y * b.x;
62550
- float hi = p + err;
62551
- float lo = err - (hi - p);
62552
- return vec2(hi, lo);
62553
- }
62554
-
62555
- // Optimized self-multiply
62556
- vec2 ds_sqr(vec2 a) {
62557
- float p = a.x * a.x;
62558
- vec2 sa = ds_split(a.x);
62559
- float err = ((sa.x * sa.x - p) + 2.0 * sa.x * sa.y) + sa.y * sa.y;
62560
- err += 2.0 * a.x * a.y;
62561
- float hi = p + err;
62562
- float lo = err - (hi - p);
62563
- return vec2(hi, lo);
62564
- }
62565
-
62566
- // Compare magnitude: returns -1, 0, or 1
62567
- float ds_cmp(vec2 a, vec2 b) {
62568
- float d = a.x - b.x;
62569
- if (d != 0.0) return sign(d);
62570
- return sign(a.y - b.y);
62571
- }
62572
- `;
62573
- var GPU_DS_ARITHMETIC_PREAMBLE_WGSL = `
62574
- fn ds_split(a: f32) -> vec2f {
62575
- const SPLIT: f32 = 4097.0;
62576
- let t = SPLIT * a;
62577
- let hi = t - (t - a);
62578
- let lo = a - hi;
62579
- return vec2f(hi, lo);
62580
- }
62581
-
62582
- fn ds_from(a: f32) -> vec2f {
62583
- return vec2f(a, 0.0);
62584
- }
62585
-
62586
- fn ds_add(a: vec2f, b: vec2f) -> vec2f {
62587
- let s = a.x + b.x;
62588
- let v = s - a.x;
62589
- let e = (a.x - (s - v)) + (b.x - v);
62590
- let lo_t = (a.y + b.y) + e;
62591
- let hi = s + lo_t;
62592
- let lo = lo_t - (hi - s);
62593
- return vec2f(hi, lo);
62594
- }
62595
-
62596
- fn ds_sub(a: vec2f, b: vec2f) -> vec2f {
62597
- return ds_add(a, vec2f(-b.x, -b.y));
62598
- }
62599
-
62600
- fn ds_mul(a: vec2f, b: vec2f) -> vec2f {
62601
- let p = a.x * b.x;
62602
- let sa = ds_split(a.x);
62603
- let sb = ds_split(b.x);
62604
- var err = ((sa.x * sb.x - p) + sa.x * sb.y + sa.y * sb.x) + sa.y * sb.y;
62605
- err += a.x * b.y + a.y * b.x;
62606
- let hi = p + err;
62607
- let lo = err - (hi - p);
62608
- return vec2f(hi, lo);
62609
- }
62610
-
62611
- fn ds_sqr(a: vec2f) -> vec2f {
62612
- let p = a.x * a.x;
62613
- let sa = ds_split(a.x);
62614
- var err = ((sa.x * sa.x - p) + 2.0 * sa.x * sa.y) + sa.y * sa.y;
62615
- err += 2.0 * a.x * a.y;
62616
- let hi = p + err;
62617
- let lo = err - (hi - p);
62618
- return vec2f(hi, lo);
62619
- }
62620
-
62621
- fn ds_cmp(a: vec2f, b: vec2f) -> f32 {
62622
- let d = a.x - b.x;
62623
- if (d != 0.0) { return sign(d); }
62624
- return sign(a.y - b.y);
62625
- }
62626
- `;
62627
- var GPU_FRACTAL_DP_PREAMBLE_GLSL = `
62628
- uniform float _dp_cx_hi;
62629
- uniform float _dp_cx_lo;
62630
- uniform float _dp_cy_hi;
62631
- uniform float _dp_cy_lo;
62632
- uniform float _dp_w;
62633
- uniform float _dp_h;
62634
-
62635
- vec4 _dp_coord() {
62636
- // Per-pixel offset from center \u2014 small, so float-precise
62637
- float dx = (v_uv.x - 0.5) * _dp_w;
62638
- float dy = (v_uv.y - 0.5) * _dp_h;
62639
- // Combine center (hi+lo) + delta with emulated double precision
62640
- vec2 cre = ds_add(vec2(_dp_cx_hi, _dp_cx_lo), ds_from(dx));
62641
- vec2 cim = ds_add(vec2(_dp_cy_hi, _dp_cy_lo), ds_from(dy));
62642
- return vec4(cre.x, cim.x, cre.y, cim.y);
62643
- }
62644
-
62645
- float _fractal_mandelbrot_dp(vec4 c, int maxIter) {
62646
- // c = (re_hi, im_hi, re_lo, im_lo)
62647
- vec2 cr = vec2(c.x, c.z); // real part as ds
62648
- vec2 ci = vec2(c.y, c.w); // imag part as ds
62649
- vec2 zr = vec2(0.0, 0.0);
62650
- vec2 zi = vec2(0.0, 0.0);
62651
- for (int i = 0; i < maxIter; i++) {
62652
- vec2 zr2 = ds_sqr(zr);
62653
- vec2 zi2 = ds_sqr(zi);
62654
- // |z|^2 > 4.0 ?
62655
- vec2 mag2 = ds_add(zr2, zi2);
62656
- if (mag2.x > 4.0)
62657
- return clamp((float(i) - log2(log2(mag2.x)) + 4.0) / float(maxIter), 0.0, 1.0);
62658
- // z = z^2 + c
62659
- vec2 new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci); // 2*zr*zi + ci
62660
- zr = ds_add(ds_sub(zr2, zi2), cr); // zr^2 - zi^2 + cr
62661
- zi = new_zi;
62662
- }
62663
- return 1.0;
62664
- }
62665
-
62666
- float _fractal_julia_dp(vec4 z_in, vec4 c, int maxIter) {
62667
- vec2 zr = vec2(z_in.x, z_in.z);
62668
- vec2 zi = vec2(z_in.y, z_in.w);
62669
- vec2 cr = vec2(c.x, c.z);
62670
- vec2 ci = vec2(c.y, c.w);
62671
- for (int i = 0; i < maxIter; i++) {
62672
- vec2 zr2 = ds_sqr(zr);
62673
- vec2 zi2 = ds_sqr(zi);
62674
- vec2 mag2 = ds_add(zr2, zi2);
62675
- if (mag2.x > 4.0)
62676
- return clamp((float(i) - log2(log2(mag2.x)) + 4.0) / float(maxIter), 0.0, 1.0);
62677
- vec2 new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci);
62678
- zr = ds_add(ds_sub(zr2, zi2), cr);
62679
- zi = new_zi;
62680
- }
62681
- return 1.0;
62682
- }
62683
- `;
62684
- var GPU_FRACTAL_DP_PREAMBLE_WGSL = `
62685
- @group(0) @binding(10) var<uniform> _dp_cx_hi: f32;
62686
- @group(0) @binding(11) var<uniform> _dp_cx_lo: f32;
62687
- @group(0) @binding(12) var<uniform> _dp_cy_hi: f32;
62688
- @group(0) @binding(13) var<uniform> _dp_cy_lo: f32;
62689
- @group(0) @binding(14) var<uniform> _dp_w: f32;
62690
- @group(0) @binding(15) var<uniform> _dp_h: f32;
62691
-
62692
- fn _dp_coord(uv: vec2f) -> vec4f {
62693
- let dx = (uv.x - 0.5) * _dp_w;
62694
- let dy = (uv.y - 0.5) * _dp_h;
62695
- let cre = ds_add(vec2f(_dp_cx_hi, _dp_cx_lo), ds_from(dx));
62696
- let cim = ds_add(vec2f(_dp_cy_hi, _dp_cy_lo), ds_from(dy));
62697
- return vec4f(cre.x, cim.x, cre.y, cim.y);
62698
- }
62699
-
62700
- fn _fractal_mandelbrot_dp(c: vec4f, maxIter: i32) -> f32 {
62701
- let cr = vec2f(c.x, c.z);
62702
- let ci = vec2f(c.y, c.w);
62703
- var zr = vec2f(0.0, 0.0);
62704
- var zi = vec2f(0.0, 0.0);
62705
- for (var i: i32 = 0; i < maxIter; i++) {
62706
- let zr2 = ds_sqr(zr);
62707
- let zi2 = ds_sqr(zi);
62708
- let mag2 = ds_add(zr2, zi2);
62709
- if (mag2.x > 4.0) {
62710
- return clamp((f32(i) - log2(log2(mag2.x)) + 4.0) / f32(maxIter), 0.0, 1.0);
62711
- }
62712
- let new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci);
62713
- zr = ds_add(ds_sub(zr2, zi2), cr);
62714
- zi = new_zi;
62715
- }
62716
- return 1.0;
62717
- }
62718
-
62719
- fn _fractal_julia_dp(z_in: vec4f, c: vec4f, maxIter: i32) -> f32 {
62720
- var zr = vec2f(z_in.x, z_in.z);
62721
- var zi = vec2f(z_in.y, z_in.w);
62722
- let cr = vec2f(c.x, c.z);
62723
- let ci = vec2f(c.y, c.w);
62724
- for (var i: i32 = 0; i < maxIter; i++) {
62725
- let zr2 = ds_sqr(zr);
62726
- let zi2 = ds_sqr(zi);
62727
- let mag2 = ds_add(zr2, zi2);
62728
- if (mag2.x > 4.0) {
62729
- return clamp((f32(i) - log2(log2(mag2.x)) + 4.0) / f32(maxIter), 0.0, 1.0);
62730
- }
62731
- let new_zi = ds_add(ds_mul(ds_add(zr, zr), zi), ci);
62732
- zr = ds_add(ds_sub(zr2, zi2), cr);
62733
- zi = new_zi;
62734
- }
62735
- return 1.0;
62736
- }
62737
- `;
62738
63410
  var GPU_FRACTAL_PREAMBLE_GLSL = `
62739
63411
  float _fractal_mandelbrot(vec2 c, int maxIter) {
62740
63412
  vec2 z = vec2(0.0, 0.0);
@@ -62778,208 +63450,6 @@ fn _fractal_julia(z_in: vec2f, c: vec2f, maxIter: i32) -> f32 {
62778
63450
  return 1.0;
62779
63451
  }
62780
63452
  `;
62781
- var GPU_FRACTAL_PT_PREAMBLE_GLSL = `
62782
- uniform sampler2D _refOrbit;
62783
- uniform int _refOrbitLen;
62784
- uniform int _refOrbitTexWidth;
62785
- uniform float _pt_offset_x;
62786
- uniform float _pt_offset_y;
62787
- uniform float _pt_w;
62788
- uniform float _pt_h;
62789
-
62790
- vec2 _pt_delta() {
62791
- float dx = _pt_offset_x + (v_uv.x - 0.5) * _pt_w;
62792
- float dy = _pt_offset_y + (v_uv.y - 0.5) * _pt_h;
62793
- return vec2(dx, dy);
62794
- }
62795
-
62796
- vec2 _pt_fetch_orbit(int i) {
62797
- int y = i / _refOrbitTexWidth;
62798
- int x = i - y * _refOrbitTexWidth;
62799
- return texelFetch(_refOrbit, ivec2(x, y), 0).rg;
62800
- }
62801
-
62802
- float _fractal_mandelbrot_pt(vec2 delta_c, int maxIter) {
62803
- float dr = 0.0;
62804
- float di = 0.0;
62805
- int orbitLen = min(maxIter, _refOrbitLen);
62806
- for (int i = 0; i < orbitLen; i++) {
62807
- vec2 Zn = _pt_fetch_orbit(i);
62808
- // delta_{n+1} = 2*Z_n*delta_n + delta_n^2 + delta_c
62809
- float new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62810
- float new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62811
- dr = new_dr;
62812
- di = new_di;
62813
- // Full z = Z_{n+1} + delta for escape check
62814
- vec2 Zn1 = (i + 1 < orbitLen) ? _pt_fetch_orbit(i + 1) : vec2(0.0);
62815
- float zr = Zn1.x + dr;
62816
- float zi = Zn1.y + di;
62817
- float mag2 = zr * zr + zi * zi;
62818
- if (mag2 > 4.0)
62819
- return clamp((float(i) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62820
- // Glitch detection: |delta|^2 > |Z|^2
62821
- float dmag2 = dr * dr + di * di;
62822
- float Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62823
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
62824
- // Rebase to absolute coordinates and continue with single-float
62825
- float abs_zr = Zn1.x + dr;
62826
- float abs_zi = Zn1.y + di;
62827
- // Reconstruct absolute c from reference + delta
62828
- // (Use ds_from for the concept, but single-float suffices for fallback)
62829
- float cx = abs_zr - dr + delta_c.x;
62830
- float cy = abs_zi - di + delta_c.y;
62831
- for (int j = i + 1; j < maxIter; j++) {
62832
- float new_zr = abs_zr * abs_zr - abs_zi * abs_zi + cx;
62833
- abs_zi = 2.0 * abs_zr * abs_zi + cy;
62834
- abs_zr = new_zr;
62835
- mag2 = abs_zr * abs_zr + abs_zi * abs_zi;
62836
- if (mag2 > 4.0)
62837
- return clamp((float(j) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62838
- }
62839
- return 1.0;
62840
- }
62841
- }
62842
- return 1.0;
62843
- }
62844
-
62845
- float _fractal_julia_pt(vec2 z_delta, vec2 delta_c, int maxIter) {
62846
- float dr = z_delta.x;
62847
- float di = z_delta.y;
62848
- int orbitLen = min(maxIter, _refOrbitLen);
62849
- for (int i = 0; i < orbitLen; i++) {
62850
- vec2 Zn = _pt_fetch_orbit(i);
62851
- float new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62852
- float new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62853
- dr = new_dr;
62854
- di = new_di;
62855
- vec2 Zn1 = (i + 1 < orbitLen) ? _pt_fetch_orbit(i + 1) : vec2(0.0);
62856
- float zr = Zn1.x + dr;
62857
- float zi = Zn1.y + di;
62858
- float mag2 = zr * zr + zi * zi;
62859
- if (mag2 > 4.0)
62860
- return clamp((float(i) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62861
- float dmag2 = dr * dr + di * di;
62862
- float Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62863
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
62864
- float abs_zr = Zn1.x + dr;
62865
- float abs_zi = Zn1.y + di;
62866
- float cx = delta_c.x;
62867
- float cy = delta_c.y;
62868
- for (int j = i + 1; j < maxIter; j++) {
62869
- float new_zr = abs_zr * abs_zr - abs_zi * abs_zi + cx;
62870
- abs_zi = 2.0 * abs_zr * abs_zi + cy;
62871
- abs_zr = new_zr;
62872
- mag2 = abs_zr * abs_zr + abs_zi * abs_zi;
62873
- if (mag2 > 4.0)
62874
- return clamp((float(j) - log2(log2(mag2)) + 4.0) / float(maxIter), 0.0, 1.0);
62875
- }
62876
- return 1.0;
62877
- }
62878
- }
62879
- return 1.0;
62880
- }
62881
- `;
62882
- var GPU_FRACTAL_PT_PREAMBLE_WGSL = `
62883
- @group(0) @binding(1) var _refOrbit: texture_2d<f32>;
62884
- var<uniform> _refOrbitLen: i32;
62885
- var<uniform> _refOrbitTexWidth: i32;
62886
- var<uniform> _pt_offset_x: f32;
62887
- var<uniform> _pt_offset_y: f32;
62888
- var<uniform> _pt_w: f32;
62889
- var<uniform> _pt_h: f32;
62890
-
62891
- fn _pt_delta(uv: vec2f) -> vec2f {
62892
- let dx = _pt_offset_x + (uv.x - 0.5) * _pt_w;
62893
- let dy = _pt_offset_y + (uv.y - 0.5) * _pt_h;
62894
- return vec2f(dx, dy);
62895
- }
62896
-
62897
- fn _pt_fetch_orbit(i: i32) -> vec2f {
62898
- let y = i / _refOrbitTexWidth;
62899
- let x = i - y * _refOrbitTexWidth;
62900
- return textureLoad(_refOrbit, vec2i(x, y), 0).rg;
62901
- }
62902
-
62903
- fn _fractal_mandelbrot_pt(delta_c: vec2f, maxIter: i32) -> f32 {
62904
- var dr: f32 = 0.0;
62905
- var di: f32 = 0.0;
62906
- let orbitLen = min(maxIter, _refOrbitLen);
62907
- for (var i: i32 = 0; i < orbitLen; i++) {
62908
- let Zn = _pt_fetch_orbit(i);
62909
- let new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62910
- let new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62911
- dr = new_dr;
62912
- di = new_di;
62913
- var Zn1 = vec2f(0.0);
62914
- if (i + 1 < orbitLen) { Zn1 = _pt_fetch_orbit(i + 1); }
62915
- let zr = Zn1.x + dr;
62916
- let zi = Zn1.y + di;
62917
- var mag2 = zr * zr + zi * zi;
62918
- if (mag2 > 4.0) {
62919
- return clamp((f32(i) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
62920
- }
62921
- let dmag2 = dr * dr + di * di;
62922
- let Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62923
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
62924
- var f_zr = Zn1.x + dr;
62925
- var f_zi = Zn1.y + di;
62926
- let cx = delta_c.x;
62927
- let cy = delta_c.y;
62928
- for (var j: i32 = i + 1; j < maxIter; j++) {
62929
- let t_zr = f_zr * f_zr - f_zi * f_zi + cx;
62930
- f_zi = 2.0 * f_zr * f_zi + cy;
62931
- f_zr = t_zr;
62932
- mag2 = f_zr * f_zr + f_zi * f_zi;
62933
- if (mag2 > 4.0) {
62934
- return clamp((f32(j) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
62935
- }
62936
- }
62937
- return 1.0;
62938
- }
62939
- }
62940
- return 1.0;
62941
- }
62942
-
62943
- fn _fractal_julia_pt(z_delta: vec2f, delta_c: vec2f, maxIter: i32) -> f32 {
62944
- var dr = z_delta.x;
62945
- var di = z_delta.y;
62946
- let orbitLen = min(maxIter, _refOrbitLen);
62947
- for (var i: i32 = 0; i < orbitLen; i++) {
62948
- let Zn = _pt_fetch_orbit(i);
62949
- let new_dr = 2.0 * (Zn.x * dr - Zn.y * di) + dr * dr - di * di + delta_c.x;
62950
- let new_di = 2.0 * (Zn.x * di + Zn.y * dr) + 2.0 * dr * di + delta_c.y;
62951
- dr = new_dr;
62952
- di = new_di;
62953
- var Zn1 = vec2f(0.0);
62954
- if (i + 1 < orbitLen) { Zn1 = _pt_fetch_orbit(i + 1); }
62955
- let zr = Zn1.x + dr;
62956
- let zi = Zn1.y + di;
62957
- var mag2 = zr * zr + zi * zi;
62958
- if (mag2 > 4.0) {
62959
- return clamp((f32(i) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
62960
- }
62961
- let dmag2 = dr * dr + di * di;
62962
- let Zmag2 = Zn.x * Zn.x + Zn.y * Zn.y;
62963
- if (dmag2 > Zmag2 && Zmag2 > 0.0) {
62964
- var f_zr = Zn1.x + dr;
62965
- var f_zi = Zn1.y + di;
62966
- let cx = delta_c.x;
62967
- let cy = delta_c.y;
62968
- for (var j: i32 = i + 1; j < maxIter; j++) {
62969
- let t_zr = f_zr * f_zr - f_zi * f_zi + cx;
62970
- f_zi = 2.0 * f_zr * f_zi + cy;
62971
- f_zr = t_zr;
62972
- mag2 = f_zr * f_zr + f_zi * f_zi;
62973
- if (mag2 > 4.0) {
62974
- return clamp((f32(j) - log2(log2(mag2)) + 4.0) / f32(maxIter), 0.0, 1.0);
62975
- }
62976
- }
62977
- return 1.0;
62978
- }
62979
- }
62980
- return 1.0;
62981
- }
62982
- `;
62983
63453
  var GPU_COLOR_PREAMBLE_GLSL = `
62984
63454
  float _gpu_srgb_to_linear(float c) {
62985
63455
  if (c <= 0.04045) return c / 12.92;
@@ -63020,28 +63490,124 @@ vec3 _gpu_oklab_to_srgb(vec3 lab) {
63020
63490
 
63021
63491
  vec3 _gpu_oklab_to_oklch(vec3 lab) {
63022
63492
  float C = length(lab.yz);
63023
- float H = atan(lab.z, lab.y);
63493
+ float H = atan(lab.z, lab.y) * (180.0 / 3.14159265359);
63494
+ if (H < 0.0) H += 360.0;
63024
63495
  return vec3(lab.x, C, H);
63025
63496
  }
63026
63497
 
63027
63498
  vec3 _gpu_oklch_to_oklab(vec3 lch) {
63028
- return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
63499
+ float h_rad = lch.z * (3.14159265359 / 180.0);
63500
+ return vec3(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
63501
+ }
63502
+
63503
+ vec3 _gpu_srgb_to_oklch(vec3 rgb) {
63504
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
63505
+ }
63506
+
63507
+ vec3 _gpu_oklch_to_srgb(vec3 lch) {
63508
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
63509
+ }
63510
+
63511
+ // HSL conversion. Hue in degrees, saturation/lightness in 0-1.
63512
+ vec3 _gpu_hsl_to_rgb(vec3 hsl) {
63513
+ float h = hsl.x;
63514
+ float s = hsl.y;
63515
+ float l = hsl.z;
63516
+ float c = (1.0 - abs(2.0 * l - 1.0)) * s;
63517
+ float h6 = h / 60.0;
63518
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
63519
+ float r = 0.0;
63520
+ float g = 0.0;
63521
+ float b = 0.0;
63522
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63523
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63524
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63525
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63526
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63527
+ else { r = c; g = 0.0; b = x; }
63528
+ float m = l - c / 2.0;
63529
+ return vec3(r + m, g + m, b + m);
63530
+ }
63531
+
63532
+ vec3 _gpu_rgb_to_hsl(vec3 rgb) {
63533
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
63534
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
63535
+ float l = (maxc + minc) / 2.0;
63536
+ float d = maxc - minc;
63537
+ if (d < 1e-6) return vec3(0.0, 0.0, l);
63538
+ float s = d / (1.0 - abs(2.0 * l - 1.0));
63539
+ float h;
63540
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
63541
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
63542
+ else h = (rgb.x - rgb.y) / d + 4.0;
63543
+ h *= 60.0;
63544
+ if (h < 0.0) h += 360.0;
63545
+ return vec3(h, s, l);
63546
+ }
63547
+
63548
+ // HSV conversion. Hue in degrees, saturation/value in 0-1.
63549
+ vec3 _gpu_hsv_to_rgb(vec3 hsv) {
63550
+ float h = hsv.x;
63551
+ float s = hsv.y;
63552
+ float v = hsv.z;
63553
+ float c = v * s;
63554
+ float h6 = h / 60.0;
63555
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
63556
+ float r = 0.0;
63557
+ float g = 0.0;
63558
+ float b = 0.0;
63559
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63560
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63561
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63562
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63563
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63564
+ else { r = c; g = 0.0; b = x; }
63565
+ float m = v - c;
63566
+ return vec3(r + m, g + m, b + m);
63567
+ }
63568
+
63569
+ vec3 _gpu_rgb_to_hsv(vec3 rgb) {
63570
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
63571
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
63572
+ float v = maxc;
63573
+ float d = maxc - minc;
63574
+ if (d < 1e-6) return vec3(0.0, 0.0, v);
63575
+ float s = (maxc < 1e-6) ? 0.0 : d / maxc;
63576
+ float h;
63577
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
63578
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
63579
+ else h = (rgb.x - rgb.y) / d + 4.0;
63580
+ h *= 60.0;
63581
+ if (h < 0.0) h += 360.0;
63582
+ return vec3(h, s, v);
63029
63583
  }
63030
63584
 
63031
- vec3 _gpu_color_mix(vec3 rgb1, vec3 rgb2, float t) {
63032
- vec3 lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
63033
- vec3 lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
63585
+ vec3 _gpu_color_mix(vec3 lch1, vec3 lch2, float t) {
63034
63586
  float L = mix(lch1.x, lch2.x, t);
63035
63587
  float C = mix(lch1.y, lch2.y, t);
63036
- float dh = lch2.z - lch1.z;
63037
- const float PI = 3.14159265359;
63038
- if (dh > PI) dh -= 2.0 * PI;
63039
- if (dh < -PI) dh += 2.0 * PI;
63040
- float H = lch1.z + dh * t;
63041
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3(L, C, H)));
63588
+ bool a1 = lch1.y < 1e-6;
63589
+ bool a2 = lch2.y < 1e-6;
63590
+ float H;
63591
+ if (a1 && a2) {
63592
+ H = lch1.z;
63593
+ } else if (a1) {
63594
+ H = lch2.z;
63595
+ } else if (a2) {
63596
+ H = lch1.z;
63597
+ } else {
63598
+ float dh = lch2.z - lch1.z;
63599
+ if (dh > 180.0) dh -= 360.0;
63600
+ if (dh < -180.0) dh += 360.0;
63601
+ H = lch1.z + dh * t;
63602
+ if (H < 0.0) H += 360.0;
63603
+ if (H >= 360.0) H -= 360.0;
63604
+ }
63605
+ return vec3(L, C, H);
63042
63606
  }
63043
63607
 
63044
- float _gpu_apca(vec3 bg, vec3 fg) {
63608
+ float _gpu_apca(vec3 lch_bg, vec3 lch_fg) {
63609
+ vec3 bg = _gpu_oklch_to_srgb(lch_bg);
63610
+ vec3 fg = _gpu_oklch_to_srgb(lch_fg);
63045
63611
  float bgR = _gpu_srgb_to_linear(bg.x);
63046
63612
  float bgG = _gpu_srgb_to_linear(bg.y);
63047
63613
  float bgB = _gpu_srgb_to_linear(bg.z);
@@ -63052,9 +63618,7 @@ float _gpu_apca(vec3 bg, vec3 fg) {
63052
63618
  float fgY = 0.2126729 * fgR + 0.7151522 * fgG + 0.0721750 * fgB;
63053
63619
  float bgC = pow(bgY, 0.56);
63054
63620
  float fgC = pow(fgY, 0.57);
63055
- float contrast = (bgC > fgC)
63056
- ? (bgC - fgC) * 1.14
63057
- : (bgC - fgC) * 1.14;
63621
+ float contrast = (bgC - fgC) * 1.14;
63058
63622
  return contrast * 100.0;
63059
63623
  }
63060
63624
  `;
@@ -63098,28 +63662,133 @@ fn _gpu_oklab_to_srgb(lab: vec3f) -> vec3f {
63098
63662
 
63099
63663
  fn _gpu_oklab_to_oklch(lab: vec3f) -> vec3f {
63100
63664
  let C = length(lab.yz);
63101
- let H = atan2(lab.z, lab.y);
63665
+ var H = atan2(lab.z, lab.y) * (180.0 / 3.14159265359);
63666
+ if (H < 0.0) { H = H + 360.0; }
63102
63667
  return vec3f(lab.x, C, H);
63103
63668
  }
63104
63669
 
63105
63670
  fn _gpu_oklch_to_oklab(lch: vec3f) -> vec3f {
63106
- return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
63671
+ let h_rad = lch.z * (3.14159265359 / 180.0);
63672
+ return vec3f(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
63673
+ }
63674
+
63675
+ fn _gpu_srgb_to_oklch(rgb: vec3f) -> vec3f {
63676
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
63677
+ }
63678
+
63679
+ fn _gpu_oklch_to_srgb(lch: vec3f) -> vec3f {
63680
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
63681
+ }
63682
+
63683
+ fn _gpu_hsl_to_rgb(hsl: vec3f) -> vec3f {
63684
+ let h = hsl.x;
63685
+ let s = hsl.y;
63686
+ let l = hsl.z;
63687
+ let c = (1.0 - abs(2.0 * l - 1.0)) * s;
63688
+ let h6 = h / 60.0;
63689
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
63690
+ var r: f32 = 0.0;
63691
+ var g: f32 = 0.0;
63692
+ var b: f32 = 0.0;
63693
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63694
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63695
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63696
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63697
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63698
+ else { r = c; g = 0.0; b = x; }
63699
+ let m = l - c / 2.0;
63700
+ return vec3f(r + m, g + m, b + m);
63701
+ }
63702
+
63703
+ fn _gpu_rgb_to_hsl(rgb: vec3f) -> vec3f {
63704
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
63705
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
63706
+ let l = (maxc + minc) / 2.0;
63707
+ let d = maxc - minc;
63708
+ if (d < 1e-6) { return vec3f(0.0, 0.0, l); }
63709
+ let s = d / (1.0 - abs(2.0 * l - 1.0));
63710
+ var h: f32;
63711
+ if (maxc == rgb.x) {
63712
+ let v = (rgb.y - rgb.z) / d;
63713
+ h = v - 6.0 * floor(v / 6.0);
63714
+ } else if (maxc == rgb.y) {
63715
+ h = (rgb.z - rgb.x) / d + 2.0;
63716
+ } else {
63717
+ h = (rgb.x - rgb.y) / d + 4.0;
63718
+ }
63719
+ h = h * 60.0;
63720
+ if (h < 0.0) { h = h + 360.0; }
63721
+ return vec3f(h, s, l);
63722
+ }
63723
+
63724
+ fn _gpu_hsv_to_rgb(hsv: vec3f) -> vec3f {
63725
+ let h = hsv.x;
63726
+ let s = hsv.y;
63727
+ let v = hsv.z;
63728
+ let c = v * s;
63729
+ let h6 = h / 60.0;
63730
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
63731
+ var r: f32 = 0.0;
63732
+ var g: f32 = 0.0;
63733
+ var b: f32 = 0.0;
63734
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
63735
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
63736
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
63737
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
63738
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
63739
+ else { r = c; g = 0.0; b = x; }
63740
+ let m = v - c;
63741
+ return vec3f(r + m, g + m, b + m);
63107
63742
  }
63108
63743
 
63109
- fn _gpu_color_mix(rgb1: vec3f, rgb2: vec3f, t: f32) -> vec3f {
63110
- let lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
63111
- let lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
63744
+ fn _gpu_rgb_to_hsv(rgb: vec3f) -> vec3f {
63745
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
63746
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
63747
+ let v = maxc;
63748
+ let d = maxc - minc;
63749
+ if (d < 1e-6) { return vec3f(0.0, 0.0, v); }
63750
+ var s: f32 = 0.0;
63751
+ if (maxc >= 1e-6) { s = d / maxc; }
63752
+ var h: f32;
63753
+ if (maxc == rgb.x) {
63754
+ let q = (rgb.y - rgb.z) / d;
63755
+ h = q - 6.0 * floor(q / 6.0);
63756
+ } else if (maxc == rgb.y) {
63757
+ h = (rgb.z - rgb.x) / d + 2.0;
63758
+ } else {
63759
+ h = (rgb.x - rgb.y) / d + 4.0;
63760
+ }
63761
+ h = h * 60.0;
63762
+ if (h < 0.0) { h = h + 360.0; }
63763
+ return vec3f(h, s, v);
63764
+ }
63765
+
63766
+ fn _gpu_color_mix(lch1: vec3f, lch2: vec3f, t: f32) -> vec3f {
63112
63767
  let L = mix(lch1.x, lch2.x, t);
63113
63768
  let C = mix(lch1.y, lch2.y, t);
63114
- let PI = 3.14159265359;
63115
- var dh = lch2.z - lch1.z;
63116
- if (dh > PI) { dh -= 2.0 * PI; }
63117
- if (dh < -PI) { dh += 2.0 * PI; }
63118
- let H = lch1.z + dh * t;
63119
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3f(L, C, H)));
63769
+ let a1 = lch1.y < 1e-6;
63770
+ let a2 = lch2.y < 1e-6;
63771
+ var H: f32;
63772
+ if (a1 && a2) {
63773
+ H = lch1.z;
63774
+ } else if (a1) {
63775
+ H = lch2.z;
63776
+ } else if (a2) {
63777
+ H = lch1.z;
63778
+ } else {
63779
+ var dh = lch2.z - lch1.z;
63780
+ if (dh > 180.0) { dh = dh - 360.0; }
63781
+ if (dh < -180.0) { dh = dh + 360.0; }
63782
+ H = lch1.z + dh * t;
63783
+ if (H < 0.0) { H = H + 360.0; }
63784
+ if (H >= 360.0) { H = H - 360.0; }
63785
+ }
63786
+ return vec3f(L, C, H);
63120
63787
  }
63121
63788
 
63122
- fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63789
+ fn _gpu_apca(lch_bg: vec3f, lch_fg: vec3f) -> f32 {
63790
+ let bg = _gpu_oklch_to_srgb(lch_bg);
63791
+ let fg = _gpu_oklch_to_srgb(lch_fg);
63123
63792
  let bgR = _gpu_srgb_to_linear(bg.x);
63124
63793
  let bgG = _gpu_srgb_to_linear(bg.y);
63125
63794
  let bgB = _gpu_srgb_to_linear(bg.z);
@@ -63407,7 +64076,7 @@ var GPUShaderTarget = class {
63407
64076
  if (stmts.length === 0) return "";
63408
64077
  const last = stmts.length - 1;
63409
64078
  stmts[last] = `return ${stmts[last]}`;
63410
- return stmts.join(";\n");
64079
+ return stmts.join(";\n") + ";";
63411
64080
  },
63412
64081
  ...options
63413
64082
  };
@@ -63418,7 +64087,6 @@ var GPUShaderTarget = class {
63418
64087
  const constants = this.getConstants();
63419
64088
  const v2 = this.languageId === "wgsl" ? "vec2f" : "vec2";
63420
64089
  const target = this.createTarget({
63421
- hints: options.hints,
63422
64090
  functions: (id) => {
63423
64091
  if (userFunctions && id in userFunctions) {
63424
64092
  const fn = userFunctions[id];
@@ -63457,89 +64125,12 @@ var GPUShaderTarget = class {
63457
64125
  if (code.includes("_gpu_besselJ"))
63458
64126
  preamble += this.languageId === "wgsl" ? GPU_BESSELJ_PREAMBLE_WGSL : GPU_BESSELJ_PREAMBLE_GLSL;
63459
64127
  if (code.includes("_fractal_")) {
63460
- if (code.includes("_fractal_mandelbrot_pt") || code.includes("_fractal_julia_pt")) {
63461
- preamble += this.languageId === "wgsl" ? GPU_DS_ARITHMETIC_PREAMBLE_WGSL : GPU_DS_ARITHMETIC_PREAMBLE_GLSL;
63462
- preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PT_PREAMBLE_WGSL : GPU_FRACTAL_PT_PREAMBLE_GLSL;
63463
- } else if (code.includes("_fractal_mandelbrot_dp") || code.includes("_fractal_julia_dp")) {
63464
- preamble += this.languageId === "wgsl" ? GPU_DS_ARITHMETIC_PREAMBLE_WGSL : GPU_DS_ARITHMETIC_PREAMBLE_GLSL;
63465
- preamble += this.languageId === "wgsl" ? GPU_FRACTAL_DP_PREAMBLE_WGSL : GPU_FRACTAL_DP_PREAMBLE_GLSL;
63466
- } else {
63467
- preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PREAMBLE_WGSL : GPU_FRACTAL_PREAMBLE_GLSL;
63468
- }
64128
+ preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PREAMBLE_WGSL : GPU_FRACTAL_PREAMBLE_GLSL;
63469
64129
  }
63470
64130
  if (code.includes("_gpu_srgb_to") || code.includes("_gpu_oklab") || code.includes("_gpu_oklch") || code.includes("_gpu_color_mix") || code.includes("_gpu_apca")) {
63471
64131
  preamble += this.languageId === "wgsl" ? GPU_COLOR_PREAMBLE_WGSL : GPU_COLOR_PREAMBLE_GLSL;
63472
64132
  }
63473
64133
  if (preamble) result.preamble = preamble;
63474
- if (code.includes("_fractal_") && options.hints?.viewport) {
63475
- const strategy = selectFractalStrategy(target);
63476
- const radius = options.hints.viewport.radius;
63477
- switch (strategy) {
63478
- case "single":
63479
- result.staleWhen = { radiusBelow: 1e-6 };
63480
- break;
63481
- case "double":
63482
- result.staleWhen = { radiusBelow: 1e-14, radiusAbove: 1e-5 };
63483
- break;
63484
- case "perturbation":
63485
- result.staleWhen = {
63486
- radiusAbove: 1e-5,
63487
- radiusBelow: radius * 0.01,
63488
- centerDistance: radius * 2
63489
- };
63490
- break;
63491
- }
63492
- }
63493
- if ((code.includes("_fractal_mandelbrot_dp") || code.includes("_fractal_julia_dp")) && options.hints?.viewport) {
63494
- const cx = hpToNumber(options.hints.viewport.center[0]);
63495
- const cy = hpToNumber(options.hints.viewport.center[1]);
63496
- const size = options.hints.viewport.radius * 2;
63497
- const cx_hi = Math.fround(cx);
63498
- const cy_hi = Math.fround(cy);
63499
- result.uniforms = {
63500
- ...result.uniforms,
63501
- _dp_cx_hi: cx_hi,
63502
- _dp_cx_lo: cx - cx_hi,
63503
- _dp_cy_hi: cy_hi,
63504
- _dp_cy_lo: cy - cy_hi,
63505
- _dp_w: size,
63506
- _dp_h: size
63507
- };
63508
- }
63509
- if ((code.includes("_fractal_mandelbrot_pt") || code.includes("_fractal_julia_pt")) && options.hints?.viewport) {
63510
- const viewport = options.hints.viewport;
63511
- const size = viewport.radius * 2;
63512
- result.uniforms = {
63513
- ...result.uniforms,
63514
- _pt_offset_x: 0,
63515
- _pt_offset_y: 0,
63516
- _pt_w: size,
63517
- _pt_h: size
63518
- };
63519
- const digits = Math.max(50, Math.ceil(-Math.log10(viewport.radius)) + 10);
63520
- const maxIter = 1e3;
63521
- const orbit = computeReferenceOrbit(
63522
- viewport.center,
63523
- maxIter,
63524
- digits
63525
- );
63526
- const orbitLen = orbit.length / 2;
63527
- const texWidth = Math.min(orbitLen, 4096);
63528
- const texHeight = Math.ceil(orbitLen / texWidth);
63529
- result.textures = {
63530
- _refOrbit: {
63531
- data: orbit,
63532
- width: texWidth,
63533
- height: texHeight,
63534
- format: "rg32f"
63535
- }
63536
- };
63537
- result.uniforms = {
63538
- ...result.uniforms,
63539
- _refOrbitLen: orbitLen,
63540
- _refOrbitTexWidth: texWidth
63541
- };
63542
- }
63543
64134
  return result;
63544
64135
  }
63545
64136
  compileToSource(expr2, _options = {}) {
@@ -63584,7 +64175,7 @@ var GLSLTarget = class extends GPUShaderTarget {
63584
64175
  if (body.includes("\n")) {
63585
64176
  const indented = body.split("\n").map((l) => ` ${l}`).join("\n");
63586
64177
  return `${returnType} ${functionName}(${params}) {
63587
- ${indented};
64178
+ ${indented}
63588
64179
  }`;
63589
64180
  }
63590
64181
  return `${returnType} ${functionName}(${params}) {
@@ -63695,7 +64286,7 @@ var WGSLTarget = class extends GPUShaderTarget {
63695
64286
  return `fn ${functionName}(${params}) -> ${toWGSLType(
63696
64287
  returnType
63697
64288
  )} {
63698
- ${indented};
64289
+ ${indented}
63699
64290
  }`;
63700
64291
  }
63701
64292
  return `fn ${functionName}(${params}) -> ${toWGSLType(returnType)} {
@@ -67680,6 +68271,7 @@ var ComputeEngine = class _ComputeEngine {
67680
68271
  this.pushScope(void 0, "global");
67681
68272
  this._compilationTargets.registerDefaults();
67682
68273
  if (options?.latexSyntax) this._latexSyntax = options.latexSyntax;
68274
+ if (options?.latexOptions) this._latexOptions = { ...options.latexOptions };
67683
68275
  hidePrivateProperties(this);
67684
68276
  }
67685
68277
  toJSON() {
@@ -68340,6 +68932,29 @@ var ComputeEngine = class _ComputeEngine {
68340
68932
  );
68341
68933
  return this._latexSyntax;
68342
68934
  }
68935
+ /** @internal Engine-wide LaTeX parse/serialize options (e.g. decimalSeparator).
68936
+ * Merged into every `parse()` and `toLatex()` call between the LatexSyntax
68937
+ * instance defaults and any per-call overrides. */
68938
+ _latexOptions = {};
68939
+ /** Engine-wide LaTeX parse/serialize options.
68940
+ *
68941
+ * These options are merged into every `parse()` and `toLatex()` call.
68942
+ * Precedence (most-specific wins):
68943
+ * 1. LatexSyntax instance defaults (set at its construction)
68944
+ * 2. `ce.latexOptions` (this property)
68945
+ * 3. Per-call options passed to `ce.parse()` / `expr.toLatex()`
68946
+ *
68947
+ * Assigning replaces the whole bag. Use spread to merge:
68948
+ * ```ts
68949
+ * ce.latexOptions = { ...ce.latexOptions, decimalSeparator: '{,}' };
68950
+ * ```
68951
+ */
68952
+ get latexOptions() {
68953
+ return this._latexOptions;
68954
+ }
68955
+ set latexOptions(options) {
68956
+ this._latexOptions = { ...options };
68957
+ }
68343
68958
  parse(latex, options) {
68344
68959
  if (latex === null || latex === void 0) return null;
68345
68960
  if (typeof latex !== "string")
@@ -68347,7 +68962,6 @@ var ComputeEngine = class _ComputeEngine {
68347
68962
  const syntax = this._requireLatexSyntax();
68348
68963
  const { form, ...parseOpts } = options ?? {};
68349
68964
  const result = syntax.parse(latex, {
68350
- decimalSeparator: ".",
68351
68965
  getSymbolType: (id) => {
68352
68966
  const def = this.lookupDefinition(id);
68353
68967
  if (!def) return BoxedType.unknown;
@@ -68359,6 +68973,7 @@ var ComputeEngine = class _ComputeEngine {
68359
68973
  const def = this.lookupDefinition(id);
68360
68974
  return !!(isValueDef(def) && def.value.subscriptEvaluate);
68361
68975
  },
68976
+ ...this._latexOptions,
68362
68977
  ...parseOpts
68363
68978
  });
68364
68979
  if (result === null) return null;
@@ -68522,7 +69137,7 @@ var ComputeEngine = class _ComputeEngine {
68522
69137
  _setDefaultEngineFactory(() => new ComputeEngine());
68523
69138
 
68524
69139
  // src/core.ts
68525
- var version = "0.55.5";
69140
+ var version = "0.56.0";
68526
69141
  export {
68527
69142
  ComputeEngine,
68528
69143
  N,