@odoo/o-spreadsheet 18.5.0-alpha.10 → 18.5.0-alpha.11

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.5.0-alpha.10
6
- * @date 2025-08-21T06:42:10.830Z
7
- * @hash 5800d1b
5
+ * @version 18.5.0-alpha.11
6
+ * @date 2025-08-26T10:14:05.357Z
7
+ * @hash b913e49
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, useExternalListener, onWillUpdateProps, onWillStart, onWillPatch, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -723,7 +723,7 @@ function batched(callback) {
723
723
  /*
724
724
  * Concatenate an array of strings.
725
725
  */
726
- function concat(chars) {
726
+ function concat$1(chars) {
727
727
  // ~40% faster than chars.join("")
728
728
  let output = "";
729
729
  for (let i = 0, len = chars.length; i < len; i++) {
@@ -1195,7 +1195,7 @@ function rgbaStringToHex(color) {
1195
1195
  if (alphaHex !== 255) {
1196
1196
  vals.push(alphaHex);
1197
1197
  }
1198
- return "#" + concat(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
1198
+ return "#" + concat$1(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
1199
1199
  }
1200
1200
  /**
1201
1201
  * RGBA to HEX representation (#RRGGBBAA).
@@ -5109,6 +5109,9 @@ function parseLiteral(content, locale) {
5109
5109
  if (content === "") {
5110
5110
  return null;
5111
5111
  }
5112
+ if (content.includes("\n")) {
5113
+ return content;
5114
+ }
5112
5115
  if (isNumber(content, DEFAULT_LOCALE)) {
5113
5116
  return parseNumber(content, DEFAULT_LOCALE);
5114
5117
  }
@@ -7317,14 +7320,11 @@ function getPasteZones(target, content) {
7317
7320
  const width = content[0].length, height = content.length;
7318
7321
  return target.map((t) => splitZoneForPaste(t, width, height)).flat();
7319
7322
  }
7320
- function parseOSClipboardContent(content, clipboardId) {
7323
+ function parseOSClipboardContent(content) {
7321
7324
  let spreadsheetContent = undefined;
7322
7325
  if (content[ClipboardMIMEType.Html]) {
7323
7326
  const htmlDocument = new DOMParser().parseFromString(content[ClipboardMIMEType.Html], "text/html");
7324
- const oSheetClipboardData = htmlDocument
7325
- .querySelector("div")
7326
- ?.getAttribute("data-osheet-clipboard");
7327
- spreadsheetContent = oSheetClipboardData && JSON.parse(oSheetClipboardData);
7327
+ spreadsheetContent = getOSheetDataFromHTML(htmlDocument);
7328
7328
  }
7329
7329
  const textContent = content[ClipboardMIMEType.PlainText] || "";
7330
7330
  let imageBlob = undefined;
@@ -7343,6 +7343,17 @@ function parseOSClipboardContent(content, clipboardId) {
7343
7343
  };
7344
7344
  return osClipboardContent;
7345
7345
  }
7346
+ function getOSheetDataFromHTML(htmlDocument) {
7347
+ const attributes = [...htmlDocument.documentElement.attributes];
7348
+ // Check if it's a Microsoft Office clipboard data (it will have some namespaces defined in the root element)
7349
+ if (attributes.some((attr) => attr.value.includes("microsoft"))) {
7350
+ return undefined;
7351
+ }
7352
+ const oSheetClipboardData = htmlDocument
7353
+ .querySelector("div")
7354
+ ?.getAttribute("data-osheet-clipboard");
7355
+ return oSheetClipboardData && JSON.parse(oSheetClipboardData);
7356
+ }
7346
7357
  /**
7347
7358
  * Applies each clipboard handler to paste its corresponding data into the target.
7348
7359
  */
@@ -18336,7 +18347,6 @@ function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
18336
18347
  const functionRegex = /[a-zA-Z0-9\_]+(\.[a-zA-Z0-9\_]+)*/;
18337
18348
  const UNARY_OPERATORS_PREFIX = ["-", "+"];
18338
18349
  const UNARY_OPERATORS_POSTFIX = ["%"];
18339
- const ASSOCIATIVE_OPERATORS = ["*", "+", "&"];
18340
18350
  class TokenList {
18341
18351
  tokens;
18342
18352
  currentIndex = 0;
@@ -18357,8 +18367,8 @@ class TokenList {
18357
18367
  }
18358
18368
  }
18359
18369
  const OP_PRIORITY = {
18370
+ "%": 40,
18360
18371
  "^": 30,
18361
- "%": 30,
18362
18372
  "*": 20,
18363
18373
  "/": 20,
18364
18374
  "+": 15,
@@ -18639,64 +18649,6 @@ function mapAst(ast, fn) {
18639
18649
  return ast;
18640
18650
  }
18641
18651
  }
18642
- /**
18643
- * Converts an ast formula to the corresponding string
18644
- */
18645
- function astToFormula(ast) {
18646
- switch (ast.type) {
18647
- case "FUNCALL":
18648
- const args = ast.args.map((arg) => astToFormula(arg));
18649
- return `${ast.value}(${args.join(",")})`;
18650
- case "NUMBER":
18651
- return ast.value.toString();
18652
- case "REFERENCE":
18653
- return ast.value;
18654
- case "STRING":
18655
- return `"${ast.value}"`;
18656
- case "BOOLEAN":
18657
- return ast.value ? "TRUE" : "FALSE";
18658
- case "UNARY_OPERATION":
18659
- return ast.postfix
18660
- ? leftOperandToFormula(ast) + ast.value
18661
- : ast.value + rightOperandToFormula(ast);
18662
- case "BIN_OPERATION":
18663
- return leftOperandToFormula(ast) + ast.value + rightOperandToFormula(ast);
18664
- default:
18665
- return ast.value;
18666
- }
18667
- }
18668
- /**
18669
- * Convert the left operand of a binary operation to the corresponding string
18670
- * and enclose the result inside parenthesis if necessary.
18671
- */
18672
- function leftOperandToFormula(operationAST) {
18673
- const mainOperator = operationAST.value;
18674
- const leftOperation = "left" in operationAST ? operationAST.left : operationAST.operand;
18675
- const leftOperator = leftOperation.value;
18676
- const needParenthesis = leftOperation.type === "BIN_OPERATION" && OP_PRIORITY[leftOperator] < OP_PRIORITY[mainOperator];
18677
- return needParenthesis ? `(${astToFormula(leftOperation)})` : astToFormula(leftOperation);
18678
- }
18679
- /**
18680
- * Convert the right operand of a binary or unary operation to the corresponding string
18681
- * and enclose the result inside parenthesis if necessary.
18682
- */
18683
- function rightOperandToFormula(operationAST) {
18684
- const mainOperator = operationAST.value;
18685
- const rightOperation = "right" in operationAST ? operationAST.right : operationAST.operand;
18686
- const rightPriority = OP_PRIORITY[rightOperation.value];
18687
- const mainPriority = OP_PRIORITY[mainOperator];
18688
- let needParenthesis = false;
18689
- if (rightOperation.type !== "BIN_OPERATION") {
18690
- needParenthesis = false;
18691
- }
18692
- else if (rightPriority < mainPriority) {
18693
- needParenthesis = true;
18694
- }
18695
- else if (rightPriority === mainPriority && !ASSOCIATIVE_OPERATORS.includes(mainOperator)) {
18696
- needParenthesis = true;
18697
- }
18698
- return needParenthesis ? `(${astToFormula(rightOperation)})` : astToFormula(rightOperation);
18699
- }
18700
18652
 
18701
18653
  /**
18702
18654
  * Add the following information on tokens:
@@ -22195,7 +22147,7 @@ function adaptFormulaStringRanges(defaultSheetId, formula, applyChange) {
22195
22147
  };
22196
22148
  }
22197
22149
  }
22198
- return concat(tokens.map((token) => token.value));
22150
+ return concat$1(tokens.map((token) => token.value));
22199
22151
  }
22200
22152
  function adaptStringRange(defaultSheetId, sheetXC, applyChange) {
22201
22153
  const sheetName = splitReference(sheetXC).sheetName;
@@ -28280,10 +28232,6 @@ class ComboChart extends AbstractChart {
28280
28232
  };
28281
28233
  }
28282
28234
  getDefinitionForExcel() {
28283
- // Excel does not support aggregating labels
28284
- if (this.aggregated) {
28285
- return undefined;
28286
- }
28287
28235
  const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
28288
28236
  const definition = this.getDefinition();
28289
28237
  return {
@@ -29485,9 +29433,6 @@ class RadarChart extends AbstractChart {
29485
29433
  };
29486
29434
  }
29487
29435
  getDefinitionForExcel() {
29488
- if (this.aggregated) {
29489
- return undefined;
29490
- }
29491
29436
  const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
29492
29437
  const definition = this.getDefinition();
29493
29438
  return {
@@ -29635,10 +29580,6 @@ class ScatterChart extends AbstractChart {
29635
29580
  return new ScatterChart(definition, this.sheetId, this.getters);
29636
29581
  }
29637
29582
  getDefinitionForExcel() {
29638
- // Excel does not support aggregating labels
29639
- if (this.aggregated) {
29640
- return undefined;
29641
- }
29642
29583
  const dataSets = this.dataSets
29643
29584
  .map((ds) => toExcelDataset(this.getters, ds))
29644
29585
  .filter((ds) => ds.range !== "");
@@ -30957,7 +30898,7 @@ function escapeXml(strings, ...expressions) {
30957
30898
  const value = expressions[i] instanceof XMLString ? expressions[i] : xmlEscape(expressions[i]);
30958
30899
  str.push(value + strings[i + 1]);
30959
30900
  }
30960
- return new XMLString(concat(str));
30901
+ return new XMLString(concat$1(str));
30961
30902
  }
30962
30903
  /**
30963
30904
  * Removes the escaped namespace of all the xml tags in the string.
@@ -32043,10 +31984,10 @@ class ChartDashboardMenuStore extends SpreadsheetStore {
32043
31984
  const item = chartSubtypeRegistry.get(type);
32044
31985
  return {
32045
31986
  id: item.chartType,
32046
- label: item.displayName,
31987
+ label: _t("Show as %(chart_type)s chart", { chart_type: item.displayName.toLowerCase() }),
32047
31988
  onClick: () => this.updateType(item.chartType),
32048
- isSelected: item.chartType === this.getters.getChartDefinition(this.chartId).type,
32049
- iconClass: this.getIconClasses(item.chartType),
31989
+ class: item.chartType === definition.type ? "active" : "",
31990
+ preview: item.preview,
32050
31991
  };
32051
31992
  });
32052
31993
  }
@@ -32082,18 +32023,6 @@ class ChartDashboardMenuStore extends SpreadsheetStore {
32082
32023
  sheetId: this.getters.getActiveSheetId(),
32083
32024
  });
32084
32025
  }
32085
- getIconClasses(type) {
32086
- if (type.includes("bar")) {
32087
- return "fa fa-bar-chart";
32088
- }
32089
- if (type.includes("line")) {
32090
- return "fa fa-line-chart";
32091
- }
32092
- if (type.includes("pie")) {
32093
- return "fa fa-pie-chart";
32094
- }
32095
- return "";
32096
- }
32097
32026
  }
32098
32027
 
32099
32028
  class ChartDashboardMenu extends Component {
@@ -32132,20 +32061,11 @@ class ChartDashboardMenu extends Component {
32132
32061
  if (definition.type === "scorecard") {
32133
32062
  return undefined;
32134
32063
  }
32135
- if (this.props.chartId === this.fullScreenFigureStore.fullScreenFigure?.id) {
32136
- return {
32137
- id: "fullScreenChart",
32138
- label: _t("Exit Full Screen"),
32139
- iconClass: "fa fa-compress",
32140
- onClick: () => {
32141
- this.fullScreenFigureStore.toggleFullScreenChart(figureId);
32142
- },
32143
- };
32144
- }
32064
+ const isFullScreen = this.props.chartId === this.fullScreenFigureStore.fullScreenFigure?.id;
32145
32065
  return {
32146
32066
  id: "fullScreenChart",
32147
- label: _t("Full Screen"),
32148
- iconClass: "fa fa-expand",
32067
+ label: isFullScreen ? _t("Exit Full Screen") : _t("Full Screen"),
32068
+ class: `text-muted fa ${isFullScreen ? "fa-compress" : "fa-expand"}`,
32149
32069
  onClick: () => {
32150
32070
  this.fullScreenFigureStore.toggleFullScreenChart(figureId);
32151
32071
  },
@@ -34327,7 +34247,7 @@ class AbstractComposerStore extends SpreadsheetStore {
34327
34247
  if (isFormula(content)) {
34328
34248
  const missing = this.getNumberOfMissingParenthesis(this.currentTokens);
34329
34249
  if (missing > 0) {
34330
- content += concat(new Array(missing).fill(")"));
34250
+ content += concat$1(new Array(missing).fill(")"));
34331
34251
  }
34332
34252
  }
34333
34253
  }
@@ -34379,9 +34299,10 @@ class AbstractComposerStore extends SpreadsheetStore {
34379
34299
  if (isNewCurrentContent || this.editionMode !== "inactive") {
34380
34300
  const locale = this.getters.getLocale();
34381
34301
  this.currentTokens = isFormula(text) ? composerTokenize(text, locale) : [];
34382
- if (this.currentTokens.length > 100) {
34302
+ const nonSpaceTokensCount = this.currentTokens.filter((token) => token.type !== "SPACE").length;
34303
+ if (nonSpaceTokensCount > 1000) {
34383
34304
  if (raise) {
34384
- this.notificationStore.raiseError(_t("This formula has over 100 parts. It can't be processed properly, consider splitting it into multiple cells"));
34305
+ this.notificationStore.raiseError(_t("This formula has over 1000 parts. It can't be processed properly, consider splitting it into multiple cells"));
34385
34306
  }
34386
34307
  }
34387
34308
  }
@@ -34802,6 +34723,8 @@ css /* scss */ `
34802
34723
  padding-right: 3px;
34803
34724
  outline: none;
34804
34725
 
34726
+ tab-size: 4;
34727
+
34805
34728
  p {
34806
34729
  margin-bottom: 0px;
34807
34730
 
@@ -48652,6 +48575,280 @@ class ClientTag extends Component {
48652
48575
  }
48653
48576
  }
48654
48577
 
48578
+ const ASSOCIATIVE_OPERATORS = ["*", "+", "&"];
48579
+ /**
48580
+ * Pretty-prints formula ASTs into readable formulas.
48581
+ *
48582
+ * Implements a Wadler-inspired pretty printer:
48583
+ * it converts an AST into a `Doc` structure,
48584
+ * and then chooses between compact (flat) or expanded (with
48585
+ * line breaks and indentation) layouts depending on space.
48586
+ *
48587
+ * References:
48588
+ * - https://lik.ai/blog/how-a-pretty-printer-works/
48589
+ * - Wadler, "A prettier printer": https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf
48590
+ */
48591
+ function prettify(ast, width = 60) {
48592
+ return "=" + print(astToDoc(ast), width - 1); // width-1 because of the leading '='
48593
+ }
48594
+ /**
48595
+ * A possible line break.
48596
+ * Printed as either space or newline.
48597
+ */
48598
+ function line() {
48599
+ return { type: "insertLine" };
48600
+ }
48601
+ /**
48602
+ * Increase indentation for a nested block.
48603
+ */
48604
+ function nest(indentLevel, doc) {
48605
+ return { type: "nest", indentLevel, doc };
48606
+ }
48607
+ /**
48608
+ * Combines multiple docs into a single doc, concatenating them side by side
48609
+ * without any line breaks or indentation.
48610
+ */
48611
+ function concat(docs) {
48612
+ return { type: "concat", docs };
48613
+ }
48614
+ /**
48615
+ * Marks a document as a unit to be printed "flat" (all on one line)
48616
+ * if it fits within the width, otherwise with line breaks.
48617
+ */
48618
+ function group(doc) {
48619
+ return chooseBetween(flatten(doc), doc);
48620
+ }
48621
+ /**
48622
+ * Creates a choice between two alternative layouts.
48623
+ * The formatter tries `doc1`; if it does not fit within
48624
+ * the line width, it falls back to `doc2`.
48625
+ */
48626
+ function chooseBetween(doc1, doc2) {
48627
+ return { type: "chooseBetween", doc1, doc2 };
48628
+ }
48629
+ /**
48630
+ * Flattens a doc into its single-line form.
48631
+ */
48632
+ function flatten(doc) {
48633
+ if (typeof doc === "string") {
48634
+ return doc;
48635
+ }
48636
+ if (doc.type === "chooseBetween") {
48637
+ // Normally should be "chooseBetween(flatten(doc.doc1), flatten(doc.doc2))",
48638
+ // but this is simplified for performance reasons.
48639
+ return flatten(doc.doc1);
48640
+ }
48641
+ if (doc.type === "concat") {
48642
+ return concat(doc.docs.map(flatten));
48643
+ }
48644
+ if (doc.type === "nest") {
48645
+ return {
48646
+ type: "nest",
48647
+ indentLevel: doc.indentLevel,
48648
+ doc: flatten(doc.doc),
48649
+ };
48650
+ }
48651
+ if (doc.type === "insertLine") {
48652
+ return "";
48653
+ }
48654
+ return doc;
48655
+ }
48656
+ const getIndentationString = memoize(function getIndentationString(indentLevel) {
48657
+ return "\n" + "\t".repeat(indentLevel);
48658
+ });
48659
+ /**
48660
+ * Converts a `Doc` into a string representation that fits within
48661
+ * the specified width.
48662
+ */
48663
+ function print(doc, width) {
48664
+ return stringify(selectBestLayout(width, doc));
48665
+ }
48666
+ /**
48667
+ * Join all segments of a LinkedString into the final string.
48668
+ */
48669
+ function stringify(linkedString) {
48670
+ let result = "";
48671
+ while (linkedString) {
48672
+ result += linkedString.subString;
48673
+ linkedString = linkedString.next;
48674
+ }
48675
+ return result;
48676
+ }
48677
+ /**
48678
+ * Layout selection for a `Doc` that fits within the given width.
48679
+ */
48680
+ function selectBestLayout(width, doc) {
48681
+ const head = {
48682
+ indentLevel: 0,
48683
+ doc,
48684
+ next: null,
48685
+ };
48686
+ return _selectBestLayout(width, 0, head);
48687
+ }
48688
+ function _selectBestLayout(width, currentIndentLevel, head) {
48689
+ if (head === null) {
48690
+ return null;
48691
+ }
48692
+ const { indentLevel, doc, next } = head;
48693
+ if (typeof doc === "string") {
48694
+ return {
48695
+ subString: doc,
48696
+ next: _selectBestLayout(width, currentIndentLevel + doc.length, next),
48697
+ };
48698
+ }
48699
+ if (doc.type === "concat") {
48700
+ let newHead = next;
48701
+ for (let i = doc.docs.length - 1; i >= 0; i--) {
48702
+ newHead = { indentLevel, doc: doc.docs[i], next: newHead };
48703
+ }
48704
+ return _selectBestLayout(width, currentIndentLevel, newHead);
48705
+ }
48706
+ if (doc.type === "nest") {
48707
+ return _selectBestLayout(width, currentIndentLevel, {
48708
+ indentLevel: indentLevel + doc.indentLevel,
48709
+ doc: doc.doc,
48710
+ next,
48711
+ });
48712
+ }
48713
+ if (doc.type === "insertLine") {
48714
+ return {
48715
+ subString: getIndentationString(indentLevel),
48716
+ next: _selectBestLayout(width, indentLevel, next),
48717
+ };
48718
+ }
48719
+ if (doc.type === "chooseBetween") {
48720
+ const head1 = { indentLevel, doc: doc.doc1, next };
48721
+ const possibleLinkedString = _selectBestLayout(width, currentIndentLevel, head1);
48722
+ if (fits(width - currentIndentLevel, possibleLinkedString)) {
48723
+ return possibleLinkedString;
48724
+ }
48725
+ const head2 = { indentLevel, doc: doc.doc2, next };
48726
+ return _selectBestLayout(width, currentIndentLevel, head2);
48727
+ }
48728
+ return null;
48729
+ }
48730
+ /**
48731
+ * Check if a layout fits on a single line within width.
48732
+ */
48733
+ function fits(width, linkedString) {
48734
+ while (linkedString) {
48735
+ if (linkedString.subString[0] === "\n") {
48736
+ return true;
48737
+ }
48738
+ width -= linkedString.subString.length;
48739
+ if (width < 0)
48740
+ return false;
48741
+ linkedString = linkedString.next;
48742
+ }
48743
+ return true;
48744
+ }
48745
+ function astToDoc(ast) {
48746
+ switch (ast.type) {
48747
+ case "NUMBER":
48748
+ return String(ast.value);
48749
+ case "STRING":
48750
+ return `"${ast.value}"`;
48751
+ case "BOOLEAN":
48752
+ return ast.value ? "TRUE" : "FALSE";
48753
+ case "REFERENCE":
48754
+ return ast.value;
48755
+ case "FUNCALL":
48756
+ const docs = ast.args.map(astToDoc);
48757
+ return wrapInParentheses(concat(docs.map((doc, i) => (i < 1 ? doc : concat([", ", line(), doc])))), ast.value);
48758
+ case "UNARY_OPERATION":
48759
+ const operandDoc = astToDoc(ast.operand);
48760
+ const needParenthesis = ast.postfix
48761
+ ? leftOperandNeedsParenthesis(ast)
48762
+ : rightOperandNeedsParenthesis(ast);
48763
+ const finalOperandDoc = needParenthesis ? wrapInParentheses(operandDoc) : operandDoc;
48764
+ return ast.postfix
48765
+ ? concat([finalOperandDoc, ast.value])
48766
+ : concat([ast.value, finalOperandDoc]);
48767
+ case "BIN_OPERATION": {
48768
+ const leftDoc = astToDoc(ast.left);
48769
+ const needParenthesisLeftDoc = leftOperandNeedsParenthesis(ast);
48770
+ const finalLeftDoc = needParenthesisLeftDoc ? wrapInParentheses(leftDoc) : leftDoc;
48771
+ const rightDoc = astToDoc(ast.right);
48772
+ const needParenthesisRightDoc = rightOperandNeedsParenthesis(ast);
48773
+ const finalRightDoc = needParenthesisRightDoc ? wrapInParentheses(rightDoc) : rightDoc;
48774
+ const operator = `${ast.value}`;
48775
+ return group(concat([finalLeftDoc, operator, nest(1, concat([line(), finalRightDoc]))]));
48776
+ }
48777
+ case "SYMBOL":
48778
+ return ast.value;
48779
+ case "EMPTY":
48780
+ return "";
48781
+ }
48782
+ }
48783
+ /**
48784
+ * Wraps a `Doc` in parentheses (with optional function name).
48785
+ */
48786
+ function wrapInParentheses(doc, functionName = undefined) {
48787
+ const docToConcat = ["(", nest(1, concat([line(), doc])), line(), ")"];
48788
+ if (functionName) {
48789
+ docToConcat.unshift(functionName);
48790
+ }
48791
+ return group(concat(docToConcat));
48792
+ }
48793
+ /**
48794
+ * Converts an ast formula to the corresponding string
48795
+ */
48796
+ function astToFormula(ast) {
48797
+ switch (ast.type) {
48798
+ case "FUNCALL":
48799
+ const args = ast.args.map((arg) => astToFormula(arg));
48800
+ return `${ast.value}(${args.join(",")})`;
48801
+ case "NUMBER":
48802
+ return ast.value.toString();
48803
+ case "REFERENCE":
48804
+ return ast.value;
48805
+ case "STRING":
48806
+ return `"${ast.value}"`;
48807
+ case "BOOLEAN":
48808
+ return ast.value ? "TRUE" : "FALSE";
48809
+ case "UNARY_OPERATION":
48810
+ if (ast.postfix) {
48811
+ const leftOperand = leftOperandNeedsParenthesis(ast)
48812
+ ? `(${astToFormula(ast.operand)})`
48813
+ : astToFormula(ast.operand);
48814
+ return leftOperand + ast.value;
48815
+ }
48816
+ const rightOperand = rightOperandNeedsParenthesis(ast)
48817
+ ? `(${astToFormula(ast.operand)})`
48818
+ : astToFormula(ast.operand);
48819
+ return ast.value + rightOperand;
48820
+ case "BIN_OPERATION":
48821
+ const leftOperation = leftOperandNeedsParenthesis(ast)
48822
+ ? `(${astToFormula(ast.left)})`
48823
+ : astToFormula(ast.left);
48824
+ const rightOperation = rightOperandNeedsParenthesis(ast)
48825
+ ? `(${astToFormula(ast.right)})`
48826
+ : astToFormula(ast.right);
48827
+ return leftOperation + ast.value + rightOperation;
48828
+ default:
48829
+ return ast.value;
48830
+ }
48831
+ }
48832
+ function leftOperandNeedsParenthesis(operationAST) {
48833
+ const mainOperator = operationAST.value;
48834
+ const leftOperation = "left" in operationAST ? operationAST.left : operationAST.operand;
48835
+ const leftOperator = leftOperation.value;
48836
+ return (leftOperation.type === "BIN_OPERATION" && OP_PRIORITY[leftOperator] < OP_PRIORITY[mainOperator]);
48837
+ }
48838
+ function rightOperandNeedsParenthesis(operationAST) {
48839
+ const mainOperator = operationAST.value;
48840
+ const rightOperation = "right" in operationAST ? operationAST.right : operationAST.operand;
48841
+ const rightPriority = OP_PRIORITY[rightOperation.value];
48842
+ const mainPriority = OP_PRIORITY[mainOperator];
48843
+ if (rightOperation.type !== "BIN_OPERATION") {
48844
+ return false;
48845
+ }
48846
+ if (rightPriority < mainPriority) {
48847
+ return true;
48848
+ }
48849
+ return rightPriority === mainPriority && !ASSOCIATIVE_OPERATORS.includes(mainOperator);
48850
+ }
48851
+
48655
48852
  const CELL_DELETED_MESSAGE = _t("The cell you are trying to edit has been deleted.");
48656
48853
  class CellComposerStore extends AbstractComposerStore {
48657
48854
  canStopEdition() {
@@ -48795,7 +48992,10 @@ class CellComposerStore extends AbstractComposerStore {
48795
48992
  const locale = this.getters.getLocale();
48796
48993
  const cell = this.getters.getCell(position);
48797
48994
  if (cell?.isFormula) {
48798
- return localizeFormula(cell.content, locale);
48995
+ const prettifiedContent = cell.compiledFormula.isBadExpression
48996
+ ? cell.content
48997
+ : prettify(parseTokens(cell.compiledFormula.tokens), 80);
48998
+ return localizeFormula(prettifiedContent, locale);
48799
48999
  }
48800
49000
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
48801
49001
  if (spreader) {
@@ -61793,10 +61993,10 @@ class CellPlugin extends CorePlugin {
61793
61993
  */
61794
61994
  getFormulaString(sheetId, tokens, dependencies, useBoundedReference = false) {
61795
61995
  if (!dependencies.length) {
61796
- return concat(tokens.map((token) => token.value));
61996
+ return concat$1(tokens.map((token) => token.value));
61797
61997
  }
61798
61998
  let rangeIndex = 0;
61799
- return concat(tokens.map((token) => {
61999
+ return concat$1(tokens.map((token) => {
61800
62000
  if (token.type === "REFERENCE") {
61801
62001
  const range = dependencies[rangeIndex++];
61802
62002
  return this.getters.getRangeString(range, sheetId, { useBoundedReference });
@@ -62071,11 +62271,11 @@ class FormulaCellWithDependencies {
62071
62271
  };
62072
62272
  }
62073
62273
  get content() {
62074
- return concat(this.compiledFormula.tokens.map((token) => token.value));
62274
+ return concat$1(this.compiledFormula.tokens.map((token) => token.value));
62075
62275
  }
62076
62276
  get contentWithFixedReferences() {
62077
62277
  let rangeIndex = 0;
62078
- return concat(this.compiledFormula.tokens.map((token) => {
62278
+ return concat$1(this.compiledFormula.tokens.map((token) => {
62079
62279
  if (token.type === "REFERENCE") {
62080
62280
  const index = rangeIndex++;
62081
62281
  return this.getRangeString(this.compiledFormula.dependencies[index], this.sheetId, {
@@ -81235,7 +81435,7 @@ class SmallBottomBar extends Component {
81235
81435
  }
81236
81436
  }
81237
81437
 
81238
- const COMPOSER_MAX_HEIGHT = 100;
81438
+ const COMPOSER_MAX_HEIGHT = 300;
81239
81439
  /* svg free of use from https://uxwing.com/formula-fx-icon/ */
81240
81440
  const FX_SVG = /*xml*/ `
81241
81441
  <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
@@ -87430,6 +87630,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
87430
87630
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
87431
87631
 
87432
87632
 
87433
- __info__.version = "18.5.0-alpha.10";
87434
- __info__.date = "2025-08-21T06:42:10.830Z";
87435
- __info__.hash = "5800d1b";
87633
+ __info__.version = "18.5.0-alpha.11";
87634
+ __info__.date = "2025-08-26T10:14:05.357Z";
87635
+ __info__.hash = "b913e49";