@fileverse-dev/dsheet 2.0.26 → 2.0.27-bundle
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.
- package/dist/editor/hooks/use-apply-templates.d.ts +2 -1
- package/dist/editor/hooks/use-xlsx-import-impl.d.ts +16 -0
- package/dist/editor/hooks/use-xlsx-import.d.ts +13 -7
- package/dist/editor/utils/xlsx-export-impl.d.ts +4 -0
- package/dist/editor/utils/xlsx-export.d.ts +4 -0
- package/dist/{executeStringFunction-Cv2qXCMb.js → executeStringFunction-DOK3z9nV.js} +4956 -4927
- package/dist/formula.js +1 -1
- package/dist/index-IhnJm9PN.js +39862 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +43 -44353
- package/dist/sheet-engine/core/api/cell.d.ts +1 -1
- package/dist/sheet-engine/core/context.d.ts +5 -0
- package/dist/sheet-engine/core/events/mouse.d.ts +1 -1
- package/dist/sheet-engine/core/events/paste.d.ts +2 -12
- package/dist/sheet-engine/core/modules/cell.d.ts +2 -1
- package/dist/sheet-engine/core/modules/formula.d.ts +4 -4
- package/dist/sheet-engine/core/modules/inline-string.d.ts +6 -6
- package/dist/sheet-engine/core/modules/rowcol.d.ts +3 -3
- package/dist/sheet-engine/core/modules/selection.d.ts +13 -7
- package/dist/sheet-engine/core/modules/sheet-metadata-hooks.d.ts +7 -0
- package/dist/sheet-engine/core/paste/clipboard-entry.d.ts +4 -0
- package/dist/sheet-engine/core/paste/formula-adjust.d.ts +7 -0
- package/dist/sheet-engine/core/paste/fortune-internal-paste.d.ts +13 -0
- package/dist/sheet-engine/core/paste/index.d.ts +5 -0
- package/dist/sheet-engine/core/paste/paste-internals.d.ts +10 -0
- package/dist/sheet-engine/core/types.d.ts +2 -2
- package/dist/sheet-engine/react/hooks/useFormulaEditorHistory.d.ts +1 -1
- package/dist/template-data-list-oP1s0Uxn.js +502875 -0
- package/dist/use-xlsx-import-impl-CHqptfOb.js +1527 -0
- package/dist/xlsx-export-impl-Bojkus14.js +3128 -0
- package/dist/xlsx-hyperlink-inline-DzewAypN.js +71 -0
- package/dist/xlsx-image-utils-Cvg0qxRA.js +126 -0
- package/package.json +1 -1
|
@@ -8,4 +8,4 @@ export declare function getCellValue(ctx: Context, row: number, column: number,
|
|
|
8
8
|
export declare function setCellValue(ctx: Context, row: number, column: number, value: any, cellInput: HTMLDivElement | null, options?: CommonOptions, callAfterUpdate?: boolean): void;
|
|
9
9
|
export declare function clearCell(ctx: Context, row: number, column: number, options?: CommonOptions): void;
|
|
10
10
|
export declare function setCellFormat(ctx: Context, row: number, column: number, attr: keyof Cell, value: any, options?: CommonOptions): void;
|
|
11
|
-
export declare function autoFillCell(ctx: Context, copyRange: SingleRange, applyRange: SingleRange, direction:
|
|
11
|
+
export declare function autoFillCell(ctx: Context, copyRange: SingleRange, applyRange: SingleRange, direction: 'up' | 'down' | 'left' | 'right'): void;
|
|
@@ -213,6 +213,11 @@ export type Context = {
|
|
|
213
213
|
luckysheet_shiftkeydown: boolean;
|
|
214
214
|
luckysheet_shiftpositon: Selection | undefined;
|
|
215
215
|
iscopyself: boolean;
|
|
216
|
+
/**
|
|
217
|
+
* True when the last in-app copy dropped per-cell `data-fortune-cell` JSON due to HTML size cap.
|
|
218
|
+
* Paste must rely on `luckysheet_copy_save` + grid data (internal route), not HTML text equality.
|
|
219
|
+
*/
|
|
220
|
+
lastInternalCopyHtmlMetadataStripped: boolean;
|
|
216
221
|
orderbyindex: number;
|
|
217
222
|
luckysheet_model_move_state: boolean;
|
|
218
223
|
luckysheet_model_xy: number[];
|
|
@@ -12,7 +12,7 @@ export declare function fixPositionOnFrozenCells(freeze: Freezen | undefined, x:
|
|
|
12
12
|
};
|
|
13
13
|
export declare function handleCellAreaMouseDown(ctx: Context, globalCache: GlobalCache, e: MouseEvent, cellInput: HTMLDivElement, container: HTMLDivElement, fxInput?: HTMLDivElement | null, canvas?: CanvasRenderingContext2D): void;
|
|
14
14
|
export declare function handleCellAreaDoubleClick(ctx: Context, globalCache: GlobalCache, settings: Settings, e: MouseEvent, container: HTMLElement): void;
|
|
15
|
-
export declare function handleContextMenu(ctx: Context, settings: Settings, e: MouseEvent, workbookContainer: HTMLDivElement, container: HTMLDivElement, area:
|
|
15
|
+
export declare function handleContextMenu(ctx: Context, settings: Settings, e: MouseEvent, workbookContainer: HTMLDivElement, container: HTMLDivElement, area: 'cell' | 'rowHeader' | 'columnHeader'): void;
|
|
16
16
|
export declare function mouseRender(ctx: Context, globalCache: GlobalCache, e: MouseEvent, cellInput: HTMLDivElement, scrollX: HTMLDivElement, scrollY: HTMLDivElement, container: HTMLDivElement, fxInput?: HTMLDivElement | null): void;
|
|
17
17
|
export declare function handleOverlayMouseMove(ctx: Context, globalCache: GlobalCache, e: MouseEvent, cellInput: HTMLDivElement, scrollX: HTMLDivElement, scrollY: HTMLDivElement, container: HTMLDivElement, fxInput?: HTMLDivElement | null): void;
|
|
18
18
|
export declare function handleOverlayMouseUp(ctx: Context, globalCache: GlobalCache, settings: Settings, e: MouseEvent, scrollbarX: HTMLDivElement, scrollbarY: HTMLDivElement, container: HTMLDivElement, cellInput: HTMLDivElement | null, fxInput: HTMLDivElement | null): void;
|
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare function columnLabelIndex(label: string): number;
|
|
4
|
-
export declare function indexToColumnLabel(index: number): string;
|
|
5
|
-
export declare class FormularCellRefError extends Error {
|
|
6
|
-
formula: string;
|
|
7
|
-
constructor(message: string, formula: string);
|
|
8
|
-
}
|
|
9
|
-
export declare function adjustFormulaForPaste(formula: string, srcCol: number, srcRow: number, destCol: number, destRow: number): string;
|
|
10
|
-
export declare function parseAsLinkIfUrl(txtdata: string, ctx: Context): void;
|
|
11
|
-
export declare function handlePaste(ctx: Context, e: ClipboardEvent): void;
|
|
12
|
-
export declare function handlePasteByClick(ctx: Context, clipboardData: string, triggerType?: string): void;
|
|
1
|
+
/** Re-export paste module (Fortune-first architecture lives under `core/paste/`). */
|
|
2
|
+
export { handlePaste, handlePasteByClick, columnLabelIndex, indexToColumnLabel, FormularCellRefError, adjustFormulaForPaste, parseAsLinkIfUrl, } from '../paste';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Context } from '../context';
|
|
2
2
|
import { Cell, CellMatrix, Range, Selection, SingleRange } from '../types';
|
|
3
|
+
import { getComputeMap } from './ConditionFormat';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Recompute auto row height for one row from wrap/inline cells; updates cfg.rowlen and ctx.config.
|
|
@@ -36,7 +37,7 @@ export declare function getRangeByTxt(ctx: Context, txt: string): any[];
|
|
|
36
37
|
export declare function isValidRangeText(ctx: Context, txt: string): boolean;
|
|
37
38
|
export declare function isAllSelectedCellsInStatus(ctx: Context, attr: keyof Cell, status: any): boolean;
|
|
38
39
|
export declare function getFontStyleByCell(cell: Cell | null | undefined, checksAF?: any[], checksCF?: any, isCheck?: boolean): any;
|
|
39
|
-
export declare function getStyleByCell(ctx: Context, d: CellMatrix, r: number, c: number): any;
|
|
40
|
+
export declare function getStyleByCell(ctx: Context, d: CellMatrix, r: number, c: number, precomputedCfCompute?: ReturnType<typeof getComputeMap>): any;
|
|
40
41
|
export declare function getInlineStringHTML(r: number, c: number, data: CellMatrix, options?: {
|
|
41
42
|
useSemanticMarkup?: boolean;
|
|
42
43
|
isRichTextCopy?: boolean;
|
|
@@ -27,7 +27,7 @@ export declare class FormulaCache {
|
|
|
27
27
|
keyboardRangeSelectionLock?: boolean;
|
|
28
28
|
/** True after arrow/Shift+arrow moved `func_selectedrange` without updating yellow selection. */
|
|
29
29
|
formulaKeyboardRefSync?: boolean;
|
|
30
|
-
formulaEditorOwner?:
|
|
30
|
+
formulaEditorOwner?: 'cell' | 'fx' | null;
|
|
31
31
|
functionRangeIndex?: number[];
|
|
32
32
|
functionRangeGlobalOffset?: number | null;
|
|
33
33
|
functionlistMap: any;
|
|
@@ -75,8 +75,8 @@ export declare function functionHTMLGenerate(txt: string): string;
|
|
|
75
75
|
export declare function getLastFormulaRangeIndex($editor: HTMLDivElement): number | null;
|
|
76
76
|
/** Range cell that contains the caret, if any (inside #luckysheet-rich-text-editor tree). */
|
|
77
77
|
export declare function getFormulaRangeIndexAtCaret($editor: HTMLDivElement): number | null;
|
|
78
|
-
export declare function setFormulaEditorOwner(ctx: Context, owner:
|
|
79
|
-
export declare function getFormulaEditorOwner(ctx: Context):
|
|
78
|
+
export declare function setFormulaEditorOwner(ctx: Context, owner: 'cell' | 'fx' | null): void;
|
|
79
|
+
export declare function getFormulaEditorOwner(ctx: Context): 'cell' | 'fx' | null;
|
|
80
80
|
/**
|
|
81
81
|
* True when the formula text looks like a truncated A1-style range: LHS has a row
|
|
82
82
|
* number but RHS after ":" is only column letters (e.g. =A1:A after deleting the
|
|
@@ -111,7 +111,7 @@ export declare function isLegacyFormulaRangeMode(ctx: Context): boolean;
|
|
|
111
111
|
export declare function israngeseleciton(ctx: Context, istooltip?: boolean): boolean;
|
|
112
112
|
export declare function isFormulaReferenceInputMode(ctx: Context): boolean;
|
|
113
113
|
export declare function maybeRecoverDirtyRangeSelection(ctx: Context): boolean;
|
|
114
|
-
export declare function functionStrChange(txt: string, type: string, rc:
|
|
114
|
+
export declare function functionStrChange(txt: string, type: string, rc: 'row' | 'col', orient: string | null, stindex: number, step: number): string;
|
|
115
115
|
export declare function rangeSetValue(ctx: Context, cellInput: HTMLDivElement, selected: any, fxInput?: HTMLDivElement | null): void;
|
|
116
116
|
export declare function onFormulaRangeDragEnd(ctx: Context): void;
|
|
117
117
|
export declare function rangeDrag(ctx: Context, e: MouseEvent, cellInput: HTMLDivElement, scrollLeft: number, scrollTop: number, container: HTMLDivElement, fxInput?: HTMLDivElement | null): void;
|
|
@@ -20,12 +20,12 @@ export declare const inlineStyleAffectAttribute: {
|
|
|
20
20
|
fc: number;
|
|
21
21
|
};
|
|
22
22
|
export declare const inlineStyleAffectCssName: {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
'font-weight': number;
|
|
24
|
+
'font-style': number;
|
|
25
|
+
'font-family': number;
|
|
26
|
+
'text-decoration': number;
|
|
27
|
+
'border-bottom': number;
|
|
28
|
+
'font-size': number;
|
|
29
29
|
color: number;
|
|
30
30
|
};
|
|
31
31
|
export declare function isInlineStringCell(cell: any): boolean;
|
|
@@ -10,10 +10,10 @@ import { Context } from '../context';
|
|
|
10
10
|
* @returns
|
|
11
11
|
*/
|
|
12
12
|
export declare function insertRowCol(ctx: Context, op: {
|
|
13
|
-
type:
|
|
13
|
+
type: 'row' | 'column';
|
|
14
14
|
index: number;
|
|
15
15
|
count: number;
|
|
16
|
-
direction:
|
|
16
|
+
direction: 'lefttop' | 'rightbottom';
|
|
17
17
|
id: string;
|
|
18
18
|
/** Pre-insert row index for each inserted row (length must equal count). */
|
|
19
19
|
templateSourceRows?: number[];
|
|
@@ -21,7 +21,7 @@ export declare function insertRowCol(ctx: Context, op: {
|
|
|
21
21
|
templateSourceColumns?: number[];
|
|
22
22
|
}, changeSelection?: boolean): void;
|
|
23
23
|
export declare function deleteRowCol(ctx: Context, op: {
|
|
24
|
-
type:
|
|
24
|
+
type: 'row' | 'column';
|
|
25
25
|
start: number;
|
|
26
26
|
end: number;
|
|
27
27
|
id?: string;
|
|
@@ -5,6 +5,11 @@ export declare const selectionCache: {
|
|
|
5
5
|
isPasteAction: boolean;
|
|
6
6
|
isPasteValuesOnly: boolean;
|
|
7
7
|
};
|
|
8
|
+
interface RangeValueToHtmlOptions {
|
|
9
|
+
includeCellMetadata?: boolean;
|
|
10
|
+
preferRichText?: boolean;
|
|
11
|
+
respectFilterHiddenRows?: boolean;
|
|
12
|
+
}
|
|
8
13
|
export declare function scrollToHighlightCell(ctx: Context, r: number, c: number): void;
|
|
9
14
|
export declare function seletedHighlistByindex(ctx: Context, r1: number, r2: number, c1: number, c2: number): {
|
|
10
15
|
left: number;
|
|
@@ -22,7 +27,7 @@ export declare function selectionIsExactlyOneMergeBlock(ctx: Context, selection:
|
|
|
22
27
|
* else null). Pass `selection` explicitly when normalizing a **new** array that is not
|
|
23
28
|
* yet assigned to `ctx.luckysheet_select_save`.
|
|
24
29
|
*/
|
|
25
|
-
export declare function syncPrimaryCellActiveFromSelection(ctx: Context, selection?: SheetType[
|
|
30
|
+
export declare function syncPrimaryCellActiveFromSelection(ctx: Context, selection?: SheetType['luckysheet_select_save'] | null): void;
|
|
26
31
|
/**
|
|
27
32
|
* Multi-cell range only: move `row_focus` / `column_focus` (and primary) in
|
|
28
33
|
* column-major order — forward: down then next column right, wrapping top-left;
|
|
@@ -43,24 +48,24 @@ export declare function setPrimaryCellActive(ctx: Context, r: number, c: number)
|
|
|
43
48
|
* that range so Enter-primary navigation + typing do not collapse the yellow box.
|
|
44
49
|
*/
|
|
45
50
|
export declare function snapSheetSelectionFocusToCellPreserveMultiRange(ctx: Context, r: number, c: number): void;
|
|
46
|
-
export declare function normalizeSelection(ctx: Context, selection: SheetType[
|
|
51
|
+
export declare function normalizeSelection(ctx: Context, selection: SheetType['luckysheet_select_save']): Selection[] | undefined;
|
|
47
52
|
export declare function selectTitlesMap(rangeMap: Record<string, number>, range1: number, range2: number): Record<string, number>;
|
|
48
53
|
export declare function selectTitlesRange(map: Record<string, number>): number[][];
|
|
49
|
-
export declare function pasteHandlerOfPaintModel(ctx: Context, copyRange: Context[
|
|
54
|
+
export declare function pasteHandlerOfPaintModel(ctx: Context, copyRange: Context['luckysheet_copy_save']): void;
|
|
50
55
|
export declare function selectionCopyShow(range: any, ctx: Context): void;
|
|
51
56
|
export declare function rowHasMerged(ctx: Context, r: number, c1: number, c2: number): boolean;
|
|
52
57
|
export declare function colHasMerged(ctx: Context, c: number, r1: number, r2: number): boolean;
|
|
53
58
|
export declare function getRowMerge(ctx: Context, rIndex: number, c1: number, c2: number): (number | null)[];
|
|
54
59
|
export declare function getColMerge(ctx: Context, cIndex: number, r1: number, r2: number): (number | null)[];
|
|
55
|
-
export declare function moveHighlightCell(ctx: Context, postion:
|
|
56
|
-
export declare function moveHighlightRange(ctx: Context, postion:
|
|
57
|
-
export declare function rangeValueToHtml(ctx: Context, sheetId: string, ranges?: Range): string | null;
|
|
60
|
+
export declare function moveHighlightCell(ctx: Context, postion: 'down' | 'right', index: number, type: 'rangeOfSelect' | 'rangeOfFormula'): void;
|
|
61
|
+
export declare function moveHighlightRange(ctx: Context, postion: 'down' | 'right', index: number, type: 'rangeOfSelect' | 'rangeOfFormula'): void;
|
|
62
|
+
export declare function rangeValueToHtml(ctx: Context, sheetId: string, ranges?: Range, options?: RangeValueToHtmlOptions): string | null;
|
|
58
63
|
export declare function copy(ctx: Context): void;
|
|
59
64
|
export declare function deleteSelectedCellText(ctx: Context): string;
|
|
60
65
|
export declare function deleteSelectedCellFormat(ctx: Context): string;
|
|
61
66
|
export declare function fillRightData(ctx: Context): string;
|
|
62
67
|
export declare function fillDownData(ctx: Context): string;
|
|
63
|
-
export declare function textFormat(ctx: Context, type:
|
|
68
|
+
export declare function textFormat(ctx: Context, type: 'left' | 'center' | 'right'): string;
|
|
64
69
|
export declare function fillDate(ctx: Context): string;
|
|
65
70
|
export declare function fillTime(ctx: Context): string;
|
|
66
71
|
export declare function selectIsOverlap(ctx: Context, range?: any): boolean;
|
|
@@ -83,3 +88,4 @@ export declare function calcSelectionInfo(ctx: Context): {
|
|
|
83
88
|
min: number;
|
|
84
89
|
average: string;
|
|
85
90
|
};
|
|
91
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Context } from '../context';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Defer sheet-level metadata hooks until after React applies Immer updates, so
|
|
5
|
+
* getSheet() / Yjs diff see dataVerification, CF rules, and hyperlinks from paste/cut.
|
|
6
|
+
*/
|
|
7
|
+
export declare function scheduleSheetMetadataSyncHooks(ctx: Context): void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function columnLabelIndex(label: string): number;
|
|
2
|
+
export declare function indexToColumnLabel(index: number): string;
|
|
3
|
+
export declare class FormularCellRefError extends Error {
|
|
4
|
+
formula: string;
|
|
5
|
+
constructor(message: string, formula: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function adjustFormulaForPaste(formula: string, srcCol: number, srcRow: number, destCol: number, destRow: number): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Context } from '../context';
|
|
2
|
+
|
|
3
|
+
export type FortuneInternalPasteDecision = {
|
|
4
|
+
/** True when clipboard/grid prep must stop (e.g. missing copy source data). */
|
|
5
|
+
abortPaste: boolean;
|
|
6
|
+
/** True when paste should use `pasteHandlerOfCopyPaste` / cut handler for Fortune HTML. */
|
|
7
|
+
internalFortunePaste: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Decides whether pasted HTML corresponds to the last in-app Fortune copy so we can use
|
|
11
|
+
* the internal paste path (grid + `luckysheet_copy_save`) instead of HTML table parsing.
|
|
12
|
+
*/
|
|
13
|
+
export declare function computeFortuneInternalPasteDecision(ctx: Context, txtdata: string): FortuneInternalPasteDecision;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Fortune-first paste: routing in `fortune-internal-paste`, handlers in `paste-internals`, wire-up in `clipboard-entry`. */
|
|
2
|
+
export { columnLabelIndex, indexToColumnLabel, FormularCellRefError, adjustFormulaForPaste, } from './formula-adjust';
|
|
3
|
+
export { computeFortuneInternalPasteDecision, type FortuneInternalPasteDecision, } from './fortune-internal-paste';
|
|
4
|
+
export { parseAsLinkIfUrl, pasteHandler, pasteHandlerOfCutPaste, pasteHandlerOfCopyPaste, handleFormulaStringPaste, resizePastedCellsToContent, shouldHandleNonTableHtml, convertAnyHtmlToTable, } from './paste-internals';
|
|
5
|
+
export { handlePaste, handlePasteByClick } from './clipboard-entry';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Context } from '../context';
|
|
2
|
+
|
|
3
|
+
export declare function pasteHandler(ctx: Context, data: any, borderInfo?: any): void;
|
|
4
|
+
export declare function pasteHandlerOfCutPaste(ctx: Context, copyRange: Context['luckysheet_copy_save']): void;
|
|
5
|
+
export declare function pasteHandlerOfCopyPaste(ctx: Context, copyRange: Context['luckysheet_copy_save'], valuesOnly?: boolean): void;
|
|
6
|
+
export declare function handleFormulaStringPaste(ctx: Context, formulaStr: string): void;
|
|
7
|
+
export declare function parseAsLinkIfUrl(txtdata: string, ctx: Context): void;
|
|
8
|
+
export declare function resizePastedCellsToContent(ctx: Context): void;
|
|
9
|
+
export declare function shouldHandleNonTableHtml(html: string): boolean;
|
|
10
|
+
export declare function convertAnyHtmlToTable(html: string): string;
|
|
@@ -2,7 +2,7 @@ import { Patch as ImmerPatch } from 'immer';
|
|
|
2
2
|
import { PatchOptions } from './utils';
|
|
3
3
|
|
|
4
4
|
export type Op = {
|
|
5
|
-
op:
|
|
5
|
+
op: 'replace' | 'remove' | 'add' | 'insertRowCol' | 'deleteRowCol' | 'addSheet' | 'deleteSheet';
|
|
6
6
|
id?: string;
|
|
7
7
|
path: (string | number)[];
|
|
8
8
|
value?: any;
|
|
@@ -245,7 +245,7 @@ export type Sheet = {
|
|
|
245
245
|
dynamicArray_compute?: any;
|
|
246
246
|
dynamicArray?: any[];
|
|
247
247
|
frozen?: {
|
|
248
|
-
type:
|
|
248
|
+
type: 'row' | 'column' | 'both' | 'rangeRow' | 'rangeColumn' | 'rangeBoth';
|
|
249
249
|
range?: {
|
|
250
250
|
row_focus: number;
|
|
251
251
|
column_focus: number;
|
|
@@ -7,7 +7,7 @@ export type FormulaHistoryEntry = {
|
|
|
7
7
|
caret: number;
|
|
8
8
|
};
|
|
9
9
|
/** Which editor receives caret placement and drives history snapshots. */
|
|
10
|
-
export type FormulaEditorHistoryPrimary =
|
|
10
|
+
export type FormulaEditorHistoryPrimary = 'cell' | 'fx';
|
|
11
11
|
type SetContext = (recipe: (ctx: Context) => void, options?: SetContextOptions) => void;
|
|
12
12
|
/**
|
|
13
13
|
* Custom undo/redo for the cell overlay and Fx bar — **one code path** for
|