@cortex-js/compute-engine 0.55.6 → 0.57.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 (258) hide show
  1. package/dist/compile.esm.js +1224 -179
  2. package/dist/compile.min.esm.js +290 -91
  3. package/dist/compile.min.umd.cjs +291 -92
  4. package/dist/compile.umd.cjs +1224 -179
  5. package/dist/compute-engine.esm.js +1973 -306
  6. package/dist/compute-engine.min.esm.js +301 -102
  7. package/dist/compute-engine.min.umd.cjs +301 -102
  8. package/dist/compute-engine.umd.cjs +1973 -306
  9. package/dist/core.esm.js +1972 -305
  10. package/dist/core.min.esm.js +300 -101
  11. package/dist/core.min.umd.cjs +300 -101
  12. package/dist/core.umd.cjs +1972 -305
  13. package/dist/interval.esm.js +360 -19
  14. package/dist/interval.min.esm.js +6 -6
  15. package/dist/interval.min.umd.cjs +6 -6
  16. package/dist/interval.umd.cjs +360 -19
  17. package/dist/latex-syntax.esm.js +427 -25
  18. package/dist/latex-syntax.min.esm.js +7 -7
  19. package/dist/latex-syntax.min.umd.cjs +7 -7
  20. package/dist/latex-syntax.umd.cjs +427 -25
  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 +6 -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 +55 -6
  105. package/dist/types/compute-engine/compilation/compile-expression.d.ts +1 -1
  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 -5
  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 -1
  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 +24 -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 +1 -1
  169. package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
  170. package/dist/types/compute-engine/latex-syntax/types.d.ts +40 -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 +5 -3
  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 +23 -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 +1 -2
  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
@@ -1,4 +1,4 @@
1
- /** Compute Engine 0.55.6 */
1
+ /** Compute Engine 0.57.0 */
2
2
  (function(global,factory){typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'],factory):(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ComputeEngine = {}));})(this, (function (exports) { 'use strict';
3
3
  var ComputeEngine = (() => {
4
4
  var __defProp = Object.defineProperty;
@@ -2316,6 +2316,7 @@ var ComputeEngine = (() => {
2316
2316
  ];
2317
2317
  var VALUE_TYPES = [
2318
2318
  "value",
2319
+ "color",
2319
2320
  ...COLLECTION_TYPES,
2320
2321
  ...SCALAR_TYPES
2321
2322
  ];
@@ -4015,6 +4016,7 @@ var ComputeEngine = (() => {
4015
4016
  symbol: [],
4016
4017
  boolean: [],
4017
4018
  string: [],
4019
+ color: [],
4018
4020
  expression: EXPRESSION_TYPES
4019
4021
  };
4020
4022
  function isPrimitiveSubtype(lhs, rhs) {
@@ -7898,9 +7900,10 @@ var ComputeEngine = (() => {
7898
7900
  if (!materialized.isLazyCollection) return materialized.latex;
7899
7901
  }
7900
7902
  const syntax = this.engine._requireLatexSyntax();
7901
- return syntax.serialize(
7902
- this.toMathJson({ prettify: true, fractionalDigits: "auto" })
7903
- );
7903
+ const json = this.toMathJson({ prettify: true, fractionalDigits: "auto" });
7904
+ const latexOpts = this.engine.latexOptions;
7905
+ if (Object.keys(latexOpts).length === 0) return syntax.serialize(json);
7906
+ return syntax.serialize(json, { ...latexOpts });
7904
7907
  }
7905
7908
  /**
7906
7909
  * Return a LaTeX representation of this expression with custom
@@ -7911,8 +7914,15 @@ var ComputeEngine = (() => {
7911
7914
  *
7912
7915
  * Numeric values are rounded to `ce.precision` significant digits
7913
7916
  * (via `fractionalDigits: 'auto'`).
7917
+ *
7918
+ * If `options.verbatim` is `true` and `verbatimLatex` is set on this
7919
+ * expression (i.e. it was parsed with `preserveLatex: true`), return
7920
+ * the verbatim source instead of re-serializing. Falls through to
7921
+ * re-serialization if no verbatim is available.
7914
7922
  */
7915
7923
  toLatex(options) {
7924
+ if (options?.verbatim === true && this.verbatimLatex !== void 0)
7925
+ return this.verbatimLatex;
7916
7926
  if (this.isLazyCollection) {
7917
7927
  const materialized = this.evaluate({
7918
7928
  materialization: options?.materialization ?? true
@@ -7924,9 +7934,13 @@ var ComputeEngine = (() => {
7924
7934
  fractionalDigits: "auto"
7925
7935
  });
7926
7936
  const syntax = this.engine._requireLatexSyntax();
7927
- if (!options || Object.keys(options).length === 0)
7928
- return syntax.serialize(json);
7929
- return syntax.serialize(json, options);
7937
+ const latexOpts = this.engine.latexOptions;
7938
+ const haveEngineOpts = Object.keys(latexOpts).length > 0;
7939
+ const haveCallOpts = options && Object.keys(options).length > 0;
7940
+ if (!haveEngineOpts && !haveCallOpts) return syntax.serialize(json);
7941
+ if (!haveEngineOpts) return syntax.serialize(json, options);
7942
+ if (!haveCallOpts) return syntax.serialize(json, { ...latexOpts });
7943
+ return syntax.serialize(json, { ...latexOpts, ...options });
7930
7944
  }
7931
7945
  /** Called by `JSON.stringify()` when serializing to json.
7932
7946
  *
@@ -7970,11 +7984,13 @@ var ComputeEngine = (() => {
7970
7984
  "number",
7971
7985
  "dictionary"
7972
7986
  ];
7973
- }
7974
- if (Array.isArray(options.shorthands))
7987
+ } else if (Array.isArray(options.shorthands)) {
7975
7988
  defaultOptions.shorthands = options.shorthands;
7989
+ }
7976
7990
  if (typeof options.metadata === "string" && options.metadata === "all" || options.metadata?.includes("all")) {
7977
7991
  defaultOptions.metadata = ["latex", "wikidata"];
7992
+ } else if (Array.isArray(options.metadata)) {
7993
+ defaultOptions.metadata = options.metadata;
7978
7994
  }
7979
7995
  if (options.fractionalDigits === "auto")
7980
7996
  defaultOptions.fractionalDigits = -this.engine.precision;
@@ -10049,6 +10065,64 @@ var ComputeEngine = (() => {
10049
10065
  }
10050
10066
 
10051
10067
  // src/compute-engine/latex-syntax/dictionary/definitions-core.ts
10068
+ var COMPONENT_ACCESS_HEADS = {
10069
+ x: "First",
10070
+ y: "Second",
10071
+ z: "Third",
10072
+ real: "Real",
10073
+ re: "Real",
10074
+ imag: "Imaginary",
10075
+ im: "Imaginary",
10076
+ count: "Length",
10077
+ total: "Sum",
10078
+ max: "Max",
10079
+ min: "Min"
10080
+ };
10081
+ function memberHead(name) {
10082
+ return COMPONENT_ACCESS_HEADS[name] ?? null;
10083
+ }
10084
+ function parseComponentAccess(parser, lhs) {
10085
+ parser.skipVisualSpace();
10086
+ if (parser.match("\\operatorname")) {
10087
+ const name = parser.parseStringGroup();
10088
+ if (name === null) return null;
10089
+ const head = memberHead(name.trim());
10090
+ if (head === null) return null;
10091
+ return [head, lhs];
10092
+ }
10093
+ const tok = parser.peek;
10094
+ if (typeof tok === "string" && tok.startsWith("\\")) {
10095
+ const bare = tok.slice(1);
10096
+ const head = memberHead(bare);
10097
+ if (head !== null) {
10098
+ parser.nextToken();
10099
+ return [head, lhs];
10100
+ }
10101
+ return null;
10102
+ }
10103
+ if (typeof tok === "string" && /^[a-zA-Z]$/.test(tok)) {
10104
+ const head = memberHead(tok);
10105
+ if (head === null) return null;
10106
+ parser.nextToken();
10107
+ return [head, lhs];
10108
+ }
10109
+ return null;
10110
+ }
10111
+ function parseWhenRestriction(parser, lhs, close) {
10112
+ parser.addBoundary(close);
10113
+ parser.skipVisualSpace();
10114
+ const cond = parser.parseExpression({ minPrec: 0 });
10115
+ if (cond === null) {
10116
+ parser.removeBoundary();
10117
+ return null;
10118
+ }
10119
+ parser.skipVisualSpace();
10120
+ if (!parser.matchBoundary()) {
10121
+ parser.removeBoundary();
10122
+ return null;
10123
+ }
10124
+ return ["When", lhs, cond];
10125
+ }
10052
10126
  function parseSequence(parser, terminator, lhs, prec, sep) {
10053
10127
  if (terminator && terminator.minPrec >= prec) return null;
10054
10128
  const result = lhs ? [lhs] : ["Nothing"];
@@ -10520,6 +10594,15 @@ var ComputeEngine = (() => {
10520
10594
  }
10521
10595
  },
10522
10596
  { name: "LatexTokens", serialize: serializeLatexTokens },
10597
+ // Component-access postfix: expr.member (C3)
10598
+ // The '.' trigger is consumed before the parse function is called.
10599
+ // Precedence 850 > 810 (At/indexing) so .x chains tightly.
10600
+ {
10601
+ kind: "postfix",
10602
+ precedence: 850,
10603
+ latexTrigger: ["."],
10604
+ parse: parseComponentAccess
10605
+ },
10523
10606
  {
10524
10607
  name: "At",
10525
10608
  kind: "postfix",
@@ -10540,6 +10623,29 @@ var ComputeEngine = (() => {
10540
10623
  latexTrigger: ["\\left", "\\lbrack"],
10541
10624
  parse: parseAt("\\right", "\\rbrack")
10542
10625
  },
10626
+ // When-restriction: `expr\left\{cond\right\}` → `When(expr, cond)` (D3)
10627
+ {
10628
+ name: "When",
10629
+ kind: "postfix",
10630
+ precedence: 800,
10631
+ latexTrigger: ["\\left", "\\{"],
10632
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\right", "\\}"]),
10633
+ serialize: (serializer, expr2) => {
10634
+ const e = operand(expr2, 1);
10635
+ const cond = operand(expr2, 2);
10636
+ if (!e || !cond) return "";
10637
+ const clauses = operator(cond) === "And" ? operands(cond) ?? [] : [cond];
10638
+ const inner = clauses.map((c) => `\\left\\{${serializer.serialize(c)}\\right\\}`).join("");
10639
+ return `${serializer.serialize(e)}${inner}`;
10640
+ }
10641
+ },
10642
+ // When-restriction: bare `expr\{cond\}` → `When(expr, cond)`
10643
+ {
10644
+ kind: "postfix",
10645
+ precedence: 800,
10646
+ latexTrigger: ["\\{"],
10647
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\}"])
10648
+ },
10543
10649
  {
10544
10650
  kind: "postfix",
10545
10651
  latexTrigger: ["_"],
@@ -10622,6 +10728,29 @@ var ComputeEngine = (() => {
10622
10728
  return "";
10623
10729
  }
10624
10730
  },
10731
+ // Additional triggers for Range: `...`, `\ldots`, and `\dots` are
10732
+ // equivalent to `..` when used as infix operators (e.g. `[1...9]`).
10733
+ // No `name` field here — names must be unique per the dictionary rules;
10734
+ // the first Range entry owns the name. When there is no LHS the symbol
10735
+ // entries near the top of the file still fire (ContinuationPlaceholder).
10736
+ {
10737
+ latexTrigger: [".", ".", "."],
10738
+ kind: "infix",
10739
+ precedence: 800,
10740
+ parse: parseRange
10741
+ },
10742
+ {
10743
+ latexTrigger: ["\\ldots"],
10744
+ kind: "infix",
10745
+ precedence: 800,
10746
+ parse: parseRange
10747
+ },
10748
+ {
10749
+ latexTrigger: ["\\dots"],
10750
+ kind: "infix",
10751
+ precedence: 800,
10752
+ parse: parseRange
10753
+ },
10625
10754
  {
10626
10755
  latexTrigger: [";"],
10627
10756
  kind: "infix",
@@ -10806,13 +10935,24 @@ var ComputeEngine = (() => {
10806
10935
  const args = operands(expr2);
10807
10936
  if (!args || args.length < 2) return "";
10808
10937
  const body = args[0];
10809
- const indexing = args[1];
10810
- if (operator(indexing) === "Element") {
10811
- const index = operand(indexing, 1);
10812
- const range2 = operand(indexing, 2);
10813
- if (operator(range2) === "Range") {
10814
- const lo = operand(range2, 1);
10815
- const hi = operand(range2, 2);
10938
+ const elements = args.slice(1);
10939
+ const allElements = elements.every((e) => operator(e) === "Element");
10940
+ if (!allElements) {
10941
+ return joinLatex([
10942
+ "\\operatorname{Loop}(",
10943
+ serializer.serialize(body),
10944
+ ", ",
10945
+ serializer.serialize(elements[0]),
10946
+ ")"
10947
+ ]);
10948
+ }
10949
+ if (elements.length === 1) {
10950
+ const elem = elements[0];
10951
+ const index = operand(elem, 1);
10952
+ const coll = operand(elem, 2);
10953
+ if (operator(coll) === "Range") {
10954
+ const lo = operand(coll, 1);
10955
+ const hi = operand(coll, 2);
10816
10956
  return joinLatex([
10817
10957
  "\\text{for }",
10818
10958
  serializer.serialize(index),
@@ -10824,13 +10964,27 @@ var ComputeEngine = (() => {
10824
10964
  serializer.serialize(body)
10825
10965
  ]);
10826
10966
  }
10967
+ return joinLatex([
10968
+ serializer.serialize(body),
10969
+ " \\operatorname{for} ",
10970
+ serializer.serialize(index),
10971
+ " = ",
10972
+ serializer.serialize(coll)
10973
+ ]);
10827
10974
  }
10975
+ const bindings = elements.map((elem) => {
10976
+ const name = operand(elem, 1);
10977
+ const coll = operand(elem, 2);
10978
+ return joinLatex([
10979
+ serializer.serialize(name),
10980
+ " = ",
10981
+ serializer.serialize(coll)
10982
+ ]);
10983
+ }).join(", ");
10828
10984
  return joinLatex([
10829
- "\\operatorname{Loop}(",
10830
10985
  serializer.serialize(body),
10831
- ", ",
10832
- serializer.serialize(indexing),
10833
- ")"
10986
+ " \\operatorname{for} ",
10987
+ bindings
10834
10988
  ]);
10835
10989
  }
10836
10990
  },
@@ -10863,6 +11017,18 @@ var ComputeEngine = (() => {
10863
11017
  precedence: 245,
10864
11018
  parse: (parser, until) => parseForExpression(parser, until)
10865
11019
  },
11020
+ // \operatorname{for} as postfix infix (list comprehension):
11021
+ // `body \operatorname{for} x = L_1, y = L_2`
11022
+ // Precedence 19 — just below comma (20) so the body is allowed to use
11023
+ // any operator (including comma sequencing) up to the keyword, and the
11024
+ // bindings can be comma-separated below us.
11025
+ {
11026
+ symbolTrigger: "for",
11027
+ kind: "infix",
11028
+ associativity: "none",
11029
+ precedence: 19,
11030
+ parse: (parser, lhs, until) => parseForComprehension(parser, lhs, until)
11031
+ },
10866
11032
  // \operatorname{break}
10867
11033
  {
10868
11034
  symbolTrigger: "break",
@@ -11067,7 +11233,10 @@ var ComputeEngine = (() => {
11067
11233
  if (!sym2 || !parser.getSymbolType(sym2).matches("function")) return null;
11068
11234
  parser.addBoundary([")"]);
11069
11235
  const expr2 = parser.parseExpression(until);
11070
- if (!parser.matchBoundary()) return null;
11236
+ if (!parser.matchBoundary()) {
11237
+ parser.removeBoundary();
11238
+ return null;
11239
+ }
11071
11240
  if (!parser.match("<}>")) return null;
11072
11241
  return ["Derivative", lhs, expr2];
11073
11242
  }
@@ -11508,7 +11677,12 @@ var ComputeEngine = (() => {
11508
11677
  if (isEmptySequence(body)) return ["List"];
11509
11678
  const h = operator(body);
11510
11679
  if (h === "Range" || h === "Linspace") return body;
11511
- if (h === "Sequence") return ["List", ...operands(body)];
11680
+ if (h === "Sequence") {
11681
+ const elems = operands(body);
11682
+ const inferred = tryInferRangeFromElements(elems, parser);
11683
+ if (inferred) return inferred;
11684
+ return ["List", ...elems];
11685
+ }
11512
11686
  if (h === "Delimiter") {
11513
11687
  const delim = stringValue(operand(body, 2)) ?? "...";
11514
11688
  if (delim === ";" || delim === ".;.") {
@@ -11521,12 +11695,37 @@ var ComputeEngine = (() => {
11521
11695
  }
11522
11696
  if (delim === "," || delim === ".,.") {
11523
11697
  body = operand(body, 1);
11524
- if (operator(body) === "Sequence") return ["List", ...operands(body)];
11698
+ if (operator(body) === "Sequence") {
11699
+ const elems = operands(body);
11700
+ const inferred = tryInferRangeFromElements(elems, parser);
11701
+ if (inferred) return inferred;
11702
+ return ["List", ...elems];
11703
+ }
11525
11704
  return ["List", body ?? "Nothing"];
11526
11705
  }
11527
11706
  }
11528
11707
  return ["List", body];
11529
11708
  }
11709
+ function tryInferRangeFromElements(elems, parser) {
11710
+ if (elems.length < 4) return null;
11711
+ const penultimate = elems[elems.length - 2];
11712
+ if (symbol(penultimate) !== "ContinuationPlaceholder") return null;
11713
+ const samples = elems.slice(0, -2);
11714
+ const endExpr = elems[elems.length - 1];
11715
+ if (samples.length < 2) return null;
11716
+ const sampleNums = samples.map(machineValue);
11717
+ if (sampleNums.some((n) => n === null)) return null;
11718
+ const nums = sampleNums;
11719
+ const step = nums[nums.length - 1] - nums[nums.length - 2];
11720
+ const tol = parser.options.tolerance;
11721
+ if (Math.abs(step) < tol)
11722
+ return parser.error("degenerate-range-step", parser.index);
11723
+ for (let i = 1; i < nums.length; i++) {
11724
+ if (Math.abs(nums[i] - nums[i - 1] - step) > tol)
11725
+ return parser.error("inconsistent-range-samples", parser.index);
11726
+ }
11727
+ return ["Range", nums[0], endExpr, step];
11728
+ }
11530
11729
  function serializeList(serializer, expr2) {
11531
11730
  if (nops(expr2) > 1 && operands(expr2).every((x) => {
11532
11731
  const op = operator(x);
@@ -11778,6 +11977,35 @@ var ComputeEngine = (() => {
11778
11977
  ["Element", index, ["Range", lower, upper]]
11779
11978
  ];
11780
11979
  }
11980
+ function parseForComprehension(parser, lhs, until) {
11981
+ const bindingTerminator = {
11982
+ minPrec: 21,
11983
+ // Above comma (20) and ; (19), so `x = L_1` is captured whole
11984
+ condition: (p) => {
11985
+ if (until?.condition?.(p)) return true;
11986
+ const saved = p.index;
11987
+ p.skipVisualSpace();
11988
+ const isComma = p.peek === ",";
11989
+ p.index = saved;
11990
+ return isComma;
11991
+ }
11992
+ };
11993
+ const elements = [];
11994
+ do {
11995
+ parser.skipVisualSpace();
11996
+ const binding = parser.parseExpression(bindingTerminator);
11997
+ if (binding === null) break;
11998
+ const op = operator(binding);
11999
+ if (op !== "Equal" && op !== "Assign") return null;
12000
+ const name = operand(binding, 1);
12001
+ const list = operand(binding, 2);
12002
+ if (!name || !list) return null;
12003
+ elements.push(["Element", name, list]);
12004
+ parser.skipVisualSpace();
12005
+ } while (parser.match(","));
12006
+ if (elements.length === 0) return null;
12007
+ return ["Loop", lhs, ...elements];
12008
+ }
11781
12009
  function parseWhereExpression(parser, lhs, until) {
11782
12010
  const bindingTerminator = {
11783
12011
  minPrec: 21,
@@ -13696,6 +13924,8 @@ var ComputeEngine = (() => {
13696
13924
  precedence: DIVISION_PRECEDENCE,
13697
13925
  parse: "Mod"
13698
13926
  },
13927
+ // Function-style alias: `\operatorname{mod}(a, b)`
13928
+ { latexTrigger: "\\operatorname{mod}", parse: "Mod" },
13699
13929
  {
13700
13930
  latexTrigger: "\\pmod",
13701
13931
  kind: "prefix",
@@ -13936,6 +14166,13 @@ var ComputeEngine = (() => {
13936
14166
  const rhs = serializer.wrap(operand(expr2, 2), ADDITION_PRECEDENCE + 3);
13937
14167
  return joinLatex([lhs, "-", rhs]);
13938
14168
  }
14169
+ },
14170
+ // Euclidean distance between two points (tuples of numbers).
14171
+ {
14172
+ name: "Distance",
14173
+ latexTrigger: ["\\operatorname{distance}"],
14174
+ kind: "function",
14175
+ serialize: (serializer, expr2) => "\\operatorname{distance}" + serializer.wrapArguments(expr2)
13939
14176
  }
13940
14177
  ];
13941
14178
  function getIndexAssignment(expr2, upper) {
@@ -15360,7 +15597,9 @@ var ComputeEngine = (() => {
15360
15597
  if (!expr2 || !symbol(expr2)) return null;
15361
15598
  return ["Mean", expr2];
15362
15599
  }
15363
- }
15600
+ },
15601
+ // Function-style alias: `\operatorname{var}(...)`
15602
+ { latexTrigger: "\\operatorname{var}", parse: "Variance" }
15364
15603
  ];
15365
15604
 
15366
15605
  // src/compute-engine/numerics/unit-data.ts
@@ -16461,7 +16700,7 @@ var ComputeEngine = (() => {
16461
16700
  36: "\\qquad"
16462
16701
  }[v] ?? "";
16463
16702
  }
16464
- }
16703
+ },
16465
16704
  // if (
16466
16705
  // [
16467
16706
  // '\\!',
@@ -16485,6 +16724,121 @@ var ComputeEngine = (() => {
16485
16724
  // name: '',
16486
16725
  // trigger: '\\check',
16487
16726
  // },
16727
+ // ---------------------------------------------------------------------------
16728
+ // Function-style aliases for collection / random operators that some
16729
+ // notations write in lowercase (e.g. `\operatorname{shuffle}(L)`).
16730
+ // The capitalized library entries already exist; these are pure parse
16731
+ // aliases so the lowercase names don't land in `unsupported-operator`.
16732
+ // ---------------------------------------------------------------------------
16733
+ { latexTrigger: "\\operatorname{random}", parse: "Random" },
16734
+ { latexTrigger: "\\operatorname{shuffle}", parse: "Shuffle" },
16735
+ { latexTrigger: "\\operatorname{repeat}", parse: "Repeat" },
16736
+ { latexTrigger: "\\operatorname{join}", parse: "Join" },
16737
+ // ---------------------------------------------------------------------------
16738
+ // Geometric primitive heads. Registered as known typed heads so consumers
16739
+ // can branch on the operator name; CE itself doesn't render them. The
16740
+ // library entries (with no evaluator) live in `library/core.ts`.
16741
+ // ---------------------------------------------------------------------------
16742
+ {
16743
+ name: "Triangle",
16744
+ latexTrigger: ["\\operatorname{triangle}"],
16745
+ kind: "function",
16746
+ serialize: (serializer, expr2) => "\\operatorname{triangle}" + serializer.wrapArguments(expr2)
16747
+ },
16748
+ // Desmos's geometric `vector(p1, p2)` — a directed segment between two
16749
+ // points. Routed to a dedicated head (not the existing column-vector
16750
+ // `Vector`, which has a narrower `(number+) -> vector` signature).
16751
+ {
16752
+ name: "GeometricVector",
16753
+ latexTrigger: ["\\operatorname{vector}"],
16754
+ kind: "function",
16755
+ serialize: (serializer, expr2) => "\\operatorname{vector}" + serializer.wrapArguments(expr2)
16756
+ },
16757
+ {
16758
+ name: "Sphere",
16759
+ latexTrigger: ["\\operatorname{sphere}"],
16760
+ kind: "function",
16761
+ serialize: (serializer, expr2) => "\\operatorname{sphere}" + serializer.wrapArguments(expr2)
16762
+ },
16763
+ {
16764
+ name: "Segment",
16765
+ latexTrigger: ["\\operatorname{segment}"],
16766
+ kind: "function",
16767
+ serialize: (serializer, expr2) => "\\operatorname{segment}" + serializer.wrapArguments(expr2)
16768
+ }
16769
+ ];
16770
+
16771
+ // src/compute-engine/latex-syntax/dictionary/definitions-colors.ts
16772
+ var DEFINITIONS_COLORS = [
16773
+ // Color constructors (one per colorspace, preserves space on evaluation)
16774
+ {
16775
+ name: "Rgb",
16776
+ latexTrigger: ["\\operatorname{rgb}"],
16777
+ kind: "function",
16778
+ serialize: (serializer, expr2) => "\\operatorname{rgb}" + serializer.wrapArguments(expr2)
16779
+ },
16780
+ {
16781
+ name: "Hsv",
16782
+ latexTrigger: ["\\operatorname{hsv}"],
16783
+ kind: "function",
16784
+ serialize: (serializer, expr2) => "\\operatorname{hsv}" + serializer.wrapArguments(expr2)
16785
+ },
16786
+ {
16787
+ name: "Hsl",
16788
+ latexTrigger: ["\\operatorname{hsl}"],
16789
+ kind: "function",
16790
+ serialize: (serializer, expr2) => "\\operatorname{hsl}" + serializer.wrapArguments(expr2)
16791
+ },
16792
+ {
16793
+ name: "Oklab",
16794
+ latexTrigger: ["\\operatorname{oklab}"],
16795
+ kind: "function",
16796
+ serialize: (serializer, expr2) => "\\operatorname{oklab}" + serializer.wrapArguments(expr2)
16797
+ },
16798
+ {
16799
+ name: "Oklch",
16800
+ latexTrigger: ["\\operatorname{oklch}"],
16801
+ kind: "function",
16802
+ serialize: (serializer, expr2) => "\\operatorname{oklch}" + serializer.wrapArguments(expr2)
16803
+ },
16804
+ // Conversion functions (color → color in the named space)
16805
+ {
16806
+ name: "AsRgb",
16807
+ latexTrigger: ["\\operatorname{asRgb}"],
16808
+ kind: "function",
16809
+ serialize: (serializer, expr2) => "\\operatorname{asRgb}" + serializer.wrapArguments(expr2)
16810
+ },
16811
+ {
16812
+ name: "AsHsv",
16813
+ latexTrigger: ["\\operatorname{asHsv}"],
16814
+ kind: "function",
16815
+ serialize: (serializer, expr2) => "\\operatorname{asHsv}" + serializer.wrapArguments(expr2)
16816
+ },
16817
+ {
16818
+ name: "AsHsl",
16819
+ latexTrigger: ["\\operatorname{asHsl}"],
16820
+ kind: "function",
16821
+ serialize: (serializer, expr2) => "\\operatorname{asHsl}" + serializer.wrapArguments(expr2)
16822
+ },
16823
+ {
16824
+ name: "AsOklab",
16825
+ latexTrigger: ["\\operatorname{asOklab}"],
16826
+ kind: "function",
16827
+ serialize: (serializer, expr2) => "\\operatorname{asOklab}" + serializer.wrapArguments(expr2)
16828
+ },
16829
+ {
16830
+ name: "AsOklch",
16831
+ latexTrigger: ["\\operatorname{asOklch}"],
16832
+ kind: "function",
16833
+ serialize: (serializer, expr2) => "\\operatorname{asOklch}" + serializer.wrapArguments(expr2)
16834
+ },
16835
+ // Perceptual difference (returns a scalar in [0, ~1])
16836
+ {
16837
+ name: "ColorDelta",
16838
+ latexTrigger: ["\\operatorname{colorDelta}"],
16839
+ kind: "function",
16840
+ serialize: (serializer, expr2) => "\\operatorname{colorDelta}" + serializer.wrapArguments(expr2)
16841
+ }
16488
16842
  ];
16489
16843
 
16490
16844
  // src/compute-engine/latex-syntax/dictionary/default-dictionary.ts
@@ -16515,7 +16869,8 @@ var ComputeEngine = (() => {
16515
16869
  ...DEFINITIONS_STATISTICS,
16516
16870
  ...DEFINITIONS_UNITS,
16517
16871
  ...DEFINITIONS_OTHERS,
16518
- ...DEFINITIONS_PHYSICS
16872
+ ...DEFINITIONS_PHYSICS,
16873
+ ...DEFINITIONS_COLORS
16519
16874
  ];
16520
16875
 
16521
16876
  // src/math-json/symbols.ts
@@ -16680,6 +17035,17 @@ var ComputeEngine = (() => {
16680
17035
  } else if (Array.isArray(openTrigger) && openTrigger.length > 0) {
16681
17036
  openTokens.push(openTrigger[0]);
16682
17037
  }
17038
+ const closeTrigger = indexedEntry.closeTrigger;
17039
+ const closeTokens = /* @__PURE__ */ new Set();
17040
+ if (typeof closeTrigger === "string") {
17041
+ const variants = DELIMITER_SHORTHAND[closeTrigger];
17042
+ if (variants) for (const v of variants) closeTokens.add(v);
17043
+ else closeTokens.add(closeTrigger);
17044
+ if (closeTrigger === "||") closeTokens.add("|");
17045
+ } else if (Array.isArray(closeTrigger) && closeTrigger.length > 0) {
17046
+ closeTokens.add(closeTrigger[0]);
17047
+ }
17048
+ indexedEntry.closeTokens = closeTokens;
16683
17049
  for (const token of openTokens) {
16684
17050
  const existing = result.matchfixByOpen.get(token);
16685
17051
  if (existing) {
@@ -16871,11 +17237,7 @@ var ComputeEngine = (() => {
16871
17237
  if (style === "scaled")
16872
17238
  return joinLatex([`\\left${openDelim}`, inner, `\\right${closeDelim}`]);
16873
17239
  if (style === "big")
16874
- return joinLatex([
16875
- `\\Bigl${openDelim}`,
16876
- inner,
16877
- `\\Bigr${closeDelim}`
16878
- ]);
17240
+ return joinLatex([`\\Bigl${openDelim}`, inner, `\\Bigr${closeDelim}`]);
16879
17241
  return joinLatex([openDelim, inner, closeDelim]);
16880
17242
  };
16881
17243
  }
@@ -18226,7 +18588,7 @@ var ComputeEngine = (() => {
18226
18588
  }
18227
18589
  if (this.match("\\hskip") || this.match("\\kern")) {
18228
18590
  this.skipSpace();
18229
- this.match("-") || this.match("+");
18591
+ if (!this.match("-")) this.match("+");
18230
18592
  while (/^[\d.]$/.test(this.peek)) this.nextToken();
18231
18593
  for (const unit of [
18232
18594
  "pt",
@@ -18740,6 +19102,19 @@ var ComputeEngine = (() => {
18740
19102
  }
18741
19103
  for (const def of defs) {
18742
19104
  this.index = start;
19105
+ if (def.closeTokens.size > 0) {
19106
+ let found = false;
19107
+ const tokens = this._tokens;
19108
+ for (let i = start; i < tokens.length; i++) {
19109
+ if (def.closeTokens.has(tokens[i])) {
19110
+ found = true;
19111
+ break;
19112
+ }
19113
+ }
19114
+ if (!found) continue;
19115
+ }
19116
+ if (typeof def.openTrigger === "string" && def.openTrigger === "." && !OPEN_DELIMITER_PREFIX[currentToken])
19117
+ continue;
18743
19118
  const matched = this.matchDelimiter(def.openTrigger, def.closeTrigger);
18744
19119
  if (!matched) continue;
18745
19120
  const bodyStart = this.index;
@@ -19402,6 +19777,19 @@ var ComputeEngine = (() => {
19402
19777
  } while (postfix !== null);
19403
19778
  }
19404
19779
  if (result !== null) result = this.parseSupsub(result);
19780
+ if (result !== null) {
19781
+ let postfix = null;
19782
+ let index = this.index;
19783
+ do {
19784
+ postfix = this.parsePostfixOperator(result, until);
19785
+ result = postfix ?? result;
19786
+ if (this.index === index && postfix !== null) {
19787
+ console.assert(this.index !== index, "No token consumed");
19788
+ break;
19789
+ }
19790
+ index = this.index;
19791
+ } while (postfix !== null);
19792
+ }
19405
19793
  if (result === null) {
19406
19794
  result = this.options.parseUnexpectedToken?.(null, this) ?? null;
19407
19795
  if (result === null && this.peek.startsWith("\\")) {
@@ -19910,6 +20298,28 @@ var ComputeEngine = (() => {
19910
20298
  }
19911
20299
 
19912
20300
  // src/compute-engine/latex-syntax/serializer.ts
20301
+ var DOT_NOTATION_MAP = {
20302
+ First: ".x",
20303
+ Second: ".y",
20304
+ Third: ".z",
20305
+ Real: ".\\operatorname{real}",
20306
+ Imaginary: ".\\operatorname{imag}",
20307
+ Length: ".\\operatorname{count}",
20308
+ Sum: ".\\operatorname{total}",
20309
+ Max: ".\\max",
20310
+ Min: ".\\min"
20311
+ };
20312
+ function trySerializeDotNotation(serializer, expr2) {
20313
+ if (!serializer.options.dotNotation) return null;
20314
+ const ops = operands(expr2);
20315
+ if (!ops || ops.length !== 1) return null;
20316
+ const head = operator(expr2);
20317
+ if (!head) return null;
20318
+ const suffix = DOT_NOTATION_MAP[head];
20319
+ if (suffix === void 0) return null;
20320
+ const lhs = serializer.wrap(ops[0], 810);
20321
+ return `${lhs}${suffix}`;
20322
+ }
19913
20323
  var ACCENT_MODIFIERS = {
19914
20324
  deg: (s) => `${s}\\degree`,
19915
20325
  prime: (s) => `${s}^{\\prime}`,
@@ -19953,6 +20363,7 @@ var ComputeEngine = (() => {
19953
20363
  constructor(dictionary, options) {
19954
20364
  this.dictionary = dictionary;
19955
20365
  this.options = {
20366
+ dotNotation: false,
19956
20367
  dmsFormat: false,
19957
20368
  angleNormalization: "none",
19958
20369
  ...options
@@ -20051,6 +20462,8 @@ var ComputeEngine = (() => {
20051
20462
  return def?.serialize?.(this, expr2) ?? serializeSymbol2(symbol(expr2)) ?? "";
20052
20463
  }
20053
20464
  serializeFunction(expr2, def) {
20465
+ const dotResult = trySerializeDotNotation(this, expr2);
20466
+ if (dotResult !== null) return dotResult;
20054
20467
  if (def?.serialize) return def.serialize(this, expr2);
20055
20468
  const h = operator(expr2);
20056
20469
  return serializeSymbol2(h, "auto") + this.wrapArguments(expr2);
@@ -20293,6 +20706,8 @@ var ComputeEngine = (() => {
20293
20706
  preserveLatex: opts.preserveLatex ?? false,
20294
20707
  quantifierScope: opts.quantifierScope ?? "tight",
20295
20708
  timeDerivativeVariable: opts.timeDerivativeVariable ?? "t",
20709
+ // Standalone mode has no engine; use the same default as ComputeEngine
20710
+ tolerance: 1e-7,
20296
20711
  // Callbacks -- standalone mode has no engine, so these are stubs
20297
20712
  getSymbolType: (_id) => BoxedType.unknown,
20298
20713
  hasSubscriptEvaluate: (_id) => false,
@@ -20325,6 +20740,7 @@ var ComputeEngine = (() => {
20325
20740
  invisiblePlus: "",
20326
20741
  multiply: "\\times",
20327
20742
  missingSymbol: "\\blacksquare",
20743
+ dotNotation: false,
20328
20744
  dmsFormat: false,
20329
20745
  angleNormalization: "none",
20330
20746
  // Style callbacks -- use same defaults as the engine
@@ -27295,7 +27711,11 @@ ${lines.join("\n")}`;
27295
27711
  //
27296
27712
  Range: {
27297
27713
  complexity: 8200,
27298
- signature: "(number, number?, step: number?) -> indexed_collection<integer>",
27714
+ signature: "(number, number?, step: number?) -> indexed_collection<number>",
27715
+ type: (ops) => {
27716
+ const allInt = ops.every((op) => op.isInteger);
27717
+ return allInt ? parseType("indexed_collection<integer>") : parseType("indexed_collection<number>");
27718
+ },
27299
27719
  canonical: (ops, { engine: ce }) => {
27300
27720
  if (ops.length === 0) return null;
27301
27721
  if (ops.length === 1) return ce._fn("Range", [ce.One, ops[0].canonical]);
@@ -27319,19 +27739,26 @@ ${lines.join("\n")}`;
27319
27739
  const [lower, upper, step] = range(expr2);
27320
27740
  if (step === 0) return 0;
27321
27741
  if (!isFinite(lower) || !isFinite(upper)) return Infinity;
27322
- return 1 + Math.max(0, Math.floor((upper - lower) / step));
27742
+ return Math.max(0, Math.floor((upper - lower) / step) + 1);
27323
27743
  },
27324
27744
  contains: (expr2, target) => {
27325
- if (!target.type.matches("integer")) return false;
27326
27745
  const t = target.re;
27746
+ if (!isFinite(t)) return false;
27327
27747
  const [lower, upper, step] = range(expr2);
27328
27748
  if (step === 0) return false;
27329
- if (step > 0) return t >= lower && t <= upper;
27330
- return t <= lower && t >= upper;
27749
+ if (step > 0) {
27750
+ if (t < lower || t > upper) return false;
27751
+ } else {
27752
+ if (t > lower || t < upper) return false;
27753
+ }
27754
+ const k = (t - lower) / step;
27755
+ const tol = expr2.engine.tolerance;
27756
+ const kRounded = Math.round(k);
27757
+ return kRounded >= 0 && Math.abs(k - kRounded) < tol;
27331
27758
  },
27332
27759
  iterator: (expr2) => {
27333
27760
  const [lower, upper, step] = range(expr2);
27334
- const maxCount = step === 0 ? 0 : Math.floor((upper - lower) / step) + 1;
27761
+ const maxCount = step === 0 ? 0 : Math.max(0, Math.floor((upper - lower) / step) + 1);
27335
27762
  let index = 1;
27336
27763
  return {
27337
27764
  next: () => {
@@ -27349,7 +27776,9 @@ ${lines.join("\n")}`;
27349
27776
  at: (expr2, index) => {
27350
27777
  if (typeof index !== "number") return void 0;
27351
27778
  const [lower, upper, step] = range(expr2);
27352
- if (index < 1 || index > 1 + (upper - lower) / step) return void 0;
27779
+ if (step === 0) return void 0;
27780
+ const maxCount = Math.max(0, Math.floor((upper - lower) / step) + 1);
27781
+ if (index < 1 || index > maxCount) return void 0;
27353
27782
  return expr2.engine.number(lower + step * (index - 1));
27354
27783
  },
27355
27784
  indexWhere: void 0,
@@ -27374,7 +27803,13 @@ ${lines.join("\n")}`;
27374
27803
  if (step > 0) return lower <= upper ? "positive" : "negative";
27375
27804
  return lower >= upper ? "positive" : "negative";
27376
27805
  },
27377
- elttype: (_expr) => "finite_integer"
27806
+ elttype: (expr2) => {
27807
+ if (!isFunction2(expr2)) return "finite_integer";
27808
+ for (let i = 1; i <= expr2.nops; i++) {
27809
+ if (!expr2[`op${i}`].isInteger) return "finite_real";
27810
+ }
27811
+ return "finite_integer";
27812
+ }
27378
27813
  }
27379
27814
  },
27380
27815
  Interval: {
@@ -27972,15 +28407,45 @@ ${lines.join("\n")}`;
27972
28407
  },
27973
28408
  First: {
27974
28409
  complexity: 8200,
27975
- signature: "(collection) -> any",
28410
+ signature: "(any) -> any",
27976
28411
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
27977
- evaluate: ([xs], { engine: ce }) => xs.at(1) ?? ce.Nothing
28412
+ evaluate: ([xs], { engine: ce }) => {
28413
+ if (!xs.isCollection)
28414
+ return ce.error([
28415
+ "incompatible-type",
28416
+ `'collection'`,
28417
+ xs.type.toString()
28418
+ ]);
28419
+ return xs.at(1) ?? ce.Nothing;
28420
+ }
27978
28421
  },
27979
28422
  Second: {
27980
28423
  complexity: 8200,
27981
- signature: "(collection) -> any",
28424
+ signature: "(any) -> any",
27982
28425
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
27983
- evaluate: ([xs], { engine: ce }) => xs.at(2) ?? ce.Nothing
28426
+ evaluate: ([xs], { engine: ce }) => {
28427
+ if (!xs.isCollection)
28428
+ return ce.error([
28429
+ "incompatible-type",
28430
+ `'collection'`,
28431
+ xs.type.toString()
28432
+ ]);
28433
+ return xs.at(2) ?? ce.Nothing;
28434
+ }
28435
+ },
28436
+ Third: {
28437
+ complexity: 8200,
28438
+ signature: "(any) -> any",
28439
+ type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28440
+ evaluate: ([xs], { engine: ce }) => {
28441
+ if (!xs.isCollection)
28442
+ return ce.error([
28443
+ "incompatible-type",
28444
+ `'collection'`,
28445
+ xs.type.toString()
28446
+ ]);
28447
+ return xs.at(3) ?? ce.Nothing;
28448
+ }
27984
28449
  },
27985
28450
  Last: {
27986
28451
  complexity: 8200,
@@ -28959,17 +29424,14 @@ ${lines.join("\n")}`;
28959
29424
  if (!isFunction2(expr2)) return [1, 0, 0];
28960
29425
  if (expr2.nops === 0) return [1, 0, 0];
28961
29426
  let op1 = expr2.op1.re;
28962
- if (!isFinite(op1)) op1 = 1;
28963
- else op1 = Math.round(op1);
29427
+ if (!isFinite(op1) && !op1) op1 = 1;
28964
29428
  if (expr2.nops === 1) return [1, op1, 1];
28965
29429
  let op2 = expr2.op2.re;
28966
29430
  if (!isFinite(op2) && !op2) op2 = 1;
28967
- else if (isFinite(op2)) op2 = Math.round(op2);
28968
- if (expr2.nops === 2) return [op1, op2, op2 > op1 ? 1 : -1];
29431
+ if (expr2.nops === 2) return [op1, op2, op2 >= op1 ? 1 : -1];
28969
29432
  let op3 = expr2.op3.re;
28970
- if (!isFinite(op3)) op3 = 1;
28971
- else op3 = Math.abs(Math.round(op3));
28972
- return [op1, op2, op1 < op2 ? op3 : -op3];
29433
+ if (!isFinite(op3) && !op3) op3 = 1;
29434
+ return [op1, op2, op3];
28973
29435
  }
28974
29436
  function rangeLast(r) {
28975
29437
  const [lower, upper, step] = r;
@@ -32302,6 +32764,29 @@ ${lines.join("\n")}`;
32302
32764
  signature: "(value*) -> number | list",
32303
32765
  evaluate: (xs, { engine }) => evaluateMinMax(engine, xs, "Infimum")
32304
32766
  },
32767
+ Distance: {
32768
+ description: "Euclidean distance between two points (tuples of numbers).",
32769
+ complexity: 6e3,
32770
+ signature: "(tuple, tuple) -> number",
32771
+ evaluate: ([a, b], { engine: ce }) => {
32772
+ if (!isFunction2(a) || !isFunction2(b))
32773
+ return ce.error("incompatible-type");
32774
+ if (a.operator !== "Tuple" || b.operator !== "Tuple")
32775
+ return ce.error("incompatible-type");
32776
+ if (a.ops.length !== b.ops.length || a.ops.length === 0)
32777
+ return ce.error("incompatible-type");
32778
+ let sumSq = 0;
32779
+ for (let i = 0; i < a.ops.length; i++) {
32780
+ const ai = a.ops[i].re;
32781
+ const bi = b.ops[i].re;
32782
+ if (!Number.isFinite(ai) || !Number.isFinite(bi))
32783
+ return ce.error("expected-value");
32784
+ const d = ai - bi;
32785
+ sumSq += d * d;
32786
+ }
32787
+ return ce.number(Math.sqrt(sumSq));
32788
+ }
32789
+ },
32305
32790
  Product: {
32306
32791
  description: "`Product(f, a, b)` computes the product of `f` from `a` to `b`",
32307
32792
  wikidata: "Q901718",
@@ -32834,16 +33319,11 @@ ${lines.join("\n")}`;
32834
33319
  );
32835
33320
  let condFn;
32836
33321
  if (typeof condition === "string") {
32837
- const latex = asLatexString(condition);
32838
- if (latex) {
32839
- const condPattern = ce.parse(latex, {
32840
- form: options?.canonical ? "canonical" : "raw"
32841
- }) ?? ce.expr("Nothing");
32842
- condFn = (x, _ce) => {
32843
- const evaluated = condPattern.subs(x).evaluate();
32844
- return isSymbol2(evaluated, "True");
32845
- };
32846
- }
33322
+ const condPattern = ce.parse(condition) ?? ce.expr("Nothing");
33323
+ condFn = (x, _ce) => {
33324
+ const evaluated = condPattern.subs(x).evaluate();
33325
+ return isSymbol2(evaluated, "True");
33326
+ };
32847
33327
  } else {
32848
33328
  if (condition !== void 0 && typeof condition !== "function")
32849
33329
  throw new Error(
@@ -32945,6 +33425,15 @@ ${e.message}
32945
33425
  function applyRule(rule, expr2, substitution, options) {
32946
33426
  if (!rule) return null;
32947
33427
  let canonical2 = options?.canonical ?? (expr2.isCanonical || expr2.isStructural);
33428
+ let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
33429
+ const because = id ?? "";
33430
+ const ce = expr2.engine;
33431
+ if (canonical2 && match2) {
33432
+ const awc = getWildcards(match2);
33433
+ const canonicalMatch = match2.canonical;
33434
+ const bwc = getWildcards(canonicalMatch);
33435
+ if (!awc.every((x) => bwc.includes(x))) return null;
33436
+ }
32948
33437
  let operandsMatched = false;
32949
33438
  if (isFunction2(expr2) && options?.recursive) {
32950
33439
  const newOps = expr2.ops.map((op) => {
@@ -32956,20 +33445,11 @@ ${e.message}
32956
33445
  if (operandsMatched) {
32957
33446
  if (!canonical2 && options?.canonical === void 0 && newOps.every((x) => x.isCanonical))
32958
33447
  canonical2 = true;
32959
- expr2 = expr2.engine.function(expr2.operator, newOps, {
33448
+ expr2 = ce.function(expr2.operator, newOps, {
32960
33449
  form: canonical2 ? "canonical" : "raw"
32961
33450
  });
32962
33451
  }
32963
33452
  }
32964
- let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
32965
- const because = id ?? "";
32966
- if (canonical2 && match2) {
32967
- const awc = getWildcards(match2);
32968
- const canonicalMatch = match2.canonical;
32969
- const bwc = getWildcards(canonicalMatch);
32970
- if (!awc.every((x) => bwc.includes(x)))
32971
- return operandsMatched ? { value: expr2, because } : null;
32972
- }
32973
33453
  const useVariations = rule.useVariations ?? options?.useVariations ?? false;
32974
33454
  const matchPermutations = options?.matchPermutations ?? true;
32975
33455
  onBeforeMatch?.(rule, expr2);
@@ -32988,7 +33468,7 @@ ${e.message}
32988
33468
  ...sub2
32989
33469
  };
32990
33470
  try {
32991
- if (!condition(conditionSub, expr2.engine))
33471
+ if (!condition(conditionSub, ce))
32992
33472
  return operandsMatched ? { value: expr2, because } : null;
32993
33473
  } catch (e) {
32994
33474
  console.error(
@@ -33003,7 +33483,8 @@ ${e.message}
33003
33483
  if (!canonical2 && options?.canonical === void 0 && replace2 instanceof _BoxedExpression && replace2.isCanonical)
33004
33484
  canonical2 = true;
33005
33485
  const result = typeof replace2 === "function" ? replace2(expr2, sub2) : replace2.subs(sub2, { canonical: canonical2 });
33006
- if (!result) return null;
33486
+ if (!result)
33487
+ return operandsMatched ? { value: canonical2 ? expr2.canonical : expr2, because } : null;
33007
33488
  onMatch?.(rule, expr2, result);
33008
33489
  if (isRuleStep(result))
33009
33490
  return canonical2 ? { ...result, value: result.value.canonical } : result;
@@ -38577,6 +39058,40 @@ ${e.message}
38577
39058
  else h = ((r - g) / d + 4) / 6;
38578
39059
  return { h: h * 360, s, l };
38579
39060
  }
39061
+ function hsvToRgb(h, s, v) {
39062
+ h = (h % 360 + 360) % 360;
39063
+ s = Math.max(0, Math.min(1, s));
39064
+ v = Math.max(0, Math.min(1, v));
39065
+ const c = v * s;
39066
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
39067
+ const m = v - c;
39068
+ let r = 0, g = 0, b = 0;
39069
+ if (h < 60) [r, g, b] = [c, x, 0];
39070
+ else if (h < 120) [r, g, b] = [x, c, 0];
39071
+ else if (h < 180) [r, g, b] = [0, c, x];
39072
+ else if (h < 240) [r, g, b] = [0, x, c];
39073
+ else if (h < 300) [r, g, b] = [x, 0, c];
39074
+ else [r, g, b] = [c, 0, x];
39075
+ return { r: (r + m) * 255, g: (g + m) * 255, b: (b + m) * 255 };
39076
+ }
39077
+ function rgbToHsv(r, g, b) {
39078
+ r /= 255;
39079
+ g /= 255;
39080
+ b /= 255;
39081
+ const max2 = Math.max(r, g, b);
39082
+ const min2 = Math.min(r, g, b);
39083
+ const d = max2 - min2;
39084
+ let h = 0;
39085
+ if (d > 0) {
39086
+ if (max2 === r) h = (g - b) / d % 6;
39087
+ else if (max2 === g) h = (b - r) / d + 2;
39088
+ else h = (r - g) / d + 4;
39089
+ h *= 60;
39090
+ if (h < 0) h += 360;
39091
+ }
39092
+ const s = max2 === 0 ? 0 : d / max2;
39093
+ return { h, s, v: max2 };
39094
+ }
38580
39095
  function parseHexColor(s) {
38581
39096
  const hex = s.startsWith("#") ? s.substring(1) : s;
38582
39097
  let r, g, b;
@@ -38601,6 +39116,12 @@ ${e.message}
38601
39116
  if (alpha !== void 0) result.alpha = alpha;
38602
39117
  return result;
38603
39118
  }
39119
+ function asOklch(color) {
39120
+ if (typeof color === "string") return rgbToOklch(parseHexColor(color));
39121
+ if ("C" in color) return color;
39122
+ if ("a" in color && "b" in color) return oklabToOklch(color);
39123
+ return rgbToOklch(color);
39124
+ }
38604
39125
  function asRgb(color) {
38605
39126
  if (typeof color === "number") {
38606
39127
  return {
@@ -39032,6 +39553,13 @@ ${e.message}
39032
39553
  };
39033
39554
  function parseColor(s, darkMode) {
39034
39555
  const str = s.trim().toLowerCase();
39556
+ const opacityMatch = str.match(/^(.+?)\s*\/\s*(\d+(?:\.\d+)?)%?\s*$/);
39557
+ if (opacityMatch) {
39558
+ const base = parseColor(opacityMatch[1].trim(), darkMode);
39559
+ const opacity = Math.max(0, Math.min(100, parseFloat(opacityMatch[2])));
39560
+ const alpha = Math.round(opacity / 100 * 255);
39561
+ return base & 4294967040 | alpha;
39562
+ }
39035
39563
  if (str.startsWith("#")) {
39036
39564
  const hex = str.substring(1);
39037
39565
  let r, g, b, a = 255;
@@ -39164,14 +39692,6 @@ ${e.message}
39164
39692
  console.warn(`parseColor: unrecognized color "${s}"`);
39165
39693
  return 0;
39166
39694
  }
39167
- function parseColorToRgb01(s, darkMode) {
39168
- const color = parseColor(s, darkMode);
39169
- return [
39170
- (color >>> 24 & 255) / 255,
39171
- (color >>> 16 & 255) / 255,
39172
- (color >>> 8 & 255) / 255
39173
- ];
39174
- }
39175
39695
  function apca(bgColor, fgColor) {
39176
39696
  const bgRgb = asRgb(bgColor);
39177
39697
  const fgRgb = asRgb(fgColor);
@@ -39230,6 +39750,12 @@ ${e.message}
39230
39750
  const contrast2 = Math.abs(apca(fg2, bg));
39231
39751
  return contrast1 >= contrast2 ? asColorNumber(fg1) : asColorNumber(fg2);
39232
39752
  }
39753
+ function oklabDeltaE(a, b) {
39754
+ const dL = a.L - b.L;
39755
+ const da = a.a - b.a;
39756
+ const db = a.b - b.b;
39757
+ return Math.sqrt(dL * dL + da * da + db * db);
39758
+ }
39233
39759
  function lerpOklch(c1, c2, f) {
39234
39760
  const L = c1.L + (c2.L - c1.L) * f;
39235
39761
  const C = c1.C + (c2.C - c1.C) * f;
@@ -41991,14 +42517,30 @@ ${e.message}
41991
42517
  };
41992
42518
 
41993
42519
  // src/compute-engine/library/colors.ts
41994
- function colorNumberToTuple(ce, color) {
41995
- const r = (color >>> 24 & 255) / 255;
41996
- const g = (color >>> 16 & 255) / 255;
41997
- const b = (color >>> 8 & 255) / 255;
41998
- const a = (color & 255) / 255;
41999
- if (Math.abs(a - 1) < 1e-4)
42000
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42001
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b), ce.number(a));
42520
+ function normalizeAlpha(a) {
42521
+ if (a === void 0) return void 0;
42522
+ if (!Number.isFinite(a)) return void 0;
42523
+ if (Math.abs(a - 1) < 1e-9) return void 0;
42524
+ return a;
42525
+ }
42526
+ function normalizeColorHead(ce, expr2) {
42527
+ if (!isFunction2(expr2) || !expr2.ops || expr2.ops.length < 4) return expr2;
42528
+ const alphaExpr = expr2.ops[3];
42529
+ if (!isNumber(alphaExpr)) return expr2;
42530
+ if (normalizeAlpha(alphaExpr.re) === void 0) {
42531
+ return ce.function(expr2.operator, [expr2.ops[0], expr2.ops[1], expr2.ops[2]]);
42532
+ }
42533
+ return expr2;
42534
+ }
42535
+ function colorNumberToOklch(ce, color) {
42536
+ const r = color >>> 24 & 255;
42537
+ const g = color >>> 16 & 255;
42538
+ const b = color >>> 8 & 255;
42539
+ const a = normalizeAlpha((color & 255) / 255);
42540
+ const c = rgbToOklch({ r, g, b });
42541
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42542
+ if (a !== void 0) args.push(ce.number(a));
42543
+ return ce.function("Oklch", args);
42002
42544
  }
42003
42545
  var ALL_PALETTES = {
42004
42546
  ...SEQUENTIAL_PALETTES,
@@ -42009,45 +42551,139 @@ ${e.message}
42009
42551
  t = Math.max(0, Math.min(1, t));
42010
42552
  const n = palette.length;
42011
42553
  if (n === 0) return ce.error("expected-value");
42012
- if (n === 1) return colorNumberToTuple(ce, parseColor(palette[0]));
42554
+ if (n === 1) return colorNumberToOklch(ce, parseColor(palette[0]));
42013
42555
  const pos = t * (n - 1);
42014
42556
  const i = Math.floor(pos);
42015
42557
  const frac = pos - i;
42016
- if (i >= n - 1) return colorNumberToTuple(ce, parseColor(palette[n - 1]));
42017
- if (frac < 1e-9) return colorNumberToTuple(ce, parseColor(palette[i]));
42018
- const rgb = interpolateOklch(palette[i], palette[i + 1], frac);
42019
- const r = rgb.r / 255;
42020
- const g = rgb.g / 255;
42021
- const b = rgb.b / 255;
42022
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42558
+ if (i >= n - 1) return colorNumberToOklch(ce, parseColor(palette[n - 1]));
42559
+ if (frac < 1e-9) return colorNumberToOklch(ce, parseColor(palette[i]));
42560
+ return oklchToExpr(
42561
+ ce,
42562
+ asOklch(interpolateOklch(palette[i], palette[i + 1], frac))
42563
+ );
42564
+ }
42565
+ var COLOR_OPERATORS = /* @__PURE__ */ new Set(["Rgb", "Hsv", "Hsl", "Oklab", "Oklch"]);
42566
+ function readColorExpr(arg) {
42567
+ if (!isFunction2(arg)) return null;
42568
+ if (!COLOR_OPERATORS.has(arg.operator)) return null;
42569
+ if (!arg.ops || arg.ops.length < 3) return null;
42570
+ const c0 = arg.ops[0].re;
42571
+ const c1 = arg.ops[1].re;
42572
+ const c2 = arg.ops[2].re;
42573
+ if (!Number.isFinite(c0) || !Number.isFinite(c1) || !Number.isFinite(c2))
42574
+ return null;
42575
+ const alpha = arg.ops.length >= 4 ? normalizeAlpha(arg.ops[3].re) : void 0;
42576
+ return { space: arg.operator, c0, c1, c2, alpha };
42577
+ }
42578
+ function colorExprToRgb(arg) {
42579
+ const c = readColorExpr(arg);
42580
+ if (!c) return null;
42581
+ const withAlpha = (rgb) => c.alpha !== void 0 ? { ...rgb, alpha: c.alpha } : rgb;
42582
+ switch (c.space) {
42583
+ case "Rgb":
42584
+ return withAlpha({ r: c.c0 * 255, g: c.c1 * 255, b: c.c2 * 255 });
42585
+ case "Hsv":
42586
+ return withAlpha(hsvToRgb(c.c0, c.c1, c.c2));
42587
+ case "Hsl":
42588
+ return withAlpha(hslToRgb(c.c0, c.c1, c.c2));
42589
+ case "Oklab":
42590
+ return withAlpha(oklabToRgb({ L: c.c0, a: c.c1, b: c.c2 }));
42591
+ case "Oklch":
42592
+ return withAlpha(oklchToRgb({ L: c.c0, C: c.c1, H: c.c2 }));
42593
+ }
42594
+ return null;
42595
+ }
42596
+ function colorExprToOklch(arg) {
42597
+ const c = readColorExpr(arg);
42598
+ if (!c) return null;
42599
+ switch (c.space) {
42600
+ case "Oklch":
42601
+ return asOklch({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42602
+ case "Oklab":
42603
+ return asOklch({ L: c.c0, a: c.c1, b: c.c2, alpha: c.alpha });
42604
+ case "Rgb":
42605
+ return asOklch({
42606
+ r: c.c0 * 255,
42607
+ g: c.c1 * 255,
42608
+ b: c.c2 * 255,
42609
+ alpha: c.alpha
42610
+ });
42611
+ case "Hsv": {
42612
+ const rgb = hsvToRgb(c.c0, c.c1, c.c2);
42613
+ return asOklch({ ...rgb, alpha: c.alpha });
42614
+ }
42615
+ case "Hsl": {
42616
+ const rgb = hslToRgb(c.c0, c.c1, c.c2);
42617
+ return asOklch({ r: rgb.r, g: rgb.g, b: rgb.b, alpha: c.alpha });
42618
+ }
42619
+ }
42620
+ return null;
42621
+ }
42622
+ function toOklch(ce, arg) {
42623
+ const direct = colorExprToOklch(arg);
42624
+ if (direct) return direct;
42625
+ const rgb = extractRgb(ce, arg);
42626
+ return rgb ? asOklch(rgb) : null;
42627
+ }
42628
+ function lerpOklchColor(a, b, t) {
42629
+ const L = a.L + (b.L - a.L) * t;
42630
+ const C = a.C + (b.C - a.C) * t;
42631
+ const aAchromatic = a.C < 1e-6;
42632
+ const bAchromatic = b.C < 1e-6;
42633
+ let H;
42634
+ if (aAchromatic && bAchromatic) H = a.H;
42635
+ else if (aAchromatic) H = b.H;
42636
+ else if (bAchromatic) H = a.H;
42637
+ else {
42638
+ let dH = b.H - a.H;
42639
+ if (dH > 180) dH -= 360;
42640
+ if (dH < -180) dH += 360;
42641
+ H = a.H + dH * t;
42642
+ if (H < 0) H += 360;
42643
+ if (H >= 360) H -= 360;
42644
+ }
42645
+ const alphaA = a.alpha ?? 1;
42646
+ const alphaB = b.alpha ?? 1;
42647
+ return { L, C, H, alpha: normalizeAlpha(alphaA + (alphaB - alphaA) * t) };
42648
+ }
42649
+ function oklchToExpr(ce, c) {
42650
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42651
+ if (c.alpha !== void 0) args.push(ce.number(c.alpha));
42652
+ return ce.function("Oklch", args);
42023
42653
  }
42024
42654
  function extractRgb(ce, arg) {
42025
42655
  if (isString(arg)) {
42026
42656
  const s = arg.string;
42027
42657
  if (!s) return void 0;
42028
42658
  const color = parseColor(s);
42029
- return {
42659
+ const rgb = {
42030
42660
  r: color >>> 24 & 255,
42031
42661
  g: color >>> 16 & 255,
42032
- b: color >>> 8 & 255,
42033
- alpha: (color & 255) / 255
42662
+ b: color >>> 8 & 255
42034
42663
  };
42664
+ const alpha = normalizeAlpha((color & 255) / 255);
42665
+ if (alpha !== void 0) rgb.alpha = alpha;
42666
+ return rgb;
42035
42667
  }
42668
+ const fromTyped = colorExprToRgb(arg);
42669
+ if (fromTyped) return fromTyped;
42036
42670
  if (arg.operator === "Tuple" && arg.ops && arg.ops.length >= 3) {
42037
42671
  const rgb = {
42038
42672
  r: arg.ops[0].re * 255,
42039
42673
  g: arg.ops[1].re * 255,
42040
42674
  b: arg.ops[2].re * 255
42041
42675
  };
42042
- if (arg.ops.length >= 4) rgb.alpha = arg.ops[3].re;
42676
+ if (arg.ops.length >= 4) {
42677
+ const alpha = normalizeAlpha(arg.ops[3].re);
42678
+ if (alpha !== void 0) rgb.alpha = alpha;
42679
+ }
42043
42680
  return rgb;
42044
42681
  }
42045
42682
  return void 0;
42046
42683
  }
42047
42684
  function componentsTuple(ce, components, alpha) {
42048
42685
  const args = components.map((v) => ce.number(v));
42049
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4)
42050
- args.push(ce.number(alpha));
42686
+ if (alpha !== void 0) args.push(ce.number(alpha));
42051
42687
  return ce.tuple(...args);
42052
42688
  }
42053
42689
  function rgbToHex(rgb) {
@@ -42055,7 +42691,7 @@ ${e.message}
42055
42691
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
42056
42692
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
42057
42693
  const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
42058
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
42694
+ if (rgb.alpha !== void 0) {
42059
42695
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
42060
42696
  return hex + a.toString(16).padStart(2, "0");
42061
42697
  }
@@ -42063,22 +42699,29 @@ ${e.message}
42063
42699
  }
42064
42700
  var COLORS_LIBRARY = {
42065
42701
  Color: {
42066
- description: "Convert a color string to a canonical sRGB tuple",
42702
+ description: "Parse a CSS-style color string to an Oklch color",
42067
42703
  complexity: 8e3,
42068
- signature: "(string) -> tuple",
42704
+ signature: "(string) -> color",
42069
42705
  evaluate: (ops, { engine: ce }) => {
42070
42706
  const input = isString(ops[0]) ? ops[0].string : void 0;
42071
42707
  if (!input) return ce.error("incompatible-type");
42072
42708
  const color = parseColor(input);
42073
42709
  if (color === 0 && input.trim().toLowerCase() !== "transparent")
42074
42710
  return ce.error("incompatible-type");
42075
- return colorNumberToTuple(ce, color);
42711
+ const r = color >>> 24 & 255;
42712
+ const g = color >>> 16 & 255;
42713
+ const b = color >>> 8 & 255;
42714
+ const a = normalizeAlpha((color & 255) / 255);
42715
+ const c = rgbToOklch({ r, g, b });
42716
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42717
+ if (a !== void 0) args.push(ce.number(a));
42718
+ return ce.function("Oklch", args);
42076
42719
  }
42077
42720
  },
42078
42721
  ColorToString: {
42079
42722
  description: "Convert a color to a string in the specified format",
42080
42723
  complexity: 8e3,
42081
- signature: "(any, string?) -> string",
42724
+ signature: "(color | string | tuple, string?) -> string",
42082
42725
  evaluate: (ops, { engine: ce }) => {
42083
42726
  const rgb = extractRgb(ce, ops[0]);
42084
42727
  if (!rgb) return ce.error("incompatible-type");
@@ -42090,7 +42733,7 @@ ${e.message}
42090
42733
  const r = Math.round(rgb.r);
42091
42734
  const g = Math.round(rgb.g);
42092
42735
  const b = Math.round(rgb.b);
42093
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42736
+ if (rgb.alpha !== void 0)
42094
42737
  return ce.string(`rgb(${r} ${g} ${b} / ${rgb.alpha})`);
42095
42738
  return ce.string(`rgb(${r} ${g} ${b})`);
42096
42739
  }
@@ -42099,17 +42742,17 @@ ${e.message}
42099
42742
  const h = Math.round(hsl.h * 10) / 10;
42100
42743
  const s = Math.round(hsl.s * 1e3) / 10;
42101
42744
  const l = Math.round(hsl.l * 1e3) / 10;
42102
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42745
+ if (rgb.alpha !== void 0)
42103
42746
  return ce.string(`hsl(${h} ${s}% ${l}% / ${rgb.alpha})`);
42104
42747
  return ce.string(`hsl(${h} ${s}% ${l}%)`);
42105
42748
  }
42106
42749
  case "oklch": {
42107
- const c = rgbToOklch(rgb);
42750
+ const c = colorExprToOklch(ops[0]) ?? asOklch(rgb);
42108
42751
  const L = Math.round(c.L * 1e3) / 1e3;
42109
42752
  const C = Math.round(c.C * 1e3) / 1e3;
42110
42753
  const H = Math.round(c.H * 10) / 10;
42111
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42112
- return ce.string(`oklch(${L} ${C} ${H} / ${rgb.alpha})`);
42754
+ if (c.alpha !== void 0)
42755
+ return ce.string(`oklch(${L} ${C} ${H} / ${c.alpha})`);
42113
42756
  return ce.string(`oklch(${L} ${C} ${H})`);
42114
42757
  }
42115
42758
  default:
@@ -42120,60 +42763,44 @@ ${e.message}
42120
42763
  ColorMix: {
42121
42764
  description: "Mix two colors in OKLCh space",
42122
42765
  complexity: 8e3,
42123
- signature: "(any, any, number?) -> tuple",
42766
+ signature: "(color | string | tuple, color | string | tuple, number?) -> color",
42124
42767
  evaluate: (ops, { engine: ce }) => {
42125
- const rgb1 = extractRgb(ce, ops[0]);
42126
- const rgb2 = extractRgb(ce, ops[1]);
42127
- if (!rgb1 || !rgb2) return ce.error("incompatible-type");
42128
42768
  let ratio = 0.5;
42129
42769
  if (ops.length >= 3 && ops[2] !== void 0) {
42130
42770
  ratio = ops[2].re;
42131
42771
  if (!Number.isFinite(ratio)) return ce.error("expected-value");
42132
42772
  ratio = Math.max(0, Math.min(1, ratio));
42133
42773
  }
42134
- const c1 = rgbToOklch(rgb1);
42135
- const c2 = rgbToOklch(rgb2);
42136
- const mixed = oklchToRgb(lerpOklch(c1, c2, ratio));
42137
- const r = mixed.r / 255;
42138
- const g = mixed.g / 255;
42139
- const b = mixed.b / 255;
42140
- const a1 = rgb1.alpha ?? 1;
42141
- const a2 = rgb2.alpha ?? 1;
42142
- const alpha = a1 + (a2 - a1) * ratio;
42143
- if (Math.abs(alpha - 1) > 1e-4)
42144
- return ce.tuple(
42145
- ce.number(r),
42146
- ce.number(g),
42147
- ce.number(b),
42148
- ce.number(alpha)
42149
- );
42150
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42774
+ const oklch1 = toOklch(ce, ops[0]);
42775
+ const oklch2 = toOklch(ce, ops[1]);
42776
+ if (!oklch1 || !oklch2) return ce.error("incompatible-type");
42777
+ return oklchToExpr(ce, lerpOklchColor(oklch1, oklch2, ratio));
42151
42778
  }
42152
42779
  },
42153
42780
  Colormap: {
42154
42781
  description: "Sample colors from a named palette",
42155
42782
  complexity: 8e3,
42156
- signature: "(string, number?) -> any",
42783
+ signature: "(string, number?) -> color | list<color>",
42157
42784
  evaluate: (ops, { engine: ce }) => {
42158
42785
  const name = isString(ops[0]) ? ops[0].string : void 0;
42159
42786
  if (!name) return ce.error("incompatible-type");
42160
42787
  const palette = ALL_PALETTES[name];
42161
42788
  if (!palette) return ce.error("expected-value", name);
42162
42789
  if (ops.length < 2 || ops[1] === void 0) {
42163
- const tuples = palette.map(
42164
- (hex) => colorNumberToTuple(ce, parseColor(hex))
42790
+ const colors = palette.map(
42791
+ (hex) => colorNumberToOklch(ce, parseColor(hex))
42165
42792
  );
42166
- return ce.function("List", tuples);
42793
+ return ce.function("List", colors);
42167
42794
  }
42168
42795
  const val = ops[1].re;
42169
42796
  if (!Number.isFinite(val)) return ce.error("expected-value");
42170
42797
  if (Number.isInteger(val) && val >= 2) {
42171
42798
  const n = val;
42172
- const tuples = [];
42799
+ const colors = [];
42173
42800
  for (let i = 0; i < n; i++) {
42174
- tuples.push(samplePalette(ce, palette, i / (n - 1)));
42801
+ colors.push(samplePalette(ce, palette, i / (n - 1)));
42175
42802
  }
42176
- return ce.function("List", tuples);
42803
+ return ce.function("List", colors);
42177
42804
  }
42178
42805
  return samplePalette(ce, palette, val);
42179
42806
  }
@@ -42181,12 +42808,25 @@ ${e.message}
42181
42808
  ColorToColorspace: {
42182
42809
  description: "Convert a color to components in a target color space",
42183
42810
  complexity: 8e3,
42184
- signature: "(any, string) -> tuple",
42811
+ signature: "(color | string | tuple, string) -> tuple",
42185
42812
  evaluate: (ops, { engine: ce }) => {
42186
- const rgb = extractRgb(ce, ops[0]);
42187
- if (!rgb) return ce.error("incompatible-type");
42188
42813
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42189
42814
  if (!space) return ce.error("incompatible-type");
42815
+ if (space === "oklch" || space === "oklab" || space === "lab") {
42816
+ const oklch2 = colorExprToOklch(ops[0]);
42817
+ if (oklch2) {
42818
+ if (space === "oklch")
42819
+ return componentsTuple(
42820
+ ce,
42821
+ [oklch2.L, oklch2.C, oklch2.H],
42822
+ oklch2.alpha
42823
+ );
42824
+ const lab = oklchToOklab(oklch2);
42825
+ return componentsTuple(ce, [lab.L, lab.a, lab.b], lab.alpha);
42826
+ }
42827
+ }
42828
+ const rgb = extractRgb(ce, ops[0]);
42829
+ if (!rgb) return ce.error("incompatible-type");
42190
42830
  const alpha = rgb.alpha;
42191
42831
  switch (space) {
42192
42832
  case "rgb":
@@ -42216,17 +42856,27 @@ ${e.message}
42216
42856
  ColorFromColorspace: {
42217
42857
  description: "Convert color space components to a canonical sRGB tuple",
42218
42858
  complexity: 8e3,
42219
- signature: "(tuple, string) -> tuple",
42859
+ signature: "(color | tuple, string) -> tuple",
42220
42860
  evaluate: (ops, { engine: ce }) => {
42221
- const tuple = ops[0];
42222
- if (!isFunction2(tuple) || tuple.operator !== "Tuple" || tuple.ops.length < 3)
42223
- return ce.error("incompatible-type");
42224
- const c0 = tuple.ops[0].re;
42225
- const c1 = tuple.ops[1].re;
42226
- const c2 = tuple.ops[2].re;
42227
- const alpha = tuple.ops.length >= 4 ? tuple.ops[3].re : void 0;
42228
42861
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42229
42862
  if (!space) return ce.error("incompatible-type");
42863
+ let c0, c1, c2;
42864
+ let alpha;
42865
+ const arg = ops[0];
42866
+ const typed = readColorExpr(arg);
42867
+ if (typed) {
42868
+ c0 = typed.c0;
42869
+ c1 = typed.c1;
42870
+ c2 = typed.c2;
42871
+ alpha = typed.alpha;
42872
+ } else if (isFunction2(arg) && arg.operator === "Tuple" && arg.ops.length >= 3) {
42873
+ c0 = arg.ops[0].re;
42874
+ c1 = arg.ops[1].re;
42875
+ c2 = arg.ops[2].re;
42876
+ alpha = arg.ops.length >= 4 ? arg.ops[3].re : void 0;
42877
+ } else {
42878
+ return ce.error("incompatible-type");
42879
+ }
42230
42880
  let rgb;
42231
42881
  switch (space) {
42232
42882
  case "rgb":
@@ -42262,7 +42912,7 @@ ${e.message}
42262
42912
  ColorContrast: {
42263
42913
  description: "APCA contrast ratio between two colors",
42264
42914
  complexity: 8e3,
42265
- signature: "(any, any) -> number",
42915
+ signature: "(color | string | tuple, color | string | tuple) -> number",
42266
42916
  evaluate: (ops, { engine: ce }) => {
42267
42917
  const bgRgb = extractRgb(ce, ops[0]);
42268
42918
  const fgRgb = extractRgb(ce, ops[1]);
@@ -42273,19 +42923,186 @@ ${e.message}
42273
42923
  ContrastingColor: {
42274
42924
  description: "Choose the foreground color with better APCA contrast against a background",
42275
42925
  complexity: 8e3,
42276
- signature: "(any, any?, any?) -> tuple",
42926
+ signature: "(color | string | tuple, (color | string | tuple)?, (color | string | tuple)?) -> color",
42277
42927
  evaluate: (ops, { engine: ce }) => {
42278
42928
  const bgRgb = extractRgb(ce, ops[0]);
42279
42929
  if (!bgRgb) return ce.error("incompatible-type");
42930
+ let packed;
42280
42931
  if (ops.length >= 3 && ops[1] !== void 0 && ops[2] !== void 0) {
42281
42932
  const fg1 = extractRgb(ce, ops[1]);
42282
42933
  const fg2 = extractRgb(ce, ops[2]);
42283
42934
  if (!fg1 || !fg2) return ce.error("incompatible-type");
42284
- const result2 = contrastingColor({ bg: bgRgb, fg1, fg2 });
42285
- return colorNumberToTuple(ce, result2);
42935
+ packed = contrastingColor({ bg: bgRgb, fg1, fg2 });
42936
+ } else {
42937
+ packed = contrastingColor(bgRgb);
42938
+ }
42939
+ const r = (packed >>> 24 & 255) / 255;
42940
+ const g = (packed >>> 16 & 255) / 255;
42941
+ const b = (packed >>> 8 & 255) / 255;
42942
+ const alpha = normalizeAlpha((packed & 255) / 255);
42943
+ const args = [ce.number(r), ce.number(g), ce.number(b)];
42944
+ if (alpha !== void 0) args.push(ce.number(alpha));
42945
+ return ce.function("Rgb", args);
42946
+ }
42947
+ },
42948
+ // ---------------------------------------------------------------------------
42949
+ // Color constructors. Each preserves its colorspace on evaluation; the
42950
+ // operator name is the discriminator. Components are interpreted per
42951
+ // colorspace conventions (Rgb channels 0-1, Hsv/Hsl hue in degrees with
42952
+ // sat/value 0-1, Oklab/Oklch L 0-1 with standard a/b/C/H ranges). The
42953
+ // optional 4th argument is alpha in [0, 1]. No clamping at evaluation time.
42954
+ // ---------------------------------------------------------------------------
42955
+ Rgb: {
42956
+ description: "sRGB color (channels 0-1, optional alpha 0-1)",
42957
+ complexity: 8e3,
42958
+ signature: "(number, number, number, number?) -> color"
42959
+ },
42960
+ Hsv: {
42961
+ description: "HSV color (hue degrees, saturation/value 0-1, optional alpha)",
42962
+ complexity: 8e3,
42963
+ signature: "(number, number, number, number?) -> color"
42964
+ },
42965
+ Hsl: {
42966
+ description: "HSL color (hue degrees, saturation/lightness 0-1, optional alpha)",
42967
+ complexity: 8e3,
42968
+ signature: "(number, number, number, number?) -> color"
42969
+ },
42970
+ Oklab: {
42971
+ description: "OKLab color (L 0-1, a/b ~ -0.4..0.4, optional alpha)",
42972
+ complexity: 8e3,
42973
+ signature: "(number, number, number, number?) -> color"
42974
+ },
42975
+ Oklch: {
42976
+ description: "OKLCh color (L 0-1, C 0-~0.4, hue degrees, optional alpha)",
42977
+ complexity: 8e3,
42978
+ signature: "(number, number, number, number?) -> color"
42979
+ },
42980
+ // ---------------------------------------------------------------------------
42981
+ // Color-space conversions. Each accepts any of the five color heads and
42982
+ // returns the same color in the named space. If the input is already in
42983
+ // the target space, returns the input unchanged.
42984
+ // ---------------------------------------------------------------------------
42985
+ AsRgb: {
42986
+ description: "Convert any color to sRGB (channels 0-1)",
42987
+ complexity: 8e3,
42988
+ signature: "(color) -> color",
42989
+ evaluate: (ops, { engine: ce }) => {
42990
+ const arg = ops[0];
42991
+ if (isFunction2(arg) && arg.operator === "Rgb")
42992
+ return normalizeColorHead(ce, arg);
42993
+ const rgb = colorExprToRgb(arg);
42994
+ if (!rgb) return ce.error("incompatible-type");
42995
+ const args = [
42996
+ ce.number(rgb.r / 255),
42997
+ ce.number(rgb.g / 255),
42998
+ ce.number(rgb.b / 255)
42999
+ ];
43000
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
43001
+ return ce.function("Rgb", args);
43002
+ }
43003
+ },
43004
+ AsHsv: {
43005
+ description: "Convert any color to HSV (hue degrees, s/v 0-1)",
43006
+ complexity: 8e3,
43007
+ signature: "(color) -> color",
43008
+ evaluate: (ops, { engine: ce }) => {
43009
+ const arg = ops[0];
43010
+ if (isFunction2(arg) && arg.operator === "Hsv")
43011
+ return normalizeColorHead(ce, arg);
43012
+ const rgb = colorExprToRgb(arg);
43013
+ if (!rgb) return ce.error("incompatible-type");
43014
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
43015
+ const args = [ce.number(hsv.h), ce.number(hsv.s), ce.number(hsv.v)];
43016
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
43017
+ return ce.function("Hsv", args);
43018
+ }
43019
+ },
43020
+ AsHsl: {
43021
+ description: "Convert any color to HSL (hue degrees, s/l 0-1)",
43022
+ complexity: 8e3,
43023
+ signature: "(color) -> color",
43024
+ evaluate: (ops, { engine: ce }) => {
43025
+ const arg = ops[0];
43026
+ if (isFunction2(arg) && arg.operator === "Hsl")
43027
+ return normalizeColorHead(ce, arg);
43028
+ const rgb = colorExprToRgb(arg);
43029
+ if (!rgb) return ce.error("incompatible-type");
43030
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
43031
+ const args = [ce.number(hsl.h), ce.number(hsl.s), ce.number(hsl.l)];
43032
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
43033
+ return ce.function("Hsl", args);
43034
+ }
43035
+ },
43036
+ AsOklab: {
43037
+ description: "Convert any color to OKLab",
43038
+ complexity: 8e3,
43039
+ signature: "(color) -> color",
43040
+ evaluate: (ops, { engine: ce }) => {
43041
+ const arg = ops[0];
43042
+ if (isFunction2(arg) && arg.operator === "Oklab")
43043
+ return normalizeColorHead(ce, arg);
43044
+ if (isFunction2(arg) && arg.operator === "Oklch") {
43045
+ const c = readColorExpr(arg);
43046
+ if (!c) return ce.error("incompatible-type");
43047
+ const lab2 = oklchToOklab({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
43048
+ const args2 = [ce.number(lab2.L), ce.number(lab2.a), ce.number(lab2.b)];
43049
+ if (lab2.alpha !== void 0) args2.push(ce.number(lab2.alpha));
43050
+ return ce.function("Oklab", args2);
43051
+ }
43052
+ const rgb = colorExprToRgb(arg);
43053
+ if (!rgb) return ce.error("incompatible-type");
43054
+ const lab = rgbToOklab(rgb);
43055
+ const args = [ce.number(lab.L), ce.number(lab.a), ce.number(lab.b)];
43056
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
43057
+ return ce.function("Oklab", args);
43058
+ }
43059
+ },
43060
+ AsOklch: {
43061
+ description: "Convert any color to OKLCh",
43062
+ complexity: 8e3,
43063
+ signature: "(color) -> color",
43064
+ evaluate: (ops, { engine: ce }) => {
43065
+ const arg = ops[0];
43066
+ if (isFunction2(arg) && arg.operator === "Oklch")
43067
+ return normalizeColorHead(ce, arg);
43068
+ if (isFunction2(arg) && arg.operator === "Oklab") {
43069
+ const c2 = readColorExpr(arg);
43070
+ if (!c2) return ce.error("incompatible-type");
43071
+ const oklch2 = oklabToOklch({
43072
+ L: c2.c0,
43073
+ a: c2.c1,
43074
+ b: c2.c2,
43075
+ alpha: c2.alpha
43076
+ });
43077
+ const args2 = [
43078
+ ce.number(oklch2.L),
43079
+ ce.number(oklch2.C),
43080
+ ce.number(oklch2.H)
43081
+ ];
43082
+ if (oklch2.alpha !== void 0) args2.push(ce.number(oklch2.alpha));
43083
+ return ce.function("Oklch", args2);
42286
43084
  }
42287
- const result = contrastingColor(bgRgb);
42288
- return colorNumberToTuple(ce, result);
43085
+ const rgb = colorExprToRgb(arg);
43086
+ if (!rgb) return ce.error("incompatible-type");
43087
+ const c = rgbToOklch(rgb);
43088
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
43089
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
43090
+ return ce.function("Oklch", args);
43091
+ }
43092
+ },
43093
+ // ---------------------------------------------------------------------------
43094
+ // Perceptual difference. Returns ΔE_OK (Euclidean distance in OKLab),
43095
+ // an approximately perceptually uniform scalar.
43096
+ // ---------------------------------------------------------------------------
43097
+ ColorDelta: {
43098
+ description: "Perceptual color difference (\u0394E_OK) between two colors",
43099
+ complexity: 8e3,
43100
+ signature: "(color | string | tuple, color | string | tuple) -> number",
43101
+ evaluate: (ops, { engine: ce }) => {
43102
+ const a = toOklch(ce, ops[0]);
43103
+ const b = toOklch(ce, ops[1]);
43104
+ if (!a || !b) return ce.error("incompatible-type");
43105
+ return ce.number(oklabDeltaE(oklchToOklab(a), oklchToOklab(b)));
42289
43106
  }
42290
43107
  }
42291
43108
  };
@@ -42344,12 +43161,42 @@ ${e.message}
42344
43161
  }
42345
43162
  },
42346
43163
  Loop: {
42347
- description: "Evaluate a body expression over elements of a collection.",
43164
+ description: "Evaluate a body expression in nested iteration over Element clauses. Later clauses see earlier bindings; independent clauses produce a Cartesian product.",
42348
43165
  lazy: true,
42349
- signature: "(body:expression, collection:expression) -> any",
42350
- type: ([body]) => body.type,
42351
- evaluate: ([body, collection], { engine: ce }) => run(runLoop(body, collection, ce), ce._timeRemaining),
42352
- evaluateAsync: async ([body, collection], { engine: ce, signal }) => runAsync(runLoop(body, collection, ce), ce._timeRemaining, signal)
43166
+ signature: "(body:expression, iterators:expression*) -> any",
43167
+ type: ([body]) => {
43168
+ if (!body) return "nothing";
43169
+ return parseType(`indexed_collection<${String(body.type)}>`);
43170
+ },
43171
+ canonical: canonicalLoop,
43172
+ evaluate: (ops, { engine: ce }) => run(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining),
43173
+ evaluateAsync: async (ops, { engine: ce, signal }) => runAsync(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining, signal)
43174
+ },
43175
+ When: {
43176
+ description: "Conditional value: returns expr when cond holds, undefined otherwise.",
43177
+ lazy: true,
43178
+ signature: "(expression, boolean) -> any",
43179
+ type: ([expr2]) => expr2.type,
43180
+ canonical: (args, { engine: ce }) => {
43181
+ if (args.length !== 2) return null;
43182
+ const [expr2, cond] = args;
43183
+ if (isFunction2(expr2, "When")) {
43184
+ const inner = expr2.op1.canonical;
43185
+ const innerCond = expr2.op2.canonical;
43186
+ return ce._fn("When", [
43187
+ inner,
43188
+ ce._fn("And", [innerCond, cond.canonical])
43189
+ ]);
43190
+ }
43191
+ return ce._fn("When", [expr2.canonical, cond.canonical]);
43192
+ },
43193
+ evaluate: ([expr2, cond], { engine: ce }) => {
43194
+ const c = cond.evaluate();
43195
+ const cs = sym(c);
43196
+ if (cs === "True") return expr2.evaluate();
43197
+ if (cs === "False") return ce.symbol("Undefined");
43198
+ return ce._fn("When", [expr2, c]);
43199
+ }
42353
43200
  },
42354
43201
  Which: {
42355
43202
  description: "Return the value for the first condition that is true.",
@@ -42409,9 +43256,141 @@ ${e.message}
42409
43256
  );
42410
43257
  return result;
42411
43258
  }
42412
- function* runLoop(body, collection, ce) {
43259
+ function canonicalLoop(ops, options) {
43260
+ const { engine: ce, scope } = options;
43261
+ if (ops.length === 0) return null;
43262
+ if (ops.length === 1) {
43263
+ return ce._fn("Loop", [ops[0].canonical]);
43264
+ }
43265
+ const body = ops[0];
43266
+ const iterators = ops.slice(1);
43267
+ const allElement = iterators.every((it) => it.operator === "Element");
43268
+ if (!allElement) {
43269
+ return ce._fn(
43270
+ "Loop",
43271
+ ops.map((op) => op.canonical)
43272
+ );
43273
+ }
43274
+ const loopScope = scope ?? {
43275
+ parent: ce.context.lexicalScope,
43276
+ bindings: /* @__PURE__ */ new Map()
43277
+ };
43278
+ loopScope.noAutoDeclare = true;
43279
+ ce.pushScope(loopScope);
43280
+ let canonicalIterators;
43281
+ let canonicalBody;
43282
+ try {
43283
+ canonicalIterators = iterators.map((it) => {
43284
+ if (!isFunction2(it, "Element")) {
43285
+ return ce._fn("Element", [
43286
+ ce.error("missing").canonical,
43287
+ ce.error("missing").canonical
43288
+ ]);
43289
+ }
43290
+ const indexExpr = it.ops[0];
43291
+ const collExpr = it.ops[1];
43292
+ if (!indexExpr || !collExpr) {
43293
+ return ce._fn("Element", [
43294
+ (indexExpr ?? ce.error("missing")).canonical,
43295
+ (collExpr ?? ce.error("missing")).canonical
43296
+ ]);
43297
+ }
43298
+ if (isSymbol2(indexExpr) && indexExpr.symbol !== "Nothing") {
43299
+ if (!ce.context.lexicalScope.bindings.has(indexExpr.symbol))
43300
+ ce.declare(indexExpr.symbol, "unknown");
43301
+ }
43302
+ return ce._fn("Element", [indexExpr.canonical, collExpr.canonical]);
43303
+ });
43304
+ canonicalBody = body.canonical;
43305
+ } finally {
43306
+ ce.popScope();
43307
+ loopScope.noAutoDeclare = false;
43308
+ }
43309
+ return ce._fn("Loop", [canonicalBody, ...canonicalIterators], {
43310
+ scope: loopScope
43311
+ });
43312
+ }
43313
+ function* runLoop(body, elements, ce) {
42413
43314
  body ??= ce.Nothing;
42414
43315
  if (sym(body) === "Nothing") return body;
43316
+ if (elements.length === 0) {
43317
+ const result = body.evaluate();
43318
+ yield result;
43319
+ return result;
43320
+ }
43321
+ if (elements.length === 1 && elements[0].operator !== "Element") {
43322
+ return yield* runLoopLegacy(body, elements[0], ce);
43323
+ }
43324
+ const results = [];
43325
+ const state = { stopped: false, broke: false, count: 0 };
43326
+ const freshScope = {
43327
+ parent: ce.context.lexicalScope,
43328
+ bindings: /* @__PURE__ */ new Map()
43329
+ };
43330
+ ce._pushEvalContext(freshScope);
43331
+ try {
43332
+ for (const elem of elements) {
43333
+ if (!isFunction2(elem, "Element")) continue;
43334
+ const idx = elem.ops[0];
43335
+ if (idx && isSymbol2(idx) && idx.symbol !== "Nothing") {
43336
+ if (!freshScope.bindings.has(idx.symbol))
43337
+ ce.declare(idx.symbol, "unknown");
43338
+ }
43339
+ }
43340
+ yield* runLoopNested(body, elements, 0, ce, results, state);
43341
+ } finally {
43342
+ ce._popEvalContext();
43343
+ }
43344
+ if (state.stopped && state.value !== void 0) {
43345
+ if (!state.broke) return state.value;
43346
+ return state.value;
43347
+ }
43348
+ return ce.function("List", results);
43349
+ }
43350
+ function* runLoopNested(body, elements, index, ce, results, state) {
43351
+ if (state.stopped) return;
43352
+ if (index === elements.length) {
43353
+ const result = body.evaluate();
43354
+ state.count += 1;
43355
+ if (state.count > ce.iterationLimit)
43356
+ throw new CancellationError({ cause: "iteration-limit-exceeded" });
43357
+ if (isFunction2(result, "Break")) {
43358
+ state.stopped = true;
43359
+ state.broke = true;
43360
+ state.value = result.op1;
43361
+ return;
43362
+ }
43363
+ if (result.operator === "Return") {
43364
+ state.stopped = true;
43365
+ state.value = result;
43366
+ return;
43367
+ }
43368
+ results.push(result);
43369
+ yield result;
43370
+ return;
43371
+ }
43372
+ const elem = elements[index];
43373
+ if (!isFunction2(elem, "Element")) {
43374
+ return;
43375
+ }
43376
+ const indexExpr = elem.ops[0];
43377
+ const collExpr = elem.ops[1];
43378
+ if (!indexExpr || !isSymbol2(indexExpr) || !collExpr) {
43379
+ return;
43380
+ }
43381
+ const name = indexExpr.symbol;
43382
+ const collection = collExpr.evaluate();
43383
+ if (!collection?.isCollection) {
43384
+ return;
43385
+ }
43386
+ const skipAssign = name === "Nothing";
43387
+ for (const value of collection.each()) {
43388
+ if (!skipAssign) ce.assign(name, value);
43389
+ yield* runLoopNested(body, elements, index + 1, ce, results, state);
43390
+ if (state.stopped) return;
43391
+ }
43392
+ }
43393
+ function* runLoopLegacy(body, collection, ce) {
42415
43394
  if (collection?.isCollection) {
42416
43395
  let result = void 0;
42417
43396
  const fn = applicable(body);
@@ -44765,6 +45744,34 @@ ${e.message}
44765
45744
  signature: "() -> expression",
44766
45745
  evaluate: (_ops, { engine }) => engine.expr(randomExpression())
44767
45746
  }
45747
+ },
45748
+ // ---------------------------------------------------------------------------
45749
+ // Opaque typed heads — registered so the names are in the standard set
45750
+ // (consumers can branch on the operator name); CE itself does not evaluate
45751
+ // them. Geometric primitives `Triangle`/`Sphere`/`Segment` and the action
45752
+ // arrow `To` (`a \to b`).
45753
+ // ---------------------------------------------------------------------------
45754
+ {
45755
+ Triangle: {
45756
+ description: "Triangle primitive \u2014 opaque typed head.",
45757
+ signature: "(any+) -> expression"
45758
+ },
45759
+ GeometricVector: {
45760
+ description: "Geometric vector (directed segment between two points) \u2014 opaque typed head. Distinct from the column-vector `Vector` operator.",
45761
+ signature: "(any, any) -> expression"
45762
+ },
45763
+ Sphere: {
45764
+ description: "Sphere primitive \u2014 opaque typed head.",
45765
+ signature: "(any+) -> expression"
45766
+ },
45767
+ Segment: {
45768
+ description: "Segment primitive \u2014 opaque typed head.",
45769
+ signature: "(any+) -> expression"
45770
+ },
45771
+ To: {
45772
+ description: "Action arrow / mapping (`a \\to b`) \u2014 opaque typed head.",
45773
+ signature: "(any, any) -> nothing"
45774
+ }
44768
45775
  }
44769
45776
  ];
44770
45777
 
@@ -51901,6 +52908,7 @@ Error in definition of "${name}"`,
51901
52908
 
51902
52909
  // src/compute-engine/boxed-expression/cache.ts
51903
52910
  function cachedValue(v, generation, fn) {
52911
+ if (v.generation === generation && v.value !== null) return v.value;
51904
52912
  v.generation = generation;
51905
52913
  v.value = fn();
51906
52914
  return v.value;
@@ -53678,6 +54686,12 @@ Error in definition of "${name}"`,
53678
54686
  function _escapeJsonString(s) {
53679
54687
  return s;
53680
54688
  }
54689
+ function _serializeLatexMetadata(ce, expr2) {
54690
+ const syntax = ce.latexSyntax;
54691
+ const opts = ce.latexOptions;
54692
+ if (Object.keys(opts).length === 0) return syntax.serialize(expr2);
54693
+ return syntax.serialize(expr2, { ...opts });
54694
+ }
53681
54695
  function serializeSubtract(ce, a, b, options, metadata) {
53682
54696
  if (isNumber(a) && a.isNegative) {
53683
54697
  const v = a.numericValue;
@@ -53982,7 +54996,7 @@ Error in definition of "${name}"`,
53982
54996
  ];
53983
54997
  const md = { ...metadata ?? {} };
53984
54998
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53985
- md.latex = _escapeJsonString(md.latex ?? ce.latexSyntax.serialize(fn));
54999
+ md.latex = _escapeJsonString(md.latex ?? _serializeLatexMetadata(ce, fn));
53986
55000
  } else md.latex = "";
53987
55001
  if (!options.metadata.includes("wikidata")) md.wikidata = "";
53988
55002
  if (!md.latex && !md.wikidata && options.shorthands.includes("function"))
@@ -54007,7 +55021,7 @@ Error in definition of "${name}"`,
54007
55021
  }
54008
55022
  metadata = { ...metadata };
54009
55023
  if (options.metadata.includes("latex") && ce.latexSyntax) {
54010
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(sym2);
55024
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, sym2);
54011
55025
  if (metadata.latex !== void 0)
54012
55026
  metadata.latex = _escapeJsonString(metadata.latex);
54013
55027
  } else metadata.latex = void 0;
@@ -54169,7 +55183,7 @@ Error in definition of "${name}"`,
54169
55183
  }
54170
55184
  }
54171
55185
  if (options.metadata.includes("latex") && ce.latexSyntax)
54172
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(result2 ?? { num });
55186
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, result2 ?? { num });
54173
55187
  if (result2) {
54174
55188
  if (metadata.latex !== void 0)
54175
55189
  return { sym: result2, latex: metadata.latex };
@@ -54185,7 +55199,7 @@ Error in definition of "${name}"`,
54185
55199
  if (value.isNaN()) {
54186
55200
  num = "NaN";
54187
55201
  if (options.metadata.includes("latex") && ce.latexSyntax)
54188
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
55202
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54189
55203
  return metadata.latex !== void 0 ? { num, latex: metadata.latex } : { num };
54190
55204
  }
54191
55205
  return serializeJsonFunction(
@@ -54219,7 +55233,7 @@ Error in definition of "${name}"`,
54219
55233
  value = Number(value);
54220
55234
  } else {
54221
55235
  if (options.metadata.includes("latex") && ce.latexSyntax)
54222
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({
55236
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, {
54223
55237
  num: value.toString()
54224
55238
  });
54225
55239
  if (metadata.latex !== void 0)
@@ -54233,7 +55247,7 @@ Error in definition of "${name}"`,
54233
55247
  result = value > 0 ? "PositiveInfinity" : "NegativeInfinity";
54234
55248
  else num = serializeRepeatingDecimals(value.toString(), options);
54235
55249
  if (options.metadata.includes("latex") && ce.latexSyntax)
54236
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
55250
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54237
55251
  if (result) {
54238
55252
  if (metadata.latex !== void 0)
54239
55253
  return { sym: result, latex: metadata.latex };
@@ -54459,6 +55473,23 @@ Error in definition of "${name}"`,
54459
55473
  };
54460
55474
  return compilePair(0);
54461
55475
  }
55476
+ if (h === "When") {
55477
+ if (args.length !== 2)
55478
+ throw new Error("When: expected exactly 2 arguments (expr, cond)");
55479
+ const fn2 = target.functions?.(h);
55480
+ if (fn2) {
55481
+ if (typeof fn2 === "function") {
55482
+ return fn2(args, (expr2) => _BaseCompiler.compile(expr2, target), target);
55483
+ }
55484
+ return `${fn2}(${args.map((x) => _BaseCompiler.compile(x, target)).join(", ")})`;
55485
+ }
55486
+ if (isSymbol2(args[1], "True"))
55487
+ return `(${_BaseCompiler.compile(args[0], target)})`;
55488
+ if (isSymbol2(args[1], "False")) return "NaN";
55489
+ const val = _BaseCompiler.compile(args[0], target);
55490
+ const cond = _BaseCompiler.compile(args[1], target);
55491
+ return `((${cond}) ? (${val}) : NaN)`;
55492
+ }
54462
55493
  if (h === "Block") {
54463
55494
  return _BaseCompiler.compileBlock(args, target);
54464
55495
  }
@@ -54533,17 +55564,91 @@ Error in definition of "${name}"`,
54533
55564
  )}${target.ws("\n")}})()`;
54534
55565
  }
54535
55566
  /**
54536
- * Compile a Loop expression with Element(index, Range(lo, hi)) indexing.
54537
- * Generates: (() => { for (let i = lo; i <= hi; i++) { body } })()
55567
+ * Compile a Loop expression.
55568
+ *
55569
+ * Two forms are supported:
55570
+ *
55571
+ * 1. **Imperative / single-Element form** (existing behaviour):
55572
+ * `Loop(body, Element(i, Range(lo, hi)))`
55573
+ * Generates a raw `for (let i = lo; i <= hi; i++) { body }` loop wrapped
55574
+ * in an IIFE. The loop counter is always a plain number. For targets
55575
+ * that wrap numeric values (e.g. interval-js uses `_IA.point()`),
55576
+ * references to the loop index inside the body are re-wrapped via
55577
+ * `target.number`. `break` / `continue` / `return` are preserved.
54538
55578
  *
54539
- * The loop counter is always a raw number. For targets that wrap numeric
54540
- * values (e.g. interval-js wraps with `_IA.point()`), references to the
54541
- * loop index inside the body are wrapped via `target.number`.
55579
+ * 2. **Comprehension / variadic-Element form** (new):
55580
+ * `Loop(body, Element(x, coll1), Element(y, coll2), )`
55581
+ * When two or more `Element` clauses are present or when the single
55582
+ * Element's collection is not a `Range` — the loop is compiled as a
55583
+ * comprehension that collects results into an array. Each clause
55584
+ * produces a `for (const name of collection)` loop, nested
55585
+ * outermost-to-innermost, and the innermost body pushes into `result`.
55586
+ *
55587
+ * Example output (JS):
55588
+ * ```js
55589
+ * (() => { const result = [];
55590
+ * for (const x of [1,2]) { for (const y of [3,4]) { result.push(body); } }
55591
+ * return result; })()
55592
+ * ```
55593
+ *
55594
+ * GLSL: multi-Element comprehension is not trivially representable in
55595
+ * GLSL (no dynamic arrays, no push). A compile-time error is thrown.
55596
+ * TODO(E3-GLSL): support GLSL multi-Element via a pre-declared fixed-size
55597
+ * array or by unrolling when bounds are known at compile time.
54542
55598
  */
54543
55599
  static compileForLoop(args, target) {
54544
55600
  if (!args[0]) throw new Error("Loop: no body");
54545
55601
  if (!args[1]) throw new Error("Loop: no indexing set");
54546
- const indexing = args[1];
55602
+ const body = args[0];
55603
+ const elements = args.slice(1);
55604
+ const useComprehension = elements.length > 1 || elements.length === 1 && isFunction2(elements[0], "Element") && !_BaseCompiler.isLegacyCompatibleRange(elements[0].ops[1]);
55605
+ if (useComprehension) {
55606
+ const lang = target.language ?? "";
55607
+ if (lang === "glsl" || lang === "wgsl") {
55608
+ throw new Error(
55609
+ `${lang.toUpperCase()}: multi-Element Loop comprehension is not yet supported. TODO(E3-GLSL): unroll or use a fixed-size array.`
55610
+ );
55611
+ }
55612
+ const narrowedElements = [];
55613
+ for (let i = 0; i < elements.length; i++) {
55614
+ const elem = elements[i];
55615
+ if (!isFunction2(elem, "Element"))
55616
+ throw new Error(
55617
+ `Loop: argument ${i + 1} must be an Element clause, got ${elem.operator ?? "?"}`
55618
+ );
55619
+ if (!isSymbol2(elem.ops[0]))
55620
+ throw new Error(
55621
+ `Loop: Element index (argument ${i + 1}) must be a symbol`
55622
+ );
55623
+ narrowedElements.push(elem);
55624
+ }
55625
+ const loopVarSet = new Set(
55626
+ narrowedElements.map(
55627
+ (e) => e.ops[0].symbol
55628
+ )
55629
+ );
55630
+ const needsWrap2 = target.number(0) !== "0";
55631
+ const bodyTarget2 = needsWrap2 ? {
55632
+ ...target,
55633
+ var: (id) => loopVarSet.has(id) ? target.number(0).replace("0", id) : target.var(id)
55634
+ } : target;
55635
+ const bodyCode = _BaseCompiler.compile(body, bodyTarget2);
55636
+ let inner = `result.push(${bodyCode});`;
55637
+ for (let i = narrowedElements.length - 1; i >= 0; i--) {
55638
+ const elem = narrowedElements[i];
55639
+ const name = elem.ops[0].symbol;
55640
+ const collExpr = elem.ops[1];
55641
+ let collection;
55642
+ if (isFunction2(collExpr, "Range")) {
55643
+ collection = _BaseCompiler.compileRangeIterable(collExpr, bodyTarget2);
55644
+ } else {
55645
+ collection = _BaseCompiler.compile(collExpr, bodyTarget2);
55646
+ }
55647
+ inner = `for (const ${name} of ${collection}) { ${inner} }`;
55648
+ }
55649
+ return `(() => { const result = []; ${inner} return result; })()`;
55650
+ }
55651
+ const indexing = elements[0];
54547
55652
  if (!isFunction2(indexing, "Element"))
54548
55653
  throw new Error("Loop: expected Element(index, Range(lo, hi))");
54549
55654
  const indexExpr = indexing.ops[0];
@@ -54561,13 +55666,72 @@ Error in definition of "${name}"`,
54561
55666
  ...target,
54562
55667
  var: (id) => id === index ? needsWrap ? target.number(0).replace("0", index) : index : target.var(id)
54563
55668
  };
54564
- const bodyStmts = _BaseCompiler.compileLoopBody(args[0], bodyTarget);
55669
+ const bodyStmts = _BaseCompiler.compileLoopBody(body, bodyTarget);
54565
55670
  return `(() => {${target.ws(
54566
55671
  "\n"
54567
55672
  )}for (let ${index} = ${lower}; ${index} <= ${upper}; ${index}++) {${target.ws(
54568
55673
  "\n"
54569
55674
  )}${bodyStmts}${target.ws("\n")}}${target.ws("\n")}})()`;
54570
55675
  }
55676
+ /**
55677
+ * Returns `true` when the given collection expression is a `Range` whose
55678
+ * runtime semantics match the legacy imperative for-loop shape
55679
+ * `for (let i = lo; i <= hi; i++)`.
55680
+ *
55681
+ * Concretely: integer-ascending bounds and step omitted-or-1. When bounds
55682
+ * are not statically numeric we accept the Range (the historical
55683
+ * behaviour) — runtime mismatch in the descending-unknown-bounds case is
55684
+ * left as a known limitation; callers can force the iterable path by
55685
+ * supplying an explicit step.
55686
+ */
55687
+ static isLegacyCompatibleRange(coll) {
55688
+ if (!isFunction2(coll, "Range")) return false;
55689
+ if (coll.ops.length >= 3) {
55690
+ const stepExpr = coll.ops[2];
55691
+ if (!isNumber(stepExpr) || stepExpr.re !== 1) return false;
55692
+ }
55693
+ const lo = coll.ops[0];
55694
+ const hi = coll.ops[1];
55695
+ if (isNumber(lo) && !Number.isInteger(lo.re)) return false;
55696
+ if (isNumber(hi) && !Number.isInteger(hi.re)) return false;
55697
+ if (isNumber(lo) && isNumber(hi) && lo.re > hi.re) return false;
55698
+ return true;
55699
+ }
55700
+ /**
55701
+ * Compile a `Range(lo, hi)` or `Range(lo, hi, step)` expression into a JS
55702
+ * iterable expression. Mirrors the runtime semantics in
55703
+ * `library/collections.ts` Range:
55704
+ * count = step === 0 ? 0 : max(0, floor((hi - lo) / step) + 1)
55705
+ * element = lo + step * k (0-indexed)
55706
+ * Default step is 1 when omitted. Bounds and step may be fractional.
55707
+ *
55708
+ * Only used from the comprehension path in `compileForLoop`.
55709
+ * Caller must have already verified `isFunction(rangeExpr, 'Range')`.
55710
+ */
55711
+ static compileRangeIterable(rangeExpr, target) {
55712
+ const loExpr = rangeExpr.ops[0];
55713
+ const hiExpr = rangeExpr.ops[1];
55714
+ const stepExpr = rangeExpr.ops[2];
55715
+ if (isNumber(loExpr) && isNumber(hiExpr) && (stepExpr === void 0 || isNumber(stepExpr))) {
55716
+ const lo2 = loExpr.re;
55717
+ const hi2 = hiExpr.re;
55718
+ const step2 = stepExpr === void 0 ? hi2 >= lo2 ? 1 : -1 : stepExpr.re;
55719
+ if (step2 === 0) return "[]";
55720
+ const len = Math.max(0, Math.floor((hi2 - lo2) / step2) + 1);
55721
+ if (step2 === 1) {
55722
+ if (lo2 === 0) return `Array.from({length:${len}},(_,k)=>k)`;
55723
+ return `Array.from({length:${len}},(_,k)=>${lo2}+k)`;
55724
+ }
55725
+ return `Array.from({length:${len}},(_,k)=>${lo2}+(${step2})*k)`;
55726
+ }
55727
+ const lo = _BaseCompiler.compile(loExpr, target);
55728
+ const hi = _BaseCompiler.compile(hiExpr, target);
55729
+ if (stepExpr === void 0) {
55730
+ return `((_lo,_hi)=>{const _st=_hi>=_lo?1:-1;return Array.from({length:Math.max(0,Math.floor((_hi-_lo)/_st)+1)},(_,k)=>_lo+_st*k);})(${lo},${hi})`;
55731
+ }
55732
+ const step = _BaseCompiler.compile(stepExpr, target);
55733
+ return `((_lo,_hi,_st)=>_st===0?[]:Array.from({length:Math.max(0,Math.floor((_hi-_lo)/_st)+1)},(_,k)=>_lo+_st*k))(${lo},${hi},${step})`;
55734
+ }
54571
55735
  /**
54572
55736
  * Compile a loop body expression as statements (not wrapped in IIFE).
54573
55737
  * Handles Break, Continue, Return as statements, and If as if-else when
@@ -54998,8 +56162,7 @@ Error in definition of "${name}"`,
54998
56162
  ce.pushScope();
54999
56163
  try {
55000
56164
  if (vars && typeof vars === "object") {
55001
- for (const [k, v] of Object.entries(vars))
55002
- ce.assign(k, v);
56165
+ for (const [k, v] of Object.entries(vars)) ce.assign(k, v);
55003
56166
  }
55004
56167
  return expr2.evaluate().re;
55005
56168
  } finally {
@@ -60229,8 +61392,7 @@ Error in definition of "${name}"`,
60229
61392
  return { re: null, im: formatFloat(iScale) };
60230
61393
  }
60231
61394
  const compiledFactors = remaining.map((r) => compile3(r));
60232
- if (iScale !== 1)
60233
- compiledFactors.unshift(formatFloat(iScale));
61395
+ if (iScale !== 1) compiledFactors.unshift(formatFloat(iScale));
60234
61396
  const imCode = foldTerms(compiledFactors, "1.0", "*");
60235
61397
  return { re: null, im: imCode };
60236
61398
  }
@@ -60834,39 +61996,130 @@ Error in definition of "${name}"`,
60834
61996
  if (args.length >= 2)
60835
61997
  return `_SYS.colormap(${compile3(args[0])}, ${compile3(args[1])})`;
60836
61998
  return `_SYS.colormap(${compile3(args[0])})`;
61999
+ },
62000
+ // -----------------------------------------------------------------------
62001
+ // Color constructor heads. All compile to OKLCh arrays at runtime — the
62002
+ // canonical color representation in this target. The constructors take
62003
+ // their own colorspace's components and convert internally.
62004
+ // (Mirrors the GPU target's design: color values are vec3 OKLCh.)
62005
+ // -----------------------------------------------------------------------
62006
+ Rgb: (args, compile3) => {
62007
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
62008
+ return `_SYS.rgb(${args.map(compile3).join(", ")})`;
62009
+ },
62010
+ Hsv: (args, compile3) => {
62011
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
62012
+ return `_SYS.hsv(${args.map(compile3).join(", ")})`;
62013
+ },
62014
+ Hsl: (args, compile3) => {
62015
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
62016
+ return `_SYS.hsl(${args.map(compile3).join(", ")})`;
62017
+ },
62018
+ Oklab: (args, compile3) => {
62019
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
62020
+ return `_SYS.oklab(${args.map(compile3).join(", ")})`;
62021
+ },
62022
+ Oklch: (args, compile3) => {
62023
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
62024
+ return `_SYS.oklch(${args.map(compile3).join(", ")})`;
62025
+ },
62026
+ // -----------------------------------------------------------------------
62027
+ // As* converters. Compile-time output convention matches the engine and
62028
+ // the GPU target: each returns components in the named space as a 3- or
62029
+ // 4-element array. `AsRgb` uses 0-1 sRGB channels (consistent across all
62030
+ // layers). `AsOklch` is the identity (canonical form).
62031
+ // -----------------------------------------------------------------------
62032
+ AsRgb: ([c], compile3) => {
62033
+ if (c === null) throw new Error("AsRgb: no argument");
62034
+ return `_SYS.asRgb(${compile3(c)})`;
62035
+ },
62036
+ AsHsv: ([c], compile3) => {
62037
+ if (c === null) throw new Error("AsHsv: no argument");
62038
+ return `_SYS.asHsv(${compile3(c)})`;
62039
+ },
62040
+ AsHsl: ([c], compile3) => {
62041
+ if (c === null) throw new Error("AsHsl: no argument");
62042
+ return `_SYS.asHsl(${compile3(c)})`;
62043
+ },
62044
+ AsOklab: ([c], compile3) => {
62045
+ if (c === null) throw new Error("AsOklab: no argument");
62046
+ return `_SYS.asOklab(${compile3(c)})`;
62047
+ },
62048
+ AsOklch: ([c], compile3) => {
62049
+ if (c === null) throw new Error("AsOklch: no argument");
62050
+ return compile3(c);
62051
+ },
62052
+ // Perceptual color difference (ΔE_OK).
62053
+ ColorDelta: ([a, b], compile3) => {
62054
+ if (a === null || b === null)
62055
+ throw new Error("ColorDelta: need two colors");
62056
+ return `_SYS.colorDelta(${compile3(a)}, ${compile3(b)})`;
62057
+ },
62058
+ // Euclidean distance between two tuples (any positive dimension).
62059
+ // The GPU target maps `Distance` to the GLSL/WGSL `distance()` builtin
62060
+ // (vec-only); this JS handler works on plain arrays of any length.
62061
+ Distance: ([a, b], compile3) => {
62062
+ if (a === null || b === null) throw new Error("Distance: need two points");
62063
+ return `_SYS.distance(${compile3(a)}, ${compile3(b)})`;
60837
62064
  }
60838
62065
  };
60839
62066
  function toRI(c) {
60840
62067
  return { re: c.re, im: c.im };
60841
62068
  }
62069
+ function normalizeAlpha2(a) {
62070
+ if (a === void 0) return void 0;
62071
+ if (!Number.isFinite(a)) return void 0;
62072
+ if (Math.abs(a - 1) < 1e-9) return void 0;
62073
+ return a;
62074
+ }
60842
62075
  function toRgb255(input) {
60843
62076
  if (typeof input === "string") {
60844
62077
  const c = parseColor(input);
60845
- return {
62078
+ const rgb2 = {
60846
62079
  r: c >>> 24 & 255,
60847
62080
  g: c >>> 16 & 255,
60848
- b: c >>> 8 & 255,
60849
- alpha: (c & 255) / 255
62081
+ b: c >>> 8 & 255
60850
62082
  };
62083
+ const alpha = normalizeAlpha2((c & 255) / 255);
62084
+ if (alpha !== void 0) rgb2.alpha = alpha;
62085
+ return rgb2;
62086
+ }
62087
+ const rgb = oklchToRgb({ L: input[0], C: input[1], H: input[2] });
62088
+ if (input.length >= 4) {
62089
+ const alpha = normalizeAlpha2(input[3]);
62090
+ if (alpha !== void 0) rgb.alpha = alpha;
60851
62091
  }
60852
- const rgb = {
60853
- r: input[0] * 255,
60854
- g: input[1] * 255,
60855
- b: input[2] * 255
60856
- };
60857
- if (input.length >= 4) rgb.alpha = input[3];
60858
62092
  return rgb;
60859
62093
  }
60860
- function packedToArray(c) {
60861
- const r = (c >>> 24 & 255) / 255;
60862
- const g = (c >>> 16 & 255) / 255;
60863
- const b = (c >>> 8 & 255) / 255;
60864
- const a = (c & 255) / 255;
60865
- return Math.abs(a - 1) < 1e-4 ? [r, g, b] : [r, g, b, a];
62094
+ function toOklch2(input) {
62095
+ if (typeof input === "string") {
62096
+ const c = parseColor(input);
62097
+ const r = c >>> 24 & 255;
62098
+ const g = c >>> 16 & 255;
62099
+ const b = c >>> 8 & 255;
62100
+ const oklch2 = rgbToOklch({ r, g, b });
62101
+ const alpha = normalizeAlpha2((c & 255) / 255);
62102
+ if (alpha !== void 0) oklch2.alpha = alpha;
62103
+ return oklch2;
62104
+ }
62105
+ return {
62106
+ L: input[0],
62107
+ C: input[1],
62108
+ H: input[2],
62109
+ alpha: input.length >= 4 ? normalizeAlpha2(input[3]) : void 0
62110
+ };
62111
+ }
62112
+ function packedToOklch(c) {
62113
+ const r = c >>> 24 & 255;
62114
+ const g = c >>> 16 & 255;
62115
+ const b = c >>> 8 & 255;
62116
+ const oklch2 = rgbToOklch({ r, g, b });
62117
+ const alpha = normalizeAlpha2((c & 255) / 255);
62118
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
60866
62119
  }
60867
62120
  var colorHelpers = {
60868
62121
  color(input) {
60869
- return packedToArray(parseColor(input));
62122
+ return packedToOklch(parseColor(input));
60870
62123
  },
60871
62124
  colorToString(input, format) {
60872
62125
  const rgb = toRgb255(input);
@@ -60877,7 +62130,7 @@ Error in definition of "${name}"`,
60877
62130
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
60878
62131
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
60879
62132
  let hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
60880
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
62133
+ if (rgb.alpha !== void 0) {
60881
62134
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
60882
62135
  hex += a.toString(16).padStart(2, "0");
60883
62136
  }
@@ -60887,7 +62140,7 @@ Error in definition of "${name}"`,
60887
62140
  const r = Math.round(rgb.r);
60888
62141
  const g = Math.round(rgb.g);
60889
62142
  const b = Math.round(rgb.b);
60890
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
62143
+ if (rgb.alpha !== void 0)
60891
62144
  return `rgb(${r} ${g} ${b} / ${rgb.alpha})`;
60892
62145
  return `rgb(${r} ${g} ${b})`;
60893
62146
  }
@@ -60896,7 +62149,7 @@ Error in definition of "${name}"`,
60896
62149
  const h = Math.round(hsl.h * 10) / 10;
60897
62150
  const s = Math.round(hsl.s * 1e3) / 10;
60898
62151
  const l = Math.round(hsl.l * 1e3) / 10;
60899
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
62152
+ if (rgb.alpha !== void 0)
60900
62153
  return `hsl(${h} ${s}% ${l}% / ${rgb.alpha})`;
60901
62154
  return `hsl(${h} ${s}% ${l}%)`;
60902
62155
  }
@@ -60905,7 +62158,7 @@ Error in definition of "${name}"`,
60905
62158
  const L = Math.round(c.L * 1e3) / 1e3;
60906
62159
  const C = Math.round(c.C * 1e3) / 1e3;
60907
62160
  const H = Math.round(c.H * 10) / 10;
60908
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
62161
+ if (rgb.alpha !== void 0)
60909
62162
  return `oklch(${L} ${C} ${H} / ${rgb.alpha})`;
60910
62163
  return `oklch(${L} ${C} ${H})`;
60911
62164
  }
@@ -60914,29 +62167,29 @@ Error in definition of "${name}"`,
60914
62167
  }
60915
62168
  },
60916
62169
  colorMix(input1, input2, ratio = 0.5) {
60917
- const rgb1 = toRgb255(input1);
60918
- const rgb2 = toRgb255(input2);
62170
+ const c1 = toOklch2(input1);
62171
+ const c2 = toOklch2(input2);
60919
62172
  ratio = Math.max(0, Math.min(1, ratio));
60920
- const c1 = rgbToOklch(rgb1);
60921
- const c2 = rgbToOklch(rgb2);
60922
- let dh = c2.H - c1.H;
60923
- if (dh > 180) dh -= 360;
60924
- if (dh < -180) dh += 360;
60925
- let H = c1.H + dh * ratio;
60926
- if (H < 0) H += 360;
60927
- if (H >= 360) H -= 360;
60928
- const mixed = oklchToRgb({
60929
- L: c1.L + (c2.L - c1.L) * ratio,
60930
- C: c1.C + (c2.C - c1.C) * ratio,
60931
- H
60932
- });
60933
- const r = mixed.r / 255;
60934
- const g = mixed.g / 255;
60935
- const b = mixed.b / 255;
60936
- const a1 = rgb1.alpha ?? 1;
60937
- const a2 = rgb2.alpha ?? 1;
60938
- const alpha = a1 + (a2 - a1) * ratio;
60939
- return Math.abs(alpha - 1) > 1e-4 ? [r, g, b, alpha] : [r, g, b];
62173
+ const c1Achromatic = c1.C < 1e-6;
62174
+ const c2Achromatic = c2.C < 1e-6;
62175
+ let H;
62176
+ if (c1Achromatic && c2Achromatic) H = c1.H;
62177
+ else if (c1Achromatic) H = c2.H;
62178
+ else if (c2Achromatic) H = c1.H;
62179
+ else {
62180
+ let dh = c2.H - c1.H;
62181
+ if (dh > 180) dh -= 360;
62182
+ if (dh < -180) dh += 360;
62183
+ H = c1.H + dh * ratio;
62184
+ if (H < 0) H += 360;
62185
+ if (H >= 360) H -= 360;
62186
+ }
62187
+ const L = c1.L + (c2.L - c1.L) * ratio;
62188
+ const C = c1.C + (c2.C - c1.C) * ratio;
62189
+ const a1 = c1.alpha ?? 1;
62190
+ const a2 = c2.alpha ?? 1;
62191
+ const alpha = normalizeAlpha2(a1 + (a2 - a1) * ratio);
62192
+ return alpha !== void 0 ? [L, C, H, alpha] : [L, C, H];
60940
62193
  },
60941
62194
  colorContrast(bg, fg) {
60942
62195
  return apca(toRgb255(bg), toRgb255(fg));
@@ -60944,11 +62197,11 @@ Error in definition of "${name}"`,
60944
62197
  contrastingColor(bg, fg1, fg2) {
60945
62198
  const bgRgb = toRgb255(bg);
60946
62199
  if (fg1 !== void 0 && fg2 !== void 0) {
60947
- return packedToArray(
62200
+ return packedToOklch(
60948
62201
  contrastingColor({ bg: bgRgb, fg1: toRgb255(fg1), fg2: toRgb255(fg2) })
60949
62202
  );
60950
62203
  }
60951
- return packedToArray(contrastingColor(bgRgb));
62204
+ return packedToOklch(contrastingColor(bgRgb));
60952
62205
  },
60953
62206
  colorToColorspace(input, space) {
60954
62207
  const rgb = toRgb255(input);
@@ -60977,7 +62230,7 @@ Error in definition of "${name}"`,
60977
62230
  default:
60978
62231
  throw new Error(`Unknown color space: ${space}`);
60979
62232
  }
60980
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
62233
+ if (alpha !== void 0) result.push(alpha);
60981
62234
  return result;
60982
62235
  },
60983
62236
  colormap(name, arg) {
@@ -60989,7 +62242,7 @@ Error in definition of "${name}"`,
60989
62242
  const palette = allPalettes[name];
60990
62243
  if (!palette) throw new Error(`Unknown palette: ${name}`);
60991
62244
  const colors = palette.map(
60992
- (hex) => parseColorToRgb01(hex)
62245
+ (hex) => packedToOklch(parseColor(hex))
60993
62246
  );
60994
62247
  if (arg === void 0) return colors;
60995
62248
  if (Number.isInteger(arg) && arg >= 2) {
@@ -61013,62 +62266,128 @@ Error in definition of "${name}"`,
61013
62266
  const frac = pos - i;
61014
62267
  if (frac === 0 || i >= colors.length - 1)
61015
62268
  return [...colors[Math.min(i, colors.length - 1)]];
61016
- const rgb1 = {
61017
- r: colors[i][0] * 255,
61018
- g: colors[i][1] * 255,
61019
- b: colors[i][2] * 255
61020
- };
61021
- const rgb2 = {
61022
- r: colors[i + 1][0] * 255,
61023
- g: colors[i + 1][1] * 255,
61024
- b: colors[i + 1][2] * 255
61025
- };
61026
- const c1 = rgbToOklch(rgb1);
61027
- const c2 = rgbToOklch(rgb2);
61028
- let dh = c2.H - c1.H;
61029
- if (dh > 180) dh -= 360;
61030
- if (dh < -180) dh += 360;
61031
- let H = c1.H + dh * frac;
61032
- if (H < 0) H += 360;
61033
- if (H >= 360) H -= 360;
61034
- const mixed = oklchToRgb({
61035
- L: c1.L + (c2.L - c1.L) * frac,
61036
- C: c1.C + (c2.C - c1.C) * frac,
61037
- H
61038
- });
61039
- return [mixed.r / 255, mixed.g / 255, mixed.b / 255];
62269
+ const [L1, C1, H1] = colors[i];
62270
+ const [L2, C2, H2] = colors[i + 1];
62271
+ const c1Achromatic = C1 < 1e-6;
62272
+ const c2Achromatic = C2 < 1e-6;
62273
+ let H;
62274
+ if (c1Achromatic && c2Achromatic) H = H1;
62275
+ else if (c1Achromatic) H = H2;
62276
+ else if (c2Achromatic) H = H1;
62277
+ else {
62278
+ let dh = H2 - H1;
62279
+ if (dh > 180) dh -= 360;
62280
+ if (dh < -180) dh += 360;
62281
+ H = H1 + dh * frac;
62282
+ if (H < 0) H += 360;
62283
+ if (H >= 360) H -= 360;
62284
+ }
62285
+ return [L1 + (L2 - L1) * frac, C1 + (C2 - C1) * frac, H];
61040
62286
  },
61041
62287
  colorFromColorspace(components, space) {
61042
62288
  const c0 = components[0];
61043
62289
  const c1 = components[1];
61044
62290
  const c2 = components[2];
61045
62291
  const alpha = components.length >= 4 ? components[3] : void 0;
61046
- let result;
62292
+ let oklch2;
61047
62293
  switch (space.toLowerCase()) {
61048
62294
  case "rgb":
61049
- result = [c0, c1, c2];
62295
+ oklch2 = rgbToOklch({ r: c0 * 255, g: c1 * 255, b: c2 * 255 });
61050
62296
  break;
61051
62297
  case "hsl": {
61052
- const r = hslToRgb(c0, c1, c2);
61053
- result = [r.r / 255, r.g / 255, r.b / 255];
62298
+ const rgb = hslToRgb(c0, c1, c2);
62299
+ oklch2 = rgbToOklch(rgb);
61054
62300
  break;
61055
62301
  }
61056
- case "oklch": {
61057
- const r = oklchToRgb({ L: c0, C: c1, H: c2 });
61058
- result = [r.r / 255, r.g / 255, r.b / 255];
62302
+ case "oklch":
62303
+ oklch2 = { L: c0, C: c1, H: c2 };
61059
62304
  break;
61060
- }
61061
62305
  case "oklab":
61062
- case "lab": {
61063
- const r = oklabToRgb({ L: c0, a: c1, b: c2 });
61064
- result = [r.r / 255, r.g / 255, r.b / 255];
62306
+ case "lab":
62307
+ oklch2 = oklabToOklch({ L: c0, a: c1, b: c2 });
61065
62308
  break;
61066
- }
61067
62309
  default:
61068
62310
  throw new Error(`Unknown color space: ${space}`);
61069
62311
  }
61070
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
61071
- return result;
62312
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
62313
+ },
62314
+ // -----------------------------------------------------------------------
62315
+ // Color constructors. Each accepts components in its colorspace's natural
62316
+ // units and returns the canonical OKLCh array `[L, C, H]` (or with alpha).
62317
+ // -----------------------------------------------------------------------
62318
+ rgb(r, g, b, alpha) {
62319
+ const c = rgbToOklch({ r: r * 255, g: g * 255, b: b * 255 });
62320
+ const a = normalizeAlpha2(alpha);
62321
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
62322
+ },
62323
+ hsv(h, s, v, alpha) {
62324
+ const rgb = hsvToRgb(h, s, v);
62325
+ const c = rgbToOklch(rgb);
62326
+ const a = normalizeAlpha2(alpha);
62327
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
62328
+ },
62329
+ hsl(h, s, l, alpha) {
62330
+ const rgb = hslToRgb(h, s, l);
62331
+ const c = rgbToOklch({ r: rgb.r, g: rgb.g, b: rgb.b });
62332
+ const a = normalizeAlpha2(alpha);
62333
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
62334
+ },
62335
+ oklab(L, a, b, alpha) {
62336
+ const c = oklabToOklch({ L, a, b });
62337
+ const al = normalizeAlpha2(alpha);
62338
+ return al !== void 0 ? [c.L, c.C, c.H, al] : [c.L, c.C, c.H];
62339
+ },
62340
+ oklch(L, C, H, alpha) {
62341
+ const a = normalizeAlpha2(alpha);
62342
+ return a !== void 0 ? [L, C, H, a] : [L, C, H];
62343
+ },
62344
+ // -----------------------------------------------------------------------
62345
+ // As* converters. Inputs are anything `toOklch` accepts (string, packed
62346
+ // int, or OKLCh array). Outputs are 3- or 4-element arrays in the named
62347
+ // space. sRGB-based outputs (asRgb/asHsv/asHsl) use 0-1 channels for
62348
+ // consistency with the GPU target's shader convention.
62349
+ // -----------------------------------------------------------------------
62350
+ asRgb(input) {
62351
+ const rgb = toRgb255(input);
62352
+ const r = rgb.r / 255;
62353
+ const g = rgb.g / 255;
62354
+ const b = rgb.b / 255;
62355
+ return rgb.alpha !== void 0 ? [r, g, b, rgb.alpha] : [r, g, b];
62356
+ },
62357
+ asHsv(input) {
62358
+ const rgb = toRgb255(input);
62359
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
62360
+ return rgb.alpha !== void 0 ? [hsv.h, hsv.s, hsv.v, rgb.alpha] : [hsv.h, hsv.s, hsv.v];
62361
+ },
62362
+ asHsl(input) {
62363
+ const rgb = toRgb255(input);
62364
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
62365
+ return rgb.alpha !== void 0 ? [hsl.h, hsl.s, hsl.l, rgb.alpha] : [hsl.h, hsl.s, hsl.l];
62366
+ },
62367
+ asOklab(input) {
62368
+ const c = toOklch2(input);
62369
+ const lab = oklchToOklab({ L: c.L, C: c.C, H: c.H });
62370
+ return c.alpha !== void 0 ? [lab.L, lab.a, lab.b, c.alpha] : [lab.L, lab.a, lab.b];
62371
+ },
62372
+ // asOklch is identity — handled at compile time as a pass-through
62373
+ // Perceptual color difference (ΔE_OK).
62374
+ colorDelta(a, b) {
62375
+ const labA = oklchToOklab(toOklch2(a));
62376
+ const labB = oklchToOklab(toOklch2(b));
62377
+ return oklabDeltaE(labA, labB);
62378
+ },
62379
+ // Euclidean distance between two tuples. Plain numeric — not a color
62380
+ // operation despite living in the same helpers block.
62381
+ distance(a, b) {
62382
+ if (!Array.isArray(a) || !Array.isArray(b))
62383
+ throw new Error("Distance: expected two arrays");
62384
+ if (a.length !== b.length) throw new Error("Distance: dimension mismatch");
62385
+ let sumSq = 0;
62386
+ for (let i = 0; i < a.length; i++) {
62387
+ const d = a[i] - b[i];
62388
+ sumSq += d * d;
62389
+ }
62390
+ return Math.sqrt(sumSq);
61072
62391
  }
61073
62392
  };
61074
62393
  var SYS_HELPERS = {
@@ -61507,6 +62826,13 @@ Error in definition of "${name}"`,
61507
62826
  function gpuVec2(target) {
61508
62827
  return target?.language === "wgsl" ? "vec2f" : "vec2";
61509
62828
  }
62829
+ function gpuVec3(target) {
62830
+ return target?.language === "wgsl" ? "vec3f" : "vec3";
62831
+ }
62832
+ function readStringLiteral(expr2) {
62833
+ if (!isString(expr2)) return null;
62834
+ return expr2.string?.toLowerCase() ?? null;
62835
+ }
61510
62836
  function compileIntArg(expr2, compile3, target) {
61511
62837
  const c = tryGetConstant(expr2);
61512
62838
  if (c !== void 0 && Number.isInteger(c)) return c.toString();
@@ -61565,7 +62891,7 @@ Error in definition of "${name}"`,
61565
62891
  `for (${indexDecl} = ${lowerStr}; ${index} <= ${upperStr}; ${index}++) {`,
61566
62892
  ` ${acc} ${op}= ${body};`,
61567
62893
  `}`,
61568
- `return ${acc}`
62894
+ `return ${acc};`
61569
62895
  ];
61570
62896
  return lines.join("\n");
61571
62897
  }
@@ -61619,8 +62945,7 @@ Error in definition of "${name}"`,
61619
62945
  const iScale = isSymbol2(iFactor, "ImaginaryUnit") ? 1 : iFactor.im;
61620
62946
  const realFactors = args.filter((_, i) => i !== iIndex);
61621
62947
  const v2 = gpuVec2(target);
61622
- if (realFactors.length === 0)
61623
- return `${v2}(0.0, ${formatFloat(iScale)})`;
62948
+ if (realFactors.length === 0) return `${v2}(0.0, ${formatFloat(iScale)})`;
61624
62949
  const factors = realFactors.map((f) => compile3(f));
61625
62950
  if (iScale !== 1) factors.unshift(formatFloat(iScale));
61626
62951
  const imCode = foldTerms(factors, "1.0", "*");
@@ -61673,8 +62998,7 @@ Error in definition of "${name}"`,
61673
62998
  if (isNumber(x) && x.im !== 0) {
61674
62999
  return `${gpuVec2(target)}(${formatFloat(-x.re)}, ${formatFloat(-x.im)})`;
61675
63000
  }
61676
- if (isSymbol2(x, "ImaginaryUnit"))
61677
- return `${gpuVec2(target)}(0.0, -1.0)`;
63001
+ if (isSymbol2(x, "ImaginaryUnit")) return `${gpuVec2(target)}(0.0, -1.0)`;
61678
63002
  return `(-${compile3(x)})`;
61679
63003
  },
61680
63004
  // Standard math functions with complex dispatch
@@ -62047,17 +63371,127 @@ Error in definition of "${name}"`,
62047
63371
  }
62048
63372
  const isWGSL = target?.language === "wgsl";
62049
63373
  const v3 = isWGSL ? "vec3f" : "vec3";
62050
- return `((_gpu_apca(${bg}, ${v3}(0.0)) > 50.0) ? ${v3}(0.0) : ${v3}(1.0))`;
63374
+ const black = `${v3}(0.0)`;
63375
+ const white = `${v3}(1.0, 0.0, 0.0)`;
63376
+ return `((_gpu_apca(${bg}, ${black}) > 50.0) ? ${black} : ${white})`;
62051
63377
  },
62052
63378
  ColorToColorspace: ([color, space], compile3) => {
62053
63379
  if (color === null || space === null)
62054
63380
  throw new Error("ColorToColorspace: need color and space");
62055
- return `_gpu_srgb_to_oklab(${compile3(color)})`;
63381
+ const spaceName = readStringLiteral(space);
63382
+ if (spaceName === null)
63383
+ throw new Error("ColorToColorspace: space must be a string literal");
63384
+ const c = compile3(color);
63385
+ switch (spaceName) {
63386
+ case "oklch":
63387
+ return c;
63388
+ case "oklab":
63389
+ case "lab":
63390
+ return `_gpu_oklch_to_oklab(${c})`;
63391
+ case "rgb":
63392
+ return `_gpu_oklch_to_srgb(${c})`;
63393
+ case "hsl":
63394
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${c}))`;
63395
+ case "hsv":
63396
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${c}))`;
63397
+ default:
63398
+ throw new Error(
63399
+ `ColorToColorspace: unsupported space "${spaceName}" on GPU target`
63400
+ );
63401
+ }
62056
63402
  },
62057
63403
  ColorFromColorspace: ([components, space], compile3) => {
62058
63404
  if (components === null || space === null)
62059
63405
  throw new Error("ColorFromColorspace: need components and space");
62060
- return `_gpu_oklab_to_srgb(${compile3(components)})`;
63406
+ const spaceName = readStringLiteral(space);
63407
+ if (spaceName === null)
63408
+ throw new Error("ColorFromColorspace: space must be a string literal");
63409
+ const c = compile3(components);
63410
+ switch (spaceName) {
63411
+ case "oklch":
63412
+ return c;
63413
+ case "oklab":
63414
+ case "lab":
63415
+ return `_gpu_oklab_to_oklch(${c})`;
63416
+ case "rgb":
63417
+ return `_gpu_srgb_to_oklch(${c})`;
63418
+ case "hsl":
63419
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${c}))`;
63420
+ case "hsv":
63421
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${c}))`;
63422
+ default:
63423
+ throw new Error(
63424
+ `ColorFromColorspace: unsupported space "${spaceName}" on GPU target`
63425
+ );
63426
+ }
63427
+ },
63428
+ // ---------------------------------------------------------------------------
63429
+ // Color literals. Each typed head compiles to a canonical OKLCh vec3.
63430
+ // Alpha (4th argument) is dropped — GPU color values are vec3 only. Pass
63431
+ // alpha as a separate uniform if it's needed at the framebuffer boundary.
63432
+ // ---------------------------------------------------------------------------
63433
+ Color: ([s], _compile2, target) => {
63434
+ if (s === null) throw new Error("Color: no argument");
63435
+ const str = readStringLiteral(s);
63436
+ if (str === null)
63437
+ throw new Error("Color: argument must be a string literal on GPU target");
63438
+ const packed = parseColor(str);
63439
+ if (packed === 0 && str.trim().toLowerCase() !== "transparent")
63440
+ throw new Error(`Color: invalid color string "${str}"`);
63441
+ const r = packed >>> 24 & 255;
63442
+ const g = packed >>> 16 & 255;
63443
+ const b = packed >>> 8 & 255;
63444
+ const oklch2 = rgbToOklch({ r, g, b });
63445
+ return `${gpuVec3(target)}(${formatFloat(oklch2.L)}, ${formatFloat(oklch2.C)}, ${formatFloat(oklch2.H)})`;
63446
+ },
63447
+ Rgb: (args, compile3, target) => {
63448
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
63449
+ const v3 = gpuVec3(target);
63450
+ return `_gpu_srgb_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
63451
+ },
63452
+ Hsv: (args, compile3, target) => {
63453
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
63454
+ const v3 = gpuVec3(target);
63455
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
63456
+ },
63457
+ Hsl: (args, compile3, target) => {
63458
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
63459
+ const v3 = gpuVec3(target);
63460
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
63461
+ },
63462
+ Oklab: (args, compile3, target) => {
63463
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
63464
+ const v3 = gpuVec3(target);
63465
+ return `_gpu_oklab_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
63466
+ },
63467
+ Oklch: (args, compile3, target) => {
63468
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
63469
+ const v3 = gpuVec3(target);
63470
+ return `${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})`;
63471
+ },
63472
+ // ---------------------------------------------------------------------------
63473
+ // As* operators. AsOklch is identity (canonical). The other As* return
63474
+ // components in the named space, equivalent to ColorToColorspace(c, 'x').
63475
+ // ---------------------------------------------------------------------------
63476
+ AsOklch: ([c], compile3) => {
63477
+ if (c === null) throw new Error("AsOklch: no argument");
63478
+ return compile3(c);
63479
+ },
63480
+ AsOklab: ([c], compile3) => {
63481
+ if (c === null) throw new Error("AsOklab: no argument");
63482
+ return `_gpu_oklch_to_oklab(${compile3(c)})`;
63483
+ },
63484
+ AsRgb: ([c], compile3) => {
63485
+ if (c === null) throw new Error("AsRgb: no argument");
63486
+ return `_gpu_oklch_to_srgb(${compile3(c)})`;
63487
+ },
63488
+ AsHsv: ([c], compile3) => {
63489
+ if (c === null) throw new Error("AsHsv: no argument");
63490
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${compile3(c)}))`;
63491
+ },
63492
+ AsHsl: ([c], compile3) => {
63493
+ if (c === null) throw new Error("AsHsl: no argument");
63494
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${compile3(c)}))`;
62061
63495
  },
62062
63496
  // Fractal functions
62063
63497
  Mandelbrot: ([c, maxIter], compile3, target) => {
@@ -62750,28 +64184,124 @@ vec3 _gpu_oklab_to_srgb(vec3 lab) {
62750
64184
 
62751
64185
  vec3 _gpu_oklab_to_oklch(vec3 lab) {
62752
64186
  float C = length(lab.yz);
62753
- float H = atan(lab.z, lab.y);
64187
+ float H = atan(lab.z, lab.y) * (180.0 / 3.14159265359);
64188
+ if (H < 0.0) H += 360.0;
62754
64189
  return vec3(lab.x, C, H);
62755
64190
  }
62756
64191
 
62757
64192
  vec3 _gpu_oklch_to_oklab(vec3 lch) {
62758
- return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
64193
+ float h_rad = lch.z * (3.14159265359 / 180.0);
64194
+ return vec3(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
64195
+ }
64196
+
64197
+ vec3 _gpu_srgb_to_oklch(vec3 rgb) {
64198
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
62759
64199
  }
62760
64200
 
62761
- vec3 _gpu_color_mix(vec3 rgb1, vec3 rgb2, float t) {
62762
- vec3 lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
62763
- vec3 lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
64201
+ vec3 _gpu_oklch_to_srgb(vec3 lch) {
64202
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
64203
+ }
64204
+
64205
+ // HSL conversion. Hue in degrees, saturation/lightness in 0-1.
64206
+ vec3 _gpu_hsl_to_rgb(vec3 hsl) {
64207
+ float h = hsl.x;
64208
+ float s = hsl.y;
64209
+ float l = hsl.z;
64210
+ float c = (1.0 - abs(2.0 * l - 1.0)) * s;
64211
+ float h6 = h / 60.0;
64212
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
64213
+ float r = 0.0;
64214
+ float g = 0.0;
64215
+ float b = 0.0;
64216
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64217
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64218
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64219
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64220
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64221
+ else { r = c; g = 0.0; b = x; }
64222
+ float m = l - c / 2.0;
64223
+ return vec3(r + m, g + m, b + m);
64224
+ }
64225
+
64226
+ vec3 _gpu_rgb_to_hsl(vec3 rgb) {
64227
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
64228
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
64229
+ float l = (maxc + minc) / 2.0;
64230
+ float d = maxc - minc;
64231
+ if (d < 1e-6) return vec3(0.0, 0.0, l);
64232
+ float s = d / (1.0 - abs(2.0 * l - 1.0));
64233
+ float h;
64234
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
64235
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
64236
+ else h = (rgb.x - rgb.y) / d + 4.0;
64237
+ h *= 60.0;
64238
+ if (h < 0.0) h += 360.0;
64239
+ return vec3(h, s, l);
64240
+ }
64241
+
64242
+ // HSV conversion. Hue in degrees, saturation/value in 0-1.
64243
+ vec3 _gpu_hsv_to_rgb(vec3 hsv) {
64244
+ float h = hsv.x;
64245
+ float s = hsv.y;
64246
+ float v = hsv.z;
64247
+ float c = v * s;
64248
+ float h6 = h / 60.0;
64249
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
64250
+ float r = 0.0;
64251
+ float g = 0.0;
64252
+ float b = 0.0;
64253
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64254
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64255
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64256
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64257
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64258
+ else { r = c; g = 0.0; b = x; }
64259
+ float m = v - c;
64260
+ return vec3(r + m, g + m, b + m);
64261
+ }
64262
+
64263
+ vec3 _gpu_rgb_to_hsv(vec3 rgb) {
64264
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
64265
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
64266
+ float v = maxc;
64267
+ float d = maxc - minc;
64268
+ if (d < 1e-6) return vec3(0.0, 0.0, v);
64269
+ float s = (maxc < 1e-6) ? 0.0 : d / maxc;
64270
+ float h;
64271
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
64272
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
64273
+ else h = (rgb.x - rgb.y) / d + 4.0;
64274
+ h *= 60.0;
64275
+ if (h < 0.0) h += 360.0;
64276
+ return vec3(h, s, v);
64277
+ }
64278
+
64279
+ vec3 _gpu_color_mix(vec3 lch1, vec3 lch2, float t) {
62764
64280
  float L = mix(lch1.x, lch2.x, t);
62765
64281
  float C = mix(lch1.y, lch2.y, t);
62766
- float dh = lch2.z - lch1.z;
62767
- const float PI = 3.14159265359;
62768
- if (dh > PI) dh -= 2.0 * PI;
62769
- if (dh < -PI) dh += 2.0 * PI;
62770
- float H = lch1.z + dh * t;
62771
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3(L, C, H)));
64282
+ bool a1 = lch1.y < 1e-6;
64283
+ bool a2 = lch2.y < 1e-6;
64284
+ float H;
64285
+ if (a1 && a2) {
64286
+ H = lch1.z;
64287
+ } else if (a1) {
64288
+ H = lch2.z;
64289
+ } else if (a2) {
64290
+ H = lch1.z;
64291
+ } else {
64292
+ float dh = lch2.z - lch1.z;
64293
+ if (dh > 180.0) dh -= 360.0;
64294
+ if (dh < -180.0) dh += 360.0;
64295
+ H = lch1.z + dh * t;
64296
+ if (H < 0.0) H += 360.0;
64297
+ if (H >= 360.0) H -= 360.0;
64298
+ }
64299
+ return vec3(L, C, H);
62772
64300
  }
62773
64301
 
62774
- float _gpu_apca(vec3 bg, vec3 fg) {
64302
+ float _gpu_apca(vec3 lch_bg, vec3 lch_fg) {
64303
+ vec3 bg = _gpu_oklch_to_srgb(lch_bg);
64304
+ vec3 fg = _gpu_oklch_to_srgb(lch_fg);
62775
64305
  float bgR = _gpu_srgb_to_linear(bg.x);
62776
64306
  float bgG = _gpu_srgb_to_linear(bg.y);
62777
64307
  float bgB = _gpu_srgb_to_linear(bg.z);
@@ -62782,9 +64312,7 @@ float _gpu_apca(vec3 bg, vec3 fg) {
62782
64312
  float fgY = 0.2126729 * fgR + 0.7151522 * fgG + 0.0721750 * fgB;
62783
64313
  float bgC = pow(bgY, 0.56);
62784
64314
  float fgC = pow(fgY, 0.57);
62785
- float contrast = (bgC > fgC)
62786
- ? (bgC - fgC) * 1.14
62787
- : (bgC - fgC) * 1.14;
64315
+ float contrast = (bgC - fgC) * 1.14;
62788
64316
  return contrast * 100.0;
62789
64317
  }
62790
64318
  `;
@@ -62828,28 +64356,133 @@ fn _gpu_oklab_to_srgb(lab: vec3f) -> vec3f {
62828
64356
 
62829
64357
  fn _gpu_oklab_to_oklch(lab: vec3f) -> vec3f {
62830
64358
  let C = length(lab.yz);
62831
- let H = atan2(lab.z, lab.y);
64359
+ var H = atan2(lab.z, lab.y) * (180.0 / 3.14159265359);
64360
+ if (H < 0.0) { H = H + 360.0; }
62832
64361
  return vec3f(lab.x, C, H);
62833
64362
  }
62834
64363
 
62835
64364
  fn _gpu_oklch_to_oklab(lch: vec3f) -> vec3f {
62836
- return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
64365
+ let h_rad = lch.z * (3.14159265359 / 180.0);
64366
+ return vec3f(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
64367
+ }
64368
+
64369
+ fn _gpu_srgb_to_oklch(rgb: vec3f) -> vec3f {
64370
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
64371
+ }
64372
+
64373
+ fn _gpu_oklch_to_srgb(lch: vec3f) -> vec3f {
64374
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
64375
+ }
64376
+
64377
+ fn _gpu_hsl_to_rgb(hsl: vec3f) -> vec3f {
64378
+ let h = hsl.x;
64379
+ let s = hsl.y;
64380
+ let l = hsl.z;
64381
+ let c = (1.0 - abs(2.0 * l - 1.0)) * s;
64382
+ let h6 = h / 60.0;
64383
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
64384
+ var r: f32 = 0.0;
64385
+ var g: f32 = 0.0;
64386
+ var b: f32 = 0.0;
64387
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64388
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64389
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64390
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64391
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64392
+ else { r = c; g = 0.0; b = x; }
64393
+ let m = l - c / 2.0;
64394
+ return vec3f(r + m, g + m, b + m);
62837
64395
  }
62838
64396
 
62839
- fn _gpu_color_mix(rgb1: vec3f, rgb2: vec3f, t: f32) -> vec3f {
62840
- let lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
62841
- let lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
64397
+ fn _gpu_rgb_to_hsl(rgb: vec3f) -> vec3f {
64398
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
64399
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
64400
+ let l = (maxc + minc) / 2.0;
64401
+ let d = maxc - minc;
64402
+ if (d < 1e-6) { return vec3f(0.0, 0.0, l); }
64403
+ let s = d / (1.0 - abs(2.0 * l - 1.0));
64404
+ var h: f32;
64405
+ if (maxc == rgb.x) {
64406
+ let v = (rgb.y - rgb.z) / d;
64407
+ h = v - 6.0 * floor(v / 6.0);
64408
+ } else if (maxc == rgb.y) {
64409
+ h = (rgb.z - rgb.x) / d + 2.0;
64410
+ } else {
64411
+ h = (rgb.x - rgb.y) / d + 4.0;
64412
+ }
64413
+ h = h * 60.0;
64414
+ if (h < 0.0) { h = h + 360.0; }
64415
+ return vec3f(h, s, l);
64416
+ }
64417
+
64418
+ fn _gpu_hsv_to_rgb(hsv: vec3f) -> vec3f {
64419
+ let h = hsv.x;
64420
+ let s = hsv.y;
64421
+ let v = hsv.z;
64422
+ let c = v * s;
64423
+ let h6 = h / 60.0;
64424
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
64425
+ var r: f32 = 0.0;
64426
+ var g: f32 = 0.0;
64427
+ var b: f32 = 0.0;
64428
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64429
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64430
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64431
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64432
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64433
+ else { r = c; g = 0.0; b = x; }
64434
+ let m = v - c;
64435
+ return vec3f(r + m, g + m, b + m);
64436
+ }
64437
+
64438
+ fn _gpu_rgb_to_hsv(rgb: vec3f) -> vec3f {
64439
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
64440
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
64441
+ let v = maxc;
64442
+ let d = maxc - minc;
64443
+ if (d < 1e-6) { return vec3f(0.0, 0.0, v); }
64444
+ var s: f32 = 0.0;
64445
+ if (maxc >= 1e-6) { s = d / maxc; }
64446
+ var h: f32;
64447
+ if (maxc == rgb.x) {
64448
+ let q = (rgb.y - rgb.z) / d;
64449
+ h = q - 6.0 * floor(q / 6.0);
64450
+ } else if (maxc == rgb.y) {
64451
+ h = (rgb.z - rgb.x) / d + 2.0;
64452
+ } else {
64453
+ h = (rgb.x - rgb.y) / d + 4.0;
64454
+ }
64455
+ h = h * 60.0;
64456
+ if (h < 0.0) { h = h + 360.0; }
64457
+ return vec3f(h, s, v);
64458
+ }
64459
+
64460
+ fn _gpu_color_mix(lch1: vec3f, lch2: vec3f, t: f32) -> vec3f {
62842
64461
  let L = mix(lch1.x, lch2.x, t);
62843
64462
  let C = mix(lch1.y, lch2.y, t);
62844
- let PI = 3.14159265359;
62845
- var dh = lch2.z - lch1.z;
62846
- if (dh > PI) { dh -= 2.0 * PI; }
62847
- if (dh < -PI) { dh += 2.0 * PI; }
62848
- let H = lch1.z + dh * t;
62849
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3f(L, C, H)));
64463
+ let a1 = lch1.y < 1e-6;
64464
+ let a2 = lch2.y < 1e-6;
64465
+ var H: f32;
64466
+ if (a1 && a2) {
64467
+ H = lch1.z;
64468
+ } else if (a1) {
64469
+ H = lch2.z;
64470
+ } else if (a2) {
64471
+ H = lch1.z;
64472
+ } else {
64473
+ var dh = lch2.z - lch1.z;
64474
+ if (dh > 180.0) { dh = dh - 360.0; }
64475
+ if (dh < -180.0) { dh = dh + 360.0; }
64476
+ H = lch1.z + dh * t;
64477
+ if (H < 0.0) { H = H + 360.0; }
64478
+ if (H >= 360.0) { H = H - 360.0; }
64479
+ }
64480
+ return vec3f(L, C, H);
62850
64481
  }
62851
64482
 
62852
- fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
64483
+ fn _gpu_apca(lch_bg: vec3f, lch_fg: vec3f) -> f32 {
64484
+ let bg = _gpu_oklch_to_srgb(lch_bg);
64485
+ let fg = _gpu_oklch_to_srgb(lch_fg);
62853
64486
  let bgR = _gpu_srgb_to_linear(bg.x);
62854
64487
  let bgG = _gpu_srgb_to_linear(bg.y);
62855
64488
  let bgB = _gpu_srgb_to_linear(bg.z);
@@ -63137,7 +64770,7 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63137
64770
  if (stmts.length === 0) return "";
63138
64771
  const last = stmts.length - 1;
63139
64772
  stmts[last] = `return ${stmts[last]}`;
63140
- return stmts.join(";\n");
64773
+ return stmts.join(";\n") + ";";
63141
64774
  },
63142
64775
  ...options
63143
64776
  };
@@ -63236,7 +64869,7 @@ fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
63236
64869
  if (body.includes("\n")) {
63237
64870
  const indented = body.split("\n").map((l) => ` ${l}`).join("\n");
63238
64871
  return `${returnType} ${functionName}(${params}) {
63239
- ${indented};
64872
+ ${indented}
63240
64873
  }`;
63241
64874
  }
63242
64875
  return `${returnType} ${functionName}(${params}) {
@@ -63347,7 +64980,7 @@ ${indented};
63347
64980
  return `fn ${functionName}(${params}) -> ${toWGSLType(
63348
64981
  returnType
63349
64982
  )} {
63350
- ${indented};
64983
+ ${indented}
63351
64984
  }`;
63352
64985
  }
63353
64986
  return `fn ${functionName}(${params}) -> ${toWGSLType(returnType)} {
@@ -67754,6 +69387,7 @@ ${code}`;
67754
69387
  this.pushScope(void 0, "global");
67755
69388
  this._compilationTargets.registerDefaults();
67756
69389
  if (options?.latexSyntax) this._latexSyntax = options.latexSyntax;
69390
+ if (options?.latexOptions) this._latexOptions = { ...options.latexOptions };
67757
69391
  hidePrivateProperties(this);
67758
69392
  }
67759
69393
  toJSON() {
@@ -68168,6 +69802,15 @@ ${code}`;
68168
69802
  lookupDefinition(id) {
68169
69803
  return lookupDefinition(this, id);
68170
69804
  }
69805
+ operatorInfo(head) {
69806
+ const def = this.lookupDefinition(head);
69807
+ if (!def || !isOperatorDef(def)) return void 0;
69808
+ const op = def.operator;
69809
+ return {
69810
+ kind: op.evaluate || op.collection ? "function" : "opaque",
69811
+ signature: op.signature
69812
+ };
69813
+ }
68171
69814
  /**
68172
69815
  * Associate a new definition to a symbol in the current context.
68173
69816
  *
@@ -68414,6 +70057,29 @@ ${code}`;
68414
70057
  );
68415
70058
  return this._latexSyntax;
68416
70059
  }
70060
+ /** @internal Engine-wide LaTeX parse/serialize options (e.g. decimalSeparator).
70061
+ * Merged into every `parse()` and `toLatex()` call between the LatexSyntax
70062
+ * instance defaults and any per-call overrides. */
70063
+ _latexOptions = {};
70064
+ /** Engine-wide LaTeX parse/serialize options.
70065
+ *
70066
+ * These options are merged into every `parse()` and `toLatex()` call.
70067
+ * Precedence (most-specific wins):
70068
+ * 1. LatexSyntax instance defaults (set at its construction)
70069
+ * 2. `ce.latexOptions` (this property)
70070
+ * 3. Per-call options passed to `ce.parse()` / `expr.toLatex()`
70071
+ *
70072
+ * Assigning replaces the whole bag. Use spread to merge:
70073
+ * ```ts
70074
+ * ce.latexOptions = { ...ce.latexOptions, decimalSeparator: '{,}' };
70075
+ * ```
70076
+ */
70077
+ get latexOptions() {
70078
+ return this._latexOptions;
70079
+ }
70080
+ set latexOptions(options) {
70081
+ this._latexOptions = { ...options };
70082
+ }
68417
70083
  parse(latex, options) {
68418
70084
  if (latex === null || latex === void 0) return null;
68419
70085
  if (typeof latex !== "string")
@@ -68421,7 +70087,6 @@ ${code}`;
68421
70087
  const syntax = this._requireLatexSyntax();
68422
70088
  const { form, ...parseOpts } = options ?? {};
68423
70089
  const result = syntax.parse(latex, {
68424
- decimalSeparator: ".",
68425
70090
  getSymbolType: (id) => {
68426
70091
  const def = this.lookupDefinition(id);
68427
70092
  if (!def) return BoxedType.unknown;
@@ -68433,6 +70098,8 @@ ${code}`;
68433
70098
  const def = this.lookupDefinition(id);
68434
70099
  return !!(isValueDef(def) && def.value.subscriptEvaluate);
68435
70100
  },
70101
+ tolerance: this.tolerance,
70102
+ ...this._latexOptions,
68436
70103
  ...parseOpts
68437
70104
  });
68438
70105
  if (result === null) return null;
@@ -68596,14 +70263,14 @@ ${code}`;
68596
70263
  _setDefaultEngineFactory(() => new ComputeEngine());
68597
70264
 
68598
70265
  // src/compute-engine.ts
68599
- var version = "0.55.6";
70266
+ var version = "0.57.0";
68600
70267
  ComputeEngine._latexSyntaxFactory = () => new LatexSyntax();
68601
70268
  _setDefaultEngineFactory(
68602
70269
  () => new ComputeEngine({ latexSyntax: new LatexSyntax() })
68603
70270
  );
68604
70271
  globalThis[/* @__PURE__ */ Symbol.for("io.cortexjs.compute-engine")] = {
68605
70272
  ComputeEngine: ComputeEngine.prototype.constructor,
68606
- version: "0.55.6"
70273
+ version: "0.57.0"
68607
70274
  };
68608
70275
  return __toCommonJS(compute_engine_exports);
68609
70276
  })();