@odoo/o-spreadsheet 18.1.21 → 18.1.23

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.1.21
6
- * @date 2025-05-20T05:54:45.398Z
7
- * @hash 89ed6a9
5
+ * @version 18.1.23
6
+ * @date 2025-05-30T08:45:44.408Z
7
+ * @hash a21fa01
8
8
  */
9
9
 
10
10
  'use strict';
@@ -4142,6 +4142,113 @@ function transposeMatrix(matrix) {
4142
4142
  }
4143
4143
  return generateMatrix(matrix[0].length, matrix.length, (i, j) => matrix[j][i]);
4144
4144
  }
4145
+ /**
4146
+ * Enables a formula function to accept matrix or vector inputs instead of simple value, computing results across multiple dimensions.
4147
+ *
4148
+ * ```
4149
+ * / |‾ ‾| \ |‾ ‾|
4150
+ * | | [A] | | | compute(A, D, E), compute(A, D, F), compute(A, D, G) |
4151
+ * applyVectorization| compute, | [B], D, [E, F, G] | | <=> | compute(B, D, E), compute(B, D, F), compute(B, D, G) |
4152
+ * | | [C] | | | compute(C, D, E), compute(C, D, F), compute(C, D, G) |
4153
+ * \ |_ _| / |_ _|
4154
+ * ```
4155
+ *
4156
+ * By default, all arguments are vectorized. To control which arguments are vectorized,
4157
+ * pass an `acceptToVectorize` boolean array of the same length as `args`:
4158
+ * - `true` enables vectorization for that argument
4159
+ * - `false` disables vectorization for that argument
4160
+ *
4161
+ * For example, with `[true, true, false]` on previous example you get:
4162
+ *
4163
+ * ```
4164
+ * |‾ ‾|
4165
+ * | compute(A, D, [E, F, G]) |
4166
+ * | compute(B, D, [E, F, G]) |
4167
+ * | compute(C, D, [E, F, G]) |
4168
+ * |_ _|
4169
+ * ```
4170
+ *
4171
+ * @remarks
4172
+ * This helper is automatically applied (by default) to **all** `compute` functions
4173
+ * across the various spreadsheet formula modules:
4174
+ * - If an argument is declared **scalar** (not `"range"`), it is vectorized.
4175
+ * - If **all** arguments are declared **ranges**, no vectorization occurs.
4176
+ * - e.g. `SUM(A1:B2)` returns a 1×1 sum over the range.
4177
+ * - e.g. `COS(A1:B2)` over `A1:B2` returns a 2×2 element-wise result.
4178
+ * - For special behaviors (e.g. the `IF` function), you may declare all arguments
4179
+ * as ranges and invoke this helper directly within your `compute` implementation.
4180
+ */
4181
+ function applyVectorization(formula, args, acceptToVectorize = undefined) {
4182
+ let countVectorizedCol = 1;
4183
+ let countVectorizedRow = 1;
4184
+ let vectorizedColLimit = Infinity;
4185
+ let vectorizedRowLimit = Infinity;
4186
+ let vectorArgsType = undefined;
4187
+ for (let i = 0; i < args.length; i++) {
4188
+ const arg = args[i];
4189
+ if (isMatrix(arg) && (acceptToVectorize === undefined || acceptToVectorize[i])) {
4190
+ const nColumns = arg.length;
4191
+ const nRows = arg[0].length;
4192
+ if (nColumns !== 1 || nRows !== 1) {
4193
+ vectorArgsType ??= new Array(args.length);
4194
+ if (nColumns !== 1 && nRows !== 1) {
4195
+ vectorArgsType[i] = "matrix";
4196
+ countVectorizedCol = Math.max(countVectorizedCol, nColumns);
4197
+ countVectorizedRow = Math.max(countVectorizedRow, nRows);
4198
+ vectorizedColLimit = Math.min(vectorizedColLimit, nColumns);
4199
+ vectorizedRowLimit = Math.min(vectorizedRowLimit, nRows);
4200
+ }
4201
+ else if (nColumns !== 1) {
4202
+ vectorArgsType[i] = "horizontal";
4203
+ countVectorizedCol = Math.max(countVectorizedCol, nColumns);
4204
+ vectorizedColLimit = Math.min(vectorizedColLimit, nColumns);
4205
+ }
4206
+ else if (nRows !== 1) {
4207
+ vectorArgsType[i] = "vertical";
4208
+ countVectorizedRow = Math.max(countVectorizedRow, nRows);
4209
+ vectorizedRowLimit = Math.min(vectorizedRowLimit, nRows);
4210
+ }
4211
+ }
4212
+ else {
4213
+ args[i] = arg[0][0];
4214
+ }
4215
+ }
4216
+ }
4217
+ if (countVectorizedCol === 1 && countVectorizedRow === 1) {
4218
+ // either this function is not vectorized or it ends up with a 1x1 dimension
4219
+ return formula(...args);
4220
+ }
4221
+ const getArgOffset = (i, j) => args.map((arg, index) => {
4222
+ switch (vectorArgsType?.[index]) {
4223
+ case "matrix":
4224
+ return arg[i][j];
4225
+ case "horizontal":
4226
+ return arg[i][0];
4227
+ case "vertical":
4228
+ return arg[0][j];
4229
+ case undefined:
4230
+ return arg;
4231
+ }
4232
+ });
4233
+ return generateMatrix(countVectorizedCol, countVectorizedRow, (col, row) => {
4234
+ if (col > vectorizedColLimit - 1 || row > vectorizedRowLimit - 1) {
4235
+ return new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
4236
+ }
4237
+ const singleCellComputeResult = formula(...getArgOffset(col, row));
4238
+ // In the case where the user tries to vectorize arguments of an array formula, we will get an
4239
+ // array for every combination of the vectorized arguments, which will lead to a 3D matrix and
4240
+ // we won't be able to return the values.
4241
+ // In this case, we keep the first element of each spreading part, just as Excel does, and
4242
+ // create an array with these parts.
4243
+ // For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
4244
+ // range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
4245
+ // for the value in A2). In this case, we will simply take the first value of each matrix and
4246
+ // return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
4247
+ return isMatrix(singleCellComputeResult)
4248
+ ? singleCellComputeResult[0][0]
4249
+ : singleCellComputeResult;
4250
+ });
4251
+ }
4145
4252
  // -----------------------------------------------------------------------------
4146
4253
  // CONDITIONAL EXPLORE FUNCTIONS
4147
4254
  // -----------------------------------------------------------------------------
@@ -7433,14 +7540,20 @@ function multiplyMatrices(matrix1, matrix2) {
7433
7540
  /**
7434
7541
  * Return the input if it's a scalar or the first element of the input if it's a matrix.
7435
7542
  */
7436
- function toScalar(matrix) {
7437
- if (!isMatrix(matrix)) {
7438
- return matrix;
7543
+ function toScalar(arg) {
7544
+ if (!isMatrix(arg)) {
7545
+ return arg;
7439
7546
  }
7440
- if (matrix.length !== 1 || matrix[0].length !== 1) {
7547
+ if (!isSingleElementMatrix(arg)) {
7441
7548
  throw new EvaluationError(_t("The value should be a scalar or a 1x1 matrix"));
7442
7549
  }
7443
- return matrix[0][0];
7550
+ return arg[0][0];
7551
+ }
7552
+ function isSingleElementMatrix(matrix) {
7553
+ return matrix.length === 1 && matrix[0].length === 1;
7554
+ }
7555
+ function isMultipleElementMatrix(arg) {
7556
+ return isMatrix(arg) && !isSingleElementMatrix(arg);
7444
7557
  }
7445
7558
 
7446
7559
  function assertSameNumberOfElements(...args) {
@@ -8182,9 +8295,10 @@ const AGGREGATOR_NAMES = {
8182
8295
  avg: _t("Average"),
8183
8296
  sum: _t("Sum"),
8184
8297
  };
8298
+ const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8185
8299
  const AGGREGATORS_BY_FIELD_TYPE = {
8186
- integer: ["max", "min", "avg", "sum", "count_distinct", "count"],
8187
- char: ["count_distinct", "count"],
8300
+ integer: NUMBER_CHAR_AGGREGATORS,
8301
+ char: NUMBER_CHAR_AGGREGATORS,
8188
8302
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8189
8303
  };
8190
8304
  const AGGREGATORS = {};
@@ -15284,7 +15398,7 @@ const FILTER = {
15284
15398
  }
15285
15399
  return mode === "row" ? transposeMatrix(result) : result;
15286
15400
  },
15287
- isExported: true,
15401
+ isExported: false,
15288
15402
  };
15289
15403
  // -----------------------------------------------------------------------------
15290
15404
  // SORT
@@ -18415,16 +18529,23 @@ const FALSE = {
18415
18529
  const IF = {
18416
18530
  description: _t("Returns value depending on logical expression."),
18417
18531
  args: [
18418
- 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.")),
18419
- arg("value_if_true (any)", _t("The value the function returns if logical_expression is TRUE.")),
18420
- arg("value_if_false (any, default=FALSE)", _t("The value the function returns if logical_expression is FALSE.")),
18532
+ 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.")),
18533
+ arg("value_if_true (any, range)", _t("The value the function returns if logical_expression is TRUE.")),
18534
+ arg("value_if_false (any, range, default=FALSE)", _t("The value the function returns if logical_expression is FALSE.")),
18421
18535
  ],
18422
18536
  compute: function (logicalExpression, valueIfTrue, valueIfFalse) {
18423
- const result = toBoolean(logicalExpression?.value) ? valueIfTrue : valueIfFalse;
18537
+ if (isMultipleElementMatrix(logicalExpression)) {
18538
+ return applyVectorization(IF.compute, [logicalExpression, valueIfTrue, valueIfFalse]);
18539
+ }
18540
+ let result = toBoolean(toScalar(logicalExpression)) ? valueIfTrue : valueIfFalse;
18541
+ // useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
18542
+ if (!isMultipleElementMatrix(result)) {
18543
+ result = toScalar(result);
18544
+ }
18424
18545
  if (result === undefined) {
18425
18546
  return { value: "" };
18426
18547
  }
18427
- if (result.value === null) {
18548
+ if (!isMatrix(result) && result.value === null) {
18428
18549
  return { ...result, value: "" };
18429
18550
  }
18430
18551
  return result;
@@ -18437,15 +18558,22 @@ const IF = {
18437
18558
  const IFERROR = {
18438
18559
  description: _t("Value if it is not an error, otherwise 2nd argument."),
18439
18560
  args: [
18440
- arg("value (any)", _t("The value to return if value itself is not an error.")),
18441
- arg(`value_if_error (any, default="empty")`, _t("The value the function returns if value is an error.")),
18561
+ arg("value (any, range)", _t("The value to return if value itself is not an error.")),
18562
+ arg(`value_if_error (any, range, default="empty")`, _t("The value the function returns if value is an error.")),
18442
18563
  ],
18443
- compute: function (value, valueIfError = { value: "" }) {
18444
- const result = isEvaluationError(value?.value) ? valueIfError : value;
18564
+ compute: function (value, valueIfError) {
18565
+ if (isMultipleElementMatrix(value)) {
18566
+ return applyVectorization(IFERROR.compute, [value, valueIfError]);
18567
+ }
18568
+ let result = isEvaluationError(toScalar(value)?.value) ? valueIfError : value;
18569
+ // useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
18570
+ if (!isMultipleElementMatrix(result)) {
18571
+ result = toScalar(result);
18572
+ }
18445
18573
  if (result === undefined) {
18446
18574
  return { value: "" };
18447
18575
  }
18448
- if (result.value === null) {
18576
+ if (!isMatrix(result) && result.value === null) {
18449
18577
  return { ...result, value: "" };
18450
18578
  }
18451
18579
  return result;
@@ -18458,15 +18586,22 @@ const IFERROR = {
18458
18586
  const IFNA = {
18459
18587
  description: _t("Value if it is not an #N/A error, otherwise 2nd argument."),
18460
18588
  args: [
18461
- arg("value (any)", _t("The value to return if value itself is not #N/A an error.")),
18462
- arg(`value_if_error (any, default="empty")`, _t("The value the function returns if value is an #N/A error.")),
18589
+ arg("value (any, range)", _t("The value to return if value itself is not #N/A an error.")),
18590
+ arg(`value_if_error (any, range, default="empty")`, _t("The value the function returns if value is an #N/A error.")),
18463
18591
  ],
18464
- compute: function (value, valueIfError = { value: "" }) {
18465
- const result = value?.value === CellErrorType.NotAvailable ? valueIfError : value;
18592
+ compute: function (value, valueIfError) {
18593
+ if (isMultipleElementMatrix(value)) {
18594
+ return applyVectorization(IFNA.compute, [value, valueIfError]);
18595
+ }
18596
+ let result = toScalar(value)?.value === CellErrorType.NotAvailable ? valueIfError : value;
18597
+ // useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
18598
+ if (!isMultipleElementMatrix(result)) {
18599
+ result = toScalar(result);
18600
+ }
18466
18601
  if (result === undefined) {
18467
18602
  return { value: "" };
18468
18603
  }
18469
- if (result.value === null) {
18604
+ if (!isMatrix(result) && result.value === null) {
18470
18605
  return { ...result, value: "" };
18471
18606
  }
18472
18607
  return result;
@@ -18479,23 +18614,31 @@ const IFNA = {
18479
18614
  const IFS = {
18480
18615
  description: _t("Returns a value depending on multiple logical expressions."),
18481
18616
  args: [
18482
- 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.")),
18483
- arg("value1 (any)", _t("The returned value if condition1 is TRUE.")),
18484
- arg("condition2 (boolean, any, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
18485
- arg("value2 (any, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
18617
+ 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.")),
18618
+ arg("value1 (any, range)", _t("The returned value if condition1 is TRUE.")),
18619
+ arg("condition2 (boolean, any, range, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
18620
+ arg("value2 (any, range, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
18486
18621
  ],
18487
18622
  compute: function (...values) {
18488
18623
  assert(() => values.length % 2 === 0, _t("Wrong number of arguments. Expected an even number of arguments."));
18489
- for (let n = 0; n < values.length - 1; n += 2) {
18490
- if (toBoolean(values[n]?.value)) {
18491
- const result = values[n + 1];
18492
- if (result === undefined) {
18624
+ while (values.length > 0) {
18625
+ if (isMultipleElementMatrix(values[0])) {
18626
+ return applyVectorization(IFS.compute, values);
18627
+ }
18628
+ const condition = toBoolean(toScalar(values.shift()));
18629
+ let valueIfTrue = values.shift();
18630
+ if (condition) {
18631
+ // useful for interpreting empty cell references as empty strings. But must be removed to make empty cell references equal to zero
18632
+ if (!isMultipleElementMatrix(valueIfTrue)) {
18633
+ valueIfTrue = toScalar(valueIfTrue);
18634
+ }
18635
+ if (valueIfTrue === undefined) {
18493
18636
  return { value: "" };
18494
18637
  }
18495
- if (result.value === null) {
18496
- return { ...result, value: "" };
18638
+ if (!isMatrix(valueIfTrue) && valueIfTrue.value === null) {
18639
+ return { ...valueIfTrue, value: "" };
18497
18640
  }
18498
- return result;
18641
+ return valueIfTrue;
18499
18642
  }
18500
18643
  }
18501
18644
  return new EvaluationError(_t("No match."));
@@ -20157,7 +20300,7 @@ const SPLIT = {
20157
20300
  }
20158
20301
  return transposeMatrix([result]);
20159
20302
  },
20160
- isExported: true,
20303
+ isExported: false,
20161
20304
  };
20162
20305
  // -----------------------------------------------------------------------------
20163
20306
  // SUBSTITUTE
@@ -20350,86 +20493,21 @@ for (let category of categories) {
20350
20493
  functionRegistry.add(name, { isExported: false, ...addDescr });
20351
20494
  }
20352
20495
  }
20353
- const notAvailableError = new NotAvailableError(_t("Array arguments to [[FUNCTION_NAME]] are of different size."));
20496
+ //------------------------------------------------------------------------------
20497
+ // CREATE COMPUTE FUNCTION
20498
+ //------------------------------------------------------------------------------
20354
20499
  function createComputeFunction(descr, functionName) {
20355
20500
  function vectorizedCompute(...args) {
20356
- let countVectorizableCol = 1;
20357
- let countVectorizableRow = 1;
20358
- let vectorizableColLimit = Infinity;
20359
- let vectorizableRowLimit = Infinity;
20360
- let vectorArgsType = undefined;
20361
- //#region Compute vectorisation limits
20501
+ const acceptToVectorize = [];
20362
20502
  for (let i = 0; i < args.length; i++) {
20363
20503
  const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
20364
20504
  const arg = args[i];
20365
- if (isMatrix(arg) && !argDefinition.acceptMatrix) {
20366
- // if argDefinition does not accept a matrix but arg is still a matrix
20367
- // --> triggers the arguments vectorization
20368
- const nColumns = arg.length;
20369
- const nRows = arg[0].length;
20370
- if (nColumns !== 1 || nRows !== 1) {
20371
- vectorArgsType ??= new Array(args.length);
20372
- if (nColumns !== 1 && nRows !== 1) {
20373
- vectorArgsType[i] = "matrix";
20374
- countVectorizableCol = Math.max(countVectorizableCol, nColumns);
20375
- countVectorizableRow = Math.max(countVectorizableRow, nRows);
20376
- vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
20377
- vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
20378
- }
20379
- else if (nColumns !== 1) {
20380
- vectorArgsType[i] = "horizontal";
20381
- countVectorizableCol = Math.max(countVectorizableCol, nColumns);
20382
- vectorizableColLimit = Math.min(vectorizableColLimit, nColumns);
20383
- }
20384
- else if (nRows !== 1) {
20385
- vectorArgsType[i] = "vertical";
20386
- countVectorizableRow = Math.max(countVectorizableRow, nRows);
20387
- vectorizableRowLimit = Math.min(vectorizableRowLimit, nRows);
20388
- }
20389
- }
20390
- else {
20391
- args[i] = arg[0][0];
20392
- }
20393
- }
20394
20505
  if (!isMatrix(arg) && argDefinition.acceptMatrixOnly) {
20395
20506
  throw new BadExpressionError(_t("Function %s expects the parameter '%s' to be reference to a cell or range.", functionName, (i + 1).toString()));
20396
20507
  }
20508
+ acceptToVectorize.push(!argDefinition.acceptMatrix);
20397
20509
  }
20398
- //#endregion
20399
- if (countVectorizableCol === 1 && countVectorizableRow === 1) {
20400
- // either this function is not vectorized or it ends up with a 1x1 dimension
20401
- return errorHandlingCompute.apply(this, args);
20402
- }
20403
- const getArgOffset = (i, j) => args.map((arg, index) => {
20404
- switch (vectorArgsType?.[index]) {
20405
- case "matrix":
20406
- return arg[i][j];
20407
- case "horizontal":
20408
- return arg[i][0];
20409
- case "vertical":
20410
- return arg[0][j];
20411
- case undefined:
20412
- return arg;
20413
- }
20414
- });
20415
- return generateMatrix(countVectorizableCol, countVectorizableRow, (col, row) => {
20416
- if (col > vectorizableColLimit - 1 || row > vectorizableRowLimit - 1) {
20417
- return notAvailableError;
20418
- }
20419
- const singleCellComputeResult = errorHandlingCompute.apply(this, getArgOffset(col, row));
20420
- // In the case where the user tries to vectorize arguments of an array formula, we will get an
20421
- // array for every combination of the vectorized arguments, which will lead to a 3D matrix and
20422
- // we won't be able to return the values.
20423
- // In this case, we keep the first element of each spreading part, just as Excel does, and
20424
- // create an array with these parts.
20425
- // For exemple, we have MUNIT(x) that return an unitary matrix of x*x. If we use it with a
20426
- // range, like MUNIT(A1:A2), we will get two unitary matrices (one for the value in A1 and one
20427
- // for the value in A2). In this case, we will simply take the first value of each matrix and
20428
- // return the array [First value of MUNIT(A1), First value of MUNIT(A2)].
20429
- return isMatrix(singleCellComputeResult)
20430
- ? singleCellComputeResult[0][0]
20431
- : singleCellComputeResult;
20432
- });
20510
+ return applyVectorization(errorHandlingCompute.bind(this), args, acceptToVectorize);
20433
20511
  }
20434
20512
  function errorHandlingCompute(...args) {
20435
20513
  for (let i = 0; i < args.length; i++) {
@@ -21274,7 +21352,7 @@ class AbstractComposerStore extends SpreadsheetStore {
21274
21352
  proposals &&
21275
21353
  !["ARG_SEPARATOR", "LEFT_PAREN", "OPERATOR"].includes(tokenAtCursor.type)) {
21276
21354
  const filteredProposals = fuzzyLookup(searchTerm, proposals, (p) => p.fuzzySearchKey || p.text);
21277
- if (!exactMatch || filteredProposals.length > 1) {
21355
+ if (!exactMatch || filteredProposals.length) {
21278
21356
  proposals = filteredProposals;
21279
21357
  }
21280
21358
  }
@@ -46141,7 +46219,9 @@ function compareDimensionValues(dimension, a, b) {
46141
46219
  return dimension.order === "asc" ? -1 : 1;
46142
46220
  }
46143
46221
  if (dimension.type === "integer" || dimension.type === "datetime") {
46144
- return dimension.order === "asc" ? Number(a) - Number(b) : Number(b) - Number(a);
46222
+ return dimension.order === "asc"
46223
+ ? toNumber(a, DEFAULT_LOCALE) - toNumber(b, DEFAULT_LOCALE)
46224
+ : toNumber(b, DEFAULT_LOCALE) - toNumber(a, DEFAULT_LOCALE);
46145
46225
  }
46146
46226
  return dimension.order === "asc" ? a.localeCompare(b) : b.localeCompare(a);
46147
46227
  }
@@ -46677,12 +46757,7 @@ class SpreadsheetPivot {
46677
46757
  entry[field.name] = { value: null, type: CellValueType.empty, formattedValue: "" };
46678
46758
  }
46679
46759
  else {
46680
- if (field.type === "char") {
46681
- entry[field.name] = { ...cell, value: cell.formattedValue || null };
46682
- }
46683
- else {
46684
- entry[field.name] = cell;
46685
- }
46760
+ entry[field.name] = cell;
46686
46761
  }
46687
46762
  }
46688
46763
  entry["__count"] = { value: 1, type: CellValueType.number, formattedValue: "1" };
@@ -48907,8 +48982,7 @@ class CellComposerStore extends AbstractComposerStore {
48907
48982
  if (!spreader) {
48908
48983
  return undefined;
48909
48984
  }
48910
- const cell = this.getters.getCell(spreader);
48911
- return cell?.content;
48985
+ return this.getters.getCellText(spreader, { showFormula: true });
48912
48986
  }
48913
48987
  get currentEditedCell() {
48914
48988
  return {
@@ -51719,6 +51793,7 @@ function useGridDrawing(refName, model, canvasSize) {
51719
51793
  const friction = 0.95;
51720
51794
  const verticalScrollFactor = 1;
51721
51795
  const horizontalScrollFactor = 1;
51796
+ const resetTimeoutDuration = 100;
51722
51797
  function useTouchScroll(ref, updateScroll, canMoveUp) {
51723
51798
  let lastX = 0;
51724
51799
  let lastY = 0;
@@ -51726,6 +51801,7 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
51726
51801
  let velocityY = 0;
51727
51802
  let isMouseDown = false;
51728
51803
  let lastTime = 0;
51804
+ let resetTimeout = null;
51729
51805
  useRefListener(ref, "touchstart", onTouchStart, { capture: false });
51730
51806
  useRefListener(ref, "touchmove", onTouchMove, { capture: false });
51731
51807
  useRefListener(ref, "touchend", onTouchEnd, { capture: false });
@@ -51738,6 +51814,10 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
51738
51814
  function onTouchMove(event) {
51739
51815
  if (!isMouseDown)
51740
51816
  return;
51817
+ if (resetTimeout) {
51818
+ clearTimeout(resetTimeout);
51819
+ resetTimeout = null;
51820
+ }
51741
51821
  const currentTime = Date.now();
51742
51822
  const { clientX, clientY } = event.touches[0];
51743
51823
  let deltaX = lastX - clientX;
@@ -51754,6 +51834,10 @@ function useTouchScroll(ref, updateScroll, canMoveUp) {
51754
51834
  }
51755
51835
  event.stopPropagation();
51756
51836
  }
51837
+ resetTimeout = setTimeout(() => {
51838
+ velocityX = 0;
51839
+ velocityY = 0;
51840
+ }, resetTimeoutDuration);
51757
51841
  updateScroll(deltaX * horizontalScrollFactor, deltaY * verticalScrollFactor);
51758
51842
  }
51759
51843
  function onTouchEnd(ev) {
@@ -76349,6 +76433,6 @@ exports.tokenColors = tokenColors;
76349
76433
  exports.tokenize = tokenize;
76350
76434
 
76351
76435
 
76352
- __info__.version = "18.1.21";
76353
- __info__.date = "2025-05-20T05:54:45.398Z";
76354
- __info__.hash = "89ed6a9";
76436
+ __info__.version = "18.1.23";
76437
+ __info__.date = "2025-05-30T08:45:44.408Z";
76438
+ __info__.hash = "a21fa01";