@odoo/o-spreadsheet 18.0.29 → 18.0.31

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.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 18.0.29
6
- * @date 2025-05-20T05:54:57.329Z
7
- * @hash 8213c0e
5
+ * @version 18.0.31
6
+ * @date 2025-05-30T08:43:10.315Z
7
+ * @hash d201086
8
8
  */
9
9
 
10
10
  'use strict';
@@ -3973,6 +3973,113 @@ function transposeMatrix(matrix) {
3973
3973
  }
3974
3974
  return generateMatrix(matrix[0].length, matrix.length, (i, j) => matrix[j][i]);
3975
3975
  }
3976
+ /**
3977
+ * Enables a formula function to accept matrix or vector inputs instead of simple value, computing results across multiple dimensions.
3978
+ *
3979
+ * ```
3980
+ * / |‾ ‾| \ |‾ ‾|
3981
+ * | | [A] | | | compute(A, D, E), compute(A, D, F), compute(A, D, G) |
3982
+ * applyVectorization| compute, | [B], D, [E, F, G] | | <=> | compute(B, D, E), compute(B, D, F), compute(B, D, G) |
3983
+ * | | [C] | | | compute(C, D, E), compute(C, D, F), compute(C, D, G) |
3984
+ * \ |_ _| / |_ _|
3985
+ * ```
3986
+ *
3987
+ * By default, all arguments are vectorized. To control which arguments are vectorized,
3988
+ * pass an `acceptToVectorize` boolean array of the same length as `args`:
3989
+ * - `true` enables vectorization for that argument
3990
+ * - `false` disables vectorization for that argument
3991
+ *
3992
+ * For example, with `[true, true, false]` on previous example you get:
3993
+ *
3994
+ * ```
3995
+ * |‾ ‾|
3996
+ * | compute(A, D, [E, F, G]) |
3997
+ * | compute(B, D, [E, F, G]) |
3998
+ * | compute(C, D, [E, F, G]) |
3999
+ * |_ _|
4000
+ * ```
4001
+ *
4002
+ * @remarks
4003
+ * This helper is automatically applied (by default) to **all** `compute` functions
4004
+ * across the various spreadsheet formula modules:
4005
+ * - If an argument is declared **scalar** (not `"range"`), it is vectorized.
4006
+ * - If **all** arguments are declared **ranges**, no vectorization occurs.
4007
+ * - e.g. `SUM(A1:B2)` returns a 1×1 sum over the range.
4008
+ * - e.g. `COS(A1:B2)` over `A1:B2` returns a 2×2 element-wise result.
4009
+ * - For special behaviors (e.g. the `IF` function), you may declare all arguments
4010
+ * as ranges and invoke this helper directly within your `compute` implementation.
4011
+ */
4012
+ function applyVectorization(formula, args, acceptToVectorize = undefined) {
4013
+ let countVectorizedCol = 1;
4014
+ let countVectorizedRow = 1;
4015
+ let vectorizedColLimit = Infinity;
4016
+ let vectorizedRowLimit = Infinity;
4017
+ let vectorArgsType = undefined;
4018
+ for (let i = 0; i < args.length; i++) {
4019
+ const arg = args[i];
4020
+ if (isMatrix(arg) && (acceptToVectorize === undefined || acceptToVectorize[i])) {
4021
+ const nColumns = arg.length;
4022
+ const nRows = arg[0].length;
4023
+ if (nColumns !== 1 || nRows !== 1) {
4024
+ vectorArgsType ??= new Array(args.length);
4025
+ if (nColumns !== 1 && nRows !== 1) {
4026
+ vectorArgsType[i] = "matrix";
4027
+ countVectorizedCol = Math.max(countVectorizedCol, nColumns);
4028
+ countVectorizedRow = Math.max(countVectorizedRow, nRows);
4029
+ vectorizedColLimit = Math.min(vectorizedColLimit, nColumns);
4030
+ vectorizedRowLimit = Math.min(vectorizedRowLimit, nRows);
4031
+ }
4032
+ else if (nColumns !== 1) {
4033
+ vectorArgsType[i] = "horizontal";
4034
+ countVectorizedCol = Math.max(countVectorizedCol, nColumns);
4035
+ vectorizedColLimit = Math.min(vectorizedColLimit, nColumns);
4036
+ }
4037
+ else if (nRows !== 1) {
4038
+ vectorArgsType[i] = "vertical";
4039
+ countVectorizedRow = Math.max(countVectorizedRow, nRows);
4040
+ vectorizedRowLimit = Math.min(vectorizedRowLimit, nRows);
4041
+ }
4042
+ }
4043
+ else {
4044
+ args[i] = arg[0][0];
4045
+ }
4046
+ }
4047
+ }
4048
+ if (countVectorizedCol === 1 && countVectorizedRow === 1) {
4049
+ // either this function is not vectorized or it ends up with a 1x1 dimension
4050
+ return formula(...args);
4051
+ }
4052
+ const getArgOffset = (i, j) => args.map((arg, index) => {
4053
+ switch (vectorArgsType?.[index]) {
4054
+ case "matrix":
4055
+ return arg[i][j];
4056
+ case "horizontal":
4057
+ return arg[i][0];
4058
+ case "vertical":
4059
+ return arg[0][j];
4060
+ case undefined:
4061
+ return arg;
4062
+ }
4063
+ });
4064
+ return generateMatrix(countVectorizedCol, countVectorizedRow, (col, row) => {
4065
+ if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) {
4066
+ return new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
4067
+ }
4068
+ const singleCellComputeResult = formula(...getArgOffset(col, row));
4069
+ // In the case where the user tries to vectorize arguments of an array formula, we will get an
4070
+ // array for every combination of the vectorized arguments, which will lead to a 3D matrix and
4071
+ // we won't be able to return the values.
4072
+ // In this case, we keep the first element of each spreading part, just as Excel does, and
4073
+ // create an array with these parts.
4074
+ // For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
4075
+ // range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
4076
+ // for the value in A2). In this case, we will simply take the first value of each matrix and
4077
+ // return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
4078
+ return isMatrix(singleCellComputeResult)
4079
+ ? singleCellComputeResult[0][0]
4080
+ : singleCellComputeResult;
4081
+ });
4082
+ }
3976
4083
  // -----------------------------------------------------------------------------
3977
4084
  // CONDITIONAL EXPLORE FUNCTIONS
3978
4085
  // -----------------------------------------------------------------------------
@@ -7279,14 +7386,20 @@ function multiplyMatrices(matrix1, matrix2) {
7279
7386
  /**
7280
7387
  * Return the input if it's a scalar or the first element of the input if it's a matrix.
7281
7388
  */
7282
- function toScalar(matrix) {
7283
- if (!isMatrix(matrix)) {
7284
- return matrix;
7389
+ function toScalar(arg) {
7390
+ if (!isMatrix(arg)) {
7391
+ return arg;
7285
7392
  }
7286
- if (matrix.length !== 1 || matrix[0].length !== 1) {
7393
+ if (!isSingleElementMatrix(arg)) {
7287
7394
  throw new EvaluationError(_t("The value should be a scalar or a 1x1 matrix"));
7288
7395
  }
7289
- return matrix[0][0];
7396
+ return arg[0][0];
7397
+ }
7398
+ function isSingleElementMatrix(matrix) {
7399
+ return matrix.length === 1 && matrix[0].length === 1;
7400
+ }
7401
+ function isMultipleElementMatrix(arg) {
7402
+ return isMatrix(arg) && !isSingleElementMatrix(arg);
7290
7403
  }
7291
7404
 
7292
7405
  function assertSameNumberOfElements(...args) {
@@ -8003,9 +8116,10 @@ const AGGREGATOR_NAMES = {
8003
8116
  avg: _t("Average"),
8004
8117
  sum: _t("Sum"),
8005
8118
  };
8119
+ const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8006
8120
  const AGGREGATORS_BY_FIELD_TYPE = {
8007
- integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
8008
- char: ["count_distinct", "count"],
8121
+ integer: NUMBER_CHAR_AGGREGATORS,
8122
+ char: NUMBER_CHAR_AGGREGATORS,
8009
8123
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8010
8124
  };
8011
8125
  const AGGREGATORS = {};
@@ -21487,7 +21601,7 @@ const FILTER = {
21487
21601
  }
21488
21602
  return mode === "row" ? transposeMatrix(result) : result;
21489
21603
  },
21490
- isExported: true,
21604
+ isExported: false,
21491
21605
  };
21492
21606
  // -----------------------------------------------------------------------------
21493
21607
  // SORT
@@ -24455,16 +24569,23 @@ const FALSE = {
24455
24569
  const IF = {
24456
24570
  description: _t("Returns value depending on logical expression."),
24457
24571
  args: [
24458
- arg("logical_expression (boolean)", _t("An expression or reference to a cell containing an expression that represents some logical value, i.e. TRUE or FALSE.")),
24459
- arg("value_if_true (any)", _t("The value the function returns if logical_expression is TRUE.")),
24460
- arg("value_if_false (any, default=FALSE)", _t("The value the function returns if logical_expression is FALSE.")),
24572
+ arg("logical_expression (boolean, range<boolean>)", _t("An expression or reference to a cell containing an expression that represents some logical value, i.e. TRUE or FALSE.")),
24573
+ arg("value_if_true (any, range)", _t("The value the function returns if logical_expression is TRUE.")),
24574
+ arg("value_if_false (any, range, default=FALSE)", _t("The value the function returns if logical_expression is FALSE.")),
24461
24575
  ],
24462
24576
  compute: function (logicalExpression, valueIfTrue, valueIfFalse) {
24463
- const result = toBoolean(logicalExpression?.value) ? valueIfTrue : valueIfFalse;
24577
+ if (isMultipleElementMatrix(logicalExpression)) {
24578
+ return applyVectorization(IF.compute, [logicalExpression, valueIfTrue, valueIfFalse]);
24579
+ }
24580
+ let result = toBoolean(toScalar(logicalExpression)) ? valueIfTrue : valueIfFalse;
24581
+ // useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
24582
+ if (!isMultipleElementMatrix(result)) {
24583
+ result = toScalar(result);
24584
+ }
24464
24585
  if (result === undefined) {
24465
24586
  return { value: "" };
24466
24587
  }
24467
- if (result.value === null) {
24588
+ if (!isMatrix(result) && result.value === null) {
24468
24589
  return { ...result, value: "" };
24469
24590
  }
24470
24591
  return result;
@@ -24477,15 +24598,22 @@ const IF = {
24477
24598
  const IFERROR = {
24478
24599
  description: _t("Value if it is not an error, otherwise 2nd argument."),
24479
24600
  args: [
24480
- arg("value (any)", _t("The value to return if value itself is not an error.")),
24481
- arg(`value_if_error (any, default="empty")`, _t("The value the function returns if value is an error.")),
24601
+ arg("value (any, range)", _t("The value to return if value itself is not an error.")),
24602
+ arg(`value_if_error (any, range, default="empty")`, _t("The value the function returns if value is an error.")),
24482
24603
  ],
24483
- compute: function (value, valueIfError = { value: "" }) {
24484
- const result = isEvaluationError(value?.value) ? valueIfError : value;
24604
+ compute: function (value, valueIfError) {
24605
+ if (isMultipleElementMatrix(value)) {
24606
+ return applyVectorization(IFERROR.compute, [value, valueIfError]);
24607
+ }
24608
+ let result = isEvaluationError(toScalar(value)?.value) ? valueIfError : value;
24609
+ // useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
24610
+ if (!isMultipleElementMatrix(result)) {
24611
+ result = toScalar(result);
24612
+ }
24485
24613
  if (result === undefined) {
24486
24614
  return { value: "" };
24487
24615
  }
24488
- if (result.value === null) {
24616
+ if (!isMatrix(result) && result.value === null) {
24489
24617
  return { ...result, value: "" };
24490
24618
  }
24491
24619
  return result;
@@ -24498,15 +24626,22 @@ const IFERROR = {
24498
24626
  const IFNA = {
24499
24627
  description: _t("Value if it is not an #N/A error, otherwise 2nd argument."),
24500
24628
  args: [
24501
- arg("value (any)", _t("The value to return if value itself is not #N/A an error.")),
24502
- arg(`value_if_error (any, default="empty")`, _t("The value the function returns if value is an #N/A error.")),
24629
+ arg("value (any, range)", _t("The value to return if value itself is not #N/A an error.")),
24630
+ arg(`value_if_error (any, range, default="empty")`, _t("The value the function returns if value is an #N/A error.")),
24503
24631
  ],
24504
- compute: function (value, valueIfError = { value: "" }) {
24505
- const result = value?.value === CellErrorType.NotAvailable ? valueIfError : value;
24632
+ compute: function (value, valueIfError) {
24633
+ if (isMultipleElementMatrix(value)) {
24634
+ return applyVectorization(IFNA.compute, [value, valueIfError]);
24635
+ }
24636
+ let result = toScalar(value)?.value === CellErrorType.NotAvailable ? valueIfError : value;
24637
+ // useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
24638
+ if (!isMultipleElementMatrix(result)) {
24639
+ result = toScalar(result);
24640
+ }
24506
24641
  if (result === undefined) {
24507
24642
  return { value: "" };
24508
24643
  }
24509
- if (result.value === null) {
24644
+ if (!isMatrix(result) && result.value === null) {
24510
24645
  return { ...result, value: "" };
24511
24646
  }
24512
24647
  return result;
@@ -24519,23 +24654,31 @@ const IFNA = {
24519
24654
  const IFS = {
24520
24655
  description: _t("Returns a value depending on multiple logical expressions."),
24521
24656
  args: [
24522
- arg("condition1 (boolean)", _t("The first condition to be evaluated. This can be a boolean, a number, an array, or a reference to any of those.")),
24523
- arg("value1 (any)", _t("The returned value if condition1 is TRUE.")),
24524
- arg("condition2 (boolean, any, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
24525
- arg("value2 (any, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
24657
+ arg("condition1 (boolean, range<boolean>)", _t("The first condition to be evaluated. This can be a boolean, a number, an array, or a reference to any of those.")),
24658
+ arg("value1 (any, range)", _t("The returned value if condition1 is TRUE.")),
24659
+ arg("condition2 (boolean, any, range, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
24660
+ arg("value2 (any, range, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
24526
24661
  ],
24527
24662
  compute: function (...values) {
24528
24663
  assert(() => values.length % 2 === 0, _t("Wrong number of arguments. Expected an even number of arguments."));
24529
- for (let n = 0; n < values.length - 1; n += 2) {
24530
- if (toBoolean(values[n]?.value)) {
24531
- const result = values[n + 1];
24532
- if (result === undefined) {
24664
+ while (values.length > 0) {
24665
+ if (isMultipleElementMatrix(values[0])) {
24666
+ return applyVectorization(IFS.compute, values);
24667
+ }
24668
+ const condition = toBoolean(toScalar(values.shift()));
24669
+ let valueIfTrue = values.shift();
24670
+ if (condition) {
24671
+ // useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
24672
+ if (!isMultipleElementMatrix(valueIfTrue)) {
24673
+ valueIfTrue = toScalar(valueIfTrue);
24674
+ }
24675
+ if (valueIfTrue === undefined) {
24533
24676
  return { value: "" };
24534
24677
  }
24535
- if (result.value === null) {
24536
- return { ...result, value: "" };
24678
+ if (!isMatrix(valueIfTrue) && valueIfTrue.value === null) {
24679
+ return { ...valueIfTrue, value: "" };
24537
24680
  }
24538
- return result;
24681
+ return valueIfTrue;
24539
24682
  }
24540
24683
  }
24541
24684
  return new EvaluationError(_t("No match."));
@@ -26179,7 +26322,7 @@ const SPLIT = {
26179
26322
  }
26180
26323
  return transposeMatrix([result]);
26181
26324
  },
26182
- isExported: true,
26325
+ isExported: false,
26183
26326
  };
26184
26327
  // -----------------------------------------------------------------------------
26185
26328
  // SUBSTITUTE
@@ -26372,86 +26515,21 @@ for (let category of categories) {
26372
26515
  functionRegistry.add(name, { isExported: false, ...addDescr });
26373
26516
  }
26374
26517
  }
26375
- const notAvailableError = new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
26518
+ //------------------------------------------------------------------------------
26519
+ // CREATE COMPUTE FUNCTION
26520
+ //------------------------------------------------------------------------------
26376
26521
  function createComputeFunction(descr, functionName) {
26377
26522
  function vectorizedCompute(...args) {
26378
- let countVectorizableCol = 1;
26379
- let countVectorizableRow = 1;
26380
- let vectorizableColLimit = Infinity;
26381
- let vectorizableRowLimit = Infinity;
26382
- let vectorArgsType = undefined;
26383
- //#region Compute vectorisation limits
26523
+ const acceptToVectorize = [];
26384
26524
  for (let i = 0; i < args.length; i++) {
26385
26525
  const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
26386
26526
  const arg = args[i];
26387
- if (isMatrix(arg) && !argDefinition.acceptMatrix) {
26388
- // if argDefinition does not accept a matrix but arg is still a matrix
26389
- // --> triggers the arguments vectorization
26390
- const nColumns = arg.length;
26391
- const nRows = arg[0].length;
26392
- if (nColumns !== 1 || nRows !== 1) {
26393
- vectorArgsType ??= new Array(args.length);
26394
- if (nColumns !== 1 && nRows !== 1) {
26395
- vectorArgsType[i] = "matrix";
26396
- countVectorizableCol = Math.max(countVectorizableCol, nColumns);
26397
- countVectorizableRow = Math.max(countVectorizableRow, nRows);
26398
- vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
26399
- vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
26400
- }
26401
- else if (nColumns !== 1) {
26402
- vectorArgsType[i] = "horizontal";
26403
- countVectorizableCol = Math.max(countVectorizableCol, nColumns);
26404
- vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
26405
- }
26406
- else if (nRows !== 1) {
26407
- vectorArgsType[i] = "vertical";
26408
- countVectorizableRow = Math.max(countVectorizableRow, nRows);
26409
- vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
26410
- }
26411
- }
26412
- else {
26413
- args[i] = arg[0][0];
26414
- }
26415
- }
26416
26527
  if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
26417
26528
  throw new BadExpressionError(_t("Function %s expects the parameter '%s' to be reference to a cell or range.", functionName, (i + 1).toString()));
26418
26529
  }
26530
+ acceptToVectorize.push(!argDefinition.acceptMatrix);
26419
26531
  }
26420
- //#endregion
26421
- if (countVectorizableCol === 1 && countVectorizableRow === 1) {
26422
- // either this function is not vectorized or it ends up with a 1x1 dimension
26423
- return errorHandlingCompute.apply(this, args);
26424
- }
26425
- const getArgOffset = (i, j) => args.map((arg, index) => {
26426
- switch (vectorArgsType?.[index]) {
26427
- case "matrix":
26428
- return arg[i][j];
26429
- case "horizontal":
26430
- return arg[i][0];
26431
- case "vertical":
26432
- return arg[0][j];
26433
- case undefined:
26434
- return arg;
26435
- }
26436
- });
26437
- return generateMatrix(countVectorizableCol, countVectorizableRow, (col, row) => {
26438
- if (col > vectorizableColLimit - 1 || row > vectorizableRowLimit - 1) {
26439
- return notAvailableError;
26440
- }
26441
- const singleCellComputeResult = errorHandlingCompute.apply(this, getArgOffset(col, row));
26442
- // In the case where the user tries to vectorize arguments of an array formula, we will get an
26443
- // array for every combination of the vectorized arguments, which will lead to a 3D matrix and
26444
- // we won't be able to return the values.
26445
- // In this case, we keep the first element of each spreading part, just as Excel does, and
26446
- // create an array with these parts.
26447
- // For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
26448
- // range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
26449
- // for the value in A2). In this case, we will simply take the first value of each matrix and
26450
- // return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
26451
- return isMatrix(singleCellComputeResult)
26452
- ? singleCellComputeResult[0][0]
26453
- : singleCellComputeResult;
26454
- });
26532
+ return applyVectorization(errorHandlingCompute.bind(this), args, acceptToVectorize);
26455
26533
  }
26456
26534
  function errorHandlingCompute(...args) {
26457
26535
  for (let i = 0; i < args.length; i++) {
@@ -39440,7 +39518,7 @@ class AbstractComposerStore extends SpreadsheetStore {
39440
39518
  proposals &&
39441
39519
  !["ARG_SEPARATOR", "LEFT_PAREN", "OPERATOR"].includes(tokenAtCursor.type)) {
39442
39520
  const filteredProposals = fuzzyLookup(searchTerm, proposals, (p) => p.fuzzySearchKey || p.text);
39443
- if (!exactMatch || filteredProposals.length > 1) {
39521
+ if (!exactMatch || filteredProposals.length) {
39444
39522
  proposals = filteredProposals;
39445
39523
  }
39446
39524
  }
@@ -44049,7 +44127,9 @@ function compareDimensionValues(dimension, a, b) {
44049
44127
  return dimension.order === "asc" ? -1 : 1;
44050
44128
  }
44051
44129
  if (dimension.type === "integer" || dimension.type === "datetime") {
44052
- return dimension.order === "asc" ? Number(a) - Number(b) : Number(b) - Number(a);
44130
+ return dimension.order === "asc"
44131
+ ? toNumber(a, DEFAULT_LOCALE) - toNumber(b, DEFAULT_LOCALE)
44132
+ : toNumber(b, DEFAULT_LOCALE) - toNumber(a, DEFAULT_LOCALE);
44053
44133
  }
44054
44134
  return dimension.order === "asc" ? a.localeCompare(b) : b.localeCompare(a);
44055
44135
  }
@@ -44585,12 +44665,7 @@ class SpreadsheetPivot {
44585
44665
  entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
44586
44666
  }
44587
44667
  else {
44588
- if (field.type === "char") {
44589
- entry[field.name] = { ...cell, value: cell.formattedValue || null };
44590
- }
44591
- else {
44592
- entry[field.name] = cell;
44593
- }
44668
+ entry[field.name] = cell;
44594
44669
  }
44595
44670
  }
44596
44671
  entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
@@ -46790,8 +46865,7 @@ class CellComposerStore extends AbstractComposerStore {
46790
46865
  if (!spreader) {
46791
46866
  return undefined;
46792
46867
  }
46793
- const cell = this.getters.getCell(spreader);
46794
- return cell?.content;
46868
+ return this.getters.getCellText(spreader, { showFormula: true });
46795
46869
  }
46796
46870
  get currentEditedCell() {
46797
46871
  return {
@@ -49594,6 +49668,7 @@ function useGridDrawing(refName, model, canvasSize) {
49594
49668
  const friction = 0.95;
49595
49669
  const verticalScrollFactor = 1;
49596
49670
  const horizontalScrollFactor = 1;
49671
+ const resetTimeoutDuration = 100;
49597
49672
  function useTouchScroll(ref, updateScroll, canMoveUp) {
49598
49673
  let lastX = 0;
49599
49674
  let lastY = 0;
@@ -49601,6 +49676,7 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
49601
49676
  let velocityY = 0;
49602
49677
  let isMouseDown = false;
49603
49678
  let lastTime = 0;
49679
+ let resetTimeout = null;
49604
49680
  useRefListener(ref, "touchstart", onTouchStart, { capture: false });
49605
49681
  useRefListener(ref, "touchmove", onTouchMove, { capture: false });
49606
49682
  useRefListener(ref, "touchend", onTouchEnd, { capture: false });
@@ -49613,6 +49689,10 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
49613
49689
  function onTouchMove(event) {
49614
49690
  if (!isMouseDown)
49615
49691
  return;
49692
+ if (resetTimeout) {
49693
+ clearTimeout(resetTimeout);
49694
+ resetTimeout = null;
49695
+ }
49616
49696
  const currentTime = Date.now();
49617
49697
  const { clientX, clientY } = event.touches[0];
49618
49698
  let deltaX = lastX - clientX;
@@ -49629,6 +49709,10 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
49629
49709
  }
49630
49710
  event.stopPropagation();
49631
49711
  }
49712
+ resetTimeout = setTimeout(() => {
49713
+ velocityX = 0;
49714
+ velocityY = 0;
49715
+ }, resetTimeoutDuration);
49632
49716
  updateScroll(deltaX * horizontalScrollFactor, deltaY * verticalScrollFactor);
49633
49717
  }
49634
49718
  function onTouchEnd(ev) {
@@ -74316,6 +74400,6 @@ exports.tokenColors = tokenColors;
74316
74400
  exports.tokenize = tokenize;
74317
74401
 
74318
74402
 
74319
- __info__.version = "18.0.29";
74320
- __info__.date = "2025-05-20T05:54:57.329Z";
74321
- __info__.hash = "8213c0e";
74403
+ __info__.version = "18.0.31";
74404
+ __info__.date = "2025-05-30T08:43:10.315Z";
74405
+ __info__.hash = "d201086";