@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
 
3
3
  // node_modules/complex-esm/dist/src/complex.js
4
4
  var cosh = Math.cosh || function(x) {
@@ -2240,6 +2240,7 @@ var SCALAR_TYPES = [
2240
2240
  ];
2241
2241
  var VALUE_TYPES = [
2242
2242
  "value",
2243
+ "color",
2243
2244
  ...COLLECTION_TYPES,
2244
2245
  ...SCALAR_TYPES
2245
2246
  ];
@@ -3939,6 +3940,7 @@ var PRIMITIVE_SUBTYPES = {
3939
3940
  symbol: [],
3940
3941
  boolean: [],
3941
3942
  string: [],
3943
+ color: [],
3942
3944
  expression: EXPRESSION_TYPES
3943
3945
  };
3944
3946
  function isPrimitiveSubtype(lhs, rhs) {
@@ -7822,9 +7824,10 @@ var _BoxedExpression = class __BoxedExpression {
7822
7824
  if (!materialized.isLazyCollection) return materialized.latex;
7823
7825
  }
7824
7826
  const syntax = this.engine._requireLatexSyntax();
7825
- return syntax.serialize(
7826
- this.toMathJson({ prettify: true, fractionalDigits: "auto" })
7827
- );
7827
+ const json = this.toMathJson({ prettify: true, fractionalDigits: "auto" });
7828
+ const latexOpts = this.engine.latexOptions;
7829
+ if (Object.keys(latexOpts).length === 0) return syntax.serialize(json);
7830
+ return syntax.serialize(json, { ...latexOpts });
7828
7831
  }
7829
7832
  /**
7830
7833
  * Return a LaTeX representation of this expression with custom
@@ -7835,8 +7838,15 @@ var _BoxedExpression = class __BoxedExpression {
7835
7838
  *
7836
7839
  * Numeric values are rounded to `ce.precision` significant digits
7837
7840
  * (via `fractionalDigits: 'auto'`).
7841
+ *
7842
+ * If `options.verbatim` is `true` and `verbatimLatex` is set on this
7843
+ * expression (i.e. it was parsed with `preserveLatex: true`), return
7844
+ * the verbatim source instead of re-serializing. Falls through to
7845
+ * re-serialization if no verbatim is available.
7838
7846
  */
7839
7847
  toLatex(options) {
7848
+ if (options?.verbatim === true && this.verbatimLatex !== void 0)
7849
+ return this.verbatimLatex;
7840
7850
  if (this.isLazyCollection) {
7841
7851
  const materialized = this.evaluate({
7842
7852
  materialization: options?.materialization ?? true
@@ -7848,9 +7858,13 @@ var _BoxedExpression = class __BoxedExpression {
7848
7858
  fractionalDigits: "auto"
7849
7859
  });
7850
7860
  const syntax = this.engine._requireLatexSyntax();
7851
- if (!options || Object.keys(options).length === 0)
7852
- return syntax.serialize(json);
7853
- return syntax.serialize(json, options);
7861
+ const latexOpts = this.engine.latexOptions;
7862
+ const haveEngineOpts = Object.keys(latexOpts).length > 0;
7863
+ const haveCallOpts = options && Object.keys(options).length > 0;
7864
+ if (!haveEngineOpts && !haveCallOpts) return syntax.serialize(json);
7865
+ if (!haveEngineOpts) return syntax.serialize(json, options);
7866
+ if (!haveCallOpts) return syntax.serialize(json, { ...latexOpts });
7867
+ return syntax.serialize(json, { ...latexOpts, ...options });
7854
7868
  }
7855
7869
  /** Called by `JSON.stringify()` when serializing to json.
7856
7870
  *
@@ -7894,11 +7908,13 @@ var _BoxedExpression = class __BoxedExpression {
7894
7908
  "number",
7895
7909
  "dictionary"
7896
7910
  ];
7897
- }
7898
- if (Array.isArray(options.shorthands))
7911
+ } else if (Array.isArray(options.shorthands)) {
7899
7912
  defaultOptions.shorthands = options.shorthands;
7913
+ }
7900
7914
  if (typeof options.metadata === "string" && options.metadata === "all" || options.metadata?.includes("all")) {
7901
7915
  defaultOptions.metadata = ["latex", "wikidata"];
7916
+ } else if (Array.isArray(options.metadata)) {
7917
+ defaultOptions.metadata = options.metadata;
7902
7918
  }
7903
7919
  if (options.fractionalDigits === "auto")
7904
7920
  defaultOptions.fractionalDigits = -this.engine.precision;
@@ -9973,6 +9989,64 @@ function parseQuantifier(kind) {
9973
9989
  }
9974
9990
 
9975
9991
  // src/compute-engine/latex-syntax/dictionary/definitions-core.ts
9992
+ var COMPONENT_ACCESS_HEADS = {
9993
+ x: "First",
9994
+ y: "Second",
9995
+ z: "Third",
9996
+ real: "Real",
9997
+ re: "Real",
9998
+ imag: "Imaginary",
9999
+ im: "Imaginary",
10000
+ count: "Length",
10001
+ total: "Sum",
10002
+ max: "Max",
10003
+ min: "Min"
10004
+ };
10005
+ function memberHead(name) {
10006
+ return COMPONENT_ACCESS_HEADS[name] ?? null;
10007
+ }
10008
+ function parseComponentAccess(parser, lhs) {
10009
+ parser.skipVisualSpace();
10010
+ if (parser.match("\\operatorname")) {
10011
+ const name = parser.parseStringGroup();
10012
+ if (name === null) return null;
10013
+ const head = memberHead(name.trim());
10014
+ if (head === null) return null;
10015
+ return [head, lhs];
10016
+ }
10017
+ const tok = parser.peek;
10018
+ if (typeof tok === "string" && tok.startsWith("\\")) {
10019
+ const bare = tok.slice(1);
10020
+ const head = memberHead(bare);
10021
+ if (head !== null) {
10022
+ parser.nextToken();
10023
+ return [head, lhs];
10024
+ }
10025
+ return null;
10026
+ }
10027
+ if (typeof tok === "string" && /^[a-zA-Z]$/.test(tok)) {
10028
+ const head = memberHead(tok);
10029
+ if (head === null) return null;
10030
+ parser.nextToken();
10031
+ return [head, lhs];
10032
+ }
10033
+ return null;
10034
+ }
10035
+ function parseWhenRestriction(parser, lhs, close) {
10036
+ parser.addBoundary(close);
10037
+ parser.skipVisualSpace();
10038
+ const cond = parser.parseExpression({ minPrec: 0 });
10039
+ if (cond === null) {
10040
+ parser.removeBoundary();
10041
+ return null;
10042
+ }
10043
+ parser.skipVisualSpace();
10044
+ if (!parser.matchBoundary()) {
10045
+ parser.removeBoundary();
10046
+ return null;
10047
+ }
10048
+ return ["When", lhs, cond];
10049
+ }
9976
10050
  function parseSequence(parser, terminator, lhs, prec, sep) {
9977
10051
  if (terminator && terminator.minPrec >= prec) return null;
9978
10052
  const result = lhs ? [lhs] : ["Nothing"];
@@ -10444,6 +10518,15 @@ var DEFINITIONS_CORE = [
10444
10518
  }
10445
10519
  },
10446
10520
  { name: "LatexTokens", serialize: serializeLatexTokens },
10521
+ // Component-access postfix: expr.member (C3)
10522
+ // The '.' trigger is consumed before the parse function is called.
10523
+ // Precedence 850 > 810 (At/indexing) so .x chains tightly.
10524
+ {
10525
+ kind: "postfix",
10526
+ precedence: 850,
10527
+ latexTrigger: ["."],
10528
+ parse: parseComponentAccess
10529
+ },
10447
10530
  {
10448
10531
  name: "At",
10449
10532
  kind: "postfix",
@@ -10464,6 +10547,29 @@ var DEFINITIONS_CORE = [
10464
10547
  latexTrigger: ["\\left", "\\lbrack"],
10465
10548
  parse: parseAt("\\right", "\\rbrack")
10466
10549
  },
10550
+ // When-restriction: `expr\left\{cond\right\}` → `When(expr, cond)` (D3)
10551
+ {
10552
+ name: "When",
10553
+ kind: "postfix",
10554
+ precedence: 800,
10555
+ latexTrigger: ["\\left", "\\{"],
10556
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\right", "\\}"]),
10557
+ serialize: (serializer, expr2) => {
10558
+ const e = operand(expr2, 1);
10559
+ const cond = operand(expr2, 2);
10560
+ if (!e || !cond) return "";
10561
+ const clauses = operator(cond) === "And" ? operands(cond) ?? [] : [cond];
10562
+ const inner = clauses.map((c) => `\\left\\{${serializer.serialize(c)}\\right\\}`).join("");
10563
+ return `${serializer.serialize(e)}${inner}`;
10564
+ }
10565
+ },
10566
+ // When-restriction: bare `expr\{cond\}` → `When(expr, cond)`
10567
+ {
10568
+ kind: "postfix",
10569
+ precedence: 800,
10570
+ latexTrigger: ["\\{"],
10571
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\}"])
10572
+ },
10467
10573
  {
10468
10574
  kind: "postfix",
10469
10575
  latexTrigger: ["_"],
@@ -10546,6 +10652,29 @@ var DEFINITIONS_CORE = [
10546
10652
  return "";
10547
10653
  }
10548
10654
  },
10655
+ // Additional triggers for Range: `...`, `\ldots`, and `\dots` are
10656
+ // equivalent to `..` when used as infix operators (e.g. `[1...9]`).
10657
+ // No `name` field here — names must be unique per the dictionary rules;
10658
+ // the first Range entry owns the name. When there is no LHS the symbol
10659
+ // entries near the top of the file still fire (ContinuationPlaceholder).
10660
+ {
10661
+ latexTrigger: [".", ".", "."],
10662
+ kind: "infix",
10663
+ precedence: 800,
10664
+ parse: parseRange
10665
+ },
10666
+ {
10667
+ latexTrigger: ["\\ldots"],
10668
+ kind: "infix",
10669
+ precedence: 800,
10670
+ parse: parseRange
10671
+ },
10672
+ {
10673
+ latexTrigger: ["\\dots"],
10674
+ kind: "infix",
10675
+ precedence: 800,
10676
+ parse: parseRange
10677
+ },
10549
10678
  {
10550
10679
  latexTrigger: [";"],
10551
10680
  kind: "infix",
@@ -10730,13 +10859,24 @@ var DEFINITIONS_CORE = [
10730
10859
  const args = operands(expr2);
10731
10860
  if (!args || args.length < 2) return "";
10732
10861
  const body = args[0];
10733
- const indexing = args[1];
10734
- if (operator(indexing) === "Element") {
10735
- const index = operand(indexing, 1);
10736
- const range2 = operand(indexing, 2);
10737
- if (operator(range2) === "Range") {
10738
- const lo = operand(range2, 1);
10739
- const hi = operand(range2, 2);
10862
+ const elements = args.slice(1);
10863
+ const allElements = elements.every((e) => operator(e) === "Element");
10864
+ if (!allElements) {
10865
+ return joinLatex([
10866
+ "\\operatorname{Loop}(",
10867
+ serializer.serialize(body),
10868
+ ", ",
10869
+ serializer.serialize(elements[0]),
10870
+ ")"
10871
+ ]);
10872
+ }
10873
+ if (elements.length === 1) {
10874
+ const elem = elements[0];
10875
+ const index = operand(elem, 1);
10876
+ const coll = operand(elem, 2);
10877
+ if (operator(coll) === "Range") {
10878
+ const lo = operand(coll, 1);
10879
+ const hi = operand(coll, 2);
10740
10880
  return joinLatex([
10741
10881
  "\\text{for }",
10742
10882
  serializer.serialize(index),
@@ -10748,13 +10888,27 @@ var DEFINITIONS_CORE = [
10748
10888
  serializer.serialize(body)
10749
10889
  ]);
10750
10890
  }
10891
+ return joinLatex([
10892
+ serializer.serialize(body),
10893
+ " \\operatorname{for} ",
10894
+ serializer.serialize(index),
10895
+ " = ",
10896
+ serializer.serialize(coll)
10897
+ ]);
10751
10898
  }
10899
+ const bindings = elements.map((elem) => {
10900
+ const name = operand(elem, 1);
10901
+ const coll = operand(elem, 2);
10902
+ return joinLatex([
10903
+ serializer.serialize(name),
10904
+ " = ",
10905
+ serializer.serialize(coll)
10906
+ ]);
10907
+ }).join(", ");
10752
10908
  return joinLatex([
10753
- "\\operatorname{Loop}(",
10754
10909
  serializer.serialize(body),
10755
- ", ",
10756
- serializer.serialize(indexing),
10757
- ")"
10910
+ " \\operatorname{for} ",
10911
+ bindings
10758
10912
  ]);
10759
10913
  }
10760
10914
  },
@@ -10787,6 +10941,18 @@ var DEFINITIONS_CORE = [
10787
10941
  precedence: 245,
10788
10942
  parse: (parser, until) => parseForExpression(parser, until)
10789
10943
  },
10944
+ // \operatorname{for} as postfix infix (list comprehension):
10945
+ // `body \operatorname{for} x = L_1, y = L_2`
10946
+ // Precedence 19 — just below comma (20) so the body is allowed to use
10947
+ // any operator (including comma sequencing) up to the keyword, and the
10948
+ // bindings can be comma-separated below us.
10949
+ {
10950
+ symbolTrigger: "for",
10951
+ kind: "infix",
10952
+ associativity: "none",
10953
+ precedence: 19,
10954
+ parse: (parser, lhs, until) => parseForComprehension(parser, lhs, until)
10955
+ },
10790
10956
  // \operatorname{break}
10791
10957
  {
10792
10958
  symbolTrigger: "break",
@@ -10991,7 +11157,10 @@ var DEFINITIONS_CORE = [
10991
11157
  if (!sym2 || !parser.getSymbolType(sym2).matches("function")) return null;
10992
11158
  parser.addBoundary([")"]);
10993
11159
  const expr2 = parser.parseExpression(until);
10994
- if (!parser.matchBoundary()) return null;
11160
+ if (!parser.matchBoundary()) {
11161
+ parser.removeBoundary();
11162
+ return null;
11163
+ }
10995
11164
  if (!parser.match("<}>")) return null;
10996
11165
  return ["Derivative", lhs, expr2];
10997
11166
  }
@@ -11432,7 +11601,12 @@ function parseBrackets(parser, body) {
11432
11601
  if (isEmptySequence(body)) return ["List"];
11433
11602
  const h = operator(body);
11434
11603
  if (h === "Range" || h === "Linspace") return body;
11435
- if (h === "Sequence") return ["List", ...operands(body)];
11604
+ if (h === "Sequence") {
11605
+ const elems = operands(body);
11606
+ const inferred = tryInferRangeFromElements(elems, parser);
11607
+ if (inferred) return inferred;
11608
+ return ["List", ...elems];
11609
+ }
11436
11610
  if (h === "Delimiter") {
11437
11611
  const delim = stringValue(operand(body, 2)) ?? "...";
11438
11612
  if (delim === ";" || delim === ".;.") {
@@ -11445,12 +11619,37 @@ function parseBrackets(parser, body) {
11445
11619
  }
11446
11620
  if (delim === "," || delim === ".,.") {
11447
11621
  body = operand(body, 1);
11448
- if (operator(body) === "Sequence") return ["List", ...operands(body)];
11622
+ if (operator(body) === "Sequence") {
11623
+ const elems = operands(body);
11624
+ const inferred = tryInferRangeFromElements(elems, parser);
11625
+ if (inferred) return inferred;
11626
+ return ["List", ...elems];
11627
+ }
11449
11628
  return ["List", body ?? "Nothing"];
11450
11629
  }
11451
11630
  }
11452
11631
  return ["List", body];
11453
11632
  }
11633
+ function tryInferRangeFromElements(elems, parser) {
11634
+ if (elems.length < 4) return null;
11635
+ const penultimate = elems[elems.length - 2];
11636
+ if (symbol(penultimate) !== "ContinuationPlaceholder") return null;
11637
+ const samples = elems.slice(0, -2);
11638
+ const endExpr = elems[elems.length - 1];
11639
+ if (samples.length < 2) return null;
11640
+ const sampleNums = samples.map(machineValue);
11641
+ if (sampleNums.some((n) => n === null)) return null;
11642
+ const nums = sampleNums;
11643
+ const step = nums[nums.length - 1] - nums[nums.length - 2];
11644
+ const tol = parser.options.tolerance;
11645
+ if (Math.abs(step) < tol)
11646
+ return parser.error("degenerate-range-step", parser.index);
11647
+ for (let i = 1; i < nums.length; i++) {
11648
+ if (Math.abs(nums[i] - nums[i - 1] - step) > tol)
11649
+ return parser.error("inconsistent-range-samples", parser.index);
11650
+ }
11651
+ return ["Range", nums[0], endExpr, step];
11652
+ }
11454
11653
  function serializeList(serializer, expr2) {
11455
11654
  if (nops(expr2) > 1 && operands(expr2).every((x) => {
11456
11655
  const op = operator(x);
@@ -11702,6 +11901,35 @@ function parseForExpression(parser, until) {
11702
11901
  ["Element", index, ["Range", lower, upper]]
11703
11902
  ];
11704
11903
  }
11904
+ function parseForComprehension(parser, lhs, until) {
11905
+ const bindingTerminator = {
11906
+ minPrec: 21,
11907
+ // Above comma (20) and ; (19), so `x = L_1` is captured whole
11908
+ condition: (p) => {
11909
+ if (until?.condition?.(p)) return true;
11910
+ const saved = p.index;
11911
+ p.skipVisualSpace();
11912
+ const isComma = p.peek === ",";
11913
+ p.index = saved;
11914
+ return isComma;
11915
+ }
11916
+ };
11917
+ const elements = [];
11918
+ do {
11919
+ parser.skipVisualSpace();
11920
+ const binding = parser.parseExpression(bindingTerminator);
11921
+ if (binding === null) break;
11922
+ const op = operator(binding);
11923
+ if (op !== "Equal" && op !== "Assign") return null;
11924
+ const name = operand(binding, 1);
11925
+ const list = operand(binding, 2);
11926
+ if (!name || !list) return null;
11927
+ elements.push(["Element", name, list]);
11928
+ parser.skipVisualSpace();
11929
+ } while (parser.match(","));
11930
+ if (elements.length === 0) return null;
11931
+ return ["Loop", lhs, ...elements];
11932
+ }
11705
11933
  function parseWhereExpression(parser, lhs, until) {
11706
11934
  const bindingTerminator = {
11707
11935
  minPrec: 21,
@@ -13620,6 +13848,8 @@ var DEFINITIONS_ARITHMETIC = [
13620
13848
  precedence: DIVISION_PRECEDENCE,
13621
13849
  parse: "Mod"
13622
13850
  },
13851
+ // Function-style alias: `\operatorname{mod}(a, b)`
13852
+ { latexTrigger: "\\operatorname{mod}", parse: "Mod" },
13623
13853
  {
13624
13854
  latexTrigger: "\\pmod",
13625
13855
  kind: "prefix",
@@ -13860,6 +14090,13 @@ var DEFINITIONS_ARITHMETIC = [
13860
14090
  const rhs = serializer.wrap(operand(expr2, 2), ADDITION_PRECEDENCE + 3);
13861
14091
  return joinLatex([lhs, "-", rhs]);
13862
14092
  }
14093
+ },
14094
+ // Euclidean distance between two points (tuples of numbers).
14095
+ {
14096
+ name: "Distance",
14097
+ latexTrigger: ["\\operatorname{distance}"],
14098
+ kind: "function",
14099
+ serialize: (serializer, expr2) => "\\operatorname{distance}" + serializer.wrapArguments(expr2)
13863
14100
  }
13864
14101
  ];
13865
14102
  function getIndexAssignment(expr2, upper) {
@@ -15284,7 +15521,9 @@ var DEFINITIONS_STATISTICS = [
15284
15521
  if (!expr2 || !symbol(expr2)) return null;
15285
15522
  return ["Mean", expr2];
15286
15523
  }
15287
- }
15524
+ },
15525
+ // Function-style alias: `\operatorname{var}(...)`
15526
+ { latexTrigger: "\\operatorname{var}", parse: "Variance" }
15288
15527
  ];
15289
15528
 
15290
15529
  // src/compute-engine/numerics/unit-data.ts
@@ -16385,7 +16624,7 @@ var DEFINITIONS_OTHERS = [
16385
16624
  36: "\\qquad"
16386
16625
  }[v] ?? "";
16387
16626
  }
16388
- }
16627
+ },
16389
16628
  // if (
16390
16629
  // [
16391
16630
  // '\\!',
@@ -16409,6 +16648,121 @@ var DEFINITIONS_OTHERS = [
16409
16648
  // name: '',
16410
16649
  // trigger: '\\check',
16411
16650
  // },
16651
+ // ---------------------------------------------------------------------------
16652
+ // Function-style aliases for collection / random operators that some
16653
+ // notations write in lowercase (e.g. `\operatorname{shuffle}(L)`).
16654
+ // The capitalized library entries already exist; these are pure parse
16655
+ // aliases so the lowercase names don't land in `unsupported-operator`.
16656
+ // ---------------------------------------------------------------------------
16657
+ { latexTrigger: "\\operatorname{random}", parse: "Random" },
16658
+ { latexTrigger: "\\operatorname{shuffle}", parse: "Shuffle" },
16659
+ { latexTrigger: "\\operatorname{repeat}", parse: "Repeat" },
16660
+ { latexTrigger: "\\operatorname{join}", parse: "Join" },
16661
+ // ---------------------------------------------------------------------------
16662
+ // Geometric primitive heads. Registered as known typed heads so consumers
16663
+ // can branch on the operator name; CE itself doesn't render them. The
16664
+ // library entries (with no evaluator) live in `library/core.ts`.
16665
+ // ---------------------------------------------------------------------------
16666
+ {
16667
+ name: "Triangle",
16668
+ latexTrigger: ["\\operatorname{triangle}"],
16669
+ kind: "function",
16670
+ serialize: (serializer, expr2) => "\\operatorname{triangle}" + serializer.wrapArguments(expr2)
16671
+ },
16672
+ // Desmos's geometric `vector(p1, p2)` — a directed segment between two
16673
+ // points. Routed to a dedicated head (not the existing column-vector
16674
+ // `Vector`, which has a narrower `(number+) -> vector` signature).
16675
+ {
16676
+ name: "GeometricVector",
16677
+ latexTrigger: ["\\operatorname{vector}"],
16678
+ kind: "function",
16679
+ serialize: (serializer, expr2) => "\\operatorname{vector}" + serializer.wrapArguments(expr2)
16680
+ },
16681
+ {
16682
+ name: "Sphere",
16683
+ latexTrigger: ["\\operatorname{sphere}"],
16684
+ kind: "function",
16685
+ serialize: (serializer, expr2) => "\\operatorname{sphere}" + serializer.wrapArguments(expr2)
16686
+ },
16687
+ {
16688
+ name: "Segment",
16689
+ latexTrigger: ["\\operatorname{segment}"],
16690
+ kind: "function",
16691
+ serialize: (serializer, expr2) => "\\operatorname{segment}" + serializer.wrapArguments(expr2)
16692
+ }
16693
+ ];
16694
+
16695
+ // src/compute-engine/latex-syntax/dictionary/definitions-colors.ts
16696
+ var DEFINITIONS_COLORS = [
16697
+ // Color constructors (one per colorspace, preserves space on evaluation)
16698
+ {
16699
+ name: "Rgb",
16700
+ latexTrigger: ["\\operatorname{rgb}"],
16701
+ kind: "function",
16702
+ serialize: (serializer, expr2) => "\\operatorname{rgb}" + serializer.wrapArguments(expr2)
16703
+ },
16704
+ {
16705
+ name: "Hsv",
16706
+ latexTrigger: ["\\operatorname{hsv}"],
16707
+ kind: "function",
16708
+ serialize: (serializer, expr2) => "\\operatorname{hsv}" + serializer.wrapArguments(expr2)
16709
+ },
16710
+ {
16711
+ name: "Hsl",
16712
+ latexTrigger: ["\\operatorname{hsl}"],
16713
+ kind: "function",
16714
+ serialize: (serializer, expr2) => "\\operatorname{hsl}" + serializer.wrapArguments(expr2)
16715
+ },
16716
+ {
16717
+ name: "Oklab",
16718
+ latexTrigger: ["\\operatorname{oklab}"],
16719
+ kind: "function",
16720
+ serialize: (serializer, expr2) => "\\operatorname{oklab}" + serializer.wrapArguments(expr2)
16721
+ },
16722
+ {
16723
+ name: "Oklch",
16724
+ latexTrigger: ["\\operatorname{oklch}"],
16725
+ kind: "function",
16726
+ serialize: (serializer, expr2) => "\\operatorname{oklch}" + serializer.wrapArguments(expr2)
16727
+ },
16728
+ // Conversion functions (color → color in the named space)
16729
+ {
16730
+ name: "AsRgb",
16731
+ latexTrigger: ["\\operatorname{asRgb}"],
16732
+ kind: "function",
16733
+ serialize: (serializer, expr2) => "\\operatorname{asRgb}" + serializer.wrapArguments(expr2)
16734
+ },
16735
+ {
16736
+ name: "AsHsv",
16737
+ latexTrigger: ["\\operatorname{asHsv}"],
16738
+ kind: "function",
16739
+ serialize: (serializer, expr2) => "\\operatorname{asHsv}" + serializer.wrapArguments(expr2)
16740
+ },
16741
+ {
16742
+ name: "AsHsl",
16743
+ latexTrigger: ["\\operatorname{asHsl}"],
16744
+ kind: "function",
16745
+ serialize: (serializer, expr2) => "\\operatorname{asHsl}" + serializer.wrapArguments(expr2)
16746
+ },
16747
+ {
16748
+ name: "AsOklab",
16749
+ latexTrigger: ["\\operatorname{asOklab}"],
16750
+ kind: "function",
16751
+ serialize: (serializer, expr2) => "\\operatorname{asOklab}" + serializer.wrapArguments(expr2)
16752
+ },
16753
+ {
16754
+ name: "AsOklch",
16755
+ latexTrigger: ["\\operatorname{asOklch}"],
16756
+ kind: "function",
16757
+ serialize: (serializer, expr2) => "\\operatorname{asOklch}" + serializer.wrapArguments(expr2)
16758
+ },
16759
+ // Perceptual difference (returns a scalar in [0, ~1])
16760
+ {
16761
+ name: "ColorDelta",
16762
+ latexTrigger: ["\\operatorname{colorDelta}"],
16763
+ kind: "function",
16764
+ serialize: (serializer, expr2) => "\\operatorname{colorDelta}" + serializer.wrapArguments(expr2)
16765
+ }
16412
16766
  ];
16413
16767
 
16414
16768
  // src/compute-engine/latex-syntax/dictionary/default-dictionary.ts
@@ -16439,7 +16793,8 @@ var LATEX_DICTIONARY = [
16439
16793
  ...DEFINITIONS_STATISTICS,
16440
16794
  ...DEFINITIONS_UNITS,
16441
16795
  ...DEFINITIONS_OTHERS,
16442
- ...DEFINITIONS_PHYSICS
16796
+ ...DEFINITIONS_PHYSICS,
16797
+ ...DEFINITIONS_COLORS
16443
16798
  ];
16444
16799
 
16445
16800
  // src/math-json/symbols.ts
@@ -16604,6 +16959,17 @@ function addEntry(result, entry, onError) {
16604
16959
  } else if (Array.isArray(openTrigger) && openTrigger.length > 0) {
16605
16960
  openTokens.push(openTrigger[0]);
16606
16961
  }
16962
+ const closeTrigger = indexedEntry.closeTrigger;
16963
+ const closeTokens = /* @__PURE__ */ new Set();
16964
+ if (typeof closeTrigger === "string") {
16965
+ const variants = DELIMITER_SHORTHAND[closeTrigger];
16966
+ if (variants) for (const v of variants) closeTokens.add(v);
16967
+ else closeTokens.add(closeTrigger);
16968
+ if (closeTrigger === "||") closeTokens.add("|");
16969
+ } else if (Array.isArray(closeTrigger) && closeTrigger.length > 0) {
16970
+ closeTokens.add(closeTrigger[0]);
16971
+ }
16972
+ indexedEntry.closeTokens = closeTokens;
16607
16973
  for (const token of openTokens) {
16608
16974
  const existing = result.matchfixByOpen.get(token);
16609
16975
  if (existing) {
@@ -16795,11 +17161,7 @@ function makeSerializeHandler(entry, latexTrigger, idTrigger) {
16795
17161
  if (style === "scaled")
16796
17162
  return joinLatex([`\\left${openDelim}`, inner, `\\right${closeDelim}`]);
16797
17163
  if (style === "big")
16798
- return joinLatex([
16799
- `\\Bigl${openDelim}`,
16800
- inner,
16801
- `\\Bigr${closeDelim}`
16802
- ]);
17164
+ return joinLatex([`\\Bigl${openDelim}`, inner, `\\Bigr${closeDelim}`]);
16803
17165
  return joinLatex([openDelim, inner, closeDelim]);
16804
17166
  };
16805
17167
  }
@@ -18150,7 +18512,7 @@ var _Parser = class __Parser {
18150
18512
  }
18151
18513
  if (this.match("\\hskip") || this.match("\\kern")) {
18152
18514
  this.skipSpace();
18153
- this.match("-") || this.match("+");
18515
+ if (!this.match("-")) this.match("+");
18154
18516
  while (/^[\d.]$/.test(this.peek)) this.nextToken();
18155
18517
  for (const unit of [
18156
18518
  "pt",
@@ -18664,6 +19026,19 @@ var _Parser = class __Parser {
18664
19026
  }
18665
19027
  for (const def of defs) {
18666
19028
  this.index = start;
19029
+ if (def.closeTokens.size > 0) {
19030
+ let found = false;
19031
+ const tokens = this._tokens;
19032
+ for (let i = start; i < tokens.length; i++) {
19033
+ if (def.closeTokens.has(tokens[i])) {
19034
+ found = true;
19035
+ break;
19036
+ }
19037
+ }
19038
+ if (!found) continue;
19039
+ }
19040
+ if (typeof def.openTrigger === "string" && def.openTrigger === "." && !OPEN_DELIMITER_PREFIX[currentToken])
19041
+ continue;
18667
19042
  const matched = this.matchDelimiter(def.openTrigger, def.closeTrigger);
18668
19043
  if (!matched) continue;
18669
19044
  const bodyStart = this.index;
@@ -19326,6 +19701,19 @@ var _Parser = class __Parser {
19326
19701
  } while (postfix !== null);
19327
19702
  }
19328
19703
  if (result !== null) result = this.parseSupsub(result);
19704
+ if (result !== null) {
19705
+ let postfix = null;
19706
+ let index = this.index;
19707
+ do {
19708
+ postfix = this.parsePostfixOperator(result, until);
19709
+ result = postfix ?? result;
19710
+ if (this.index === index && postfix !== null) {
19711
+ console.assert(this.index !== index, "No token consumed");
19712
+ break;
19713
+ }
19714
+ index = this.index;
19715
+ } while (postfix !== null);
19716
+ }
19329
19717
  if (result === null) {
19330
19718
  result = this.options.parseUnexpectedToken?.(null, this) ?? null;
19331
19719
  if (result === null && this.peek.startsWith("\\")) {
@@ -19834,6 +20222,28 @@ function toDecimalNumber(wholePart, fractionalPart, exp3) {
19834
20222
  }
19835
20223
 
19836
20224
  // src/compute-engine/latex-syntax/serializer.ts
20225
+ var DOT_NOTATION_MAP = {
20226
+ First: ".x",
20227
+ Second: ".y",
20228
+ Third: ".z",
20229
+ Real: ".\\operatorname{real}",
20230
+ Imaginary: ".\\operatorname{imag}",
20231
+ Length: ".\\operatorname{count}",
20232
+ Sum: ".\\operatorname{total}",
20233
+ Max: ".\\max",
20234
+ Min: ".\\min"
20235
+ };
20236
+ function trySerializeDotNotation(serializer, expr2) {
20237
+ if (!serializer.options.dotNotation) return null;
20238
+ const ops = operands(expr2);
20239
+ if (!ops || ops.length !== 1) return null;
20240
+ const head = operator(expr2);
20241
+ if (!head) return null;
20242
+ const suffix = DOT_NOTATION_MAP[head];
20243
+ if (suffix === void 0) return null;
20244
+ const lhs = serializer.wrap(ops[0], 810);
20245
+ return `${lhs}${suffix}`;
20246
+ }
19837
20247
  var ACCENT_MODIFIERS = {
19838
20248
  deg: (s) => `${s}\\degree`,
19839
20249
  prime: (s) => `${s}^{\\prime}`,
@@ -19877,6 +20287,7 @@ var Serializer5 = class {
19877
20287
  constructor(dictionary, options) {
19878
20288
  this.dictionary = dictionary;
19879
20289
  this.options = {
20290
+ dotNotation: false,
19880
20291
  dmsFormat: false,
19881
20292
  angleNormalization: "none",
19882
20293
  ...options
@@ -19975,6 +20386,8 @@ var Serializer5 = class {
19975
20386
  return def?.serialize?.(this, expr2) ?? serializeSymbol2(symbol(expr2)) ?? "";
19976
20387
  }
19977
20388
  serializeFunction(expr2, def) {
20389
+ const dotResult = trySerializeDotNotation(this, expr2);
20390
+ if (dotResult !== null) return dotResult;
19978
20391
  if (def?.serialize) return def.serialize(this, expr2);
19979
20392
  const h = operator(expr2);
19980
20393
  return serializeSymbol2(h, "auto") + this.wrapArguments(expr2);
@@ -20217,6 +20630,8 @@ function defaultParseOptions(opts) {
20217
20630
  preserveLatex: opts.preserveLatex ?? false,
20218
20631
  quantifierScope: opts.quantifierScope ?? "tight",
20219
20632
  timeDerivativeVariable: opts.timeDerivativeVariable ?? "t",
20633
+ // Standalone mode has no engine; use the same default as ComputeEngine
20634
+ tolerance: 1e-7,
20220
20635
  // Callbacks -- standalone mode has no engine, so these are stubs
20221
20636
  getSymbolType: (_id) => BoxedType.unknown,
20222
20637
  hasSubscriptEvaluate: (_id) => false,
@@ -20249,6 +20664,7 @@ function defaultSerializeOptions(opts) {
20249
20664
  invisiblePlus: "",
20250
20665
  multiply: "\\times",
20251
20666
  missingSymbol: "\\blacksquare",
20667
+ dotNotation: false,
20252
20668
  dmsFormat: false,
20253
20669
  angleNormalization: "none",
20254
20670
  // Style callbacks -- use same defaults as the engine
@@ -27219,7 +27635,11 @@ var COLLECTIONS_LIBRARY = {
27219
27635
  //
27220
27636
  Range: {
27221
27637
  complexity: 8200,
27222
- signature: "(number, number?, step: number?) -> indexed_collection<integer>",
27638
+ signature: "(number, number?, step: number?) -> indexed_collection<number>",
27639
+ type: (ops) => {
27640
+ const allInt = ops.every((op) => op.isInteger);
27641
+ return allInt ? parseType("indexed_collection<integer>") : parseType("indexed_collection<number>");
27642
+ },
27223
27643
  canonical: (ops, { engine: ce }) => {
27224
27644
  if (ops.length === 0) return null;
27225
27645
  if (ops.length === 1) return ce._fn("Range", [ce.One, ops[0].canonical]);
@@ -27243,19 +27663,26 @@ var COLLECTIONS_LIBRARY = {
27243
27663
  const [lower, upper, step] = range(expr2);
27244
27664
  if (step === 0) return 0;
27245
27665
  if (!isFinite(lower) || !isFinite(upper)) return Infinity;
27246
- return 1 + Math.max(0, Math.floor((upper - lower) / step));
27666
+ return Math.max(0, Math.floor((upper - lower) / step) + 1);
27247
27667
  },
27248
27668
  contains: (expr2, target) => {
27249
- if (!target.type.matches("integer")) return false;
27250
27669
  const t = target.re;
27670
+ if (!isFinite(t)) return false;
27251
27671
  const [lower, upper, step] = range(expr2);
27252
27672
  if (step === 0) return false;
27253
- if (step > 0) return t >= lower && t <= upper;
27254
- return t <= lower && t >= upper;
27673
+ if (step > 0) {
27674
+ if (t < lower || t > upper) return false;
27675
+ } else {
27676
+ if (t > lower || t < upper) return false;
27677
+ }
27678
+ const k = (t - lower) / step;
27679
+ const tol = expr2.engine.tolerance;
27680
+ const kRounded = Math.round(k);
27681
+ return kRounded >= 0 && Math.abs(k - kRounded) < tol;
27255
27682
  },
27256
27683
  iterator: (expr2) => {
27257
27684
  const [lower, upper, step] = range(expr2);
27258
- const maxCount = step === 0 ? 0 : Math.floor((upper - lower) / step) + 1;
27685
+ const maxCount = step === 0 ? 0 : Math.max(0, Math.floor((upper - lower) / step) + 1);
27259
27686
  let index = 1;
27260
27687
  return {
27261
27688
  next: () => {
@@ -27273,7 +27700,9 @@ var COLLECTIONS_LIBRARY = {
27273
27700
  at: (expr2, index) => {
27274
27701
  if (typeof index !== "number") return void 0;
27275
27702
  const [lower, upper, step] = range(expr2);
27276
- if (index < 1 || index > 1 + (upper - lower) / step) return void 0;
27703
+ if (step === 0) return void 0;
27704
+ const maxCount = Math.max(0, Math.floor((upper - lower) / step) + 1);
27705
+ if (index < 1 || index > maxCount) return void 0;
27277
27706
  return expr2.engine.number(lower + step * (index - 1));
27278
27707
  },
27279
27708
  indexWhere: void 0,
@@ -27298,7 +27727,13 @@ var COLLECTIONS_LIBRARY = {
27298
27727
  if (step > 0) return lower <= upper ? "positive" : "negative";
27299
27728
  return lower >= upper ? "positive" : "negative";
27300
27729
  },
27301
- elttype: (_expr) => "finite_integer"
27730
+ elttype: (expr2) => {
27731
+ if (!isFunction2(expr2)) return "finite_integer";
27732
+ for (let i = 1; i <= expr2.nops; i++) {
27733
+ if (!expr2[`op${i}`].isInteger) return "finite_real";
27734
+ }
27735
+ return "finite_integer";
27736
+ }
27302
27737
  }
27303
27738
  },
27304
27739
  Interval: {
@@ -27896,15 +28331,45 @@ var COLLECTIONS_LIBRARY = {
27896
28331
  },
27897
28332
  First: {
27898
28333
  complexity: 8200,
27899
- signature: "(collection) -> any",
28334
+ signature: "(any) -> any",
27900
28335
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
27901
- evaluate: ([xs], { engine: ce }) => xs.at(1) ?? ce.Nothing
28336
+ evaluate: ([xs], { engine: ce }) => {
28337
+ if (!xs.isCollection)
28338
+ return ce.error([
28339
+ "incompatible-type",
28340
+ `'collection'`,
28341
+ xs.type.toString()
28342
+ ]);
28343
+ return xs.at(1) ?? ce.Nothing;
28344
+ }
27902
28345
  },
27903
28346
  Second: {
27904
28347
  complexity: 8200,
27905
- signature: "(collection) -> any",
28348
+ signature: "(any) -> any",
28349
+ type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28350
+ evaluate: ([xs], { engine: ce }) => {
28351
+ if (!xs.isCollection)
28352
+ return ce.error([
28353
+ "incompatible-type",
28354
+ `'collection'`,
28355
+ xs.type.toString()
28356
+ ]);
28357
+ return xs.at(2) ?? ce.Nothing;
28358
+ }
28359
+ },
28360
+ Third: {
28361
+ complexity: 8200,
28362
+ signature: "(any) -> any",
27906
28363
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
27907
- evaluate: ([xs], { engine: ce }) => xs.at(2) ?? ce.Nothing
28364
+ evaluate: ([xs], { engine: ce }) => {
28365
+ if (!xs.isCollection)
28366
+ return ce.error([
28367
+ "incompatible-type",
28368
+ `'collection'`,
28369
+ xs.type.toString()
28370
+ ]);
28371
+ return xs.at(3) ?? ce.Nothing;
28372
+ }
27908
28373
  },
27909
28374
  Last: {
27910
28375
  complexity: 8200,
@@ -28883,17 +29348,14 @@ function range(expr2) {
28883
29348
  if (!isFunction2(expr2)) return [1, 0, 0];
28884
29349
  if (expr2.nops === 0) return [1, 0, 0];
28885
29350
  let op1 = expr2.op1.re;
28886
- if (!isFinite(op1)) op1 = 1;
28887
- else op1 = Math.round(op1);
29351
+ if (!isFinite(op1) && !op1) op1 = 1;
28888
29352
  if (expr2.nops === 1) return [1, op1, 1];
28889
29353
  let op2 = expr2.op2.re;
28890
29354
  if (!isFinite(op2) && !op2) op2 = 1;
28891
- else if (isFinite(op2)) op2 = Math.round(op2);
28892
- if (expr2.nops === 2) return [op1, op2, op2 > op1 ? 1 : -1];
29355
+ if (expr2.nops === 2) return [op1, op2, op2 >= op1 ? 1 : -1];
28893
29356
  let op3 = expr2.op3.re;
28894
- if (!isFinite(op3)) op3 = 1;
28895
- else op3 = Math.abs(Math.round(op3));
28896
- return [op1, op2, op1 < op2 ? op3 : -op3];
29357
+ if (!isFinite(op3) && !op3) op3 = 1;
29358
+ return [op1, op2, op3];
28897
29359
  }
28898
29360
  function rangeLast(r) {
28899
29361
  const [lower, upper, step] = r;
@@ -32226,6 +32688,29 @@ var ARITHMETIC_LIBRARY = [
32226
32688
  signature: "(value*) -> number | list",
32227
32689
  evaluate: (xs, { engine }) => evaluateMinMax(engine, xs, "Infimum")
32228
32690
  },
32691
+ Distance: {
32692
+ description: "Euclidean distance between two points (tuples of numbers).",
32693
+ complexity: 6e3,
32694
+ signature: "(tuple, tuple) -> number",
32695
+ evaluate: ([a, b], { engine: ce }) => {
32696
+ if (!isFunction2(a) || !isFunction2(b))
32697
+ return ce.error("incompatible-type");
32698
+ if (a.operator !== "Tuple" || b.operator !== "Tuple")
32699
+ return ce.error("incompatible-type");
32700
+ if (a.ops.length !== b.ops.length || a.ops.length === 0)
32701
+ return ce.error("incompatible-type");
32702
+ let sumSq = 0;
32703
+ for (let i = 0; i < a.ops.length; i++) {
32704
+ const ai = a.ops[i].re;
32705
+ const bi = b.ops[i].re;
32706
+ if (!Number.isFinite(ai) || !Number.isFinite(bi))
32707
+ return ce.error("expected-value");
32708
+ const d = ai - bi;
32709
+ sumSq += d * d;
32710
+ }
32711
+ return ce.number(Math.sqrt(sumSq));
32712
+ }
32713
+ },
32229
32714
  Product: {
32230
32715
  description: "`Product(f, a, b)` computes the product of `f` from `a` to `b`",
32231
32716
  wikidata: "Q901718",
@@ -32758,16 +33243,11 @@ function boxRule(ce, rule, options) {
32758
33243
  );
32759
33244
  let condFn;
32760
33245
  if (typeof condition === "string") {
32761
- const latex = asLatexString(condition);
32762
- if (latex) {
32763
- const condPattern = ce.parse(latex, {
32764
- form: options?.canonical ? "canonical" : "raw"
32765
- }) ?? ce.expr("Nothing");
32766
- condFn = (x, _ce) => {
32767
- const evaluated = condPattern.subs(x).evaluate();
32768
- return isSymbol2(evaluated, "True");
32769
- };
32770
- }
33246
+ const condPattern = ce.parse(condition) ?? ce.expr("Nothing");
33247
+ condFn = (x, _ce) => {
33248
+ const evaluated = condPattern.subs(x).evaluate();
33249
+ return isSymbol2(evaluated, "True");
33250
+ };
32771
33251
  } else {
32772
33252
  if (condition !== void 0 && typeof condition !== "function")
32773
33253
  throw new Error(
@@ -32869,6 +33349,15 @@ ${e.message}
32869
33349
  function applyRule(rule, expr2, substitution, options) {
32870
33350
  if (!rule) return null;
32871
33351
  let canonical2 = options?.canonical ?? (expr2.isCanonical || expr2.isStructural);
33352
+ let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
33353
+ const because = id ?? "";
33354
+ const ce = expr2.engine;
33355
+ if (canonical2 && match2) {
33356
+ const awc = getWildcards(match2);
33357
+ const canonicalMatch = match2.canonical;
33358
+ const bwc = getWildcards(canonicalMatch);
33359
+ if (!awc.every((x) => bwc.includes(x))) return null;
33360
+ }
32872
33361
  let operandsMatched = false;
32873
33362
  if (isFunction2(expr2) && options?.recursive) {
32874
33363
  const newOps = expr2.ops.map((op) => {
@@ -32880,20 +33369,11 @@ function applyRule(rule, expr2, substitution, options) {
32880
33369
  if (operandsMatched) {
32881
33370
  if (!canonical2 && options?.canonical === void 0 && newOps.every((x) => x.isCanonical))
32882
33371
  canonical2 = true;
32883
- expr2 = expr2.engine.function(expr2.operator, newOps, {
33372
+ expr2 = ce.function(expr2.operator, newOps, {
32884
33373
  form: canonical2 ? "canonical" : "raw"
32885
33374
  });
32886
33375
  }
32887
33376
  }
32888
- let { match: match2, replace: replace2, condition, id, onMatch, onBeforeMatch } = rule;
32889
- const because = id ?? "";
32890
- if (canonical2 && match2) {
32891
- const awc = getWildcards(match2);
32892
- const canonicalMatch = match2.canonical;
32893
- const bwc = getWildcards(canonicalMatch);
32894
- if (!awc.every((x) => bwc.includes(x)))
32895
- return operandsMatched ? { value: expr2, because } : null;
32896
- }
32897
33377
  const useVariations = rule.useVariations ?? options?.useVariations ?? false;
32898
33378
  const matchPermutations = options?.matchPermutations ?? true;
32899
33379
  onBeforeMatch?.(rule, expr2);
@@ -32912,7 +33392,7 @@ function applyRule(rule, expr2, substitution, options) {
32912
33392
  ...sub2
32913
33393
  };
32914
33394
  try {
32915
- if (!condition(conditionSub, expr2.engine))
33395
+ if (!condition(conditionSub, ce))
32916
33396
  return operandsMatched ? { value: expr2, because } : null;
32917
33397
  } catch (e) {
32918
33398
  console.error(
@@ -32927,7 +33407,8 @@ function applyRule(rule, expr2, substitution, options) {
32927
33407
  if (!canonical2 && options?.canonical === void 0 && replace2 instanceof _BoxedExpression && replace2.isCanonical)
32928
33408
  canonical2 = true;
32929
33409
  const result = typeof replace2 === "function" ? replace2(expr2, sub2) : replace2.subs(sub2, { canonical: canonical2 });
32930
- if (!result) return null;
33410
+ if (!result)
33411
+ return operandsMatched ? { value: canonical2 ? expr2.canonical : expr2, because } : null;
32931
33412
  onMatch?.(rule, expr2, result);
32932
33413
  if (isRuleStep(result))
32933
33414
  return canonical2 ? { ...result, value: result.value.canonical } : result;
@@ -38501,6 +38982,40 @@ function rgbToHsl(r, g, b) {
38501
38982
  else h = ((r - g) / d + 4) / 6;
38502
38983
  return { h: h * 360, s, l };
38503
38984
  }
38985
+ function hsvToRgb(h, s, v) {
38986
+ h = (h % 360 + 360) % 360;
38987
+ s = Math.max(0, Math.min(1, s));
38988
+ v = Math.max(0, Math.min(1, v));
38989
+ const c = v * s;
38990
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
38991
+ const m = v - c;
38992
+ let r = 0, g = 0, b = 0;
38993
+ if (h < 60) [r, g, b] = [c, x, 0];
38994
+ else if (h < 120) [r, g, b] = [x, c, 0];
38995
+ else if (h < 180) [r, g, b] = [0, c, x];
38996
+ else if (h < 240) [r, g, b] = [0, x, c];
38997
+ else if (h < 300) [r, g, b] = [x, 0, c];
38998
+ else [r, g, b] = [c, 0, x];
38999
+ return { r: (r + m) * 255, g: (g + m) * 255, b: (b + m) * 255 };
39000
+ }
39001
+ function rgbToHsv(r, g, b) {
39002
+ r /= 255;
39003
+ g /= 255;
39004
+ b /= 255;
39005
+ const max2 = Math.max(r, g, b);
39006
+ const min2 = Math.min(r, g, b);
39007
+ const d = max2 - min2;
39008
+ let h = 0;
39009
+ if (d > 0) {
39010
+ if (max2 === r) h = (g - b) / d % 6;
39011
+ else if (max2 === g) h = (b - r) / d + 2;
39012
+ else h = (r - g) / d + 4;
39013
+ h *= 60;
39014
+ if (h < 0) h += 360;
39015
+ }
39016
+ const s = max2 === 0 ? 0 : d / max2;
39017
+ return { h, s, v: max2 };
39018
+ }
38504
39019
  function parseHexColor(s) {
38505
39020
  const hex = s.startsWith("#") ? s.substring(1) : s;
38506
39021
  let r, g, b;
@@ -38525,6 +39040,12 @@ function parseHexColor(s) {
38525
39040
  if (alpha !== void 0) result.alpha = alpha;
38526
39041
  return result;
38527
39042
  }
39043
+ function asOklch(color) {
39044
+ if (typeof color === "string") return rgbToOklch(parseHexColor(color));
39045
+ if ("C" in color) return color;
39046
+ if ("a" in color && "b" in color) return oklabToOklch(color);
39047
+ return rgbToOklch(color);
39048
+ }
38528
39049
  function asRgb(color) {
38529
39050
  if (typeof color === "number") {
38530
39051
  return {
@@ -38956,6 +39477,13 @@ var NAMED_COLORS = {
38956
39477
  };
38957
39478
  function parseColor(s, darkMode) {
38958
39479
  const str = s.trim().toLowerCase();
39480
+ const opacityMatch = str.match(/^(.+?)\s*\/\s*(\d+(?:\.\d+)?)%?\s*$/);
39481
+ if (opacityMatch) {
39482
+ const base = parseColor(opacityMatch[1].trim(), darkMode);
39483
+ const opacity = Math.max(0, Math.min(100, parseFloat(opacityMatch[2])));
39484
+ const alpha = Math.round(opacity / 100 * 255);
39485
+ return base & 4294967040 | alpha;
39486
+ }
38959
39487
  if (str.startsWith("#")) {
38960
39488
  const hex = str.substring(1);
38961
39489
  let r, g, b, a = 255;
@@ -39088,14 +39616,6 @@ function parseColor(s, darkMode) {
39088
39616
  console.warn(`parseColor: unrecognized color "${s}"`);
39089
39617
  return 0;
39090
39618
  }
39091
- function parseColorToRgb01(s, darkMode) {
39092
- const color = parseColor(s, darkMode);
39093
- return [
39094
- (color >>> 24 & 255) / 255,
39095
- (color >>> 16 & 255) / 255,
39096
- (color >>> 8 & 255) / 255
39097
- ];
39098
- }
39099
39619
  function apca(bgColor, fgColor) {
39100
39620
  const bgRgb = asRgb(bgColor);
39101
39621
  const fgRgb = asRgb(fgColor);
@@ -39154,6 +39674,12 @@ function contrastingColor(arg) {
39154
39674
  const contrast2 = Math.abs(apca(fg2, bg));
39155
39675
  return contrast1 >= contrast2 ? asColorNumber(fg1) : asColorNumber(fg2);
39156
39676
  }
39677
+ function oklabDeltaE(a, b) {
39678
+ const dL = a.L - b.L;
39679
+ const da = a.a - b.a;
39680
+ const db = a.b - b.b;
39681
+ return Math.sqrt(dL * dL + da * da + db * db);
39682
+ }
39157
39683
  function lerpOklch(c1, c2, f) {
39158
39684
  const L = c1.L + (c2.L - c1.L) * f;
39159
39685
  const C = c1.C + (c2.C - c1.C) * f;
@@ -41915,14 +42441,30 @@ var SEQUENTIAL_PALETTES = {
41915
42441
  };
41916
42442
 
41917
42443
  // src/compute-engine/library/colors.ts
41918
- function colorNumberToTuple(ce, color) {
41919
- const r = (color >>> 24 & 255) / 255;
41920
- const g = (color >>> 16 & 255) / 255;
41921
- const b = (color >>> 8 & 255) / 255;
41922
- const a = (color & 255) / 255;
41923
- if (Math.abs(a - 1) < 1e-4)
41924
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
41925
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b), ce.number(a));
42444
+ function normalizeAlpha(a) {
42445
+ if (a === void 0) return void 0;
42446
+ if (!Number.isFinite(a)) return void 0;
42447
+ if (Math.abs(a - 1) < 1e-9) return void 0;
42448
+ return a;
42449
+ }
42450
+ function normalizeColorHead(ce, expr2) {
42451
+ if (!isFunction2(expr2) || !expr2.ops || expr2.ops.length < 4) return expr2;
42452
+ const alphaExpr = expr2.ops[3];
42453
+ if (!isNumber(alphaExpr)) return expr2;
42454
+ if (normalizeAlpha(alphaExpr.re) === void 0) {
42455
+ return ce.function(expr2.operator, [expr2.ops[0], expr2.ops[1], expr2.ops[2]]);
42456
+ }
42457
+ return expr2;
42458
+ }
42459
+ function colorNumberToOklch(ce, color) {
42460
+ const r = color >>> 24 & 255;
42461
+ const g = color >>> 16 & 255;
42462
+ const b = color >>> 8 & 255;
42463
+ const a = normalizeAlpha((color & 255) / 255);
42464
+ const c = rgbToOklch({ r, g, b });
42465
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42466
+ if (a !== void 0) args.push(ce.number(a));
42467
+ return ce.function("Oklch", args);
41926
42468
  }
41927
42469
  var ALL_PALETTES = {
41928
42470
  ...SEQUENTIAL_PALETTES,
@@ -41933,45 +42475,139 @@ function samplePalette(ce, palette, t) {
41933
42475
  t = Math.max(0, Math.min(1, t));
41934
42476
  const n = palette.length;
41935
42477
  if (n === 0) return ce.error("expected-value");
41936
- if (n === 1) return colorNumberToTuple(ce, parseColor(palette[0]));
42478
+ if (n === 1) return colorNumberToOklch(ce, parseColor(palette[0]));
41937
42479
  const pos = t * (n - 1);
41938
42480
  const i = Math.floor(pos);
41939
42481
  const frac = pos - i;
41940
- if (i >= n - 1) return colorNumberToTuple(ce, parseColor(palette[n - 1]));
41941
- if (frac < 1e-9) return colorNumberToTuple(ce, parseColor(palette[i]));
41942
- const rgb = interpolateOklch(palette[i], palette[i + 1], frac);
41943
- const r = rgb.r / 255;
41944
- const g = rgb.g / 255;
41945
- const b = rgb.b / 255;
41946
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42482
+ if (i >= n - 1) return colorNumberToOklch(ce, parseColor(palette[n - 1]));
42483
+ if (frac < 1e-9) return colorNumberToOklch(ce, parseColor(palette[i]));
42484
+ return oklchToExpr(
42485
+ ce,
42486
+ asOklch(interpolateOklch(palette[i], palette[i + 1], frac))
42487
+ );
42488
+ }
42489
+ var COLOR_OPERATORS = /* @__PURE__ */ new Set(["Rgb", "Hsv", "Hsl", "Oklab", "Oklch"]);
42490
+ function readColorExpr(arg) {
42491
+ if (!isFunction2(arg)) return null;
42492
+ if (!COLOR_OPERATORS.has(arg.operator)) return null;
42493
+ if (!arg.ops || arg.ops.length < 3) return null;
42494
+ const c0 = arg.ops[0].re;
42495
+ const c1 = arg.ops[1].re;
42496
+ const c2 = arg.ops[2].re;
42497
+ if (!Number.isFinite(c0) || !Number.isFinite(c1) || !Number.isFinite(c2))
42498
+ return null;
42499
+ const alpha = arg.ops.length >= 4 ? normalizeAlpha(arg.ops[3].re) : void 0;
42500
+ return { space: arg.operator, c0, c1, c2, alpha };
42501
+ }
42502
+ function colorExprToRgb(arg) {
42503
+ const c = readColorExpr(arg);
42504
+ if (!c) return null;
42505
+ const withAlpha = (rgb) => c.alpha !== void 0 ? { ...rgb, alpha: c.alpha } : rgb;
42506
+ switch (c.space) {
42507
+ case "Rgb":
42508
+ return withAlpha({ r: c.c0 * 255, g: c.c1 * 255, b: c.c2 * 255 });
42509
+ case "Hsv":
42510
+ return withAlpha(hsvToRgb(c.c0, c.c1, c.c2));
42511
+ case "Hsl":
42512
+ return withAlpha(hslToRgb(c.c0, c.c1, c.c2));
42513
+ case "Oklab":
42514
+ return withAlpha(oklabToRgb({ L: c.c0, a: c.c1, b: c.c2 }));
42515
+ case "Oklch":
42516
+ return withAlpha(oklchToRgb({ L: c.c0, C: c.c1, H: c.c2 }));
42517
+ }
42518
+ return null;
42519
+ }
42520
+ function colorExprToOklch(arg) {
42521
+ const c = readColorExpr(arg);
42522
+ if (!c) return null;
42523
+ switch (c.space) {
42524
+ case "Oklch":
42525
+ return asOklch({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42526
+ case "Oklab":
42527
+ return asOklch({ L: c.c0, a: c.c1, b: c.c2, alpha: c.alpha });
42528
+ case "Rgb":
42529
+ return asOklch({
42530
+ r: c.c0 * 255,
42531
+ g: c.c1 * 255,
42532
+ b: c.c2 * 255,
42533
+ alpha: c.alpha
42534
+ });
42535
+ case "Hsv": {
42536
+ const rgb = hsvToRgb(c.c0, c.c1, c.c2);
42537
+ return asOklch({ ...rgb, alpha: c.alpha });
42538
+ }
42539
+ case "Hsl": {
42540
+ const rgb = hslToRgb(c.c0, c.c1, c.c2);
42541
+ return asOklch({ r: rgb.r, g: rgb.g, b: rgb.b, alpha: c.alpha });
42542
+ }
42543
+ }
42544
+ return null;
42545
+ }
42546
+ function toOklch(ce, arg) {
42547
+ const direct = colorExprToOklch(arg);
42548
+ if (direct) return direct;
42549
+ const rgb = extractRgb(ce, arg);
42550
+ return rgb ? asOklch(rgb) : null;
42551
+ }
42552
+ function lerpOklchColor(a, b, t) {
42553
+ const L = a.L + (b.L - a.L) * t;
42554
+ const C = a.C + (b.C - a.C) * t;
42555
+ const aAchromatic = a.C < 1e-6;
42556
+ const bAchromatic = b.C < 1e-6;
42557
+ let H;
42558
+ if (aAchromatic && bAchromatic) H = a.H;
42559
+ else if (aAchromatic) H = b.H;
42560
+ else if (bAchromatic) H = a.H;
42561
+ else {
42562
+ let dH = b.H - a.H;
42563
+ if (dH > 180) dH -= 360;
42564
+ if (dH < -180) dH += 360;
42565
+ H = a.H + dH * t;
42566
+ if (H < 0) H += 360;
42567
+ if (H >= 360) H -= 360;
42568
+ }
42569
+ const alphaA = a.alpha ?? 1;
42570
+ const alphaB = b.alpha ?? 1;
42571
+ return { L, C, H, alpha: normalizeAlpha(alphaA + (alphaB - alphaA) * t) };
42572
+ }
42573
+ function oklchToExpr(ce, c) {
42574
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42575
+ if (c.alpha !== void 0) args.push(ce.number(c.alpha));
42576
+ return ce.function("Oklch", args);
41947
42577
  }
41948
42578
  function extractRgb(ce, arg) {
41949
42579
  if (isString(arg)) {
41950
42580
  const s = arg.string;
41951
42581
  if (!s) return void 0;
41952
42582
  const color = parseColor(s);
41953
- return {
42583
+ const rgb = {
41954
42584
  r: color >>> 24 & 255,
41955
42585
  g: color >>> 16 & 255,
41956
- b: color >>> 8 & 255,
41957
- alpha: (color & 255) / 255
42586
+ b: color >>> 8 & 255
41958
42587
  };
42588
+ const alpha = normalizeAlpha((color & 255) / 255);
42589
+ if (alpha !== void 0) rgb.alpha = alpha;
42590
+ return rgb;
41959
42591
  }
42592
+ const fromTyped = colorExprToRgb(arg);
42593
+ if (fromTyped) return fromTyped;
41960
42594
  if (arg.operator === "Tuple" && arg.ops && arg.ops.length >= 3) {
41961
42595
  const rgb = {
41962
42596
  r: arg.ops[0].re * 255,
41963
42597
  g: arg.ops[1].re * 255,
41964
42598
  b: arg.ops[2].re * 255
41965
42599
  };
41966
- if (arg.ops.length >= 4) rgb.alpha = arg.ops[3].re;
42600
+ if (arg.ops.length >= 4) {
42601
+ const alpha = normalizeAlpha(arg.ops[3].re);
42602
+ if (alpha !== void 0) rgb.alpha = alpha;
42603
+ }
41967
42604
  return rgb;
41968
42605
  }
41969
42606
  return void 0;
41970
42607
  }
41971
42608
  function componentsTuple(ce, components, alpha) {
41972
42609
  const args = components.map((v) => ce.number(v));
41973
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4)
41974
- args.push(ce.number(alpha));
42610
+ if (alpha !== void 0) args.push(ce.number(alpha));
41975
42611
  return ce.tuple(...args);
41976
42612
  }
41977
42613
  function rgbToHex(rgb) {
@@ -41979,7 +42615,7 @@ function rgbToHex(rgb) {
41979
42615
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
41980
42616
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
41981
42617
  const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
41982
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
42618
+ if (rgb.alpha !== void 0) {
41983
42619
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
41984
42620
  return hex + a.toString(16).padStart(2, "0");
41985
42621
  }
@@ -41987,22 +42623,29 @@ function rgbToHex(rgb) {
41987
42623
  }
41988
42624
  var COLORS_LIBRARY = {
41989
42625
  Color: {
41990
- description: "Convert a color string to a canonical sRGB tuple",
42626
+ description: "Parse a CSS-style color string to an Oklch color",
41991
42627
  complexity: 8e3,
41992
- signature: "(string) -> tuple",
42628
+ signature: "(string) -> color",
41993
42629
  evaluate: (ops, { engine: ce }) => {
41994
42630
  const input = isString(ops[0]) ? ops[0].string : void 0;
41995
42631
  if (!input) return ce.error("incompatible-type");
41996
42632
  const color = parseColor(input);
41997
42633
  if (color === 0 && input.trim().toLowerCase() !== "transparent")
41998
42634
  return ce.error("incompatible-type");
41999
- return colorNumberToTuple(ce, color);
42635
+ const r = color >>> 24 & 255;
42636
+ const g = color >>> 16 & 255;
42637
+ const b = color >>> 8 & 255;
42638
+ const a = normalizeAlpha((color & 255) / 255);
42639
+ const c = rgbToOklch({ r, g, b });
42640
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
42641
+ if (a !== void 0) args.push(ce.number(a));
42642
+ return ce.function("Oklch", args);
42000
42643
  }
42001
42644
  },
42002
42645
  ColorToString: {
42003
42646
  description: "Convert a color to a string in the specified format",
42004
42647
  complexity: 8e3,
42005
- signature: "(any, string?) -> string",
42648
+ signature: "(color | string | tuple, string?) -> string",
42006
42649
  evaluate: (ops, { engine: ce }) => {
42007
42650
  const rgb = extractRgb(ce, ops[0]);
42008
42651
  if (!rgb) return ce.error("incompatible-type");
@@ -42014,7 +42657,7 @@ var COLORS_LIBRARY = {
42014
42657
  const r = Math.round(rgb.r);
42015
42658
  const g = Math.round(rgb.g);
42016
42659
  const b = Math.round(rgb.b);
42017
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42660
+ if (rgb.alpha !== void 0)
42018
42661
  return ce.string(`rgb(${r} ${g} ${b} / ${rgb.alpha})`);
42019
42662
  return ce.string(`rgb(${r} ${g} ${b})`);
42020
42663
  }
@@ -42023,17 +42666,17 @@ var COLORS_LIBRARY = {
42023
42666
  const h = Math.round(hsl.h * 10) / 10;
42024
42667
  const s = Math.round(hsl.s * 1e3) / 10;
42025
42668
  const l = Math.round(hsl.l * 1e3) / 10;
42026
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42669
+ if (rgb.alpha !== void 0)
42027
42670
  return ce.string(`hsl(${h} ${s}% ${l}% / ${rgb.alpha})`);
42028
42671
  return ce.string(`hsl(${h} ${s}% ${l}%)`);
42029
42672
  }
42030
42673
  case "oklch": {
42031
- const c = rgbToOklch(rgb);
42674
+ const c = colorExprToOklch(ops[0]) ?? asOklch(rgb);
42032
42675
  const L = Math.round(c.L * 1e3) / 1e3;
42033
42676
  const C = Math.round(c.C * 1e3) / 1e3;
42034
42677
  const H = Math.round(c.H * 10) / 10;
42035
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
42036
- return ce.string(`oklch(${L} ${C} ${H} / ${rgb.alpha})`);
42678
+ if (c.alpha !== void 0)
42679
+ return ce.string(`oklch(${L} ${C} ${H} / ${c.alpha})`);
42037
42680
  return ce.string(`oklch(${L} ${C} ${H})`);
42038
42681
  }
42039
42682
  default:
@@ -42044,60 +42687,44 @@ var COLORS_LIBRARY = {
42044
42687
  ColorMix: {
42045
42688
  description: "Mix two colors in OKLCh space",
42046
42689
  complexity: 8e3,
42047
- signature: "(any, any, number?) -> tuple",
42690
+ signature: "(color | string | tuple, color | string | tuple, number?) -> color",
42048
42691
  evaluate: (ops, { engine: ce }) => {
42049
- const rgb1 = extractRgb(ce, ops[0]);
42050
- const rgb2 = extractRgb(ce, ops[1]);
42051
- if (!rgb1 || !rgb2) return ce.error("incompatible-type");
42052
42692
  let ratio = 0.5;
42053
42693
  if (ops.length >= 3 && ops[2] !== void 0) {
42054
42694
  ratio = ops[2].re;
42055
42695
  if (!Number.isFinite(ratio)) return ce.error("expected-value");
42056
42696
  ratio = Math.max(0, Math.min(1, ratio));
42057
42697
  }
42058
- const c1 = rgbToOklch(rgb1);
42059
- const c2 = rgbToOklch(rgb2);
42060
- const mixed = oklchToRgb(lerpOklch(c1, c2, ratio));
42061
- const r = mixed.r / 255;
42062
- const g = mixed.g / 255;
42063
- const b = mixed.b / 255;
42064
- const a1 = rgb1.alpha ?? 1;
42065
- const a2 = rgb2.alpha ?? 1;
42066
- const alpha = a1 + (a2 - a1) * ratio;
42067
- if (Math.abs(alpha - 1) > 1e-4)
42068
- return ce.tuple(
42069
- ce.number(r),
42070
- ce.number(g),
42071
- ce.number(b),
42072
- ce.number(alpha)
42073
- );
42074
- return ce.tuple(ce.number(r), ce.number(g), ce.number(b));
42698
+ const oklch1 = toOklch(ce, ops[0]);
42699
+ const oklch2 = toOklch(ce, ops[1]);
42700
+ if (!oklch1 || !oklch2) return ce.error("incompatible-type");
42701
+ return oklchToExpr(ce, lerpOklchColor(oklch1, oklch2, ratio));
42075
42702
  }
42076
42703
  },
42077
42704
  Colormap: {
42078
42705
  description: "Sample colors from a named palette",
42079
42706
  complexity: 8e3,
42080
- signature: "(string, number?) -> any",
42707
+ signature: "(string, number?) -> color | list<color>",
42081
42708
  evaluate: (ops, { engine: ce }) => {
42082
42709
  const name = isString(ops[0]) ? ops[0].string : void 0;
42083
42710
  if (!name) return ce.error("incompatible-type");
42084
42711
  const palette = ALL_PALETTES[name];
42085
42712
  if (!palette) return ce.error("expected-value", name);
42086
42713
  if (ops.length < 2 || ops[1] === void 0) {
42087
- const tuples = palette.map(
42088
- (hex) => colorNumberToTuple(ce, parseColor(hex))
42714
+ const colors = palette.map(
42715
+ (hex) => colorNumberToOklch(ce, parseColor(hex))
42089
42716
  );
42090
- return ce.function("List", tuples);
42717
+ return ce.function("List", colors);
42091
42718
  }
42092
42719
  const val = ops[1].re;
42093
42720
  if (!Number.isFinite(val)) return ce.error("expected-value");
42094
42721
  if (Number.isInteger(val) && val >= 2) {
42095
42722
  const n = val;
42096
- const tuples = [];
42723
+ const colors = [];
42097
42724
  for (let i = 0; i < n; i++) {
42098
- tuples.push(samplePalette(ce, palette, i / (n - 1)));
42725
+ colors.push(samplePalette(ce, palette, i / (n - 1)));
42099
42726
  }
42100
- return ce.function("List", tuples);
42727
+ return ce.function("List", colors);
42101
42728
  }
42102
42729
  return samplePalette(ce, palette, val);
42103
42730
  }
@@ -42105,12 +42732,25 @@ var COLORS_LIBRARY = {
42105
42732
  ColorToColorspace: {
42106
42733
  description: "Convert a color to components in a target color space",
42107
42734
  complexity: 8e3,
42108
- signature: "(any, string) -> tuple",
42735
+ signature: "(color | string | tuple, string) -> tuple",
42109
42736
  evaluate: (ops, { engine: ce }) => {
42110
- const rgb = extractRgb(ce, ops[0]);
42111
- if (!rgb) return ce.error("incompatible-type");
42112
42737
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42113
42738
  if (!space) return ce.error("incompatible-type");
42739
+ if (space === "oklch" || space === "oklab" || space === "lab") {
42740
+ const oklch2 = colorExprToOklch(ops[0]);
42741
+ if (oklch2) {
42742
+ if (space === "oklch")
42743
+ return componentsTuple(
42744
+ ce,
42745
+ [oklch2.L, oklch2.C, oklch2.H],
42746
+ oklch2.alpha
42747
+ );
42748
+ const lab = oklchToOklab(oklch2);
42749
+ return componentsTuple(ce, [lab.L, lab.a, lab.b], lab.alpha);
42750
+ }
42751
+ }
42752
+ const rgb = extractRgb(ce, ops[0]);
42753
+ if (!rgb) return ce.error("incompatible-type");
42114
42754
  const alpha = rgb.alpha;
42115
42755
  switch (space) {
42116
42756
  case "rgb":
@@ -42140,17 +42780,27 @@ var COLORS_LIBRARY = {
42140
42780
  ColorFromColorspace: {
42141
42781
  description: "Convert color space components to a canonical sRGB tuple",
42142
42782
  complexity: 8e3,
42143
- signature: "(tuple, string) -> tuple",
42783
+ signature: "(color | tuple, string) -> tuple",
42144
42784
  evaluate: (ops, { engine: ce }) => {
42145
- const tuple = ops[0];
42146
- if (!isFunction2(tuple) || tuple.operator !== "Tuple" || tuple.ops.length < 3)
42147
- return ce.error("incompatible-type");
42148
- const c0 = tuple.ops[0].re;
42149
- const c1 = tuple.ops[1].re;
42150
- const c2 = tuple.ops[2].re;
42151
- const alpha = tuple.ops.length >= 4 ? tuple.ops[3].re : void 0;
42152
42785
  const space = isString(ops[1]) ? ops[1].string?.toLowerCase() : void 0;
42153
42786
  if (!space) return ce.error("incompatible-type");
42787
+ let c0, c1, c2;
42788
+ let alpha;
42789
+ const arg = ops[0];
42790
+ const typed = readColorExpr(arg);
42791
+ if (typed) {
42792
+ c0 = typed.c0;
42793
+ c1 = typed.c1;
42794
+ c2 = typed.c2;
42795
+ alpha = typed.alpha;
42796
+ } else if (isFunction2(arg) && arg.operator === "Tuple" && arg.ops.length >= 3) {
42797
+ c0 = arg.ops[0].re;
42798
+ c1 = arg.ops[1].re;
42799
+ c2 = arg.ops[2].re;
42800
+ alpha = arg.ops.length >= 4 ? arg.ops[3].re : void 0;
42801
+ } else {
42802
+ return ce.error("incompatible-type");
42803
+ }
42154
42804
  let rgb;
42155
42805
  switch (space) {
42156
42806
  case "rgb":
@@ -42186,7 +42836,7 @@ var COLORS_LIBRARY = {
42186
42836
  ColorContrast: {
42187
42837
  description: "APCA contrast ratio between two colors",
42188
42838
  complexity: 8e3,
42189
- signature: "(any, any) -> number",
42839
+ signature: "(color | string | tuple, color | string | tuple) -> number",
42190
42840
  evaluate: (ops, { engine: ce }) => {
42191
42841
  const bgRgb = extractRgb(ce, ops[0]);
42192
42842
  const fgRgb = extractRgb(ce, ops[1]);
@@ -42197,19 +42847,186 @@ var COLORS_LIBRARY = {
42197
42847
  ContrastingColor: {
42198
42848
  description: "Choose the foreground color with better APCA contrast against a background",
42199
42849
  complexity: 8e3,
42200
- signature: "(any, any?, any?) -> tuple",
42850
+ signature: "(color | string | tuple, (color | string | tuple)?, (color | string | tuple)?) -> color",
42201
42851
  evaluate: (ops, { engine: ce }) => {
42202
42852
  const bgRgb = extractRgb(ce, ops[0]);
42203
42853
  if (!bgRgb) return ce.error("incompatible-type");
42854
+ let packed;
42204
42855
  if (ops.length >= 3 && ops[1] !== void 0 && ops[2] !== void 0) {
42205
42856
  const fg1 = extractRgb(ce, ops[1]);
42206
42857
  const fg2 = extractRgb(ce, ops[2]);
42207
42858
  if (!fg1 || !fg2) return ce.error("incompatible-type");
42208
- const result2 = contrastingColor({ bg: bgRgb, fg1, fg2 });
42209
- return colorNumberToTuple(ce, result2);
42859
+ packed = contrastingColor({ bg: bgRgb, fg1, fg2 });
42860
+ } else {
42861
+ packed = contrastingColor(bgRgb);
42862
+ }
42863
+ const r = (packed >>> 24 & 255) / 255;
42864
+ const g = (packed >>> 16 & 255) / 255;
42865
+ const b = (packed >>> 8 & 255) / 255;
42866
+ const alpha = normalizeAlpha((packed & 255) / 255);
42867
+ const args = [ce.number(r), ce.number(g), ce.number(b)];
42868
+ if (alpha !== void 0) args.push(ce.number(alpha));
42869
+ return ce.function("Rgb", args);
42870
+ }
42871
+ },
42872
+ // ---------------------------------------------------------------------------
42873
+ // Color constructors. Each preserves its colorspace on evaluation; the
42874
+ // operator name is the discriminator. Components are interpreted per
42875
+ // colorspace conventions (Rgb channels 0-1, Hsv/Hsl hue in degrees with
42876
+ // sat/value 0-1, Oklab/Oklch L 0-1 with standard a/b/C/H ranges). The
42877
+ // optional 4th argument is alpha in [0, 1]. No clamping at evaluation time.
42878
+ // ---------------------------------------------------------------------------
42879
+ Rgb: {
42880
+ description: "sRGB color (channels 0-1, optional alpha 0-1)",
42881
+ complexity: 8e3,
42882
+ signature: "(number, number, number, number?) -> color"
42883
+ },
42884
+ Hsv: {
42885
+ description: "HSV color (hue degrees, saturation/value 0-1, optional alpha)",
42886
+ complexity: 8e3,
42887
+ signature: "(number, number, number, number?) -> color"
42888
+ },
42889
+ Hsl: {
42890
+ description: "HSL color (hue degrees, saturation/lightness 0-1, optional alpha)",
42891
+ complexity: 8e3,
42892
+ signature: "(number, number, number, number?) -> color"
42893
+ },
42894
+ Oklab: {
42895
+ description: "OKLab color (L 0-1, a/b ~ -0.4..0.4, optional alpha)",
42896
+ complexity: 8e3,
42897
+ signature: "(number, number, number, number?) -> color"
42898
+ },
42899
+ Oklch: {
42900
+ description: "OKLCh color (L 0-1, C 0-~0.4, hue degrees, optional alpha)",
42901
+ complexity: 8e3,
42902
+ signature: "(number, number, number, number?) -> color"
42903
+ },
42904
+ // ---------------------------------------------------------------------------
42905
+ // Color-space conversions. Each accepts any of the five color heads and
42906
+ // returns the same color in the named space. If the input is already in
42907
+ // the target space, returns the input unchanged.
42908
+ // ---------------------------------------------------------------------------
42909
+ AsRgb: {
42910
+ description: "Convert any color to sRGB (channels 0-1)",
42911
+ complexity: 8e3,
42912
+ signature: "(color) -> color",
42913
+ evaluate: (ops, { engine: ce }) => {
42914
+ const arg = ops[0];
42915
+ if (isFunction2(arg) && arg.operator === "Rgb")
42916
+ return normalizeColorHead(ce, arg);
42917
+ const rgb = colorExprToRgb(arg);
42918
+ if (!rgb) return ce.error("incompatible-type");
42919
+ const args = [
42920
+ ce.number(rgb.r / 255),
42921
+ ce.number(rgb.g / 255),
42922
+ ce.number(rgb.b / 255)
42923
+ ];
42924
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42925
+ return ce.function("Rgb", args);
42926
+ }
42927
+ },
42928
+ AsHsv: {
42929
+ description: "Convert any color to HSV (hue degrees, s/v 0-1)",
42930
+ complexity: 8e3,
42931
+ signature: "(color) -> color",
42932
+ evaluate: (ops, { engine: ce }) => {
42933
+ const arg = ops[0];
42934
+ if (isFunction2(arg) && arg.operator === "Hsv")
42935
+ return normalizeColorHead(ce, arg);
42936
+ const rgb = colorExprToRgb(arg);
42937
+ if (!rgb) return ce.error("incompatible-type");
42938
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
42939
+ const args = [ce.number(hsv.h), ce.number(hsv.s), ce.number(hsv.v)];
42940
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42941
+ return ce.function("Hsv", args);
42942
+ }
42943
+ },
42944
+ AsHsl: {
42945
+ description: "Convert any color to HSL (hue degrees, s/l 0-1)",
42946
+ complexity: 8e3,
42947
+ signature: "(color) -> color",
42948
+ evaluate: (ops, { engine: ce }) => {
42949
+ const arg = ops[0];
42950
+ if (isFunction2(arg) && arg.operator === "Hsl")
42951
+ return normalizeColorHead(ce, arg);
42952
+ const rgb = colorExprToRgb(arg);
42953
+ if (!rgb) return ce.error("incompatible-type");
42954
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
42955
+ const args = [ce.number(hsl.h), ce.number(hsl.s), ce.number(hsl.l)];
42956
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42957
+ return ce.function("Hsl", args);
42958
+ }
42959
+ },
42960
+ AsOklab: {
42961
+ description: "Convert any color to OKLab",
42962
+ complexity: 8e3,
42963
+ signature: "(color) -> color",
42964
+ evaluate: (ops, { engine: ce }) => {
42965
+ const arg = ops[0];
42966
+ if (isFunction2(arg) && arg.operator === "Oklab")
42967
+ return normalizeColorHead(ce, arg);
42968
+ if (isFunction2(arg) && arg.operator === "Oklch") {
42969
+ const c = readColorExpr(arg);
42970
+ if (!c) return ce.error("incompatible-type");
42971
+ const lab2 = oklchToOklab({ L: c.c0, C: c.c1, H: c.c2, alpha: c.alpha });
42972
+ const args2 = [ce.number(lab2.L), ce.number(lab2.a), ce.number(lab2.b)];
42973
+ if (lab2.alpha !== void 0) args2.push(ce.number(lab2.alpha));
42974
+ return ce.function("Oklab", args2);
42975
+ }
42976
+ const rgb = colorExprToRgb(arg);
42977
+ if (!rgb) return ce.error("incompatible-type");
42978
+ const lab = rgbToOklab(rgb);
42979
+ const args = [ce.number(lab.L), ce.number(lab.a), ce.number(lab.b)];
42980
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
42981
+ return ce.function("Oklab", args);
42982
+ }
42983
+ },
42984
+ AsOklch: {
42985
+ description: "Convert any color to OKLCh",
42986
+ complexity: 8e3,
42987
+ signature: "(color) -> color",
42988
+ evaluate: (ops, { engine: ce }) => {
42989
+ const arg = ops[0];
42990
+ if (isFunction2(arg) && arg.operator === "Oklch")
42991
+ return normalizeColorHead(ce, arg);
42992
+ if (isFunction2(arg) && arg.operator === "Oklab") {
42993
+ const c2 = readColorExpr(arg);
42994
+ if (!c2) return ce.error("incompatible-type");
42995
+ const oklch2 = oklabToOklch({
42996
+ L: c2.c0,
42997
+ a: c2.c1,
42998
+ b: c2.c2,
42999
+ alpha: c2.alpha
43000
+ });
43001
+ const args2 = [
43002
+ ce.number(oklch2.L),
43003
+ ce.number(oklch2.C),
43004
+ ce.number(oklch2.H)
43005
+ ];
43006
+ if (oklch2.alpha !== void 0) args2.push(ce.number(oklch2.alpha));
43007
+ return ce.function("Oklch", args2);
42210
43008
  }
42211
- const result = contrastingColor(bgRgb);
42212
- return colorNumberToTuple(ce, result);
43009
+ const rgb = colorExprToRgb(arg);
43010
+ if (!rgb) return ce.error("incompatible-type");
43011
+ const c = rgbToOklch(rgb);
43012
+ const args = [ce.number(c.L), ce.number(c.C), ce.number(c.H)];
43013
+ if (rgb.alpha !== void 0) args.push(ce.number(rgb.alpha));
43014
+ return ce.function("Oklch", args);
43015
+ }
43016
+ },
43017
+ // ---------------------------------------------------------------------------
43018
+ // Perceptual difference. Returns ΔE_OK (Euclidean distance in OKLab),
43019
+ // an approximately perceptually uniform scalar.
43020
+ // ---------------------------------------------------------------------------
43021
+ ColorDelta: {
43022
+ description: "Perceptual color difference (\u0394E_OK) between two colors",
43023
+ complexity: 8e3,
43024
+ signature: "(color | string | tuple, color | string | tuple) -> number",
43025
+ evaluate: (ops, { engine: ce }) => {
43026
+ const a = toOklch(ce, ops[0]);
43027
+ const b = toOklch(ce, ops[1]);
43028
+ if (!a || !b) return ce.error("incompatible-type");
43029
+ return ce.number(oklabDeltaE(oklchToOklab(a), oklchToOklab(b)));
42213
43030
  }
42214
43031
  }
42215
43032
  };
@@ -42268,12 +43085,42 @@ var CONTROL_STRUCTURES_LIBRARY = [
42268
43085
  }
42269
43086
  },
42270
43087
  Loop: {
42271
- description: "Evaluate a body expression over elements of a collection.",
43088
+ description: "Evaluate a body expression in nested iteration over Element clauses. Later clauses see earlier bindings; independent clauses produce a Cartesian product.",
43089
+ lazy: true,
43090
+ signature: "(body:expression, iterators:expression*) -> any",
43091
+ type: ([body]) => {
43092
+ if (!body) return "nothing";
43093
+ return parseType(`indexed_collection<${String(body.type)}>`);
43094
+ },
43095
+ canonical: canonicalLoop,
43096
+ evaluate: (ops, { engine: ce }) => run(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining),
43097
+ evaluateAsync: async (ops, { engine: ce, signal }) => runAsync(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining, signal)
43098
+ },
43099
+ When: {
43100
+ description: "Conditional value: returns expr when cond holds, undefined otherwise.",
42272
43101
  lazy: true,
42273
- signature: "(body:expression, collection:expression) -> any",
42274
- type: ([body]) => body.type,
42275
- evaluate: ([body, collection], { engine: ce }) => run(runLoop(body, collection, ce), ce._timeRemaining),
42276
- evaluateAsync: async ([body, collection], { engine: ce, signal }) => runAsync(runLoop(body, collection, ce), ce._timeRemaining, signal)
43102
+ signature: "(expression, boolean) -> any",
43103
+ type: ([expr2]) => expr2.type,
43104
+ canonical: (args, { engine: ce }) => {
43105
+ if (args.length !== 2) return null;
43106
+ const [expr2, cond] = args;
43107
+ if (isFunction2(expr2, "When")) {
43108
+ const inner = expr2.op1.canonical;
43109
+ const innerCond = expr2.op2.canonical;
43110
+ return ce._fn("When", [
43111
+ inner,
43112
+ ce._fn("And", [innerCond, cond.canonical])
43113
+ ]);
43114
+ }
43115
+ return ce._fn("When", [expr2.canonical, cond.canonical]);
43116
+ },
43117
+ evaluate: ([expr2, cond], { engine: ce }) => {
43118
+ const c = cond.evaluate();
43119
+ const cs = sym(c);
43120
+ if (cs === "True") return expr2.evaluate();
43121
+ if (cs === "False") return ce.symbol("Undefined");
43122
+ return ce._fn("When", [expr2, c]);
43123
+ }
42277
43124
  },
42278
43125
  Which: {
42279
43126
  description: "Return the value for the first condition that is true.",
@@ -42333,9 +43180,141 @@ function canonicalBlock(ops, options) {
42333
43180
  );
42334
43181
  return result;
42335
43182
  }
42336
- function* runLoop(body, collection, ce) {
43183
+ function canonicalLoop(ops, options) {
43184
+ const { engine: ce, scope } = options;
43185
+ if (ops.length === 0) return null;
43186
+ if (ops.length === 1) {
43187
+ return ce._fn("Loop", [ops[0].canonical]);
43188
+ }
43189
+ const body = ops[0];
43190
+ const iterators = ops.slice(1);
43191
+ const allElement = iterators.every((it) => it.operator === "Element");
43192
+ if (!allElement) {
43193
+ return ce._fn(
43194
+ "Loop",
43195
+ ops.map((op) => op.canonical)
43196
+ );
43197
+ }
43198
+ const loopScope = scope ?? {
43199
+ parent: ce.context.lexicalScope,
43200
+ bindings: /* @__PURE__ */ new Map()
43201
+ };
43202
+ loopScope.noAutoDeclare = true;
43203
+ ce.pushScope(loopScope);
43204
+ let canonicalIterators;
43205
+ let canonicalBody;
43206
+ try {
43207
+ canonicalIterators = iterators.map((it) => {
43208
+ if (!isFunction2(it, "Element")) {
43209
+ return ce._fn("Element", [
43210
+ ce.error("missing").canonical,
43211
+ ce.error("missing").canonical
43212
+ ]);
43213
+ }
43214
+ const indexExpr = it.ops[0];
43215
+ const collExpr = it.ops[1];
43216
+ if (!indexExpr || !collExpr) {
43217
+ return ce._fn("Element", [
43218
+ (indexExpr ?? ce.error("missing")).canonical,
43219
+ (collExpr ?? ce.error("missing")).canonical
43220
+ ]);
43221
+ }
43222
+ if (isSymbol2(indexExpr) && indexExpr.symbol !== "Nothing") {
43223
+ if (!ce.context.lexicalScope.bindings.has(indexExpr.symbol))
43224
+ ce.declare(indexExpr.symbol, "unknown");
43225
+ }
43226
+ return ce._fn("Element", [indexExpr.canonical, collExpr.canonical]);
43227
+ });
43228
+ canonicalBody = body.canonical;
43229
+ } finally {
43230
+ ce.popScope();
43231
+ loopScope.noAutoDeclare = false;
43232
+ }
43233
+ return ce._fn("Loop", [canonicalBody, ...canonicalIterators], {
43234
+ scope: loopScope
43235
+ });
43236
+ }
43237
+ function* runLoop(body, elements, ce) {
42337
43238
  body ??= ce.Nothing;
42338
43239
  if (sym(body) === "Nothing") return body;
43240
+ if (elements.length === 0) {
43241
+ const result = body.evaluate();
43242
+ yield result;
43243
+ return result;
43244
+ }
43245
+ if (elements.length === 1 && elements[0].operator !== "Element") {
43246
+ return yield* runLoopLegacy(body, elements[0], ce);
43247
+ }
43248
+ const results = [];
43249
+ const state = { stopped: false, broke: false, count: 0 };
43250
+ const freshScope = {
43251
+ parent: ce.context.lexicalScope,
43252
+ bindings: /* @__PURE__ */ new Map()
43253
+ };
43254
+ ce._pushEvalContext(freshScope);
43255
+ try {
43256
+ for (const elem of elements) {
43257
+ if (!isFunction2(elem, "Element")) continue;
43258
+ const idx = elem.ops[0];
43259
+ if (idx && isSymbol2(idx) && idx.symbol !== "Nothing") {
43260
+ if (!freshScope.bindings.has(idx.symbol))
43261
+ ce.declare(idx.symbol, "unknown");
43262
+ }
43263
+ }
43264
+ yield* runLoopNested(body, elements, 0, ce, results, state);
43265
+ } finally {
43266
+ ce._popEvalContext();
43267
+ }
43268
+ if (state.stopped && state.value !== void 0) {
43269
+ if (!state.broke) return state.value;
43270
+ return state.value;
43271
+ }
43272
+ return ce.function("List", results);
43273
+ }
43274
+ function* runLoopNested(body, elements, index, ce, results, state) {
43275
+ if (state.stopped) return;
43276
+ if (index === elements.length) {
43277
+ const result = body.evaluate();
43278
+ state.count += 1;
43279
+ if (state.count > ce.iterationLimit)
43280
+ throw new CancellationError({ cause: "iteration-limit-exceeded" });
43281
+ if (isFunction2(result, "Break")) {
43282
+ state.stopped = true;
43283
+ state.broke = true;
43284
+ state.value = result.op1;
43285
+ return;
43286
+ }
43287
+ if (result.operator === "Return") {
43288
+ state.stopped = true;
43289
+ state.value = result;
43290
+ return;
43291
+ }
43292
+ results.push(result);
43293
+ yield result;
43294
+ return;
43295
+ }
43296
+ const elem = elements[index];
43297
+ if (!isFunction2(elem, "Element")) {
43298
+ return;
43299
+ }
43300
+ const indexExpr = elem.ops[0];
43301
+ const collExpr = elem.ops[1];
43302
+ if (!indexExpr || !isSymbol2(indexExpr) || !collExpr) {
43303
+ return;
43304
+ }
43305
+ const name = indexExpr.symbol;
43306
+ const collection = collExpr.evaluate();
43307
+ if (!collection?.isCollection) {
43308
+ return;
43309
+ }
43310
+ const skipAssign = name === "Nothing";
43311
+ for (const value of collection.each()) {
43312
+ if (!skipAssign) ce.assign(name, value);
43313
+ yield* runLoopNested(body, elements, index + 1, ce, results, state);
43314
+ if (state.stopped) return;
43315
+ }
43316
+ }
43317
+ function* runLoopLegacy(body, collection, ce) {
42339
43318
  if (collection?.isCollection) {
42340
43319
  let result = void 0;
42341
43320
  const fn = applicable(body);
@@ -44689,6 +45668,34 @@ var CORE_LIBRARY = [
44689
45668
  signature: "() -> expression",
44690
45669
  evaluate: (_ops, { engine }) => engine.expr(randomExpression())
44691
45670
  }
45671
+ },
45672
+ // ---------------------------------------------------------------------------
45673
+ // Opaque typed heads — registered so the names are in the standard set
45674
+ // (consumers can branch on the operator name); CE itself does not evaluate
45675
+ // them. Geometric primitives `Triangle`/`Sphere`/`Segment` and the action
45676
+ // arrow `To` (`a \to b`).
45677
+ // ---------------------------------------------------------------------------
45678
+ {
45679
+ Triangle: {
45680
+ description: "Triangle primitive \u2014 opaque typed head.",
45681
+ signature: "(any+) -> expression"
45682
+ },
45683
+ GeometricVector: {
45684
+ description: "Geometric vector (directed segment between two points) \u2014 opaque typed head. Distinct from the column-vector `Vector` operator.",
45685
+ signature: "(any, any) -> expression"
45686
+ },
45687
+ Sphere: {
45688
+ description: "Sphere primitive \u2014 opaque typed head.",
45689
+ signature: "(any+) -> expression"
45690
+ },
45691
+ Segment: {
45692
+ description: "Segment primitive \u2014 opaque typed head.",
45693
+ signature: "(any+) -> expression"
45694
+ },
45695
+ To: {
45696
+ description: "Action arrow / mapping (`a \\to b`) \u2014 opaque typed head.",
45697
+ signature: "(any, any) -> nothing"
45698
+ }
44692
45699
  }
44693
45700
  ];
44694
45701
 
@@ -51825,6 +52832,7 @@ function orderConvexHull(points) {
51825
52832
 
51826
52833
  // src/compute-engine/boxed-expression/cache.ts
51827
52834
  function cachedValue(v, generation, fn) {
52835
+ if (v.generation === generation && v.value !== null) return v.value;
51828
52836
  v.generation = generation;
51829
52837
  v.value = fn();
51830
52838
  return v.value;
@@ -53602,6 +54610,12 @@ function _setProduct(fn) {
53602
54610
  function _escapeJsonString(s) {
53603
54611
  return s;
53604
54612
  }
54613
+ function _serializeLatexMetadata(ce, expr2) {
54614
+ const syntax = ce.latexSyntax;
54615
+ const opts = ce.latexOptions;
54616
+ if (Object.keys(opts).length === 0) return syntax.serialize(expr2);
54617
+ return syntax.serialize(expr2, { ...opts });
54618
+ }
53605
54619
  function serializeSubtract(ce, a, b, options, metadata) {
53606
54620
  if (isNumber(a) && a.isNegative) {
53607
54621
  const v = a.numericValue;
@@ -53906,7 +54920,7 @@ function serializeJsonFunction(ce, name, args, options, metadata) {
53906
54920
  ];
53907
54921
  const md = { ...metadata ?? {} };
53908
54922
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53909
- md.latex = _escapeJsonString(md.latex ?? ce.latexSyntax.serialize(fn));
54923
+ md.latex = _escapeJsonString(md.latex ?? _serializeLatexMetadata(ce, fn));
53910
54924
  } else md.latex = "";
53911
54925
  if (!options.metadata.includes("wikidata")) md.wikidata = "";
53912
54926
  if (!md.latex && !md.wikidata && options.shorthands.includes("function"))
@@ -53931,7 +54945,7 @@ function serializeJsonSymbol(ce, sym2, options, metadata) {
53931
54945
  }
53932
54946
  metadata = { ...metadata };
53933
54947
  if (options.metadata.includes("latex") && ce.latexSyntax) {
53934
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(sym2);
54948
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, sym2);
53935
54949
  if (metadata.latex !== void 0)
53936
54950
  metadata.latex = _escapeJsonString(metadata.latex);
53937
54951
  } else metadata.latex = void 0;
@@ -54093,7 +55107,7 @@ function serializeJsonNumber(ce, value, options, metadata) {
54093
55107
  }
54094
55108
  }
54095
55109
  if (options.metadata.includes("latex") && ce.latexSyntax)
54096
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize(result2 ?? { num });
55110
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, result2 ?? { num });
54097
55111
  if (result2) {
54098
55112
  if (metadata.latex !== void 0)
54099
55113
  return { sym: result2, latex: metadata.latex };
@@ -54109,7 +55123,7 @@ function serializeJsonNumber(ce, value, options, metadata) {
54109
55123
  if (value.isNaN()) {
54110
55124
  num = "NaN";
54111
55125
  if (options.metadata.includes("latex") && ce.latexSyntax)
54112
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
55126
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54113
55127
  return metadata.latex !== void 0 ? { num, latex: metadata.latex } : { num };
54114
55128
  }
54115
55129
  return serializeJsonFunction(
@@ -54143,7 +55157,7 @@ function serializeJsonNumber(ce, value, options, metadata) {
54143
55157
  value = Number(value);
54144
55158
  } else {
54145
55159
  if (options.metadata.includes("latex") && ce.latexSyntax)
54146
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({
55160
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, {
54147
55161
  num: value.toString()
54148
55162
  });
54149
55163
  if (metadata.latex !== void 0)
@@ -54157,7 +55171,7 @@ function serializeJsonNumber(ce, value, options, metadata) {
54157
55171
  result = value > 0 ? "PositiveInfinity" : "NegativeInfinity";
54158
55172
  else num = serializeRepeatingDecimals(value.toString(), options);
54159
55173
  if (options.metadata.includes("latex") && ce.latexSyntax)
54160
- metadata.latex = metadata.latex ?? ce.latexSyntax.serialize({ num });
55174
+ metadata.latex = metadata.latex ?? _serializeLatexMetadata(ce, { num });
54161
55175
  if (result) {
54162
55176
  if (metadata.latex !== void 0)
54163
55177
  return { sym: result, latex: metadata.latex };
@@ -54383,6 +55397,23 @@ var BaseCompiler = class _BaseCompiler {
54383
55397
  };
54384
55398
  return compilePair(0);
54385
55399
  }
55400
+ if (h === "When") {
55401
+ if (args.length !== 2)
55402
+ throw new Error("When: expected exactly 2 arguments (expr, cond)");
55403
+ const fn2 = target.functions?.(h);
55404
+ if (fn2) {
55405
+ if (typeof fn2 === "function") {
55406
+ return fn2(args, (expr2) => _BaseCompiler.compile(expr2, target), target);
55407
+ }
55408
+ return `${fn2}(${args.map((x) => _BaseCompiler.compile(x, target)).join(", ")})`;
55409
+ }
55410
+ if (isSymbol2(args[1], "True"))
55411
+ return `(${_BaseCompiler.compile(args[0], target)})`;
55412
+ if (isSymbol2(args[1], "False")) return "NaN";
55413
+ const val = _BaseCompiler.compile(args[0], target);
55414
+ const cond = _BaseCompiler.compile(args[1], target);
55415
+ return `((${cond}) ? (${val}) : NaN)`;
55416
+ }
54386
55417
  if (h === "Block") {
54387
55418
  return _BaseCompiler.compileBlock(args, target);
54388
55419
  }
@@ -54457,17 +55488,91 @@ var BaseCompiler = class _BaseCompiler {
54457
55488
  )}${target.ws("\n")}})()`;
54458
55489
  }
54459
55490
  /**
54460
- * Compile a Loop expression with Element(index, Range(lo, hi)) indexing.
54461
- * Generates: (() => { for (let i = lo; i <= hi; i++) { body } })()
55491
+ * Compile a Loop expression.
55492
+ *
55493
+ * Two forms are supported:
55494
+ *
55495
+ * 1. **Imperative / single-Element form** (existing behaviour):
55496
+ * `Loop(body, Element(i, Range(lo, hi)))`
55497
+ * Generates a raw `for (let i = lo; i <= hi; i++) { body }` loop wrapped
55498
+ * in an IIFE. The loop counter is always a plain number. For targets
55499
+ * that wrap numeric values (e.g. interval-js uses `_IA.point()`),
55500
+ * references to the loop index inside the body are re-wrapped via
55501
+ * `target.number`. `break` / `continue` / `return` are preserved.
55502
+ *
55503
+ * 2. **Comprehension / variadic-Element form** (new):
55504
+ * `Loop(body, Element(x, coll1), Element(y, coll2), …)`
55505
+ * When two or more `Element` clauses are present — or when the single
55506
+ * Element's collection is not a `Range` — the loop is compiled as a
55507
+ * comprehension that collects results into an array. Each clause
55508
+ * produces a `for (const name of collection)` loop, nested
55509
+ * outermost-to-innermost, and the innermost body pushes into `result`.
55510
+ *
55511
+ * Example output (JS):
55512
+ * ```js
55513
+ * (() => { const result = [];
55514
+ * for (const x of [1,2]) { for (const y of [3,4]) { result.push(body); } }
55515
+ * return result; })()
55516
+ * ```
54462
55517
  *
54463
- * The loop counter is always a raw number. For targets that wrap numeric
54464
- * values (e.g. interval-js wraps with `_IA.point()`), references to the
54465
- * loop index inside the body are wrapped via `target.number`.
55518
+ * GLSL: multi-Element comprehension is not trivially representable in
55519
+ * GLSL (no dynamic arrays, no push). A compile-time error is thrown.
55520
+ * TODO(E3-GLSL): support GLSL multi-Element via a pre-declared fixed-size
55521
+ * array or by unrolling when bounds are known at compile time.
54466
55522
  */
54467
55523
  static compileForLoop(args, target) {
54468
55524
  if (!args[0]) throw new Error("Loop: no body");
54469
55525
  if (!args[1]) throw new Error("Loop: no indexing set");
54470
- const indexing = args[1];
55526
+ const body = args[0];
55527
+ const elements = args.slice(1);
55528
+ const useComprehension = elements.length > 1 || elements.length === 1 && isFunction2(elements[0], "Element") && !_BaseCompiler.isLegacyCompatibleRange(elements[0].ops[1]);
55529
+ if (useComprehension) {
55530
+ const lang = target.language ?? "";
55531
+ if (lang === "glsl" || lang === "wgsl") {
55532
+ throw new Error(
55533
+ `${lang.toUpperCase()}: multi-Element Loop comprehension is not yet supported. TODO(E3-GLSL): unroll or use a fixed-size array.`
55534
+ );
55535
+ }
55536
+ const narrowedElements = [];
55537
+ for (let i = 0; i < elements.length; i++) {
55538
+ const elem = elements[i];
55539
+ if (!isFunction2(elem, "Element"))
55540
+ throw new Error(
55541
+ `Loop: argument ${i + 1} must be an Element clause, got ${elem.operator ?? "?"}`
55542
+ );
55543
+ if (!isSymbol2(elem.ops[0]))
55544
+ throw new Error(
55545
+ `Loop: Element index (argument ${i + 1}) must be a symbol`
55546
+ );
55547
+ narrowedElements.push(elem);
55548
+ }
55549
+ const loopVarSet = new Set(
55550
+ narrowedElements.map(
55551
+ (e) => e.ops[0].symbol
55552
+ )
55553
+ );
55554
+ const needsWrap2 = target.number(0) !== "0";
55555
+ const bodyTarget2 = needsWrap2 ? {
55556
+ ...target,
55557
+ var: (id) => loopVarSet.has(id) ? target.number(0).replace("0", id) : target.var(id)
55558
+ } : target;
55559
+ const bodyCode = _BaseCompiler.compile(body, bodyTarget2);
55560
+ let inner = `result.push(${bodyCode});`;
55561
+ for (let i = narrowedElements.length - 1; i >= 0; i--) {
55562
+ const elem = narrowedElements[i];
55563
+ const name = elem.ops[0].symbol;
55564
+ const collExpr = elem.ops[1];
55565
+ let collection;
55566
+ if (isFunction2(collExpr, "Range")) {
55567
+ collection = _BaseCompiler.compileRangeIterable(collExpr, bodyTarget2);
55568
+ } else {
55569
+ collection = _BaseCompiler.compile(collExpr, bodyTarget2);
55570
+ }
55571
+ inner = `for (const ${name} of ${collection}) { ${inner} }`;
55572
+ }
55573
+ return `(() => { const result = []; ${inner} return result; })()`;
55574
+ }
55575
+ const indexing = elements[0];
54471
55576
  if (!isFunction2(indexing, "Element"))
54472
55577
  throw new Error("Loop: expected Element(index, Range(lo, hi))");
54473
55578
  const indexExpr = indexing.ops[0];
@@ -54485,13 +55590,72 @@ var BaseCompiler = class _BaseCompiler {
54485
55590
  ...target,
54486
55591
  var: (id) => id === index ? needsWrap ? target.number(0).replace("0", index) : index : target.var(id)
54487
55592
  };
54488
- const bodyStmts = _BaseCompiler.compileLoopBody(args[0], bodyTarget);
55593
+ const bodyStmts = _BaseCompiler.compileLoopBody(body, bodyTarget);
54489
55594
  return `(() => {${target.ws(
54490
55595
  "\n"
54491
55596
  )}for (let ${index} = ${lower}; ${index} <= ${upper}; ${index}++) {${target.ws(
54492
55597
  "\n"
54493
55598
  )}${bodyStmts}${target.ws("\n")}}${target.ws("\n")}})()`;
54494
55599
  }
55600
+ /**
55601
+ * Returns `true` when the given collection expression is a `Range` whose
55602
+ * runtime semantics match the legacy imperative for-loop shape
55603
+ * `for (let i = lo; i <= hi; i++)`.
55604
+ *
55605
+ * Concretely: integer-ascending bounds and step omitted-or-1. When bounds
55606
+ * are not statically numeric we accept the Range (the historical
55607
+ * behaviour) — runtime mismatch in the descending-unknown-bounds case is
55608
+ * left as a known limitation; callers can force the iterable path by
55609
+ * supplying an explicit step.
55610
+ */
55611
+ static isLegacyCompatibleRange(coll) {
55612
+ if (!isFunction2(coll, "Range")) return false;
55613
+ if (coll.ops.length >= 3) {
55614
+ const stepExpr = coll.ops[2];
55615
+ if (!isNumber(stepExpr) || stepExpr.re !== 1) return false;
55616
+ }
55617
+ const lo = coll.ops[0];
55618
+ const hi = coll.ops[1];
55619
+ if (isNumber(lo) && !Number.isInteger(lo.re)) return false;
55620
+ if (isNumber(hi) && !Number.isInteger(hi.re)) return false;
55621
+ if (isNumber(lo) && isNumber(hi) && lo.re > hi.re) return false;
55622
+ return true;
55623
+ }
55624
+ /**
55625
+ * Compile a `Range(lo, hi)` or `Range(lo, hi, step)` expression into a JS
55626
+ * iterable expression. Mirrors the runtime semantics in
55627
+ * `library/collections.ts` Range:
55628
+ * count = step === 0 ? 0 : max(0, floor((hi - lo) / step) + 1)
55629
+ * element = lo + step * k (0-indexed)
55630
+ * Default step is 1 when omitted. Bounds and step may be fractional.
55631
+ *
55632
+ * Only used from the comprehension path in `compileForLoop`.
55633
+ * Caller must have already verified `isFunction(rangeExpr, 'Range')`.
55634
+ */
55635
+ static compileRangeIterable(rangeExpr, target) {
55636
+ const loExpr = rangeExpr.ops[0];
55637
+ const hiExpr = rangeExpr.ops[1];
55638
+ const stepExpr = rangeExpr.ops[2];
55639
+ if (isNumber(loExpr) && isNumber(hiExpr) && (stepExpr === void 0 || isNumber(stepExpr))) {
55640
+ const lo2 = loExpr.re;
55641
+ const hi2 = hiExpr.re;
55642
+ const step2 = stepExpr === void 0 ? hi2 >= lo2 ? 1 : -1 : stepExpr.re;
55643
+ if (step2 === 0) return "[]";
55644
+ const len = Math.max(0, Math.floor((hi2 - lo2) / step2) + 1);
55645
+ if (step2 === 1) {
55646
+ if (lo2 === 0) return `Array.from({length:${len}},(_,k)=>k)`;
55647
+ return `Array.from({length:${len}},(_,k)=>${lo2}+k)`;
55648
+ }
55649
+ return `Array.from({length:${len}},(_,k)=>${lo2}+(${step2})*k)`;
55650
+ }
55651
+ const lo = _BaseCompiler.compile(loExpr, target);
55652
+ const hi = _BaseCompiler.compile(hiExpr, target);
55653
+ if (stepExpr === void 0) {
55654
+ 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})`;
55655
+ }
55656
+ const step = _BaseCompiler.compile(stepExpr, target);
55657
+ 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})`;
55658
+ }
54495
55659
  /**
54496
55660
  * Compile a loop body expression as statements (not wrapped in IIFE).
54497
55661
  * Handles Break, Continue, Return as statements, and If as if-else when
@@ -54922,8 +56086,7 @@ function compile(expr2, options) {
54922
56086
  ce.pushScope();
54923
56087
  try {
54924
56088
  if (vars && typeof vars === "object") {
54925
- for (const [k, v] of Object.entries(vars))
54926
- ce.assign(k, v);
56089
+ for (const [k, v] of Object.entries(vars)) ce.assign(k, v);
54927
56090
  }
54928
56091
  return expr2.evaluate().re;
54929
56092
  } finally {
@@ -60153,8 +61316,7 @@ function tryGetComplexParts(expr2, compile3) {
60153
61316
  return { re: null, im: formatFloat(iScale) };
60154
61317
  }
60155
61318
  const compiledFactors = remaining.map((r) => compile3(r));
60156
- if (iScale !== 1)
60157
- compiledFactors.unshift(formatFloat(iScale));
61319
+ if (iScale !== 1) compiledFactors.unshift(formatFloat(iScale));
60158
61320
  const imCode = foldTerms(compiledFactors, "1.0", "*");
60159
61321
  return { re: null, im: imCode };
60160
61322
  }
@@ -60758,39 +61920,130 @@ var JAVASCRIPT_FUNCTIONS = {
60758
61920
  if (args.length >= 2)
60759
61921
  return `_SYS.colormap(${compile3(args[0])}, ${compile3(args[1])})`;
60760
61922
  return `_SYS.colormap(${compile3(args[0])})`;
61923
+ },
61924
+ // -----------------------------------------------------------------------
61925
+ // Color constructor heads. All compile to OKLCh arrays at runtime — the
61926
+ // canonical color representation in this target. The constructors take
61927
+ // their own colorspace's components and convert internally.
61928
+ // (Mirrors the GPU target's design: color values are vec3 OKLCh.)
61929
+ // -----------------------------------------------------------------------
61930
+ Rgb: (args, compile3) => {
61931
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
61932
+ return `_SYS.rgb(${args.map(compile3).join(", ")})`;
61933
+ },
61934
+ Hsv: (args, compile3) => {
61935
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
61936
+ return `_SYS.hsv(${args.map(compile3).join(", ")})`;
61937
+ },
61938
+ Hsl: (args, compile3) => {
61939
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
61940
+ return `_SYS.hsl(${args.map(compile3).join(", ")})`;
61941
+ },
61942
+ Oklab: (args, compile3) => {
61943
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
61944
+ return `_SYS.oklab(${args.map(compile3).join(", ")})`;
61945
+ },
61946
+ Oklch: (args, compile3) => {
61947
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
61948
+ return `_SYS.oklch(${args.map(compile3).join(", ")})`;
61949
+ },
61950
+ // -----------------------------------------------------------------------
61951
+ // As* converters. Compile-time output convention matches the engine and
61952
+ // the GPU target: each returns components in the named space as a 3- or
61953
+ // 4-element array. `AsRgb` uses 0-1 sRGB channels (consistent across all
61954
+ // layers). `AsOklch` is the identity (canonical form).
61955
+ // -----------------------------------------------------------------------
61956
+ AsRgb: ([c], compile3) => {
61957
+ if (c === null) throw new Error("AsRgb: no argument");
61958
+ return `_SYS.asRgb(${compile3(c)})`;
61959
+ },
61960
+ AsHsv: ([c], compile3) => {
61961
+ if (c === null) throw new Error("AsHsv: no argument");
61962
+ return `_SYS.asHsv(${compile3(c)})`;
61963
+ },
61964
+ AsHsl: ([c], compile3) => {
61965
+ if (c === null) throw new Error("AsHsl: no argument");
61966
+ return `_SYS.asHsl(${compile3(c)})`;
61967
+ },
61968
+ AsOklab: ([c], compile3) => {
61969
+ if (c === null) throw new Error("AsOklab: no argument");
61970
+ return `_SYS.asOklab(${compile3(c)})`;
61971
+ },
61972
+ AsOklch: ([c], compile3) => {
61973
+ if (c === null) throw new Error("AsOklch: no argument");
61974
+ return compile3(c);
61975
+ },
61976
+ // Perceptual color difference (ΔE_OK).
61977
+ ColorDelta: ([a, b], compile3) => {
61978
+ if (a === null || b === null)
61979
+ throw new Error("ColorDelta: need two colors");
61980
+ return `_SYS.colorDelta(${compile3(a)}, ${compile3(b)})`;
61981
+ },
61982
+ // Euclidean distance between two tuples (any positive dimension).
61983
+ // The GPU target maps `Distance` to the GLSL/WGSL `distance()` builtin
61984
+ // (vec-only); this JS handler works on plain arrays of any length.
61985
+ Distance: ([a, b], compile3) => {
61986
+ if (a === null || b === null) throw new Error("Distance: need two points");
61987
+ return `_SYS.distance(${compile3(a)}, ${compile3(b)})`;
60761
61988
  }
60762
61989
  };
60763
61990
  function toRI(c) {
60764
61991
  return { re: c.re, im: c.im };
60765
61992
  }
61993
+ function normalizeAlpha2(a) {
61994
+ if (a === void 0) return void 0;
61995
+ if (!Number.isFinite(a)) return void 0;
61996
+ if (Math.abs(a - 1) < 1e-9) return void 0;
61997
+ return a;
61998
+ }
60766
61999
  function toRgb255(input) {
60767
62000
  if (typeof input === "string") {
60768
62001
  const c = parseColor(input);
60769
- return {
62002
+ const rgb2 = {
60770
62003
  r: c >>> 24 & 255,
60771
62004
  g: c >>> 16 & 255,
60772
- b: c >>> 8 & 255,
60773
- alpha: (c & 255) / 255
62005
+ b: c >>> 8 & 255
60774
62006
  };
62007
+ const alpha = normalizeAlpha2((c & 255) / 255);
62008
+ if (alpha !== void 0) rgb2.alpha = alpha;
62009
+ return rgb2;
62010
+ }
62011
+ const rgb = oklchToRgb({ L: input[0], C: input[1], H: input[2] });
62012
+ if (input.length >= 4) {
62013
+ const alpha = normalizeAlpha2(input[3]);
62014
+ if (alpha !== void 0) rgb.alpha = alpha;
60775
62015
  }
60776
- const rgb = {
60777
- r: input[0] * 255,
60778
- g: input[1] * 255,
60779
- b: input[2] * 255
60780
- };
60781
- if (input.length >= 4) rgb.alpha = input[3];
60782
62016
  return rgb;
60783
62017
  }
60784
- function packedToArray(c) {
60785
- const r = (c >>> 24 & 255) / 255;
60786
- const g = (c >>> 16 & 255) / 255;
60787
- const b = (c >>> 8 & 255) / 255;
60788
- const a = (c & 255) / 255;
60789
- return Math.abs(a - 1) < 1e-4 ? [r, g, b] : [r, g, b, a];
62018
+ function toOklch2(input) {
62019
+ if (typeof input === "string") {
62020
+ const c = parseColor(input);
62021
+ const r = c >>> 24 & 255;
62022
+ const g = c >>> 16 & 255;
62023
+ const b = c >>> 8 & 255;
62024
+ const oklch2 = rgbToOklch({ r, g, b });
62025
+ const alpha = normalizeAlpha2((c & 255) / 255);
62026
+ if (alpha !== void 0) oklch2.alpha = alpha;
62027
+ return oklch2;
62028
+ }
62029
+ return {
62030
+ L: input[0],
62031
+ C: input[1],
62032
+ H: input[2],
62033
+ alpha: input.length >= 4 ? normalizeAlpha2(input[3]) : void 0
62034
+ };
62035
+ }
62036
+ function packedToOklch(c) {
62037
+ const r = c >>> 24 & 255;
62038
+ const g = c >>> 16 & 255;
62039
+ const b = c >>> 8 & 255;
62040
+ const oklch2 = rgbToOklch({ r, g, b });
62041
+ const alpha = normalizeAlpha2((c & 255) / 255);
62042
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
60790
62043
  }
60791
62044
  var colorHelpers = {
60792
62045
  color(input) {
60793
- return packedToArray(parseColor(input));
62046
+ return packedToOklch(parseColor(input));
60794
62047
  },
60795
62048
  colorToString(input, format) {
60796
62049
  const rgb = toRgb255(input);
@@ -60801,7 +62054,7 @@ var colorHelpers = {
60801
62054
  const g = Math.round(Math.max(0, Math.min(255, rgb.g)));
60802
62055
  const b = Math.round(Math.max(0, Math.min(255, rgb.b)));
60803
62056
  let hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
60804
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4) {
62057
+ if (rgb.alpha !== void 0) {
60805
62058
  const a = Math.round(Math.max(0, Math.min(255, rgb.alpha * 255)));
60806
62059
  hex += a.toString(16).padStart(2, "0");
60807
62060
  }
@@ -60811,7 +62064,7 @@ var colorHelpers = {
60811
62064
  const r = Math.round(rgb.r);
60812
62065
  const g = Math.round(rgb.g);
60813
62066
  const b = Math.round(rgb.b);
60814
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
62067
+ if (rgb.alpha !== void 0)
60815
62068
  return `rgb(${r} ${g} ${b} / ${rgb.alpha})`;
60816
62069
  return `rgb(${r} ${g} ${b})`;
60817
62070
  }
@@ -60820,7 +62073,7 @@ var colorHelpers = {
60820
62073
  const h = Math.round(hsl.h * 10) / 10;
60821
62074
  const s = Math.round(hsl.s * 1e3) / 10;
60822
62075
  const l = Math.round(hsl.l * 1e3) / 10;
60823
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
62076
+ if (rgb.alpha !== void 0)
60824
62077
  return `hsl(${h} ${s}% ${l}% / ${rgb.alpha})`;
60825
62078
  return `hsl(${h} ${s}% ${l}%)`;
60826
62079
  }
@@ -60829,7 +62082,7 @@ var colorHelpers = {
60829
62082
  const L = Math.round(c.L * 1e3) / 1e3;
60830
62083
  const C = Math.round(c.C * 1e3) / 1e3;
60831
62084
  const H = Math.round(c.H * 10) / 10;
60832
- if (rgb.alpha !== void 0 && Math.abs(rgb.alpha - 1) > 1e-4)
62085
+ if (rgb.alpha !== void 0)
60833
62086
  return `oklch(${L} ${C} ${H} / ${rgb.alpha})`;
60834
62087
  return `oklch(${L} ${C} ${H})`;
60835
62088
  }
@@ -60838,29 +62091,29 @@ var colorHelpers = {
60838
62091
  }
60839
62092
  },
60840
62093
  colorMix(input1, input2, ratio = 0.5) {
60841
- const rgb1 = toRgb255(input1);
60842
- const rgb2 = toRgb255(input2);
62094
+ const c1 = toOklch2(input1);
62095
+ const c2 = toOklch2(input2);
60843
62096
  ratio = Math.max(0, Math.min(1, ratio));
60844
- const c1 = rgbToOklch(rgb1);
60845
- const c2 = rgbToOklch(rgb2);
60846
- let dh = c2.H - c1.H;
60847
- if (dh > 180) dh -= 360;
60848
- if (dh < -180) dh += 360;
60849
- let H = c1.H + dh * ratio;
60850
- if (H < 0) H += 360;
60851
- if (H >= 360) H -= 360;
60852
- const mixed = oklchToRgb({
60853
- L: c1.L + (c2.L - c1.L) * ratio,
60854
- C: c1.C + (c2.C - c1.C) * ratio,
60855
- H
60856
- });
60857
- const r = mixed.r / 255;
60858
- const g = mixed.g / 255;
60859
- const b = mixed.b / 255;
60860
- const a1 = rgb1.alpha ?? 1;
60861
- const a2 = rgb2.alpha ?? 1;
60862
- const alpha = a1 + (a2 - a1) * ratio;
60863
- return Math.abs(alpha - 1) > 1e-4 ? [r, g, b, alpha] : [r, g, b];
62097
+ const c1Achromatic = c1.C < 1e-6;
62098
+ const c2Achromatic = c2.C < 1e-6;
62099
+ let H;
62100
+ if (c1Achromatic && c2Achromatic) H = c1.H;
62101
+ else if (c1Achromatic) H = c2.H;
62102
+ else if (c2Achromatic) H = c1.H;
62103
+ else {
62104
+ let dh = c2.H - c1.H;
62105
+ if (dh > 180) dh -= 360;
62106
+ if (dh < -180) dh += 360;
62107
+ H = c1.H + dh * ratio;
62108
+ if (H < 0) H += 360;
62109
+ if (H >= 360) H -= 360;
62110
+ }
62111
+ const L = c1.L + (c2.L - c1.L) * ratio;
62112
+ const C = c1.C + (c2.C - c1.C) * ratio;
62113
+ const a1 = c1.alpha ?? 1;
62114
+ const a2 = c2.alpha ?? 1;
62115
+ const alpha = normalizeAlpha2(a1 + (a2 - a1) * ratio);
62116
+ return alpha !== void 0 ? [L, C, H, alpha] : [L, C, H];
60864
62117
  },
60865
62118
  colorContrast(bg, fg) {
60866
62119
  return apca(toRgb255(bg), toRgb255(fg));
@@ -60868,11 +62121,11 @@ var colorHelpers = {
60868
62121
  contrastingColor(bg, fg1, fg2) {
60869
62122
  const bgRgb = toRgb255(bg);
60870
62123
  if (fg1 !== void 0 && fg2 !== void 0) {
60871
- return packedToArray(
62124
+ return packedToOklch(
60872
62125
  contrastingColor({ bg: bgRgb, fg1: toRgb255(fg1), fg2: toRgb255(fg2) })
60873
62126
  );
60874
62127
  }
60875
- return packedToArray(contrastingColor(bgRgb));
62128
+ return packedToOklch(contrastingColor(bgRgb));
60876
62129
  },
60877
62130
  colorToColorspace(input, space) {
60878
62131
  const rgb = toRgb255(input);
@@ -60901,7 +62154,7 @@ var colorHelpers = {
60901
62154
  default:
60902
62155
  throw new Error(`Unknown color space: ${space}`);
60903
62156
  }
60904
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
62157
+ if (alpha !== void 0) result.push(alpha);
60905
62158
  return result;
60906
62159
  },
60907
62160
  colormap(name, arg) {
@@ -60913,7 +62166,7 @@ var colorHelpers = {
60913
62166
  const palette = allPalettes[name];
60914
62167
  if (!palette) throw new Error(`Unknown palette: ${name}`);
60915
62168
  const colors = palette.map(
60916
- (hex) => parseColorToRgb01(hex)
62169
+ (hex) => packedToOklch(parseColor(hex))
60917
62170
  );
60918
62171
  if (arg === void 0) return colors;
60919
62172
  if (Number.isInteger(arg) && arg >= 2) {
@@ -60937,62 +62190,128 @@ var colorHelpers = {
60937
62190
  const frac = pos - i;
60938
62191
  if (frac === 0 || i >= colors.length - 1)
60939
62192
  return [...colors[Math.min(i, colors.length - 1)]];
60940
- const rgb1 = {
60941
- r: colors[i][0] * 255,
60942
- g: colors[i][1] * 255,
60943
- b: colors[i][2] * 255
60944
- };
60945
- const rgb2 = {
60946
- r: colors[i + 1][0] * 255,
60947
- g: colors[i + 1][1] * 255,
60948
- b: colors[i + 1][2] * 255
60949
- };
60950
- const c1 = rgbToOklch(rgb1);
60951
- const c2 = rgbToOklch(rgb2);
60952
- let dh = c2.H - c1.H;
60953
- if (dh > 180) dh -= 360;
60954
- if (dh < -180) dh += 360;
60955
- let H = c1.H + dh * frac;
60956
- if (H < 0) H += 360;
60957
- if (H >= 360) H -= 360;
60958
- const mixed = oklchToRgb({
60959
- L: c1.L + (c2.L - c1.L) * frac,
60960
- C: c1.C + (c2.C - c1.C) * frac,
60961
- H
60962
- });
60963
- return [mixed.r / 255, mixed.g / 255, mixed.b / 255];
62193
+ const [L1, C1, H1] = colors[i];
62194
+ const [L2, C2, H2] = colors[i + 1];
62195
+ const c1Achromatic = C1 < 1e-6;
62196
+ const c2Achromatic = C2 < 1e-6;
62197
+ let H;
62198
+ if (c1Achromatic && c2Achromatic) H = H1;
62199
+ else if (c1Achromatic) H = H2;
62200
+ else if (c2Achromatic) H = H1;
62201
+ else {
62202
+ let dh = H2 - H1;
62203
+ if (dh > 180) dh -= 360;
62204
+ if (dh < -180) dh += 360;
62205
+ H = H1 + dh * frac;
62206
+ if (H < 0) H += 360;
62207
+ if (H >= 360) H -= 360;
62208
+ }
62209
+ return [L1 + (L2 - L1) * frac, C1 + (C2 - C1) * frac, H];
60964
62210
  },
60965
62211
  colorFromColorspace(components, space) {
60966
62212
  const c0 = components[0];
60967
62213
  const c1 = components[1];
60968
62214
  const c2 = components[2];
60969
62215
  const alpha = components.length >= 4 ? components[3] : void 0;
60970
- let result;
62216
+ let oklch2;
60971
62217
  switch (space.toLowerCase()) {
60972
62218
  case "rgb":
60973
- result = [c0, c1, c2];
62219
+ oklch2 = rgbToOklch({ r: c0 * 255, g: c1 * 255, b: c2 * 255 });
60974
62220
  break;
60975
62221
  case "hsl": {
60976
- const r = hslToRgb(c0, c1, c2);
60977
- result = [r.r / 255, r.g / 255, r.b / 255];
62222
+ const rgb = hslToRgb(c0, c1, c2);
62223
+ oklch2 = rgbToOklch(rgb);
60978
62224
  break;
60979
62225
  }
60980
- case "oklch": {
60981
- const r = oklchToRgb({ L: c0, C: c1, H: c2 });
60982
- result = [r.r / 255, r.g / 255, r.b / 255];
62226
+ case "oklch":
62227
+ oklch2 = { L: c0, C: c1, H: c2 };
60983
62228
  break;
60984
- }
60985
62229
  case "oklab":
60986
- case "lab": {
60987
- const r = oklabToRgb({ L: c0, a: c1, b: c2 });
60988
- result = [r.r / 255, r.g / 255, r.b / 255];
62230
+ case "lab":
62231
+ oklch2 = oklabToOklch({ L: c0, a: c1, b: c2 });
60989
62232
  break;
60990
- }
60991
62233
  default:
60992
62234
  throw new Error(`Unknown color space: ${space}`);
60993
62235
  }
60994
- if (alpha !== void 0 && Math.abs(alpha - 1) > 1e-4) result.push(alpha);
60995
- return result;
62236
+ return alpha !== void 0 ? [oklch2.L, oklch2.C, oklch2.H, alpha] : [oklch2.L, oklch2.C, oklch2.H];
62237
+ },
62238
+ // -----------------------------------------------------------------------
62239
+ // Color constructors. Each accepts components in its colorspace's natural
62240
+ // units and returns the canonical OKLCh array `[L, C, H]` (or with alpha).
62241
+ // -----------------------------------------------------------------------
62242
+ rgb(r, g, b, alpha) {
62243
+ const c = rgbToOklch({ r: r * 255, g: g * 255, b: b * 255 });
62244
+ const a = normalizeAlpha2(alpha);
62245
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
62246
+ },
62247
+ hsv(h, s, v, alpha) {
62248
+ const rgb = hsvToRgb(h, s, v);
62249
+ const c = rgbToOklch(rgb);
62250
+ const a = normalizeAlpha2(alpha);
62251
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
62252
+ },
62253
+ hsl(h, s, l, alpha) {
62254
+ const rgb = hslToRgb(h, s, l);
62255
+ const c = rgbToOklch({ r: rgb.r, g: rgb.g, b: rgb.b });
62256
+ const a = normalizeAlpha2(alpha);
62257
+ return a !== void 0 ? [c.L, c.C, c.H, a] : [c.L, c.C, c.H];
62258
+ },
62259
+ oklab(L, a, b, alpha) {
62260
+ const c = oklabToOklch({ L, a, b });
62261
+ const al = normalizeAlpha2(alpha);
62262
+ return al !== void 0 ? [c.L, c.C, c.H, al] : [c.L, c.C, c.H];
62263
+ },
62264
+ oklch(L, C, H, alpha) {
62265
+ const a = normalizeAlpha2(alpha);
62266
+ return a !== void 0 ? [L, C, H, a] : [L, C, H];
62267
+ },
62268
+ // -----------------------------------------------------------------------
62269
+ // As* converters. Inputs are anything `toOklch` accepts (string, packed
62270
+ // int, or OKLCh array). Outputs are 3- or 4-element arrays in the named
62271
+ // space. sRGB-based outputs (asRgb/asHsv/asHsl) use 0-1 channels for
62272
+ // consistency with the GPU target's shader convention.
62273
+ // -----------------------------------------------------------------------
62274
+ asRgb(input) {
62275
+ const rgb = toRgb255(input);
62276
+ const r = rgb.r / 255;
62277
+ const g = rgb.g / 255;
62278
+ const b = rgb.b / 255;
62279
+ return rgb.alpha !== void 0 ? [r, g, b, rgb.alpha] : [r, g, b];
62280
+ },
62281
+ asHsv(input) {
62282
+ const rgb = toRgb255(input);
62283
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
62284
+ return rgb.alpha !== void 0 ? [hsv.h, hsv.s, hsv.v, rgb.alpha] : [hsv.h, hsv.s, hsv.v];
62285
+ },
62286
+ asHsl(input) {
62287
+ const rgb = toRgb255(input);
62288
+ const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
62289
+ return rgb.alpha !== void 0 ? [hsl.h, hsl.s, hsl.l, rgb.alpha] : [hsl.h, hsl.s, hsl.l];
62290
+ },
62291
+ asOklab(input) {
62292
+ const c = toOklch2(input);
62293
+ const lab = oklchToOklab({ L: c.L, C: c.C, H: c.H });
62294
+ return c.alpha !== void 0 ? [lab.L, lab.a, lab.b, c.alpha] : [lab.L, lab.a, lab.b];
62295
+ },
62296
+ // asOklch is identity — handled at compile time as a pass-through
62297
+ // Perceptual color difference (ΔE_OK).
62298
+ colorDelta(a, b) {
62299
+ const labA = oklchToOklab(toOklch2(a));
62300
+ const labB = oklchToOklab(toOklch2(b));
62301
+ return oklabDeltaE(labA, labB);
62302
+ },
62303
+ // Euclidean distance between two tuples. Plain numeric — not a color
62304
+ // operation despite living in the same helpers block.
62305
+ distance(a, b) {
62306
+ if (!Array.isArray(a) || !Array.isArray(b))
62307
+ throw new Error("Distance: expected two arrays");
62308
+ if (a.length !== b.length) throw new Error("Distance: dimension mismatch");
62309
+ let sumSq = 0;
62310
+ for (let i = 0; i < a.length; i++) {
62311
+ const d = a[i] - b[i];
62312
+ sumSq += d * d;
62313
+ }
62314
+ return Math.sqrt(sumSq);
60996
62315
  }
60997
62316
  };
60998
62317
  var SYS_HELPERS = {
@@ -61431,6 +62750,13 @@ var GPU_OPERATORS = {
61431
62750
  function gpuVec2(target) {
61432
62751
  return target?.language === "wgsl" ? "vec2f" : "vec2";
61433
62752
  }
62753
+ function gpuVec3(target) {
62754
+ return target?.language === "wgsl" ? "vec3f" : "vec3";
62755
+ }
62756
+ function readStringLiteral(expr2) {
62757
+ if (!isString(expr2)) return null;
62758
+ return expr2.string?.toLowerCase() ?? null;
62759
+ }
61434
62760
  function compileIntArg(expr2, compile3, target) {
61435
62761
  const c = tryGetConstant(expr2);
61436
62762
  if (c !== void 0 && Number.isInteger(c)) return c.toString();
@@ -61489,7 +62815,7 @@ function compileGPUSumProduct(kind, args, _compile2, target) {
61489
62815
  `for (${indexDecl} = ${lowerStr}; ${index} <= ${upperStr}; ${index}++) {`,
61490
62816
  ` ${acc} ${op}= ${body};`,
61491
62817
  `}`,
61492
- `return ${acc}`
62818
+ `return ${acc};`
61493
62819
  ];
61494
62820
  return lines.join("\n");
61495
62821
  }
@@ -61543,8 +62869,7 @@ var GPU_FUNCTIONS = {
61543
62869
  const iScale = isSymbol2(iFactor, "ImaginaryUnit") ? 1 : iFactor.im;
61544
62870
  const realFactors = args.filter((_, i) => i !== iIndex);
61545
62871
  const v2 = gpuVec2(target);
61546
- if (realFactors.length === 0)
61547
- return `${v2}(0.0, ${formatFloat(iScale)})`;
62872
+ if (realFactors.length === 0) return `${v2}(0.0, ${formatFloat(iScale)})`;
61548
62873
  const factors = realFactors.map((f) => compile3(f));
61549
62874
  if (iScale !== 1) factors.unshift(formatFloat(iScale));
61550
62875
  const imCode = foldTerms(factors, "1.0", "*");
@@ -61597,8 +62922,7 @@ var GPU_FUNCTIONS = {
61597
62922
  if (isNumber(x) && x.im !== 0) {
61598
62923
  return `${gpuVec2(target)}(${formatFloat(-x.re)}, ${formatFloat(-x.im)})`;
61599
62924
  }
61600
- if (isSymbol2(x, "ImaginaryUnit"))
61601
- return `${gpuVec2(target)}(0.0, -1.0)`;
62925
+ if (isSymbol2(x, "ImaginaryUnit")) return `${gpuVec2(target)}(0.0, -1.0)`;
61602
62926
  return `(-${compile3(x)})`;
61603
62927
  },
61604
62928
  // Standard math functions with complex dispatch
@@ -61971,17 +63295,127 @@ var GPU_FUNCTIONS = {
61971
63295
  }
61972
63296
  const isWGSL = target?.language === "wgsl";
61973
63297
  const v3 = isWGSL ? "vec3f" : "vec3";
61974
- return `((_gpu_apca(${bg}, ${v3}(0.0)) > 50.0) ? ${v3}(0.0) : ${v3}(1.0))`;
63298
+ const black = `${v3}(0.0)`;
63299
+ const white = `${v3}(1.0, 0.0, 0.0)`;
63300
+ return `((_gpu_apca(${bg}, ${black}) > 50.0) ? ${black} : ${white})`;
61975
63301
  },
61976
63302
  ColorToColorspace: ([color, space], compile3) => {
61977
63303
  if (color === null || space === null)
61978
63304
  throw new Error("ColorToColorspace: need color and space");
61979
- return `_gpu_srgb_to_oklab(${compile3(color)})`;
63305
+ const spaceName = readStringLiteral(space);
63306
+ if (spaceName === null)
63307
+ throw new Error("ColorToColorspace: space must be a string literal");
63308
+ const c = compile3(color);
63309
+ switch (spaceName) {
63310
+ case "oklch":
63311
+ return c;
63312
+ case "oklab":
63313
+ case "lab":
63314
+ return `_gpu_oklch_to_oklab(${c})`;
63315
+ case "rgb":
63316
+ return `_gpu_oklch_to_srgb(${c})`;
63317
+ case "hsl":
63318
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${c}))`;
63319
+ case "hsv":
63320
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${c}))`;
63321
+ default:
63322
+ throw new Error(
63323
+ `ColorToColorspace: unsupported space "${spaceName}" on GPU target`
63324
+ );
63325
+ }
61980
63326
  },
61981
63327
  ColorFromColorspace: ([components, space], compile3) => {
61982
63328
  if (components === null || space === null)
61983
63329
  throw new Error("ColorFromColorspace: need components and space");
61984
- return `_gpu_oklab_to_srgb(${compile3(components)})`;
63330
+ const spaceName = readStringLiteral(space);
63331
+ if (spaceName === null)
63332
+ throw new Error("ColorFromColorspace: space must be a string literal");
63333
+ const c = compile3(components);
63334
+ switch (spaceName) {
63335
+ case "oklch":
63336
+ return c;
63337
+ case "oklab":
63338
+ case "lab":
63339
+ return `_gpu_oklab_to_oklch(${c})`;
63340
+ case "rgb":
63341
+ return `_gpu_srgb_to_oklch(${c})`;
63342
+ case "hsl":
63343
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${c}))`;
63344
+ case "hsv":
63345
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${c}))`;
63346
+ default:
63347
+ throw new Error(
63348
+ `ColorFromColorspace: unsupported space "${spaceName}" on GPU target`
63349
+ );
63350
+ }
63351
+ },
63352
+ // ---------------------------------------------------------------------------
63353
+ // Color literals. Each typed head compiles to a canonical OKLCh vec3.
63354
+ // Alpha (4th argument) is dropped — GPU color values are vec3 only. Pass
63355
+ // alpha as a separate uniform if it's needed at the framebuffer boundary.
63356
+ // ---------------------------------------------------------------------------
63357
+ Color: ([s], _compile2, target) => {
63358
+ if (s === null) throw new Error("Color: no argument");
63359
+ const str = readStringLiteral(s);
63360
+ if (str === null)
63361
+ throw new Error("Color: argument must be a string literal on GPU target");
63362
+ const packed = parseColor(str);
63363
+ if (packed === 0 && str.trim().toLowerCase() !== "transparent")
63364
+ throw new Error(`Color: invalid color string "${str}"`);
63365
+ const r = packed >>> 24 & 255;
63366
+ const g = packed >>> 16 & 255;
63367
+ const b = packed >>> 8 & 255;
63368
+ const oklch2 = rgbToOklch({ r, g, b });
63369
+ return `${gpuVec3(target)}(${formatFloat(oklch2.L)}, ${formatFloat(oklch2.C)}, ${formatFloat(oklch2.H)})`;
63370
+ },
63371
+ Rgb: (args, compile3, target) => {
63372
+ if (args.length < 3) throw new Error("Rgb: need 3 components");
63373
+ const v3 = gpuVec3(target);
63374
+ return `_gpu_srgb_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
63375
+ },
63376
+ Hsv: (args, compile3, target) => {
63377
+ if (args.length < 3) throw new Error("Hsv: need 3 components");
63378
+ const v3 = gpuVec3(target);
63379
+ return `_gpu_srgb_to_oklch(_gpu_hsv_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
63380
+ },
63381
+ Hsl: (args, compile3, target) => {
63382
+ if (args.length < 3) throw new Error("Hsl: need 3 components");
63383
+ const v3 = gpuVec3(target);
63384
+ return `_gpu_srgb_to_oklch(_gpu_hsl_to_rgb(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})))`;
63385
+ },
63386
+ Oklab: (args, compile3, target) => {
63387
+ if (args.length < 3) throw new Error("Oklab: need 3 components");
63388
+ const v3 = gpuVec3(target);
63389
+ return `_gpu_oklab_to_oklch(${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])}))`;
63390
+ },
63391
+ Oklch: (args, compile3, target) => {
63392
+ if (args.length < 3) throw new Error("Oklch: need 3 components");
63393
+ const v3 = gpuVec3(target);
63394
+ return `${v3}(${compile3(args[0])}, ${compile3(args[1])}, ${compile3(args[2])})`;
63395
+ },
63396
+ // ---------------------------------------------------------------------------
63397
+ // As* operators. AsOklch is identity (canonical). The other As* return
63398
+ // components in the named space, equivalent to ColorToColorspace(c, 'x').
63399
+ // ---------------------------------------------------------------------------
63400
+ AsOklch: ([c], compile3) => {
63401
+ if (c === null) throw new Error("AsOklch: no argument");
63402
+ return compile3(c);
63403
+ },
63404
+ AsOklab: ([c], compile3) => {
63405
+ if (c === null) throw new Error("AsOklab: no argument");
63406
+ return `_gpu_oklch_to_oklab(${compile3(c)})`;
63407
+ },
63408
+ AsRgb: ([c], compile3) => {
63409
+ if (c === null) throw new Error("AsRgb: no argument");
63410
+ return `_gpu_oklch_to_srgb(${compile3(c)})`;
63411
+ },
63412
+ AsHsv: ([c], compile3) => {
63413
+ if (c === null) throw new Error("AsHsv: no argument");
63414
+ return `_gpu_rgb_to_hsv(_gpu_oklch_to_srgb(${compile3(c)}))`;
63415
+ },
63416
+ AsHsl: ([c], compile3) => {
63417
+ if (c === null) throw new Error("AsHsl: no argument");
63418
+ return `_gpu_rgb_to_hsl(_gpu_oklch_to_srgb(${compile3(c)}))`;
61985
63419
  },
61986
63420
  // Fractal functions
61987
63421
  Mandelbrot: ([c, maxIter], compile3, target) => {
@@ -62674,28 +64108,124 @@ vec3 _gpu_oklab_to_srgb(vec3 lab) {
62674
64108
 
62675
64109
  vec3 _gpu_oklab_to_oklch(vec3 lab) {
62676
64110
  float C = length(lab.yz);
62677
- float H = atan(lab.z, lab.y);
64111
+ float H = atan(lab.z, lab.y) * (180.0 / 3.14159265359);
64112
+ if (H < 0.0) H += 360.0;
62678
64113
  return vec3(lab.x, C, H);
62679
64114
  }
62680
64115
 
62681
64116
  vec3 _gpu_oklch_to_oklab(vec3 lch) {
62682
- return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
64117
+ float h_rad = lch.z * (3.14159265359 / 180.0);
64118
+ return vec3(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
64119
+ }
64120
+
64121
+ vec3 _gpu_srgb_to_oklch(vec3 rgb) {
64122
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
64123
+ }
64124
+
64125
+ vec3 _gpu_oklch_to_srgb(vec3 lch) {
64126
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
64127
+ }
64128
+
64129
+ // HSL conversion. Hue in degrees, saturation/lightness in 0-1.
64130
+ vec3 _gpu_hsl_to_rgb(vec3 hsl) {
64131
+ float h = hsl.x;
64132
+ float s = hsl.y;
64133
+ float l = hsl.z;
64134
+ float c = (1.0 - abs(2.0 * l - 1.0)) * s;
64135
+ float h6 = h / 60.0;
64136
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
64137
+ float r = 0.0;
64138
+ float g = 0.0;
64139
+ float b = 0.0;
64140
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64141
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64142
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64143
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64144
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64145
+ else { r = c; g = 0.0; b = x; }
64146
+ float m = l - c / 2.0;
64147
+ return vec3(r + m, g + m, b + m);
64148
+ }
64149
+
64150
+ vec3 _gpu_rgb_to_hsl(vec3 rgb) {
64151
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
64152
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
64153
+ float l = (maxc + minc) / 2.0;
64154
+ float d = maxc - minc;
64155
+ if (d < 1e-6) return vec3(0.0, 0.0, l);
64156
+ float s = d / (1.0 - abs(2.0 * l - 1.0));
64157
+ float h;
64158
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
64159
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
64160
+ else h = (rgb.x - rgb.y) / d + 4.0;
64161
+ h *= 60.0;
64162
+ if (h < 0.0) h += 360.0;
64163
+ return vec3(h, s, l);
64164
+ }
64165
+
64166
+ // HSV conversion. Hue in degrees, saturation/value in 0-1.
64167
+ vec3 _gpu_hsv_to_rgb(vec3 hsv) {
64168
+ float h = hsv.x;
64169
+ float s = hsv.y;
64170
+ float v = hsv.z;
64171
+ float c = v * s;
64172
+ float h6 = h / 60.0;
64173
+ float x = c * (1.0 - abs(mod(h6, 2.0) - 1.0));
64174
+ float r = 0.0;
64175
+ float g = 0.0;
64176
+ float b = 0.0;
64177
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64178
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64179
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64180
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64181
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64182
+ else { r = c; g = 0.0; b = x; }
64183
+ float m = v - c;
64184
+ return vec3(r + m, g + m, b + m);
62683
64185
  }
62684
64186
 
62685
- vec3 _gpu_color_mix(vec3 rgb1, vec3 rgb2, float t) {
62686
- vec3 lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
62687
- vec3 lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
64187
+ vec3 _gpu_rgb_to_hsv(vec3 rgb) {
64188
+ float maxc = max(max(rgb.x, rgb.y), rgb.z);
64189
+ float minc = min(min(rgb.x, rgb.y), rgb.z);
64190
+ float v = maxc;
64191
+ float d = maxc - minc;
64192
+ if (d < 1e-6) return vec3(0.0, 0.0, v);
64193
+ float s = (maxc < 1e-6) ? 0.0 : d / maxc;
64194
+ float h;
64195
+ if (maxc == rgb.x) h = mod((rgb.y - rgb.z) / d, 6.0);
64196
+ else if (maxc == rgb.y) h = (rgb.z - rgb.x) / d + 2.0;
64197
+ else h = (rgb.x - rgb.y) / d + 4.0;
64198
+ h *= 60.0;
64199
+ if (h < 0.0) h += 360.0;
64200
+ return vec3(h, s, v);
64201
+ }
64202
+
64203
+ vec3 _gpu_color_mix(vec3 lch1, vec3 lch2, float t) {
62688
64204
  float L = mix(lch1.x, lch2.x, t);
62689
64205
  float C = mix(lch1.y, lch2.y, t);
62690
- float dh = lch2.z - lch1.z;
62691
- const float PI = 3.14159265359;
62692
- if (dh > PI) dh -= 2.0 * PI;
62693
- if (dh < -PI) dh += 2.0 * PI;
62694
- float H = lch1.z + dh * t;
62695
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3(L, C, H)));
64206
+ bool a1 = lch1.y < 1e-6;
64207
+ bool a2 = lch2.y < 1e-6;
64208
+ float H;
64209
+ if (a1 && a2) {
64210
+ H = lch1.z;
64211
+ } else if (a1) {
64212
+ H = lch2.z;
64213
+ } else if (a2) {
64214
+ H = lch1.z;
64215
+ } else {
64216
+ float dh = lch2.z - lch1.z;
64217
+ if (dh > 180.0) dh -= 360.0;
64218
+ if (dh < -180.0) dh += 360.0;
64219
+ H = lch1.z + dh * t;
64220
+ if (H < 0.0) H += 360.0;
64221
+ if (H >= 360.0) H -= 360.0;
64222
+ }
64223
+ return vec3(L, C, H);
62696
64224
  }
62697
64225
 
62698
- float _gpu_apca(vec3 bg, vec3 fg) {
64226
+ float _gpu_apca(vec3 lch_bg, vec3 lch_fg) {
64227
+ vec3 bg = _gpu_oklch_to_srgb(lch_bg);
64228
+ vec3 fg = _gpu_oklch_to_srgb(lch_fg);
62699
64229
  float bgR = _gpu_srgb_to_linear(bg.x);
62700
64230
  float bgG = _gpu_srgb_to_linear(bg.y);
62701
64231
  float bgB = _gpu_srgb_to_linear(bg.z);
@@ -62706,9 +64236,7 @@ float _gpu_apca(vec3 bg, vec3 fg) {
62706
64236
  float fgY = 0.2126729 * fgR + 0.7151522 * fgG + 0.0721750 * fgB;
62707
64237
  float bgC = pow(bgY, 0.56);
62708
64238
  float fgC = pow(fgY, 0.57);
62709
- float contrast = (bgC > fgC)
62710
- ? (bgC - fgC) * 1.14
62711
- : (bgC - fgC) * 1.14;
64239
+ float contrast = (bgC - fgC) * 1.14;
62712
64240
  return contrast * 100.0;
62713
64241
  }
62714
64242
  `;
@@ -62752,28 +64280,133 @@ fn _gpu_oklab_to_srgb(lab: vec3f) -> vec3f {
62752
64280
 
62753
64281
  fn _gpu_oklab_to_oklch(lab: vec3f) -> vec3f {
62754
64282
  let C = length(lab.yz);
62755
- let H = atan2(lab.z, lab.y);
64283
+ var H = atan2(lab.z, lab.y) * (180.0 / 3.14159265359);
64284
+ if (H < 0.0) { H = H + 360.0; }
62756
64285
  return vec3f(lab.x, C, H);
62757
64286
  }
62758
64287
 
62759
64288
  fn _gpu_oklch_to_oklab(lch: vec3f) -> vec3f {
62760
- return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));
64289
+ let h_rad = lch.z * (3.14159265359 / 180.0);
64290
+ return vec3f(lch.x, lch.y * cos(h_rad), lch.y * sin(h_rad));
64291
+ }
64292
+
64293
+ fn _gpu_srgb_to_oklch(rgb: vec3f) -> vec3f {
64294
+ return _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb));
64295
+ }
64296
+
64297
+ fn _gpu_oklch_to_srgb(lch: vec3f) -> vec3f {
64298
+ return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(lch));
64299
+ }
64300
+
64301
+ fn _gpu_hsl_to_rgb(hsl: vec3f) -> vec3f {
64302
+ let h = hsl.x;
64303
+ let s = hsl.y;
64304
+ let l = hsl.z;
64305
+ let c = (1.0 - abs(2.0 * l - 1.0)) * s;
64306
+ let h6 = h / 60.0;
64307
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
64308
+ var r: f32 = 0.0;
64309
+ var g: f32 = 0.0;
64310
+ var b: f32 = 0.0;
64311
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64312
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64313
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64314
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64315
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64316
+ else { r = c; g = 0.0; b = x; }
64317
+ let m = l - c / 2.0;
64318
+ return vec3f(r + m, g + m, b + m);
64319
+ }
64320
+
64321
+ fn _gpu_rgb_to_hsl(rgb: vec3f) -> vec3f {
64322
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
64323
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
64324
+ let l = (maxc + minc) / 2.0;
64325
+ let d = maxc - minc;
64326
+ if (d < 1e-6) { return vec3f(0.0, 0.0, l); }
64327
+ let s = d / (1.0 - abs(2.0 * l - 1.0));
64328
+ var h: f32;
64329
+ if (maxc == rgb.x) {
64330
+ let v = (rgb.y - rgb.z) / d;
64331
+ h = v - 6.0 * floor(v / 6.0);
64332
+ } else if (maxc == rgb.y) {
64333
+ h = (rgb.z - rgb.x) / d + 2.0;
64334
+ } else {
64335
+ h = (rgb.x - rgb.y) / d + 4.0;
64336
+ }
64337
+ h = h * 60.0;
64338
+ if (h < 0.0) { h = h + 360.0; }
64339
+ return vec3f(h, s, l);
64340
+ }
64341
+
64342
+ fn _gpu_hsv_to_rgb(hsv: vec3f) -> vec3f {
64343
+ let h = hsv.x;
64344
+ let s = hsv.y;
64345
+ let v = hsv.z;
64346
+ let c = v * s;
64347
+ let h6 = h / 60.0;
64348
+ let x = c * (1.0 - abs((h6 - 2.0 * floor(h6 / 2.0)) - 1.0));
64349
+ var r: f32 = 0.0;
64350
+ var g: f32 = 0.0;
64351
+ var b: f32 = 0.0;
64352
+ if (h6 < 1.0) { r = c; g = x; b = 0.0; }
64353
+ else if (h6 < 2.0) { r = x; g = c; b = 0.0; }
64354
+ else if (h6 < 3.0) { r = 0.0; g = c; b = x; }
64355
+ else if (h6 < 4.0) { r = 0.0; g = x; b = c; }
64356
+ else if (h6 < 5.0) { r = x; g = 0.0; b = c; }
64357
+ else { r = c; g = 0.0; b = x; }
64358
+ let m = v - c;
64359
+ return vec3f(r + m, g + m, b + m);
62761
64360
  }
62762
64361
 
62763
- fn _gpu_color_mix(rgb1: vec3f, rgb2: vec3f, t: f32) -> vec3f {
62764
- let lch1 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb1));
62765
- let lch2 = _gpu_oklab_to_oklch(_gpu_srgb_to_oklab(rgb2));
64362
+ fn _gpu_rgb_to_hsv(rgb: vec3f) -> vec3f {
64363
+ let maxc = max(max(rgb.x, rgb.y), rgb.z);
64364
+ let minc = min(min(rgb.x, rgb.y), rgb.z);
64365
+ let v = maxc;
64366
+ let d = maxc - minc;
64367
+ if (d < 1e-6) { return vec3f(0.0, 0.0, v); }
64368
+ var s: f32 = 0.0;
64369
+ if (maxc >= 1e-6) { s = d / maxc; }
64370
+ var h: f32;
64371
+ if (maxc == rgb.x) {
64372
+ let q = (rgb.y - rgb.z) / d;
64373
+ h = q - 6.0 * floor(q / 6.0);
64374
+ } else if (maxc == rgb.y) {
64375
+ h = (rgb.z - rgb.x) / d + 2.0;
64376
+ } else {
64377
+ h = (rgb.x - rgb.y) / d + 4.0;
64378
+ }
64379
+ h = h * 60.0;
64380
+ if (h < 0.0) { h = h + 360.0; }
64381
+ return vec3f(h, s, v);
64382
+ }
64383
+
64384
+ fn _gpu_color_mix(lch1: vec3f, lch2: vec3f, t: f32) -> vec3f {
62766
64385
  let L = mix(lch1.x, lch2.x, t);
62767
64386
  let C = mix(lch1.y, lch2.y, t);
62768
- let PI = 3.14159265359;
62769
- var dh = lch2.z - lch1.z;
62770
- if (dh > PI) { dh -= 2.0 * PI; }
62771
- if (dh < -PI) { dh += 2.0 * PI; }
62772
- let H = lch1.z + dh * t;
62773
- return _gpu_oklab_to_srgb(_gpu_oklch_to_oklab(vec3f(L, C, H)));
64387
+ let a1 = lch1.y < 1e-6;
64388
+ let a2 = lch2.y < 1e-6;
64389
+ var H: f32;
64390
+ if (a1 && a2) {
64391
+ H = lch1.z;
64392
+ } else if (a1) {
64393
+ H = lch2.z;
64394
+ } else if (a2) {
64395
+ H = lch1.z;
64396
+ } else {
64397
+ var dh = lch2.z - lch1.z;
64398
+ if (dh > 180.0) { dh = dh - 360.0; }
64399
+ if (dh < -180.0) { dh = dh + 360.0; }
64400
+ H = lch1.z + dh * t;
64401
+ if (H < 0.0) { H = H + 360.0; }
64402
+ if (H >= 360.0) { H = H - 360.0; }
64403
+ }
64404
+ return vec3f(L, C, H);
62774
64405
  }
62775
64406
 
62776
- fn _gpu_apca(bg: vec3f, fg: vec3f) -> f32 {
64407
+ fn _gpu_apca(lch_bg: vec3f, lch_fg: vec3f) -> f32 {
64408
+ let bg = _gpu_oklch_to_srgb(lch_bg);
64409
+ let fg = _gpu_oklch_to_srgb(lch_fg);
62777
64410
  let bgR = _gpu_srgb_to_linear(bg.x);
62778
64411
  let bgG = _gpu_srgb_to_linear(bg.y);
62779
64412
  let bgB = _gpu_srgb_to_linear(bg.z);
@@ -63061,7 +64694,7 @@ var GPUShaderTarget = class {
63061
64694
  if (stmts.length === 0) return "";
63062
64695
  const last = stmts.length - 1;
63063
64696
  stmts[last] = `return ${stmts[last]}`;
63064
- return stmts.join(";\n");
64697
+ return stmts.join(";\n") + ";";
63065
64698
  },
63066
64699
  ...options
63067
64700
  };
@@ -63160,7 +64793,7 @@ var GLSLTarget = class extends GPUShaderTarget {
63160
64793
  if (body.includes("\n")) {
63161
64794
  const indented = body.split("\n").map((l) => ` ${l}`).join("\n");
63162
64795
  return `${returnType} ${functionName}(${params}) {
63163
- ${indented};
64796
+ ${indented}
63164
64797
  }`;
63165
64798
  }
63166
64799
  return `${returnType} ${functionName}(${params}) {
@@ -63271,7 +64904,7 @@ var WGSLTarget = class extends GPUShaderTarget {
63271
64904
  return `fn ${functionName}(${params}) -> ${toWGSLType(
63272
64905
  returnType
63273
64906
  )} {
63274
- ${indented};
64907
+ ${indented}
63275
64908
  }`;
63276
64909
  }
63277
64910
  return `fn ${functionName}(${params}) -> ${toWGSLType(returnType)} {
@@ -67678,6 +69311,7 @@ var ComputeEngine = class _ComputeEngine {
67678
69311
  this.pushScope(void 0, "global");
67679
69312
  this._compilationTargets.registerDefaults();
67680
69313
  if (options?.latexSyntax) this._latexSyntax = options.latexSyntax;
69314
+ if (options?.latexOptions) this._latexOptions = { ...options.latexOptions };
67681
69315
  hidePrivateProperties(this);
67682
69316
  }
67683
69317
  toJSON() {
@@ -68092,6 +69726,15 @@ var ComputeEngine = class _ComputeEngine {
68092
69726
  lookupDefinition(id) {
68093
69727
  return lookupDefinition(this, id);
68094
69728
  }
69729
+ operatorInfo(head) {
69730
+ const def = this.lookupDefinition(head);
69731
+ if (!def || !isOperatorDef(def)) return void 0;
69732
+ const op = def.operator;
69733
+ return {
69734
+ kind: op.evaluate || op.collection ? "function" : "opaque",
69735
+ signature: op.signature
69736
+ };
69737
+ }
68095
69738
  /**
68096
69739
  * Associate a new definition to a symbol in the current context.
68097
69740
  *
@@ -68338,6 +69981,29 @@ var ComputeEngine = class _ComputeEngine {
68338
69981
  );
68339
69982
  return this._latexSyntax;
68340
69983
  }
69984
+ /** @internal Engine-wide LaTeX parse/serialize options (e.g. decimalSeparator).
69985
+ * Merged into every `parse()` and `toLatex()` call between the LatexSyntax
69986
+ * instance defaults and any per-call overrides. */
69987
+ _latexOptions = {};
69988
+ /** Engine-wide LaTeX parse/serialize options.
69989
+ *
69990
+ * These options are merged into every `parse()` and `toLatex()` call.
69991
+ * Precedence (most-specific wins):
69992
+ * 1. LatexSyntax instance defaults (set at its construction)
69993
+ * 2. `ce.latexOptions` (this property)
69994
+ * 3. Per-call options passed to `ce.parse()` / `expr.toLatex()`
69995
+ *
69996
+ * Assigning replaces the whole bag. Use spread to merge:
69997
+ * ```ts
69998
+ * ce.latexOptions = { ...ce.latexOptions, decimalSeparator: '{,}' };
69999
+ * ```
70000
+ */
70001
+ get latexOptions() {
70002
+ return this._latexOptions;
70003
+ }
70004
+ set latexOptions(options) {
70005
+ this._latexOptions = { ...options };
70006
+ }
68341
70007
  parse(latex, options) {
68342
70008
  if (latex === null || latex === void 0) return null;
68343
70009
  if (typeof latex !== "string")
@@ -68345,7 +70011,6 @@ var ComputeEngine = class _ComputeEngine {
68345
70011
  const syntax = this._requireLatexSyntax();
68346
70012
  const { form, ...parseOpts } = options ?? {};
68347
70013
  const result = syntax.parse(latex, {
68348
- decimalSeparator: ".",
68349
70014
  getSymbolType: (id) => {
68350
70015
  const def = this.lookupDefinition(id);
68351
70016
  if (!def) return BoxedType.unknown;
@@ -68357,6 +70022,8 @@ var ComputeEngine = class _ComputeEngine {
68357
70022
  const def = this.lookupDefinition(id);
68358
70023
  return !!(isValueDef(def) && def.value.subscriptEvaluate);
68359
70024
  },
70025
+ tolerance: this.tolerance,
70026
+ ...this._latexOptions,
68360
70027
  ...parseOpts
68361
70028
  });
68362
70029
  if (result === null) return null;
@@ -68520,14 +70187,14 @@ var ComputeEngine = class _ComputeEngine {
68520
70187
  _setDefaultEngineFactory(() => new ComputeEngine());
68521
70188
 
68522
70189
  // src/compute-engine.ts
68523
- var version = "0.55.6";
70190
+ var version = "0.57.0";
68524
70191
  ComputeEngine._latexSyntaxFactory = () => new LatexSyntax();
68525
70192
  _setDefaultEngineFactory(
68526
70193
  () => new ComputeEngine({ latexSyntax: new LatexSyntax() })
68527
70194
  );
68528
70195
  globalThis[/* @__PURE__ */ Symbol.for("io.cortexjs.compute-engine")] = {
68529
70196
  ComputeEngine: ComputeEngine.prototype.constructor,
68530
- version: "0.55.6"
70197
+ version: "0.57.0"
68531
70198
  };
68532
70199
  export {
68533
70200
  DEFINITIONS_ALGEBRA as ALGEBRA_DICTIONARY,