@odoo/o-spreadsheet 18.2.0-alpha.3 → 18.2.0-alpha.5

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.2.0-alpha.3
6
- * @date 2025-01-27T10:07:28.716Z
7
- * @hash 63a13e5
5
+ * @version 18.2.0-alpha.5
6
+ * @date 2025-01-31T07:59:30.667Z
7
+ * @hash efce841
8
8
  */
9
9
 
10
10
  'use strict';
@@ -3361,11 +3361,11 @@ const getInvaluableSymbolsRegexp = memoize(function getInvaluableSymbolsRegexp(l
3361
3361
  * number from the point of view of the isNumber function.
3362
3362
  */
3363
3363
  function parseNumber(str, locale) {
3364
+ // remove invaluable characters
3365
+ str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3364
3366
  if (locale.decimalSeparator !== ".") {
3365
3367
  str = str.replace(locale.decimalSeparator, ".");
3366
3368
  }
3367
- // remove invaluable characters
3368
- str = str.replace(getInvaluableSymbolsRegexp(locale), "");
3369
3369
  let n = Number(str);
3370
3370
  if (isNaN(n) && str.includes("%")) {
3371
3371
  n = Number(str.split("%")[0]);
@@ -32538,36 +32538,58 @@ class ErrorToolTip extends owl.Component {
32538
32538
  static maxSize = { maxHeight: ERROR_TOOLTIP_MAX_HEIGHT };
32539
32539
  static template = "o-spreadsheet-ErrorToolTip";
32540
32540
  static props = {
32541
- errors: Array,
32541
+ cellPosition: Object,
32542
32542
  onClosed: { type: Function, optional: true },
32543
32543
  };
32544
+ get dataValidationErrorMessage() {
32545
+ return this.env.model.getters.getInvalidDataValidationMessage(this.props.cellPosition);
32546
+ }
32547
+ get evaluationError() {
32548
+ const cell = this.env.model.getters.getEvaluatedCell(this.props.cellPosition);
32549
+ if (cell.message) {
32550
+ return cell;
32551
+ }
32552
+ return undefined;
32553
+ }
32554
+ get errorOriginPositionString() {
32555
+ const evaluationError = this.evaluationError;
32556
+ const position = evaluationError?.errorOriginPosition;
32557
+ if (!position || deepEquals(position, this.props.cellPosition)) {
32558
+ return "";
32559
+ }
32560
+ const sheetId = position.sheetId;
32561
+ return this.env.model.getters.getRangeString(this.env.model.getters.getRangeFromZone(sheetId, positionToZone(position)), this.env.model.getters.getActiveSheetId());
32562
+ }
32563
+ selectCell() {
32564
+ const position = this.evaluationError?.errorOriginPosition;
32565
+ if (!position) {
32566
+ return;
32567
+ }
32568
+ const activeSheetId = this.env.model.getters.getActiveSheetId();
32569
+ if (position.sheetId !== activeSheetId) {
32570
+ this.env.model.dispatch("ACTIVATE_SHEET", {
32571
+ sheetIdFrom: activeSheetId,
32572
+ sheetIdTo: position.sheetId,
32573
+ });
32574
+ }
32575
+ this.env.model.selection.selectCell(position.col, position.row);
32576
+ }
32544
32577
  }
32545
32578
  const ErrorToolTipPopoverBuilder = {
32546
32579
  onHover: (position, getters) => {
32547
32580
  const cell = getters.getEvaluatedCell(position);
32548
- const errors = [];
32549
- if (cell.type === CellValueType.error && !!cell.message) {
32550
- errors.push({
32551
- title: _t("Error"),
32552
- message: cell.message,
32553
- });
32554
- }
32555
- const validationErrorMessage = getters.getInvalidDataValidationMessage(position);
32556
- if (validationErrorMessage) {
32557
- errors.push({
32558
- title: _t("Invalid"),
32559
- message: validationErrorMessage,
32560
- });
32561
- }
32562
- if (!errors.length) {
32563
- return { isOpen: false };
32581
+ if ((cell.type === CellValueType.error && !!cell.message) ||
32582
+ getters.getInvalidDataValidationMessage(position)) {
32583
+ return {
32584
+ isOpen: true,
32585
+ props: {
32586
+ cellPosition: position,
32587
+ },
32588
+ Component: ErrorToolTip,
32589
+ cellCorner: "TopRight",
32590
+ };
32564
32591
  }
32565
- return {
32566
- isOpen: true,
32567
- props: { errors: errors },
32568
- Component: ErrorToolTip,
32569
- cellCorner: "TopRight",
32570
- };
32592
+ return { isOpen: false };
32571
32593
  },
32572
32594
  };
32573
32595
 
@@ -33131,6 +33153,100 @@ function* iterateChildren(el) {
33131
33153
  function getOpenedMenus() {
33132
33154
  return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
33133
33155
  }
33156
+ function getCurrentSelection(el) {
33157
+ let { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
33158
+ let startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
33159
+ let endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
33160
+ return {
33161
+ start: startSizeBefore,
33162
+ end: endSizeBefore,
33163
+ };
33164
+ }
33165
+ function getStartAndEndSelection(el) {
33166
+ const selection = document.getSelection();
33167
+ return {
33168
+ startElement: selection.anchorNode || el,
33169
+ startSelectionOffset: selection.anchorOffset,
33170
+ endElement: selection.focusNode || el,
33171
+ endSelectionOffset: selection.focusOffset,
33172
+ };
33173
+ }
33174
+ /**
33175
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
33176
+ * The selected node is either a Text node or an Element node.
33177
+ *
33178
+ * case 1 -Text node:
33179
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
33180
+ * content length of all previous nodes.
33181
+ *
33182
+ * case 2 - Element node:
33183
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
33184
+ * all the nodes prior to the selected node as well as the content of the child node before the offset.
33185
+ *
33186
+ * See the MDN documentation for more details.
33187
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
33188
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
33189
+ *
33190
+ */
33191
+ function findSelectionIndex(el, nodeToFind, nodeOffset) {
33192
+ let usedCharacters = 0;
33193
+ let it = iterateChildren(el);
33194
+ let current = it.next();
33195
+ let isFirstParagraph = true;
33196
+ while (!current.done && current.value !== nodeToFind) {
33197
+ if (!current.value.hasChildNodes()) {
33198
+ if (current.value.textContent) {
33199
+ usedCharacters += current.value.textContent.length;
33200
+ }
33201
+ }
33202
+ // One new paragraph = one new line character, except for the first paragraph
33203
+ if (current.value.nodeName === "P" ||
33204
+ (current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
33205
+ ) {
33206
+ if (isFirstParagraph) {
33207
+ isFirstParagraph = false;
33208
+ }
33209
+ else {
33210
+ usedCharacters++;
33211
+ }
33212
+ }
33213
+ current = it.next();
33214
+ }
33215
+ if (current.value !== nodeToFind) {
33216
+ /** This situation can happen if the code is called while the selection is not currently on the element.
33217
+ * In this case, we return 0 because we don't know the size of the text before the selection.
33218
+ *
33219
+ * A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
33220
+ */
33221
+ return 0;
33222
+ }
33223
+ else {
33224
+ if (!current.value.hasChildNodes()) {
33225
+ usedCharacters += nodeOffset;
33226
+ }
33227
+ else {
33228
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
33229
+ usedCharacters += children.reduce((acc, child, index) => {
33230
+ if (child.textContent !== null) {
33231
+ // need to account for paragraph nodes that implicitly add a new line
33232
+ // except for the last paragraph
33233
+ let chars = child.textContent.length;
33234
+ if (child.nodeName === "P" && index !== children.length - 1) {
33235
+ chars++;
33236
+ }
33237
+ return acc + chars;
33238
+ }
33239
+ else {
33240
+ return acc;
33241
+ }
33242
+ }, 0);
33243
+ }
33244
+ }
33245
+ if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
33246
+ usedCharacters++;
33247
+ }
33248
+ return usedCharacters;
33249
+ }
33134
33250
  const letterRegex = /^[a-zA-Z]$/;
33135
33251
  /**
33136
33252
  * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
@@ -40096,14 +40212,21 @@ class ChartTypePicker extends owl.Component {
40096
40212
  class MainChartPanelStore extends SpreadsheetStore {
40097
40213
  mutators = ["activatePanel", "changeChartType"];
40098
40214
  panel = "configuration";
40099
- creationContext = {};
40215
+ creationContexts = {};
40100
40216
  activatePanel(panel) {
40101
40217
  this.panel = panel;
40102
40218
  }
40103
40219
  changeChartType(figureId, newDisplayType) {
40104
- this.creationContext = {
40105
- ...this.creationContext,
40106
- ...this.getters.getContextCreationChart(figureId),
40220
+ const currentCreationContext = this.getters.getContextCreationChart(figureId);
40221
+ const savedCreationContext = this.creationContexts[figureId] || {};
40222
+ let newRanges = currentCreationContext?.range;
40223
+ if (newRanges?.every((range, i) => deepEquals(range, savedCreationContext.range?.[i]))) {
40224
+ newRanges = Object.assign([], savedCreationContext.range, currentCreationContext?.range);
40225
+ }
40226
+ this.creationContexts[figureId] = {
40227
+ ...savedCreationContext,
40228
+ ...currentCreationContext,
40229
+ range: newRanges,
40107
40230
  };
40108
40231
  const sheetId = this.getters.getFigureSheetId(figureId);
40109
40232
  if (!sheetId) {
@@ -40119,12 +40242,8 @@ class MainChartPanelStore extends SpreadsheetStore {
40119
40242
  getChartDefinitionFromContextCreation(figureId, newDisplayType) {
40120
40243
  const newChartInfo = chartSubtypeRegistry.get(newDisplayType);
40121
40244
  const ChartClass = chartRegistry.get(newChartInfo.chartType);
40122
- const contextCreation = {
40123
- ...this.creationContext,
40124
- ...this.getters.getContextCreationChart(figureId),
40125
- };
40126
40245
  return {
40127
- ...ChartClass.getChartDefinitionFromContextCreation(contextCreation),
40246
+ ...ChartClass.getChartDefinitionFromContextCreation(this.creationContexts[figureId]),
40128
40247
  ...newChartInfo.subtypeDefinition,
40129
40248
  };
40130
40249
  }
@@ -40335,6 +40454,10 @@ class ContentEditableHelper {
40335
40454
  if (currentStart === start && currentEnd === end) {
40336
40455
  return;
40337
40456
  }
40457
+ if (selection.rangeCount === 0) {
40458
+ const range = document.createRange();
40459
+ selection.addRange(range);
40460
+ }
40338
40461
  const currentRange = selection.getRangeAt(0);
40339
40462
  let range;
40340
40463
  if (this.el.contains(currentRange.startContainer)) {
@@ -40497,7 +40620,7 @@ class ContentEditableHelper {
40497
40620
  if (!focusedNode || !this.el.contains(focusedNode))
40498
40621
  return;
40499
40622
  const element = focusedNode instanceof HTMLElement ? focusedNode : focusedNode.parentElement;
40500
- element?.scrollIntoView({ block: "nearest" });
40623
+ element?.scrollIntoView?.({ block: "nearest" });
40501
40624
  }
40502
40625
  /**
40503
40626
  * remove the current selection of the user
@@ -40517,100 +40640,7 @@ class ContentEditableHelper {
40517
40640
  * finds the indexes of the current selection.
40518
40641
  * */
40519
40642
  getCurrentSelection() {
40520
- let { startElement, endElement, startSelectionOffset, endSelectionOffset } = this.getStartAndEndSelection();
40521
- let startSizeBefore = this.findSelectionIndex(startElement, startSelectionOffset);
40522
- let endSizeBefore = this.findSelectionIndex(endElement, endSelectionOffset);
40523
- return {
40524
- start: startSizeBefore,
40525
- end: endSizeBefore,
40526
- };
40527
- }
40528
- /**
40529
- * Computes the text 'index' inside this.el based on the currently selected node and its offset.
40530
- * The selected node is either a Text node or an Element node.
40531
- *
40532
- * case 1 -Text node:
40533
- * the offset is the number of characters from the start of the node. We have to add this offset to the
40534
- * content length of all previous nodes.
40535
- *
40536
- * case 2 - Element node:
40537
- * the offset is the number of child nodes before the selected node. We have to add the content length of
40538
- * all the bnodes prior to the selected node as well as the content of the child node before the offset.
40539
- *
40540
- * See the MDN documentation for more details.
40541
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
40542
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
40543
- *
40544
- */
40545
- findSelectionIndex(nodeToFind, nodeOffset) {
40546
- let usedCharacters = 0;
40547
- let it = iterateChildren(this.el);
40548
- let current = it.next();
40549
- let isFirstParagraph = true;
40550
- while (!current.done && current.value !== nodeToFind) {
40551
- if (!current.value.hasChildNodes()) {
40552
- if (current.value.textContent) {
40553
- usedCharacters += current.value.textContent.length;
40554
- }
40555
- }
40556
- // One new paragraph = one new line character, except for the first paragraph
40557
- if (current.value.nodeName === "P" ||
40558
- (current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
40559
- ) {
40560
- if (isFirstParagraph) {
40561
- isFirstParagraph = false;
40562
- }
40563
- else {
40564
- usedCharacters++;
40565
- }
40566
- }
40567
- current = it.next();
40568
- }
40569
- if (current.value !== nodeToFind) {
40570
- /** This situation can happen if the code is called while the selection is not currently on the ContentEditableHelper.
40571
- * In this case, we return 0 because we don't know the size of the text before the selection.
40572
- *
40573
- * A known occurence is triggered since the introduction of commit d4663158 (PR #2038).
40574
- *
40575
- * FIXME: find a way to test eventhough the selection API is not available in jsDOM.
40576
- */
40577
- return 0;
40578
- }
40579
- else {
40580
- if (!current.value.hasChildNodes()) {
40581
- usedCharacters += nodeOffset;
40582
- }
40583
- else {
40584
- const children = [...current.value.childNodes].slice(0, nodeOffset);
40585
- usedCharacters += children.reduce((acc, child, index) => {
40586
- if (child.textContent !== null) {
40587
- // need to account for paragraph nodes that implicitely add a new line
40588
- // except for the last paragraph
40589
- let chars = child.textContent.length;
40590
- if (child.nodeName === "P" && index !== children.length - 1) {
40591
- chars++;
40592
- }
40593
- return acc + chars;
40594
- }
40595
- else {
40596
- return acc;
40597
- }
40598
- }, 0);
40599
- }
40600
- }
40601
- if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
40602
- usedCharacters++;
40603
- }
40604
- return usedCharacters;
40605
- }
40606
- getStartAndEndSelection() {
40607
- const selection = document.getSelection();
40608
- return {
40609
- startElement: selection.anchorNode || this.el,
40610
- startSelectionOffset: selection.anchorOffset,
40611
- endElement: selection.focusNode || this.el,
40612
- endSelectionOffset: selection.focusOffset,
40613
- };
40643
+ return getCurrentSelection(this.el);
40614
40644
  }
40615
40645
  getText() {
40616
40646
  let text = "";
@@ -40874,6 +40904,12 @@ class Composer extends owl.Component {
40874
40904
  }
40875
40905
  this.contentHelper.updateEl(el);
40876
40906
  });
40907
+ this.env.model.selection.observe(this, {
40908
+ handleEvent: () => this.autoCompleteState.hide(),
40909
+ });
40910
+ owl.onWillUnmount(() => {
40911
+ this.env.model.selection.detachObserver(this);
40912
+ });
40877
40913
  owl.useEffect(() => {
40878
40914
  this.processContent();
40879
40915
  if (document.activeElement === this.contentHelper.el &&
@@ -49213,7 +49249,7 @@ function isAxisVisible(getters, figure, axis) {
49213
49249
  axisStartEndPositions.push({ x: axis.position, y: figure.y + figure.height });
49214
49250
  break;
49215
49251
  }
49216
- return axisStartEndPositions.some(getters.isPositionVisible);
49252
+ return axisStartEndPositions.some(getters.isPixelPositionVisible);
49217
49253
  }
49218
49254
  /**
49219
49255
  * Get a snap line for the given figure, if the figure can snap to any other figure
@@ -51240,8 +51276,8 @@ class GridRenderer {
51240
51276
  previousColIndex = col;
51241
51277
  }
51242
51278
  else {
51243
- nextColIndex = this.findNextEmptyCol(col, right, row);
51244
- previousColIndex = this.findPreviousEmptyCol(col, left, row);
51279
+ nextColIndex = box.border?.right ? zone.right : this.findNextEmptyCol(col, right, row);
51280
+ previousColIndex = box.border?.left ? zone.left : this.findPreviousEmptyCol(col, left, row);
51245
51281
  box.isOverflow = true;
51246
51282
  }
51247
51283
  switch (align) {
@@ -52714,7 +52750,7 @@ class BordersPlugin extends CorePlugin {
52714
52750
  // map and slice preserve empty values and do not set `undefined` instead
52715
52751
  const bordersCopy = borders
52716
52752
  .slice()
52717
- .map((col) => col?.slice().map((border) => ({ ...border })));
52753
+ .map((col) => col?.slice().map((border) => deepCopy(border)));
52718
52754
  this.history.update("borders", cmd.sheetIdTo, bordersCopy);
52719
52755
  }
52720
52756
  break;
@@ -52744,32 +52780,12 @@ class BordersPlugin extends CorePlugin {
52744
52780
  const elements = [...cmd.elements].sort((a, b) => b - a);
52745
52781
  for (const group of groupConsecutive(elements)) {
52746
52782
  if (cmd.dimension === "COL") {
52747
- if (group[0] >= this.getters.getNumberCols(cmd.sheetId)) {
52748
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52749
- this.history.update("borders", cmd.sheetId, group[0] + 1, row, "vertical", undefined);
52750
- }
52751
- }
52752
- if (group[group.length - 1] === 0) {
52753
- for (let row = 0; row < this.getters.getNumberRows(cmd.sheetId); row++) {
52754
- this.history.update("borders", cmd.sheetId, 0, row, "vertical", undefined);
52755
- }
52756
- }
52757
- const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52783
+ const zone = this.getters.getColsZone(cmd.sheetId, group[group.length - 1], group[0]);
52758
52784
  this.clearInsideBorders(cmd.sheetId, [zone]);
52759
52785
  this.shiftBordersHorizontally(cmd.sheetId, group[0] + 1, -group.length);
52760
52786
  }
52761
52787
  else {
52762
- if (group[0] >= this.getters.getNumberRows(cmd.sheetId)) {
52763
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52764
- this.history.update("borders", cmd.sheetId, col, group[0] + 1, "horizontal", undefined);
52765
- }
52766
- }
52767
- if (group[group.length - 1] === 0) {
52768
- for (let col = 0; col < this.getters.getNumberCols(cmd.sheetId); col++) {
52769
- this.history.update("borders", cmd.sheetId, col, 0, "horizontal", undefined);
52770
- }
52771
- }
52772
- const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1] + 1, group[0]);
52788
+ const zone = this.getters.getRowsZone(cmd.sheetId, group[group.length - 1], group[0]);
52773
52789
  this.clearInsideBorders(cmd.sheetId, [zone]);
52774
52790
  this.shiftBordersVertically(cmd.sheetId, group[0] + 1, -group.length);
52775
52791
  }
@@ -52794,16 +52810,12 @@ class BordersPlugin extends CorePlugin {
52794
52810
  let colLeftOfInsertion;
52795
52811
  let colRightOfInsertion;
52796
52812
  if (cmd.position === "before") {
52797
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity, {
52798
- moveFirstLeftBorder: true,
52799
- });
52813
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base, cmd.quantity);
52800
52814
  colLeftOfInsertion = cmd.base - 1;
52801
52815
  colRightOfInsertion = cmd.base + cmd.quantity;
52802
52816
  }
52803
52817
  else {
52804
- this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52805
- moveFirstLeftBorder: false,
52806
- });
52818
+ this.shiftBordersHorizontally(cmd.sheetId, cmd.base + 1, cmd.quantity);
52807
52819
  colLeftOfInsertion = cmd.base;
52808
52820
  colRightOfInsertion = cmd.base + cmd.quantity + 1;
52809
52821
  }
@@ -52818,16 +52830,12 @@ class BordersPlugin extends CorePlugin {
52818
52830
  let rowAboveInsertion;
52819
52831
  let rowBelowInsertion;
52820
52832
  if (cmd.position === "before") {
52821
- this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity, {
52822
- moveFirstTopBorder: true,
52823
- });
52833
+ this.shiftBordersVertically(cmd.sheetId, cmd.base, cmd.quantity);
52824
52834
  rowAboveInsertion = cmd.base - 1;
52825
52835
  rowBelowInsertion = cmd.base + cmd.quantity;
52826
52836
  }
52827
52837
  else {
52828
- this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity, {
52829
- moveFirstTopBorder: false,
52830
- });
52838
+ this.shiftBordersVertically(cmd.sheetId, cmd.base + 1, cmd.quantity);
52831
52839
  rowAboveInsertion = cmd.base;
52832
52840
  rowBelowInsertion = cmd.base + cmd.quantity + 1;
52833
52841
  }
@@ -52837,16 +52845,8 @@ class BordersPlugin extends CorePlugin {
52837
52845
  // Getters
52838
52846
  // ---------------------------------------------------------------------------
52839
52847
  getCellBorder({ sheetId, col, row }) {
52840
- const border = {
52841
- top: this.borders[sheetId]?.[col]?.[row]?.horizontal,
52842
- bottom: this.borders[sheetId]?.[col]?.[row + 1]?.horizontal,
52843
- left: this.borders[sheetId]?.[col]?.[row]?.vertical,
52844
- right: this.borders[sheetId]?.[col + 1]?.[row]?.vertical,
52845
- };
52846
- if (!border.bottom && !border.left && !border.right && !border.top) {
52847
- return null;
52848
- }
52849
- return border;
52848
+ const border = this.borders[sheetId]?.[col]?.[row];
52849
+ return border?.top || border?.bottom || border?.left || border?.right ? deepCopy(border) : null;
52850
52850
  }
52851
52851
  getBordersColors(sheetId) {
52852
52852
  const colors = [];
@@ -52854,11 +52854,13 @@ class BordersPlugin extends CorePlugin {
52854
52854
  if (sheetBorders) {
52855
52855
  for (const borders of sheetBorders.filter(isDefined)) {
52856
52856
  for (const cellBorder of borders) {
52857
- if (cellBorder?.horizontal) {
52858
- colors.push(cellBorder.horizontal.color);
52859
- }
52860
- if (cellBorder?.vertical) {
52861
- colors.push(cellBorder.vertical.color);
52857
+ if (cellBorder) {
52858
+ for (const direction of ["top", "bottom", "left", "right"]) {
52859
+ const color = cellBorder[direction]?.color;
52860
+ if (color) {
52861
+ colors.push(color);
52862
+ }
52863
+ }
52862
52864
  }
52863
52865
  }
52864
52866
  }
@@ -52911,7 +52913,7 @@ class BordersPlugin extends CorePlugin {
52911
52913
  getCommonSides(border1, border2) {
52912
52914
  const commonBorder = {};
52913
52915
  for (let side of ["top", "bottom", "left", "right"]) {
52914
- if (border1[side] && border1[side] === border2[side]) {
52916
+ if (border1[side] && deepEquals(border1[side], border2[side])) {
52915
52917
  commonBorder[side] = border1[side];
52916
52918
  }
52917
52919
  }
@@ -52956,23 +52958,15 @@ class BordersPlugin extends CorePlugin {
52956
52958
  * @param start starting column (included)
52957
52959
  * @param delta how much borders will be moved (negative if moved to the left)
52958
52960
  */
52959
- shiftBordersHorizontally(sheetId, start, delta, { moveFirstLeftBorder } = {}) {
52961
+ shiftBordersHorizontally(sheetId, start, delta) {
52960
52962
  const borders = this.borders[sheetId];
52961
52963
  if (!borders)
52962
52964
  return;
52963
- if (delta < 0) {
52964
- this.moveBordersOfColumn(sheetId, start, delta, "vertical", {
52965
- destructive: false,
52966
- });
52967
- }
52968
52965
  this.getColumnsWithBorders(sheetId)
52969
52966
  .filter((col) => col >= start)
52970
52967
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52971
52968
  .forEach((col) => {
52972
- if ((col === start && moveFirstLeftBorder) || col !== start) {
52973
- this.moveBordersOfColumn(sheetId, col, delta, "vertical");
52974
- }
52975
- this.moveBordersOfColumn(sheetId, col, delta, "horizontal");
52969
+ this.moveBordersOfColumn(sheetId, col, delta);
52976
52970
  });
52977
52971
  }
52978
52972
  /**
@@ -52981,12 +52975,12 @@ class BordersPlugin extends CorePlugin {
52981
52975
  * @param start starting row (included)
52982
52976
  * @param delta how much borders will be moved (negative if moved to the above)
52983
52977
  */
52984
- shiftBordersVertically(sheetId, start, delta, { moveFirstTopBorder } = {}) {
52978
+ shiftBordersVertically(sheetId, start, delta) {
52985
52979
  const borders = this.borders[sheetId];
52986
52980
  if (!borders)
52987
52981
  return;
52988
52982
  if (delta < 0) {
52989
- this.moveBordersOfRow(sheetId, start, delta, "horizontal", {
52983
+ this.moveBordersOfRow(sheetId, start, delta, {
52990
52984
  destructive: false,
52991
52985
  });
52992
52986
  }
@@ -52994,10 +52988,7 @@ class BordersPlugin extends CorePlugin {
52994
52988
  .filter((row) => row >= start)
52995
52989
  .sort((a, b) => (delta < 0 ? a - b : b - a)) // start by the end when moving up
52996
52990
  .forEach((row) => {
52997
- if ((row === start && moveFirstTopBorder) || row !== start) {
52998
- this.moveBordersOfRow(sheetId, row, delta, "horizontal");
52999
- }
53000
- this.moveBordersOfRow(sheetId, row, delta, "vertical");
52991
+ this.moveBordersOfRow(sheetId, row, delta);
53001
52992
  });
53002
52993
  }
53003
52994
  /**
@@ -53011,15 +53002,15 @@ class BordersPlugin extends CorePlugin {
53011
53002
  * argument `destructive` is given false, the target border is preserved if
53012
53003
  * the moved border is empty
53013
53004
  */
53014
- moveBordersOfRow(sheetId, row, delta, borderDirection, { destructive } = { destructive: true }) {
53005
+ moveBordersOfRow(sheetId, row, delta, { destructive } = { destructive: true }) {
53015
53006
  const borders = this.borders[sheetId];
53016
53007
  if (!borders)
53017
53008
  return;
53018
53009
  this.getColumnsWithBorders(sheetId).forEach((col) => {
53019
- const targetBorder = borders[col]?.[row + delta]?.[borderDirection];
53020
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53021
- this.history.update("borders", sheetId, col, row + delta, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53022
- this.history.update("borders", sheetId, col, row, borderDirection, undefined);
53010
+ const targetBorder = borders[col]?.[row + delta];
53011
+ const movedBorder = borders[col]?.[row];
53012
+ this.history.update("borders", sheetId, col, row + delta, destructive ? movedBorder : movedBorder || targetBorder);
53013
+ this.history.update("borders", sheetId, col, row, undefined);
53023
53014
  });
53024
53015
  }
53025
53016
  /**
@@ -53033,15 +53024,17 @@ class BordersPlugin extends CorePlugin {
53033
53024
  * argument `destructive` is given false, the target border is preserved if
53034
53025
  * the moved border is empty
53035
53026
  */
53036
- moveBordersOfColumn(sheetId, col, delta, borderDirection, { destructive } = { destructive: true }) {
53027
+ moveBordersOfColumn(sheetId, col, delta, { destructive } = { destructive: true }) {
53037
53028
  const borders = this.borders[sheetId];
53038
53029
  if (!borders)
53039
53030
  return;
53040
53031
  this.getRowsRange(sheetId).forEach((row) => {
53041
- const targetBorder = borders[col + delta]?.[row]?.[borderDirection];
53042
- const movedBorder = borders[col]?.[row]?.[borderDirection];
53043
- this.history.update("borders", sheetId, col + delta, row, borderDirection, destructive ? movedBorder : movedBorder || targetBorder);
53044
- this.history.update("borders", sheetId, col, row, borderDirection, undefined);
53032
+ const targetBorder = borders[col + delta]?.[row];
53033
+ const movedBorder = borders[col]?.[row];
53034
+ this.history.update("borders", sheetId, col + delta, row, destructive ? movedBorder : movedBorder || targetBorder);
53035
+ if (destructive) {
53036
+ this.history.update("borders", sheetId, col, row, undefined);
53037
+ }
53045
53038
  });
53046
53039
  }
53047
53040
  /**
@@ -53049,33 +53042,69 @@ class BordersPlugin extends CorePlugin {
53049
53042
  * It overrides the current border if override === true.
53050
53043
  */
53051
53044
  setBorder(sheetId, col, row, border, override = true) {
53052
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.vertical) {
53053
- this.history.update("borders", sheetId, col, row, "vertical", border?.left);
53045
+ const maxCol = this.getters.getNumberCols(sheetId) - 1;
53046
+ const maxRow = this.getters.getNumberRows(sheetId) - 1;
53047
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.left) {
53048
+ this.history.update("borders", sheetId, col, row, "left", border?.left);
53049
+ if (border?.left &&
53050
+ col > 0 &&
53051
+ !deepEquals(this.getCellBorder({ sheetId, col: col - 1, row })?.right, border?.left)) {
53052
+ this.history.update("borders", sheetId, col - 1, row, "right", undefined);
53053
+ }
53054
53054
  }
53055
- if (override || !this.borders?.[sheetId]?.[col]?.[row]?.horizontal) {
53056
- this.history.update("borders", sheetId, col, row, "horizontal", border?.top);
53055
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.top) {
53056
+ this.history.update("borders", sheetId, col, row, "top", border?.top);
53057
+ if (border?.top &&
53058
+ row > 0 &&
53059
+ !deepEquals(this.getCellBorder({ sheetId, col, row: row - 1 })?.bottom, border?.top)) {
53060
+ this.history.update("borders", sheetId, col, row - 1, "bottom", undefined);
53061
+ }
53057
53062
  }
53058
- if (override || !this.borders?.[sheetId]?.[col + 1]?.[row]?.vertical) {
53059
- this.history.update("borders", sheetId, col + 1, row, "vertical", border?.right);
53063
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.right) {
53064
+ this.history.update("borders", sheetId, col, row, "right", border?.right);
53065
+ if (border?.right &&
53066
+ col < maxCol &&
53067
+ !deepEquals(this.getCellBorder({ sheetId, col: col + 1, row })?.left, border?.right)) {
53068
+ this.history.update("borders", sheetId, col + 1, row, "left", undefined);
53069
+ }
53060
53070
  }
53061
- if (override || !this.borders?.[sheetId]?.[col]?.[row + 1]?.horizontal) {
53062
- this.history.update("borders", sheetId, col, row + 1, "horizontal", border?.bottom);
53071
+ if (override || !this.borders[sheetId]?.[col]?.[row]?.bottom) {
53072
+ this.history.update("borders", sheetId, col, row, "bottom", border?.bottom);
53073
+ if (border?.bottom &&
53074
+ row < maxRow &&
53075
+ !deepEquals(this.getCellBorder({ sheetId, col, row: row + 1 })?.top, border?.bottom)) {
53076
+ this.history.update("borders", sheetId, col, row + 1, "top", undefined);
53077
+ }
53063
53078
  }
53064
53079
  }
53065
53080
  /**
53066
53081
  * Remove the borders of a zone
53067
53082
  */
53068
- clearBorders(sheetId, zones) {
53083
+ clearBorders(sheetId, zones, eraseBoundaries = false) {
53084
+ const maxCol = this.getters.getNumberCols(sheetId) - 1;
53085
+ const maxRow = this.getters.getNumberRows(sheetId) - 1;
53069
53086
  for (let zone of recomputeZones(zones)) {
53070
53087
  for (let row = zone.top; row <= zone.bottom; row++) {
53071
- this.history.update("borders", sheetId, zone.right + 1, row, "vertical", undefined);
53088
+ if (eraseBoundaries) {
53089
+ if (zone.left > 0) {
53090
+ this.history.update("borders", sheetId, zone.left - 1, row, "right", undefined);
53091
+ }
53092
+ if (zone.right < maxCol) {
53093
+ this.history.update("borders", sheetId, zone.right + 1, row, "left", undefined);
53094
+ }
53095
+ }
53072
53096
  for (let col = zone.left; col <= zone.right; col++) {
53073
53097
  this.history.update("borders", sheetId, col, row, undefined);
53098
+ if (eraseBoundaries) {
53099
+ if (zone.top > 0) {
53100
+ this.history.update("borders", sheetId, col, zone.top - 1, "bottom", undefined);
53101
+ }
53102
+ if (zone.bottom < maxRow) {
53103
+ this.history.update("borders", sheetId, col, zone.bottom + 1, "top", undefined);
53104
+ }
53105
+ }
53074
53106
  }
53075
53107
  }
53076
- for (let col = zone.left; col <= zone.right; col++) {
53077
- this.history.update("borders", sheetId, col, zone.bottom + 1, "horizontal", undefined);
53078
- }
53079
53108
  }
53080
53109
  }
53081
53110
  /**
@@ -53105,41 +53134,63 @@ class BordersPlugin extends CorePlugin {
53105
53134
  */
53106
53135
  setBorders(sheetId, zones, position, border) {
53107
53136
  if (position === "clear") {
53108
- return this.clearBorders(sheetId, zones);
53137
+ return this.clearBorders(sheetId, zones, true);
53109
53138
  }
53110
53139
  for (let zone of recomputeZones(zones)) {
53111
- if (position === "h" || position === "hv" || position === "all") {
53112
- for (let row = zone.top + 1; row <= zone.bottom; row++) {
53140
+ if (position === "all") {
53141
+ for (let row = zone.top; row <= zone.bottom; row++) {
53113
53142
  for (let col = zone.left; col <= zone.right; col++) {
53114
- this.addBorder(sheetId, col, row, { top: border });
53143
+ this.addBorder(sheetId, col, row, {
53144
+ top: border,
53145
+ right: border,
53146
+ bottom: border,
53147
+ left: border,
53148
+ });
53149
+ }
53150
+ }
53151
+ }
53152
+ if (position === "h" || position === "hv") {
53153
+ if (zone.top === zone.bottom) {
53154
+ continue;
53155
+ }
53156
+ for (let col = zone.left; col <= zone.right; col++) {
53157
+ this.addBorder(sheetId, col, zone.top, { bottom: border });
53158
+ for (let row = zone.top + 1; row < zone.bottom; row++) {
53159
+ this.addBorder(sheetId, col, row, { top: border, bottom: border });
53115
53160
  }
53161
+ this.addBorder(sheetId, col, zone.bottom, { top: border });
53116
53162
  }
53117
53163
  }
53118
- if (position === "v" || position === "hv" || position === "all") {
53164
+ if (position === "v" || position === "hv") {
53165
+ if (zone.left === zone.right) {
53166
+ continue;
53167
+ }
53119
53168
  for (let row = zone.top; row <= zone.bottom; row++) {
53120
- for (let col = zone.left + 1; col <= zone.right; col++) {
53121
- this.addBorder(sheetId, col, row, { left: border });
53169
+ this.addBorder(sheetId, zone.left, row, { right: border });
53170
+ for (let col = zone.left + 1; col < zone.right; col++) {
53171
+ this.addBorder(sheetId, col, row, { left: border, right: border });
53122
53172
  }
53173
+ this.addBorder(sheetId, zone.right, row, { left: border });
53123
53174
  }
53124
53175
  }
53125
- if (position === "left" || position === "all" || position === "external") {
53176
+ if (position === "left" || position === "external") {
53126
53177
  for (let row = zone.top; row <= zone.bottom; row++) {
53127
53178
  this.addBorder(sheetId, zone.left, row, { left: border });
53128
53179
  }
53129
53180
  }
53130
- if (position === "right" || position === "all" || position === "external") {
53181
+ if (position === "right" || position === "external") {
53131
53182
  for (let row = zone.top; row <= zone.bottom; row++) {
53132
- this.addBorder(sheetId, zone.right + 1, row, { left: border });
53183
+ this.addBorder(sheetId, zone.right, row, { right: border });
53133
53184
  }
53134
53185
  }
53135
- if (position === "top" || position === "all" || position === "external") {
53186
+ if (position === "top" || position === "external") {
53136
53187
  for (let col = zone.left; col <= zone.right; col++) {
53137
53188
  this.addBorder(sheetId, col, zone.top, { top: border });
53138
53189
  }
53139
53190
  }
53140
- if (position === "bottom" || position === "all" || position === "external") {
53191
+ if (position === "bottom" || position === "external") {
53141
53192
  for (let col = zone.left; col <= zone.right; col++) {
53142
- this.addBorder(sheetId, col, zone.bottom + 1, { top: border });
53193
+ this.addBorder(sheetId, col, zone.bottom, { bottom: border });
53143
53194
  }
53144
53195
  }
53145
53196
  }
@@ -54685,10 +54736,20 @@ class DataValidationPlugin extends CorePlugin {
54685
54736
  for (const sheet of data.sheets) {
54686
54737
  sheet.dataValidationRules = [];
54687
54738
  for (const rule of this.rules[sheet.id]) {
54688
- sheet.dataValidationRules.push({
54689
- ...rule,
54739
+ const excelRule = {
54740
+ ...deepCopy(rule),
54690
54741
  ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useBoundedReference: true })),
54691
- });
54742
+ };
54743
+ if (rule.criterion.type === "isValueInRange") {
54744
+ excelRule.criterion.values = rule.criterion.values.map((value) => {
54745
+ const range = this.getters.getRangeFromSheetXC(sheet.id, value);
54746
+ return this.getters.getRangeString(range, sheet.id, {
54747
+ useBoundedReference: true,
54748
+ useFixedReference: true,
54749
+ });
54750
+ });
54751
+ }
54752
+ sheet.dataValidationRules.push(excelRule);
54692
54753
  }
54693
54754
  }
54694
54755
  }
@@ -56109,9 +56170,10 @@ class RangeAdapter {
56109
56170
  * @param range the range (received from getRangeFromXC or getRangeFromZone)
56110
56171
  * @param forSheetId the id of the sheet where the range string is supposed to be used.
56111
56172
  * @param options
56112
- * @param options.useBoundedReference if true, the range will be returned with fixed row and column
56173
+ * @param options.useBoundedReference if true, the range will be returned with bounded row and column
56174
+ * @param options.useFixedReference if true, the range will be returned with fixed row and column
56113
56175
  */
56114
- getRangeString(range, forSheetId, options = { useBoundedReference: false }) {
56176
+ getRangeString(range, forSheetId, options = { useBoundedReference: false, useFixedReference: false }) {
56115
56177
  if (!range) {
56116
56178
  return CellErrorType.InvalidReference;
56117
56179
  }
@@ -56214,10 +56276,10 @@ class RangeAdapter {
56214
56276
  /**
56215
56277
  * Get a Xc string that represent a part of a range
56216
56278
  */
56217
- getRangePartString(range, part, options = { useBoundedReference: false }) {
56218
- const colFixed = range.parts && range.parts[part]?.colFixed ? "$" : "";
56279
+ getRangePartString(range, part, options = { useBoundedReference: false, useFixedReference: false }) {
56280
+ const colFixed = range.parts[part]?.colFixed || options.useFixedReference ? "$" : "";
56219
56281
  const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
56220
- const rowFixed = range.parts && range.parts[part]?.rowFixed ? "$" : "";
56282
+ const rowFixed = range.parts[part]?.rowFixed || options.useFixedReference ? "$" : "";
56221
56283
  const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
56222
56284
  let str = "";
56223
56285
  if (range.isFullCol && !options.useBoundedReference) {
@@ -59873,7 +59935,11 @@ class Evaluator {
59873
59935
  computeFormulaCell(formulaPosition, cellData) {
59874
59936
  const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, formulaPosition.sheetId, this.buildSafeGetSymbolValue(), formulaPosition);
59875
59937
  if (!isMatrix(formulaReturn)) {
59876
- return createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
59938
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
59939
+ if (evaluatedCell.type === CellValueType.error) {
59940
+ evaluatedCell.errorOriginPosition = formulaReturn.errorOriginPosition ?? formulaPosition;
59941
+ }
59942
+ return evaluatedCell;
59877
59943
  }
59878
59944
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
59879
59945
  const nbColumns = formulaReturn.length;
@@ -59946,6 +60012,9 @@ class Evaluator {
59946
60012
  const position = { sheetId, col: i + col, row: j + row };
59947
60013
  const cell = this.getters.getCell(position);
59948
60014
  const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
60015
+ if (evaluatedCell.type === CellValueType.error) {
60016
+ evaluatedCell.errorOriginPosition = matrixResult[i][j].errorOriginPosition ?? position;
60017
+ }
59949
60018
  this.evaluatedCells.set(position, evaluatedCell);
59950
60019
  };
59951
60020
  return spreadValues;
@@ -61504,7 +61573,7 @@ function withPivotPresentationLayer (PivotClass) {
61504
61573
  const symbolIndex = rowDomain.findIndex((row) => row.field === symbolName);
61505
61574
  return this.getPivotHeaderValueAndFormat(rowDomain.slice(0, symbolIndex + 1));
61506
61575
  }
61507
- return this._getPivotCellValueAndFormat(symbolName, domain);
61576
+ return this.getPivotCellValueAndFormat(symbolName, domain);
61508
61577
  };
61509
61578
  const result = this.getters.evaluateCompiledFormula(measure.computedBy.sheetId, formula, getSymbolValue);
61510
61579
  if (isMatrix(result)) {
@@ -63403,6 +63472,7 @@ class Session extends EventBus {
63403
63472
  waitingUndoRedoAck = false;
63404
63473
  isReplayingInitialRevisions = false;
63405
63474
  processedRevisions = new Set();
63475
+ lastRevisionMessage = undefined;
63406
63476
  uuidGenerator = new UuidGenerator();
63407
63477
  lastLocalOperation;
63408
63478
  /**
@@ -63503,7 +63573,10 @@ class Session extends EventBus {
63503
63573
  * Notify the server that the user client left the collaborative session
63504
63574
  */
63505
63575
  async leave(data) {
63506
- if (data && Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
63576
+ if (data &&
63577
+ Object.keys(this.clients).length === 1 &&
63578
+ this.lastRevisionMessage &&
63579
+ this.lastRevisionMessage?.type !== "SNAPSHOT_CREATED") {
63507
63580
  await this.snapshot(data());
63508
63581
  }
63509
63582
  delete this.clients[this.clientId];
@@ -63724,6 +63797,7 @@ class Session extends EventBus {
63724
63797
  this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
63725
63798
  this.serverRevisionId = message.nextRevisionId;
63726
63799
  this.processedRevisions.add(message.nextRevisionId);
63800
+ this.lastRevisionMessage = message;
63727
63801
  this.sendPendingMessage();
63728
63802
  break;
63729
63803
  }
@@ -64685,14 +64759,12 @@ class SheetUIPlugin extends UIPlugin {
64685
64759
  }
64686
64760
  break;
64687
64761
  case "AUTORESIZE_ROWS":
64688
- for (let row of cmd.rows) {
64689
- this.dispatch("RESIZE_COLUMNS_ROWS", {
64690
- elements: [row],
64691
- dimension: "ROW",
64692
- size: null,
64693
- sheetId: cmd.sheetId,
64694
- });
64695
- }
64762
+ this.dispatch("RESIZE_COLUMNS_ROWS", {
64763
+ elements: cmd.rows,
64764
+ dimension: "ROW",
64765
+ size: null,
64766
+ sheetId: cmd.sheetId,
64767
+ });
64696
64768
  break;
64697
64769
  }
64698
64770
  }
@@ -67443,7 +67515,7 @@ class SheetViewPlugin extends UIPlugin {
67443
67515
  "getSheetViewVisibleCols",
67444
67516
  "getSheetViewVisibleRows",
67445
67517
  "getFrozenSheetViewRatio",
67446
- "isPositionVisible",
67518
+ "isPixelPositionVisible",
67447
67519
  "getColDimensionsInViewport",
67448
67520
  "getRowDimensionsInViewport",
67449
67521
  "getAllActiveViewportsZones",
@@ -68067,7 +68139,7 @@ class SheetViewPlugin extends UIPlugin {
68067
68139
  }
68068
68140
  return result;
68069
68141
  }
68070
- isPositionVisible(position) {
68142
+ isPixelPositionVisible(position) {
68071
68143
  const { scrollX, scrollY } = this.getters.getActiveSheetScrollInfo();
68072
68144
  const { x: mainViewportX, y: mainViewportY } = this.getters.getMainViewportCoordinates();
68073
68145
  const { width, height } = this.getters.getSheetViewDimension();
@@ -70462,12 +70534,8 @@ css /* scss */ `
70462
70534
  .o-spreadsheet {
70463
70535
  position: relative;
70464
70536
  display: grid;
70465
- color: ${TEXT_BODY};
70466
70537
  font-size: 14px;
70467
70538
 
70468
- input {
70469
- background-color: white;
70470
- }
70471
70539
  .text-muted {
70472
70540
  color: ${TEXT_BODY_MUTED} !important;
70473
70541
  }
@@ -71714,6 +71782,9 @@ class EventStream {
71714
71782
  observe(owner, callbacks) {
71715
71783
  this.observers.set(owner, { owner, callbacks });
71716
71784
  }
71785
+ detachObserver(owner) {
71786
+ this.observers.delete(owner);
71787
+ }
71717
71788
  /**
71718
71789
  * Capture the stream for yourself
71719
71790
  */
@@ -71806,6 +71877,9 @@ class SelectionStreamProcessorImpl {
71806
71877
  observe(owner, callbacks) {
71807
71878
  this.stream.observe(owner, callbacks);
71808
71879
  }
71880
+ detachObserver(owner) {
71881
+ this.stream.detachObserver(owner);
71882
+ }
71809
71883
  release(owner) {
71810
71884
  if (this.stream.isListening(owner)) {
71811
71885
  this.stream.release(owner);
@@ -75255,6 +75329,6 @@ exports.tokenColors = tokenColors;
75255
75329
  exports.tokenize = tokenize;
75256
75330
 
75257
75331
 
75258
- __info__.version = "18.2.0-alpha.3";
75259
- __info__.date = "2025-01-27T10:07:28.716Z";
75260
- __info__.hash = "63a13e5";
75332
+ __info__.version = "18.2.0-alpha.5";
75333
+ __info__.date = "2025-01-31T07:59:30.667Z";
75334
+ __info__.hash = "efce841";