@extend-ai/react-xlsx 0.20.2 → 0.20.4
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/browser-IQ4UDS5M.js +209 -0
- package/dist/browser-IQ4UDS5M.js.map +1 -0
- package/dist/browser-S6E2BS5S.cjs +212 -0
- package/dist/browser-S6E2BS5S.cjs.map +1 -0
- package/dist/{chart-renderer-F355CYSE.js → chart-renderer-4HTNG3SX.js} +94 -180
- package/dist/chart-renderer-4HTNG3SX.js.map +1 -0
- package/dist/{chart-renderer-7DTEXXG6.js → chart-renderer-5IN2B3YS.js} +94 -180
- package/dist/chart-renderer-5IN2B3YS.js.map +1 -0
- package/dist/{chart-renderer-LGW4EH4M.cjs → chart-renderer-6CWSXOSC.cjs} +324 -410
- package/dist/chart-renderer-6CWSXOSC.cjs.map +1 -0
- package/dist/{chart-renderer-3QDNTMRN.cjs → chart-renderer-H4BLRO5P.cjs} +324 -410
- package/dist/chart-renderer-H4BLRO5P.cjs.map +1 -0
- package/dist/chunk-GX5LAENE.cjs +108 -0
- package/dist/chunk-GX5LAENE.cjs.map +1 -0
- package/dist/{chunk-GXYDJPHM.js → chunk-H62SZBEY.js} +401 -132
- package/dist/chunk-H62SZBEY.js.map +1 -0
- package/dist/{chunk-3LMOGW6A.js → chunk-IISMO3AX.js} +30 -15
- package/dist/chunk-IISMO3AX.js.map +1 -0
- package/dist/{chunk-ZJ76CZAL.cjs → chunk-KQSUAIZD.cjs} +1168 -899
- package/dist/chunk-KQSUAIZD.cjs.map +1 -0
- package/dist/{chunk-IYGZTLDY.js → chunk-LZXL5E4W.js} +16 -7
- package/dist/chunk-LZXL5E4W.js.map +1 -0
- package/dist/chunk-MYN475JP.cjs +32 -0
- package/dist/chunk-MYN475JP.cjs.map +1 -0
- package/dist/{chunk-D2UA4P6N.cjs → chunk-NAYVSTCX.cjs} +22 -9
- package/dist/chunk-NAYVSTCX.cjs.map +1 -0
- package/dist/chunk-PK3MCNDK.js +32 -0
- package/dist/chunk-PK3MCNDK.js.map +1 -0
- package/dist/full-3FSPB3R6.cjs +212 -0
- package/dist/full-3FSPB3R6.cjs.map +1 -0
- package/dist/full-GPY27HZY.js +209 -0
- package/dist/full-GPY27HZY.js.map +1 -0
- package/dist/index.cjs +834 -602
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +347 -115
- package/dist/index.js.map +1 -1
- package/dist/{internal-BJsg5os3.d.cts → internal-Cks9H21d.d.cts} +56 -1
- package/dist/{internal-BJsg5os3.d.ts → internal-Cks9H21d.d.ts} +56 -1
- package/dist/internal.cjs +3 -3
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +2 -2
- package/dist/office_charts_bg.wasm +0 -0
- package/dist/office_charts_full_bg.wasm +0 -0
- package/dist/payload-B55ZKNYO.js +6 -0
- package/dist/payload-B55ZKNYO.js.map +1 -0
- package/dist/payload-E2KM77DZ.js +6 -0
- package/dist/payload-E2KM77DZ.js.map +1 -0
- package/dist/payload-K4S5RLJN.cjs +8 -0
- package/dist/payload-K4S5RLJN.cjs.map +1 -0
- package/dist/payload-PL4646XV.cjs +8 -0
- package/dist/payload-PL4646XV.cjs.map +1 -0
- package/dist/rust-office-charts-LICENSE.txt +21 -0
- package/dist/{shared-chart-3STFRVWT.js → shared-chart-5KTGZEZ5.js} +38 -20
- package/dist/shared-chart-5KTGZEZ5.js.map +1 -0
- package/dist/{shared-chart-DVC5S2P6.cjs → shared-chart-ACR4M7BP.cjs} +37 -20
- package/dist/shared-chart-ACR4M7BP.cjs.map +1 -0
- package/dist/{shared-chart-BFQNYOIQ.js → shared-chart-EGN5JTKT.js} +37 -20
- package/dist/shared-chart-EGN5JTKT.js.map +1 -0
- package/dist/{shared-chart-MFQSQG4K.cjs → shared-chart-XAV7VA45.cjs} +37 -19
- package/dist/shared-chart-XAV7VA45.cjs.map +1 -0
- package/dist/{surface-regl-R2N54JT2.js → surface-webgl-2ZZ6JQR2.js} +87 -151
- package/dist/surface-webgl-2ZZ6JQR2.js.map +1 -0
- package/dist/{surface-regl-BBJ6OIKK.cjs → surface-webgl-3HS6VBCK.cjs} +88 -152
- package/dist/surface-webgl-3HS6VBCK.cjs.map +1 -0
- package/dist/{surface-regl-7CDGGVDG.js → surface-webgl-4DQXDDXU.js} +87 -151
- package/dist/surface-webgl-4DQXDDXU.js.map +1 -0
- package/dist/{surface-regl-SUJ2TFZA.cjs → surface-webgl-SVYD564V.cjs} +88 -152
- package/dist/surface-webgl-SVYD564V.cjs.map +1 -0
- package/dist/vanilla.cjs +3 -3
- package/dist/vanilla.d.cts +2 -2
- package/dist/vanilla.d.ts +2 -2
- package/dist/vanilla.js +2 -2
- package/dist/xlsx-worker.js +298 -92
- package/dist/xlsx-worker.js.map +1 -1
- package/package.json +7 -15
- package/dist/chart-renderer-3QDNTMRN.cjs.map +0 -1
- package/dist/chart-renderer-7DTEXXG6.js.map +0 -1
- package/dist/chart-renderer-F355CYSE.js.map +0 -1
- package/dist/chart-renderer-LGW4EH4M.cjs.map +0 -1
- package/dist/chunk-3LMOGW6A.js.map +0 -1
- package/dist/chunk-D2UA4P6N.cjs.map +0 -1
- package/dist/chunk-GXYDJPHM.js.map +0 -1
- package/dist/chunk-IYGZTLDY.js.map +0 -1
- package/dist/chunk-OCF75R75.cjs +0 -90
- package/dist/chunk-OCF75R75.cjs.map +0 -1
- package/dist/chunk-ZJ76CZAL.cjs.map +0 -1
- package/dist/region-map-geography-24TXETDP.cjs +0 -184
- package/dist/region-map-geography-24TXETDP.cjs.map +0 -1
- package/dist/region-map-geography-OR6DHAF7.js +0 -182
- package/dist/region-map-geography-OR6DHAF7.js.map +0 -1
- package/dist/shared-chart-3STFRVWT.js.map +0 -1
- package/dist/shared-chart-BFQNYOIQ.js.map +0 -1
- package/dist/shared-chart-DVC5S2P6.cjs.map +0 -1
- package/dist/shared-chart-MFQSQG4K.cjs.map +0 -1
- package/dist/surface-regl-7CDGGVDG.js.map +0 -1
- package/dist/surface-regl-BBJ6OIKK.cjs.map +0 -1
- package/dist/surface-regl-R2N54JT2.js.map +0 -1
- package/dist/surface-regl-SUJ2TFZA.cjs.map +0 -1
- package/dist/world-atlas-LICENSE.txt +0 -13
- /package/dist/{us-atlas-LICENSE.txt → rust-office-charts-MAP-DATA-LICENSE.txt} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/react-xlsx/react-xlsx/packages/react-xlsx/dist/chunk-KQSUAIZD.cjs","../src/range-reference.ts","../src/errors.ts","../src/wasm.ts","../src/number-locale.ts","../src/spreadsheet-fonts.ts","../src/controller.tsx","../src/chartsheet-payload.ts","../src/charts.ts","../src/colors.ts","../src/images.ts","../src/safe-calculate.ts","../src/range-highlight.ts","../src/worker-client.ts","../src/zip-entry-names.ts","../../../node_modules/.pnpm/preact@10.29.8/node_modules/preact/compat/client.mjs","../../../node_modules/.pnpm/@tanstack+virtual-core@3.13.23/node_modules/@tanstack/virtual-core/src/utils.ts","../../../node_modules/.pnpm/@tanstack+virtual-core@3.13.23/node_modules/@tanstack/virtual-core/src/index.ts","../../../node_modules/.pnpm/@tanstack+react-virtual@3.13.23_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@tanstack/react-virtual/src/index.tsx","../src/builtin-table-styles.ts","../src/cell-text-clip.ts","../src/scroll-display-limit.ts","../src/merged-range-lookup.ts","../src/cell-alignment.ts","../src/viewer-palette.ts","../src/context-menu.tsx","../src/lazy-chart.tsx","../src/XlsxViewer.tsx","../src/vanilla.tsx"],"names":["document","q","styleAppearance","plotAreaShapeProperties","x","ext","cx","cy","y","normalizeHexColor","parseHexColor","rgbToHsl","hueToRgb","hslToRgb","rgbToHex","REL_NS","EMU_PER_PIXEL","normalizeArchivePath","dirname","parseXml","serializeXml","readArchiveText","isElementNode","extNode","updateAnchorNode","MIN_ROW_HEIGHT_PX","normalizeWorksheetVisibility","parseA1CellReference","parseA1RangeReference","cloneBytes","createAbortError","timeoutHandle","visibleSheetIndexByWorkbookSheetIndex","getCellDisplayValue","options","_","DEFAULT_ROW_HEIGHT","DEFAULT_COL_WIDTH","INTERNAL_CLIPBOARD_MIME","normalizeRange","visible","columnLabel","cellAddressToA1","resolveInheritedCellStyle","decodeHtmlEntities","mapBorder","minValue","maxValue","formatBinaryBytes","isLiveZooming","liveZoomScale","firstVisibleRow","lastVisibleRow","XlsxViewer"],"mappings":"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N;AAES;AAEG;AACV,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACC,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACD,EAAA;AAAc,EAAA;AACnB;AL6SU;AACA;AM5SD;AN8SC;AACA;AOhUD;AAIH;AAIG;AACD,EAAA;AACD,EAAA;AACCA,EAAAA;AACCA,EAAAA;AACT;AAES;AACD,EAAA;AACC,EAAA;AACT;AAES;AACH,EAAA;AACE,EAAA;AACN,EAAA;AACO,IAAA;AACD,IAAA;AACE,MAAA;AACJ,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACD,EAAA;AACAA,EAAAA;AACN,EAAA;AACM,IAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,IAAA;AACN,EAAA;AACO,EAAA;AACT;AAEgB;AAIR,EAAA;AACF,EAAA;AACE,EAAA;AACA,EAAA;AACA,EAAA;AACN,EAAA;AACQ,IAAA;AACA,IAAA;AACD,IAAA;AACC,IAAA;AACD,IAAA;AACC,IAAA;AACA,IAAA;AACA,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACI,MAAA;AACJ,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACD,MAAA;AACH,IAAA;AACA,IAAA;AACF,EAAA;AACO,EAAA;AACT;APqTU;AACA;AQpYD;AAmBH;AACA;AAGA;AACA;AACA;AACA;AACA;AACJ,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF;AAWS;AACA,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEJ,EAAA;AACQ,IAAA;AACA,IAAA;AAEF,IAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AAEI,IAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AAEI,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AAEI,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AAEI,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AAEI,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AAEI,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AAEA,IAAA;AACF,EAAA;AAEK,EAAA;AACE,EAAA;AACT;AAES;AACD,EAAA;AACC,EAAA;AACT;AAEgB;AACR,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEC,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACI,IAAA;AACC,EAAA;AACT;AAEgB;AACR,EAAA;AACA,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAEgB;AAMR,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACF,EAAA;AACM,EAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACF,EAAA;AACM,EAAA;AAEI,IAAA;AACJ,IAAA;AACA,IAAA;AAEF,EAAA;AACE,EAAA;AACJ,IAAA;AACF,EAAA;AACM,EAAA;AAMC,IAAA;AACD,IAAA;AACJ,IAAA;AACF,EAAA;AACM,EAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACI,EAAA;AACE,IAAA;AACC,EAAA;AACL,IAAA;AACF,EAAA;AAEM,EAAA;AACD,IAAA;AACH,IAAA;AACA,IAAA;AAEA,IAAA;AACA,IAAA;AACM,IAAA;AAON,IAAA;AACA,IAAA;AAEA,IAAA;AACF,EAAA;AAEO,EAAA;AACF,IAAA;AACH,IAAA;AACA,IAAA;AACK,MAAA;AACH,MAAA;AACA,IAAA;AACJ,EAAA;AACF;AAES;AACA,EAAA;AACT;AACM;AACA;AACJ,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACK,EAAA;AACA,EAAA;AACL,EAAA;AACO,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACP;AACM;AACJ,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF;AA6BS;AACA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACD,EAAA;AACE,EAAA;AACN,IAAA;AACF,EAAA;AACQ,EAAA;AACN,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AAED,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACA,EAAA;AACJ,EAAA;AACQ,IAAA;AACA,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AAKD,EAAA;AAGC,EAAA;AACT;AAOS;AAID,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACC,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACD,EAAA;AACH;AAES;AAID,EAAA;AACF,EAAA;AACI,IAAA;AACJ,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACE,UAAA;AACF,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACE,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACD,QAAA;AACH,MAAA;AACA,MAAA;AACD,IAAA;AACG,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACC,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AAEM,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACD,MAAA;AACH,IAAA;AAEA,IAAA;AACD,EAAA;AACH;AAES;AACD,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACE,IAAA;AACN,IAAA;AACG,EAAA;AACC,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACM,EAAA;AAEN,EAAA;AACQ,IAAA;AACA,IAAA;AACA,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACC,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AAEA,IAAA;AACD,EAAA;AACH;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEA,EAAA;AACQ,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACC,EAAA;AACD,IAAA;AACA,IAAA;AACH,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACF,IAAA;AACE,MAAA;AACJ,EAAA;AACF;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AACO,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEF,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACF,EAAA;AAEI,EAAA;AACD,IAAA;AACH,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACJ,EAAA;AAEQ,EAAA;AACV;AAES;AACH,EAAA;AACA,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACI,IAAA;AACN,IAAA;AACF,EAAA;AAEMC,EAAAA;AACA,EAAA;AAEC,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACP,EAAA;AACF;AAES;AACA,EAAA;AAGT;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACD,EAAA;AACC,EAAA;AACA,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AACC,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AAED,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AAIA,EAAA;AACC,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACF,IAAA;AACF,EAAA;AACE,IAAA;AACF,EAAA;AACE,IAAA;AACF,EAAA;AAEK,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACJ,EAAA;AACQ,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEA,EAAA;AACE,IAAA;AACM,MAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AACI,IAAA;AACN,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AAGE,IAAA;AACA,IAAA;AAED,EAAA;AAEH,EAAA;AACF,IAAA;AACF,EAAA;AAEA,EAAA;AACQ,IAAA;AACA,IAAA;AACN,IAAA;AACD,EAAA;AACK,EAAA;AAGC,EAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACK,IAAA;AACP,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACQ,EAAA;AACD,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACF,IAAA;AACE,MAAA;AACJ,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AAEA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACN,EAAA;AACQ,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AAEM,IAAA;AACA,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACG,MAAA;AACA,MAAA;AACJ,IAAA;AACH,EAAA;AACO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACJ,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACM,EAAA;AASC,EAAA;AACT;AAES;AAID,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACN,EAAA;AACQ,IAAA;AACA,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AAKD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AAOT;AAES;AAKD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAGA,EAAA;AACA,EAAA;AACA,EAAA;AAGA,EAAA;AAEA,EAAA;AACA,EAAA;AACA,EAAA;AAEC,EAAA;AACL,IAAA;AAGA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AAGC,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACN,EAAA;AACM,IAAA;AACN,EAAA;AACM,EAAA;AACA,EAAA;AACA,EAAA;AACN,EAAA;AACQ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACM,EAAA;AACD,EAAA;AACG,IAAA;AACR,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAGF,EAAA;AACI,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACR,EAAA;AACM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAGA,EAAA;AACA,EAAA;AAEA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACJ,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACM,IAAA;AACA,IAAA;AACA,IAAA;AAKA,IAAA;AAKA,IAAA;AACA,IAAA;AACN,IAAA;AACK,MAAA;AACH,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACM,EAAA;AACA,EAAA;AACF,EAAA;AACE,EAAA;AACE,IAAA;AACN,IAAA;AACA,IAAA;AACD,EAAA;AACD,EAAA;AACF;AAES;AACD,EAAA;AAEN,EAAA;AACQ,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACM,IAAA;AACA,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACD,IAAA;AACH,EAAA;AAEO,EAAA;AACT;AAES;AA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;AACA,YAAA;AACE,cAAA;AAA4B,YAAA;AAEhC,UAAA;AACF,QAAA;AACE,UAAA;AACA,UAAA;AACE,YAAA;AACF,UAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAED,EAAA;AACH,IAAA;AACM,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACM,IAAA;AACF,IAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACE,UAAA;AACA,UAAA;AACA,QAAA;AACF,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACM,EAAA;AACD,EAAA;AACH,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AAEQ,EAAA;AACD,IAAA;AACA,IAAA;AACH,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACK,IAAA;AACA,IAAA;AACH,MAAA;AACA,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACK,IAAA;AACA,IAAA;AACH,MAAA;AACA,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACK,IAAA;AACH,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AACA,MAAA;AACF,IAAA;AACE,MAAA;AACJ,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AACJ,IAAA;AACA,IAAA;AACK,IAAA;AACH,EAAA;AACE,EAAA;AACF,EAAA;AACI,IAAA;AACR,EAAA;AACM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAGA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAGA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACN,EAAA;AACQ,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACM,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,IAAA;AACA,IAAA;AACA,IAAA;AACN,EAAA;AAEM,EAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AACI,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACA,EAAA;AACQ,IAAA;AACF,IAAA;AACF,MAAA;AACA,MAAA;AACI,MAAA;AACN,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AAGA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACI,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACM,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACF,EAAA;AACK,EAAA;AACG,IAAA;AACR,EAAA;AACM,EAAA;AACD,IAAA;AACA,IAAA;AACL,EAAA;AACM,EAAA;AACA,EAAA;AACA,EAAA;AAMF,EAAA;AACA,EAAA;AACC,EAAA;AACH,IAAA;AACE,MAAA;AACA,MAAA;AACI,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACI,IAAA;AACR,EAAA;AACM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AASF,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACEC,IAAAA;AACN,IAAA;AACD,EAAA;AACK,EAAA;AACA,EAAA;AACA,EAAA;AAGF,EAAA;AAEI,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,MAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACD,IAAA;AACK,IAAA;AACD,EAAA;AACL,IAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACF;AAES;AACA,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AACA,EAAA;AACI,IAAA;AACA,IAAA;AACF,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AAGA,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACN,EAAA;AACM,IAAA;AACF,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACC,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACM,EAAA;AACN,IAAA;AACF,EAAA;AACF;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AACJ,IAAA;AACH,EAAA;AACF;AAES;AACP,EAAA;AACF;AAES;AACA,EAAA;AACJ,IAAA;AACH,EAAA;AACF;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEMJ,EAAAA;AACA,EAAA;AACC,EAAA;AACA,EAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACE,EAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACE,EAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACE,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACA,EAAA;AACJ,EAAA;AACQ,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACJ,EAAA;AACQ,IAAA;AACR,EAAA;AAEO,EAAA;AACA,IAAA;AACA,IAAA;AACP,EAAA;AACF;AAES;AACA,EAAA;AACD,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACA,IAAA;AACH,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAES;AACH,EAAA;AACA,EAAA;AACG,EAAA;AACC,IAAA;AACN,IAAA;AACA,IAAA;AACF,EAAA;AAC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A,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACE,IAAA;AACJ,IAAA;AACK,IAAA;AACL,IAAA;AAGA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACO,EAAA;AACD,IAAA;AACD,IAAA;AACL,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AAKI,IAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACC,IAAA;AACH,IAAA;AACA,IAAA;AACK,IAAA;AACA,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AAEM,MAAA;AACA,MAAA;AAMF,IAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AACA,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AAEM,IAAA;AACN,IAAA;AACM,MAAA;AACF,QAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AAGI,MAAA;AACF,QAAA;AACF,MAAA;AAEA,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AAEF,EAAA;AACG,EAAA;AACL,IAAA;AACK,IAAA;AAC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,EAAA;AACN,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACN,EAAA;AACA,EAAA;AACI,EAAA;AACI,IAAA;AACN,IAAA;AACA,IAAA;AACF,EAAA;AAEA,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACE,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AAEA,IAAA;AACI,IAAA;AACF,MAAA;AAOA,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AAOA,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACF,IAAA;AACD,EAAA;AACH;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACA,oBAAA;AACF,EAAA;AACI,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACE,sBAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACE,sBAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,qCAAA;AAEF,EAAA;AACI,EAAA;AACF,qCAAA;AAEF,EAAA;AACI,EAAA;AACF,qCAAA;AAEF,EAAA;AACI,EAAA;AACF,qCAAA;AAEF,EAAA;AACI,EAAA;AACF,qCAAA;AAEF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACI,IAAA;AACN,IAAA;AACI,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,E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F,IAAA;AAEA,IAAA;AACI,IAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AA2JS;AACD,EAAA;AACA,EAAA;AAEN,EAAA;AACQ,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACD,EAAA;AAEM,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AACN,EAAA;AACO,EAAA;AACT;AAESC;AACA,EAAA;AACT;AAES;AACAA,EAAAA;AACT;AAESC;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACN,EAAA;AACO,IAAA;AACH,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AAEOD,EAAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACF,EAAA;AACG,EAAA;AACL,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACE,EAAA;AACV;AAES;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAESE;AACD,EAAA;AACAnB,EAAAA;AACFA,EAAAA;AACF,IAAA;AACF,EAAA;AACOA,EAAAA;AACT;AAESoB;AACA,EAAA;AACT;AAESC;AACD,EAAA;AACC,EAAA;AACT;AAES;AACH,EAAA;AACJ,EAAA;AACM,IAAA;AACF,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACA,EAAA;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACC,EAAA;AACT;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ACA,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAGK,EAAA;AACH,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACG,EAAA;AACD,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACD,EAAA;AAEM,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEF,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AAEO,EAAA;AACT;AAES;AAOD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEF,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;A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;AACF,MAAA;AACD,IAAA;AACF,EAAA;AAEK,EAAA;AACN,EAAA;AACQ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACD,IAAA;AACC,MAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AAEA,IAAA;AACM,MAAA;AACF,QAAA;AACE,UAAA;AACA,UAAA;AACA,UAAA;AACF,QAAA;AACA,QAAA;AACE,UAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACD,EAAA;AAEM,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACA,EAAA;AACT;AAESS;AACD,EAAA;AACE,EAAA;AACN,IAAA;AACF,EAAA;AACQ,EAAA;AACN,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACD,EAAA;AACC,EAAA;AACF,IAAA;AACA,IAAA;AACA,IAAA;AACL,EAAA;AACF;AAESK;AACA,EAAA;AAGT;AAES;AAID,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AAEJ,EAAA;AACQ,IAAA;AACA,IAAA;AACN,IAAA;AACE,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACJ,IAAA;AACD,EAAA;AAEG,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACN,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACC,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACP,EAAA;AACM,EAAA;AACC,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACF,IAAA;AACF,EAAA;AACE,IAAA;AACF,EAAA;AACQ,IAAA;AACA,IAAA;AACA,IAAA;AACN,IAAA;AACF,EAAA;AACE,IAAA;AACF,EAAA;AAEK,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAWS;AACF,EAAA;AACH,IAAA;AACE,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACC,EAAA;AACC,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACA,EAAA;AACA,IAAA;AACH,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACG,EAAA;AACP;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEC,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAEgB;AACV,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACC,IAAA;AACL,IAAA;AACK,IAAA;AACL,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACM,IAAA;AACF,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAEgB;AAIV,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACC,IAAA;AACL,IAAA;AACK,IAAA;AACL,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACI,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACM,IAAA;AACR,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACQ,EAAA;AACD,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACJ,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACO,EAAA;AACC,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACA,EAAA;AACL,IAAA;AACM,IAAA;AACN,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACQ,EAAA;AACD,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACF,IAAA;AACE,MAAA;AACJ,EAAA;AACF;AAES;AAID,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AAGA,IAAA;AACF,EAAA;AACF;AAES;AAIC,EAAA;AACD,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACJ,EAAA;AACF;AAES;AAMA,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACI,IAAA;AACJ,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACG,IAAA;AACL,EAAA;AACF;AAEgB;AAIP,EAAA;AACD,IAAA;AACF,MAAA;AACA,MAAA;AAKF,IAAA;AACE,MAAA;AACF,IAAA;AACD,EAAA;AACH;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AAIC,EAAA;AACT;AAES;AACH,EAAA;AACI,IAAA;AACA,IAAA;AACN,IAAA;AACF,EAAA;AAEI,EAAA;AACI,IAAA;AACA,IAAA;AACN,IAAA;AACF,EAAA;AAEI,EAAA;AACI,IAAA;AACAS,IAAAA;AACN,IAAA;AAEM,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AAEF,IAAA;AACN,EAAA;AAEM,EAAA;AACA,EAAA;AACC,EAAA;AAEK,IAAA;AACN,IAAA;AACK,MAAA;AACA,MAAA;AACL,IAAA;AACA,IAAA;AACM,MAAA;AACA,MAAA;AACN,IAAA;AAEF,EAAA;AACN;AAES;AACH,EAAA;AACF,IAAA;AACM,MAAA;AACA,MAAA;AACD,MAAA;AACA,MAAA;AACL,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACM,MAAA;AACA,MAAA;AACD,MAAA;AACA,MAAA;AACL,IAAA;AACF,EAAA;AAEO,EAAA;AACD,IAAA;AACA,IAAA;AACD,IAAA;AACA,IAAA;AACL,EAAA;AACF;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACJ,EAAA;AACE,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACJ,EAAA;AACE,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACM,MAAA;AACA,MAAA;AACD,MAAA;AACA,MAAA;AACL,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEC,EAAA;AACD,IAAA;AACA,IAAA;AACD,IAAA;AACA,IAAA;AACL,EAAA;AACF;AAES;AACA,EAAA;AACC,IAAA;AACN,IAAA;AACK,MAAA;AACA,MAAA;AACL,IAAA;AACA,IAAA;AACM,MAAA;AACA,MAAA;AACN,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACD,IAAA;AACA,IAAA;AA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IAAA;AACI,IAAA;AACE,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AAiBD,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AACA,IAAA;AACF,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACA,MAAA;AACF,QAAA;AACA,QAAA;AACA,QAAA;AACE,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACD,QAAA;AACH,MAAA;AACA,MAAA;AACF,IAAA;AAEI,IAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACD,MAAA;AACD,MAAA;AACF,IAAA;AAEI,IAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACD,MAAA;AACD,MAAA;AACF,IAAA;AAEI,IAAA;AACF,MAAA;AACF,IAAA;AAEM,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACM,IAAA;AACA,MAAA;AACA,MAAA;AACD,MAAA;AACA,MAAA;AACJ,IAAA;AACK,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACI,MAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACD,EAAA;AAEM,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AAWD,EAAA;AACD,EAAA;AACH,IAAA;AACE,MAAA;AACA,MAAA;AACA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ACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACA,EAAA;AACF,IAAA;AACM,EAAA;AACN,IAAA;AACF,EAAA;AACM,EAAA;AACA,EAAA;AACN,EAAA;AACO,IAAA;AACH,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACD,EAAA;AACN,EAAA;AACM,IAAA;AACA,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACM,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AACA,IAAA;AACM,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACA,MAAA;AACF,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACA,EAAA;AACD,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACD,EAAA;AACH;AAsBgB;AACV,EAAA;AACF,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACE,IAAA;AACI,IAAA;AACE,MAAA;AACF,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AACI,EAAA;AACE,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAEgB;AACV,EAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AACE,IAAA;AACA,IAAA;AACF,EAAA;AACF;AX01NU;AACA;AYn/NM;AACR,EAAA;AACF,EAAA;AACD,EAAA;AACD,IAAA;AACA,IAAA;AACM,MAAA;AAEF,QAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACI,MAAA;AAEF,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACA,EAAA;AACG,EAAA;AACC,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACE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AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACJ,EAAA;AACF;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACA,EAAA;AACL,IAAA;AACI,IAAA;AACJ,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAMS;AACA,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AAKN,EAAA;AACQ,IAAA;AAIF,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAES;AAIH,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACQ,EAAA;AACD,IAAA;AACA,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AAKG,IAAA;AACA,IAAA;AACH,MAAA;AACF,IAAA;AACE,MAAA;AACJ,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACA,IAAA;AACA,IAAA;AACP,EAAA;AACF;AAES;AACD,EAAA;AAEC,EAAA;AACA,IAAA;AACH,MAAA;AACF,IAAA;AAEM,IAAA;AACA,IAAA;AAEI,MAAA;AACF,QAAA;AACF,MAAA;AAEA,MAAA;AACA,MAAA;AAED,IAAA;AACC,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AAEA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AAC4B,IAAA;AAC/B,EAAA;AACH;AAES;AACA,EAAA;AACL,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACK,IAAA;AACH,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AAEJ,EAAA;AAKJ;AAES;AACA,EAAA;AAET;AAES;AAID,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AAMA,IAAA;AACA,IAAA;AACA,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AAEA,IAAA;AACE,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACD,IAAA;AACF,EAAA;AAEK,EAAA;AACA,EAAA;AACE,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AAEM,IAAA;AACA,IAAA;AACJ,MAAA;AAGA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACD,IAAA;AAED,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACmB,IAAA;AACtB,EAAA;AAEO,EAAA;AACV;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACM,IAAA;AACR,EAAA;AACM,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACM,IAAA;AACR,EAAA;AAEO,EAAA;AACT;AAES;AACA,EAAA;AAQT;AAESC;AACD,EAAA;AACN,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACI,IAAA;AACA,IAAA;AACF,IAAA;AACF,MAAA;AAGA,MAAA;AACF,IAAA;AAEA,IAAA;AACM,EAAA;AACN,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AACF,EAAA;AACI,IAAA;AACR,EAAA;AACF;AAES;AACA,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACE,IAAA;AACF,MAAA;AACF,IAAA;AAEA,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACM,EAAA;AACN,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AAEA,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AAKH,EAAA;AACF,IAAA;AACK,EAAA;AACL,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AACF;AAES;AACA,EAAA;AAMT;AAES;AACD,EAAA;AACA,EAAA;AAEF,EAAA;AACG,IAAA;AACP,EAAA;AAEO,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;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACJ,IAAA;AACE,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACA,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AAEM,EAAA;AACE,IAAA;AACD,IAAA;AACH,MAAA;AACF,IAAA;AAEA,IAAA;AACK,MAAA;AACH,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACD,EAAA;AAEM,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AAST;AAES;AACA,EAAA;AACT;AAES;AACD,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AACF,IAAA;AACE,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,yBAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACF,IAAA;AACH,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACA,EAAA;AACP,EAAA;AACO,EAAA;AACA,EAAA;AACP,EAAA;AACF;AAES;AACD,EAAA;AACN,EAAA;AACM,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACA,EAAA;AACP,EAAA;AACO,EAAA;AACA,EAAA;AACP,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACA,EAAA;AACP,EAAA;AACO,EAAA;AACA,EAAA;AACP,EAAA;AACF;AAES;AACD,EAAA;AACC,EAAA;AACA,EAAA;AACA,EAA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A;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AAED,IAAA;AACK,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACA,MAAA;AACD,IAAA;AACD,EAAA;AAEE,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACE,EAAA;AAEE,EAAA;AACE,IAAA;AACA,IAAA;AACJ,MAAA;AACA,MAAA;AACA,sBAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACF,IAAA;AACA,IAAA;AACM,IAAA;AACJ,MAAA;AACA,sBAAA;AACA,sBAAA;AACA,sBAAA;AACA,sBAAA;AACA,sBAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACA,IAAA;AACE,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACA,IAAA;AACE,EAAA;AAEkB,EAAA;AACpB,oBAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACE,EAAA;AAEkB,EAAA;AACf,IAAA;AACH,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AAEI,IAAA;AACE,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAA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A;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAA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,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF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F,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAIS;AACD,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACC,EAAA;AACA,EAAA;AACC,EAAA;AAGT;AAEgB;AAEZ,EAAA;AAMJ;A1Bo8aU;AACA;A2Bl7aJ;AAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYzBG;AACAC;AACA;AACA;AACAC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AACI,EAAA;AACR,EAAA;AACM,EAAA;AACC,EAAA;AACA,EAAA;AACT;AACM;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAmBA;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACA,EAAA;AACD,EAAA;AACC,EAAA;AACA,EAAA;AACD,EAAA;AACL;AAEM;AACJ,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACD;AAEK;AACA;AACA;AACA;AACA;AAEG;AACH,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACM,EAAA;AACC,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;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;AAES;AAKD,EAAA;AACF,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACI,MAAA;AACF,QAAA;AACE,UAAA;AACA,UAAA;AACA,QAAA;AACF,QAAA;AACE,UAAA;AACD,QAAA;AACD,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACI,IAAA;AACA,IAAA;AACA,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACM,IAAA;AACF,IAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,MAAA;AACF,MAAA;AACE,QAAA;AACD,MAAA;AACD,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AAQH,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACN,EAAA;AACA,EAAA;AACO,EAAA;AACT;AAES;AAOC,EAAA;AACJ,EAAA;AACF,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AAEI,EAAA;AACI,IAAA;AACA,IAAA;AACA,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AAEQ,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACV;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACD,EAAA;AACE,EAAA;AACD,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACF,IAAA;AACE,MAAA;AACJ,EAAA;AACF;AAES;AACA,EAAA;AACF,IAAA;AACA,IAAA;AACL,EAAA;AACF;AAES;AACA,EAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AACO,EAAA;AACC,IAAA;AACN,IAAA;AACK,IAAA;AACP,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AACA,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACK,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAEa;AACA;AAsFJ;AACA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AAGT;AAESC;AACA,EAAA;AACL,IAAA;AACE,MAAA;AACA,MAAA;AACF,IAAA;AACK,IAAA;AACH,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACD,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AAEJ,EAAA;AAKJ;AAES;AAIH,EAAA;AAEJ,EAAA;AACO,IAAA;AACH,MAAA;AACF,IAAA;AAEM,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AAEJ,EAAA;AAKJ;AAES;AACA,EAAA;AACT;AAES;AACD,EAAA;AACJ,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AACC,EAAA;AACT;AA0BS;AACD,EAAA;AACN,EAAA;AACE,IAAA;AACF,EAAA;AACO,EAAA;AACT;AASS;AACP,EAAA;AACM,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;A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IAAA;AACN,IAAA;AAEF,EAAA;AACA,EAAA;AAGQ,IAAA;AACN,IAAA;AAEF,EAAA;AAEA,EAAA;AACA,EAAA;AAEF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACN,EAAA;AAEE,EAAA;AACA,EAAA;AACF,IAAA;AACF,EAAA;AACE,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AAMA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AAGT;AAES;AACD,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACA,EAAA;AACC,IAAA;AACR,EAAA;AACF;AAES;AACA,EAAA;AACC,IAAA;AACJ,MAAA;AACA,MAAA;AACD,IAAA;AACH,EAAA;AACF;AAES;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACA,IAAA;AACA,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AACA,EAAA;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACL,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACK,EAAA;AACT;AAES;AACA,EAAA;AACA,EAAA;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACA,IAAA;AACL,IAAA;AACA,IAAA;AACK,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACA,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACG,IAAA;AACA,IAAA;AACH,IAAA;AACF,EAAA;AACH;AAES;AACA,EAAA;AACC,IAAA;AACD,IAAA;AACC,IAAA;AACA,IAAA;AACD,IAAA;AACA,IAAA;AACC,IAAA;AACP,EAAA;AACH;AAES;AACA,EAAA;AACC,IAAA;AACD,IAAA;AACC,IAAA;AACA,IAAA;AACD,IAAA;AACA,IAAA;AACC,IAAA;AACD,IAAA;AACN,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACD,IAAA;AACH,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACF,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACL,IAAA;AACA,IAAA;AACG,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACI,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACC,IAAA;AACL,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACK,IAAA;AACP,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACD,IAAA;AACH,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACG,IAAA;AACL,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACC,IAAA;AACA,IAAA;AACD,IAAA;AACA,IAAA;AACC,IAAA;AACA,IAAA;AACD,IAAA;AACJ,IAAA;AACA,IAAA;AACF,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACG,IAAA;AACE,IAAA;AACA,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACF,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACI,IAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACF,EAAA;AACH;AAES;AACA,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,EAAA;AACT;AAES;AACA,EAAA;AACC,IAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACC,IAAA;AACP,EAAA;AACH;AAES;AACA,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,EAAA;AACT;AAES;AACA,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACD,IAAA;AACH,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACG,IAAA;AACL,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACC,IAAA;AACL,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACK,IAAA;AACP,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACJ,IAAA;AACA,IAAA;AACG,IAAA;AACE,IAAA;AACA,IAAA;AACL,IAAA;AACA,IAAA;AACF,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACJ,IAAA;AACA,IAAA;AACG,IAAA;AACC,IAAA;AACC,IAAA;AACL,IAAA;AACA,IAAA;AACF,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACF,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACJ,IAAA;AACA,IAAA;AACG,IAAA;AACC,IAAA;AACC,IAAA;AACL,IAAA;AACA,IAAA;AACK,IAAA;AACA,IAAA;AACF,IAAA;AACL,EAAA;AACH;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACD,IAAA;AACH,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACG,IAAA;AACL,EAAA;AACH;AAES;AACC,EAAA;AACD,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACA,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,IAAA;AACH,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACG,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AACA,IAAA;AACA,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACM,IAAA;AACR,EAAA;AAEO,EAAA;AACT;AAES;AACA,EAAA;AACT;AAESnC;AACD,EAAA;AACA,EAAA;AACF,EAAA;AACI,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACCG,EAAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACA,EAAA;AACT;AAES;AACD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACC,IAAA;AACN,IAAA;AACD,EAAA;AAEM,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACE,EAAA;AACV;AAES;AAKF,EAAA;AACH,IAAA;AACF,EAAA;AAEK,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAiCSD;AACH,EAAA;AACA,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAESC;AACH,EAAA;AACI,IAAA;AACN,IAAA;AACF,EAAA;AAEMZ,EAAAA;AACA,EAAA;AAEC,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACP,EAAA;AACF;AAmBS;AACA,EAAA;AACT;AAES;AAID,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AAID,EAAA;AACJ,IAAA;AACA,qCAAA;AACA,EAAA;AAEE,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACJ,EAAA;AAEO,EAAA;AACF,IAAA;AACH,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACO,EAAA;AACT;AAES;AAMD,EAAA;AACA,EAAA;AAGA,EAAA;AACA,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AAEK,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACA,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACE,IAAA;AACE,MAAA;AACN,IAAA;AACI,IAAA;AACE,MAAA;AACN,IAAA;AACI,IAAA;AACE,MAAA;AACN,IAAA;AACI,IAAA;AACE,MAAA;AACN,IAAA;AACM,IAAA;AACF,IAAA;AACF,MAAA;AACI,MAAA;AACN,IAAA;AACI,IAAA;AACF,MAAA;AACI,MAAA;AACN,IAAA;AAEM,IAAA;AACF,IAAA;AACE,MAAA;AACN,IAAA;AACF,EAAA;AAEI,EAAA;AAEA,EAAA;AACI,IAAA;AACF,IAAA;AACN,EAAA;AACM,IAAA;AACN,EAAA;AAEM,EAAA;AACF,EAAA;AACE,IAAA;AACE,MAAA;AACN,IAAA;AACI,IAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACI,IAAA;AACE,MAAA;AACA,MAAA;AACN,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACI,IAAA;AACA,IAAA;AACF,IAAA;AACE,MAAA;AACF,QAAA;AACA,QAAA;AACD,MAAA;AACH,IAAA;AACI,IAAA;AACE,MAAA;AACN,IAAA;AACI,IAAA;AACE,MAAA;AACN,IAAA;AACI,IAAA;AACE,MAAA;AACN,IAAA;AACI,IAAA;AACE,MAAA;AACN,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAEI;AAEK;AACH,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AAMF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACF,IAAA;AACF,EAAA;AAEA,EAAA;AACM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEQ,EAAA;AACD,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEF,EAAA;AACF,IAAA;AACF,EAAA;AAEA,EAAA;AACM,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEQ,EAAA;AACF,EAAA;AACE,EAAA;AACV;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AAEJ,EAAA;AAOE,IAAA;AACJ,EAAA;AAEO,EAAA;AACT;AAES;AACC,EAAA;AACV;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACI,IAAA;AACN,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACF,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AAEE,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACC,EAAA;AACT;AAsBS2B;AACA,EAAA;AACD,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACC,EAAA;AAGT;AAES;AACH,EAAA;AACJ,EAAA;AACQ,IAAA;AACF,IAAA;AACE,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AAMH,EAAA;AACC,EAAA;AACH,IAAA;AACF,EAAA;AAEK,EAAA;AACC,IAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AAEA,IAAA;AACF,EAAA;AAEM,EAAA;AACA,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACI,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACI,MAAA;AACF,QAAA;AACF,MAAA;A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AACE,YAAA;AACF,UAAA;AACF,QAAA;AACF,MAAA;AACA,MAAA;AACD,IAAA;AACD,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACJ,EAAA;AACM,IAAA;AACF,MAAA;AACF,IAAA;AACD,EAAA;AAEK,EAAA;AAGA,EAAA;AACC,EAAA;AACT;AAES;AAKD,EAAA;AACD,EAAA;AACH,IAAA;AACF,EAAA;AAEM,EAAA;AACH,IAAA;AACH,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACC,EAAA;AACT;AAES;AACH,EAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACF,EAAA;AAEA,EAAA;AACQ,IAAA;AACF,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAiDS;AACP,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACe;AACT,EAAA;AAGJ,EAAA;AACE,oBAAA;AAAC,MAAA;AAAA,MAAA;AACC,QAAA;AACA,QAAA;AACA,QAAA;AACE,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AAC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CA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AAEA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACC,MAAA;AACA,MAAA;AACD,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACC,MAAA;AACA,MAAA;AACD,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACC,MAAA;AACA,MAAA;AACD,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACC,MAAA;AACA,MAAA;AACD,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACE,EAAA;AACE,EAAA;AAME,IAAA;AACA,IAAA;AAGN,IAAA;AACM,IAAA;AACF,IAAA;AACE,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AA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ACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACEiC,UAAAA;AACA,UAAA;AACA,UAAA;AACF,QAAA;AACF,MAAA;AAEI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACE,UAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACE,UAAA;AACF,QAAA;AACA,QAAA;AAIE,UAAA;AACF,QAAA;AACA,QAAA;AAIE,UAAA;AACF,QAAA;AAEA,QAAA;AAIA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AAEA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACI,IAAA;AACF,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACF,IAAA;AACA,IAAA;AACI,IAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACD,MAAA;AACG,MAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACC,EAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAA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","file":"/home/runner/work/react-xlsx/react-xlsx/packages/react-xlsx/dist/chunk-KQSUAIZD.cjs","sourcesContent":[null,"import type { XlsxCellAddress, XlsxParsedRange } from \"./types\";\n\nexport const XLSX_MAX_ROWS = 1_048_576;\nexport const XLSX_MAX_COLUMNS = 16_384;\n\nfunction parseColumn(value: string): number | null {\n let column = 0;\n for (const character of value.toUpperCase()) {\n column = column * 26 + character.charCodeAt(0) - 64;\n }\n return column > 0 && column <= XLSX_MAX_COLUMNS ? column - 1 : null;\n}\n\nfunction parseRow(value: string): number | null {\n const row = Number(value);\n return row > 0 && row <= XLSX_MAX_ROWS ? row - 1 : null;\n}\n\nfunction parseCell(value: string): XlsxCellAddress | null {\n const match = /^\\$?([a-z]{1,3})\\$?([1-9]\\d{0,6})$/i.exec(value);\n if (!match) {\n return null;\n }\n const col = parseColumn(match[1]);\n const row = parseRow(match[2]);\n return col === null || row === null ? null : { col, row };\n}\n\n/** Parses one rectangular A1 reference into normalized, zero-based coordinates. */\nexport function parseXlsxRange(reference: string): XlsxParsedRange | null {\n if (typeof reference !== \"string\") {\n return null;\n }\n let address = reference.trim().replace(/^=\\s*/, \"\");\n let sheetName: string | null = null;\n if (address.startsWith(\"'\")) {\n const match = /^'((?:[^']|'')+)'!(.+)$/.exec(address);\n if (!match) {\n return null;\n }\n sheetName = match[1].replace(/''/g, \"'\");\n address = match[2];\n } else if (address.includes(\"!\")) {\n const match = /^([^\\s'!\\[\\]:*?/\\\\+,;(){}=<>\"&^%\\-]+)!(.+)$/.exec(address);\n if (!match) {\n return null;\n }\n sheetName = match[1];\n address = match[2];\n }\n if (sheetName !== null && (\n sheetName.length > 31 || /[\\[\\]:*?/\\\\\\u0000-\\u001f]/.test(sheetName)\n || sheetName.startsWith(\"'\") || sheetName.endsWith(\"'\")\n )) {\n return null;\n }\n\n const parts = address.split(\":\");\n if (parts.length > 2) {\n return null;\n }\n const start = parseCell(parts[0]);\n const end = parseCell(parts[1] ?? parts[0]);\n if (start && end) {\n return {\n kind: \"cells\",\n range: {\n start: { col: Math.min(start.col, end.col), row: Math.min(start.row, end.row) },\n end: { col: Math.max(start.col, end.col), row: Math.max(start.row, end.row) }\n },\n sheetName\n };\n }\n\n const columns = /^\\$?([a-z]{1,3}):\\$?([a-z]{1,3})$/i.exec(address);\n if (columns) {\n const first = parseColumn(columns[1]);\n const last = parseColumn(columns[2]);\n if (first !== null && last !== null) {\n return {\n kind: \"columns\",\n range: {\n start: { col: Math.min(first, last), row: 0 },\n end: { col: Math.max(first, last), row: XLSX_MAX_ROWS - 1 }\n },\n sheetName\n };\n }\n }\n\n const rows = /^\\$?([1-9]\\d{0,6}):\\$?([1-9]\\d{0,6})$/.exec(address);\n if (rows) {\n const first = parseRow(rows[1]);\n const last = parseRow(rows[2]);\n if (first !== null && last !== null) {\n return {\n kind: \"rows\",\n range: {\n start: { col: 0, row: Math.min(first, last) },\n end: { col: XLSX_MAX_COLUMNS - 1, row: Math.max(first, last) }\n },\n sheetName\n };\n }\n }\n return null;\n}\n","export function formatBinaryBytes(bytes: number) {\n if (!Number.isFinite(bytes) || bytes <= 0) return \"0 B\";\n\n const units = [\"B\", \"KB\", \"MB\", \"GB\"];\n let value = bytes;\n let unitIndex = 0;\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex += 1;\n }\n return `${value >= 10 || unitIndex === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[unitIndex]}`;\n}\n\nexport class XlsxFileSizeLimitExceededError extends Error {\n fileSizeBytes: number;\n maxFileSizeBytes: number;\n\n constructor(fileSizeBytes: number, maxFileSizeBytes: number) {\n super(\n `XLSX file size ${formatBinaryBytes(fileSizeBytes)} exceeds the configured limit of ${formatBinaryBytes(maxFileSizeBytes)}.`\n );\n this.name = \"XlsxFileSizeLimitExceededError\";\n this.fileSizeBytes = fileSizeBytes;\n this.maxFileSizeBytes = maxFileSizeBytes;\n }\n}\n","let wasmModulePromise: Promise<typeof import(\"@dukelib/sheets-wasm\")> | null = null;\n\nexport type XlsxWasmSource =\n | string\n | URL\n | Request\n | Response\n | BufferSource\n | WebAssembly.Module;\n\nexport type WorkerWasmSource = string | ArrayBuffer | WebAssembly.Module;\n\nlet hasConfiguredWasmSource = false;\nlet configuredWasmSource: XlsxWasmSource | undefined;\nlet configuredWorkerWasmSource: WorkerWasmSource | undefined;\n\nfunction bufferSourceToArrayBuffer(source: ArrayBuffer | ArrayBufferView<ArrayBufferLike>): ArrayBuffer {\n if (source instanceof ArrayBuffer) {\n return source.slice(0);\n }\n\n const bytes = new Uint8Array(source.buffer, source.byteOffset, source.byteLength);\n const copy = new Uint8Array(bytes);\n return copy.buffer;\n}\n\nfunction sourceToWorkerSource(source: XlsxWasmSource): WorkerWasmSource | undefined {\n if (typeof source === \"string\") {\n return source;\n }\n if (typeof URL !== \"undefined\" && source instanceof URL) {\n return source.href;\n }\n if (typeof Request !== \"undefined\" && source instanceof Request) {\n return source.url;\n }\n if (source instanceof ArrayBuffer || ArrayBuffer.isView(source)) {\n return bufferSourceToArrayBuffer(source);\n }\n if (typeof WebAssembly !== \"undefined\" && source instanceof WebAssembly.Module) {\n return source;\n }\n\n return undefined;\n}\n\nexport function setWasmSource(source: XlsxWasmSource): void {\n hasConfiguredWasmSource = true;\n configuredWasmSource = source;\n configuredWorkerWasmSource = sourceToWorkerSource(source);\n}\n\nexport function initWasm(source?: XlsxWasmSource) {\n if (source !== undefined) {\n setWasmSource(source);\n }\n\n return getSheetsWasmModule();\n}\n\nexport function canUseConfiguredWasmSourceInWorker(): boolean {\n return !hasConfiguredWasmSource || configuredWorkerWasmSource !== undefined;\n}\n\nexport function getConfiguredWorkerWasmSource(): WorkerWasmSource | undefined {\n return configuredWorkerWasmSource;\n}\n\nexport function getSheetsWasmModule() {\n if (!wasmModulePromise) {\n wasmModulePromise = import(\"@dukelib/sheets-wasm\").then(async (mod) => {\n if (configuredWasmSource !== undefined) {\n await mod.default({ module_or_path: configuredWasmSource });\n } else {\n await mod.default();\n }\n return mod;\n });\n }\n\n return wasmModulePromise;\n}\n","import type { Worksheet } from \"@dukelib/sheets-wasm\";\n\nconst separators = new Map<string, { decimal: string; group: string }>();\n\nexport function parseLocaleNumber(text: string, locale: string): number | undefined {\n if (locale === \"en-US\") return undefined;\n const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);\n const decimal = parts.find((part) => part.type === \"decimal\")?.value ?? \".\";\n const group = parts.find((part) => part.type === \"group\")?.value ?? \",\";\n let normalized = text.trim();\n const percent = normalized.endsWith(\"%\");\n if (percent) normalized = normalized.slice(0, -1).trim();\n normalized = group.trim() === \"\" ? normalized.replace(/[ \\u00a0\\u202f]/g, \"\") : normalized.split(group).join(\"\");\n normalized = normalized.replace(decimal, \".\");\n if (!/^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?$/.test(normalized)) return undefined;\n const value = Number(normalized) / (percent ? 100 : 1);\n return Number.isFinite(value) ? value : undefined;\n}\n\nexport function localizeCellNumber(worksheet: Worksheet, row: number, col: number, text: string, locale = \"en-US\"): string {\n if (locale === \"en-US\" || !text) return text;\n const value = worksheet.getCalculatedValueAt(row, col);\n try {\n if (!value.is_number) return text;\n } finally {\n value.free();\n }\n const style = worksheet.getCellStyleAt(row, col) as {\n numberFormat?: { isDateFormat?: boolean; formatString?: string };\n } | null;\n const format = style?.numberFormat;\n if (format?.isDateFormat) return text;\n\n let symbols = separators.get(locale);\n if (!symbols) {\n const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);\n symbols = {\n decimal: parts.find((part) => part.type === \"decimal\")?.value ?? \".\",\n group: parts.find((part) => part.type === \"group\")?.value ?? \",\"\n };\n if (separators.size >= 100) separators.clear();\n separators.set(locale, symbols);\n }\n if (symbols.decimal === \".\" && symbols.group === \",\") return text;\n\n const protectedPositions = new Set<number>();\n const code = format?.formatString ?? \"\";\n const literalPattern = /\"[^\"]*\"|\\\\.|\\[[^\\]]*\\]/g;\n const firstPlaceholder = code.replace(literalPattern, (token) => \" \".repeat(token.length)).search(/[0#?]/);\n const literals = [...code.matchAll(literalPattern)];\n let suffixEnd = text.length;\n let prefixStart = 0;\n const protect = (token: string, suffix: boolean) => {\n const literal = token.startsWith('\"') ? token.slice(1, -1)\n : token.startsWith(\"\\\\\") ? token.slice(1)\n : token.startsWith(\"[$\") ? token.slice(2, -1).split(\"-\")[0] : \"\";\n if (!literal) return;\n const offset = suffix ? text.lastIndexOf(literal, suffixEnd - literal.length) : text.indexOf(literal, prefixStart);\n if (offset >= 0) {\n for (let i = offset; i < offset + literal.length; i++) protectedPositions.add(i);\n if (suffix) suffixEnd = offset;\n else prefixStart = offset + literal.length;\n }\n };\n for (const token of literals.filter((token) => token.index < firstPlaceholder)) protect(token[0], false);\n for (const token of literals.filter((token) => token.index >= firstPlaceholder).reverse()) protect(token[0], true);\n return text.replace(/\\d[\\d,]*(?:\\.\\d*)?|\\.\\d+/g, (number, offset: number) =>\n number.replace(/[.,]/g, (separator, index: number) => protectedPositions.has(offset + index)\n ? separator\n : separator === \".\" ? symbols.decimal : symbols.group)\n );\n}\n\nexport function localizeRows(worksheet: Worksheet, rows: unknown, locale = \"en-US\"): void {\n if (locale === \"en-US\" || !Array.isArray(rows)) return;\n for (const row of rows) {\n for (const cell of row.cells ?? []) {\n if (!cell.isMergedSecondary && typeof cell.value === \"string\") {\n const value = worksheet.getCalculatedValueAt(row.index, cell.col);\n try {\n cell.numericValue = value.is_number ? value.asNumber() : null;\n } finally {\n value.free();\n }\n cell.value = localizeCellNumber(worksheet, row.index, cell.col, cell.value, locale);\n }\n }\n }\n}\n","let fontLoadPromise: Promise<void> | undefined;\n\nexport const SPREADSHEET_FALLBACK_FONT = \"React XLSX Carlito\";\n\nexport function loadSpreadsheetFonts(): Promise<void> {\n if (typeof document === \"undefined\" || typeof FontFace === \"undefined\") {\n return Promise.resolve();\n }\n return fontLoadPromise ??= import(\"./spreadsheet-font-assets\").then(async ({ spreadsheetFontFaces }) => {\n await Promise.allSettled(spreadsheetFontFaces.map(async ({ source, ...descriptors }) => {\n const face = new FontFace(SPREADSHEET_FALLBACK_FONT, `url(${JSON.stringify(source)})`, descriptors);\n await face.load();\n document.fonts.add(face);\n }));\n }).catch(() => {});\n}\n","import { localizeCellNumber, parseLocaleNumber } from \"./number-locale\";\nimport { loadSpreadsheetFonts } from \"./spreadsheet-fonts\";\nimport * as React from \"react\";\nimport { formatBinaryBytes, XlsxFileSizeLimitExceededError } from \"./errors\";\nexport { XlsxFileSizeLimitExceededError } from \"./errors\";\nimport type {\n DrawingAnchor as DukeDrawingAnchor,\n DrawingColor as DukeDrawingColor,\n DrawingInput as DukeDrawingInput,\n DrawingRunFont as DukeDrawingRunFont,\n DrawingText as DukeDrawingText,\n FormControlDrawing as DukeFormControlDrawing,\n FormControlKind as DukeFormControlKind,\n FormControlKindInput as DukeFormControlKindInput,\n ImageDrawing as DukeImageDrawing,\n Workbook\n} from \"@dukelib/sheets-wasm\";\nimport { strFromU8, strToU8, unzipSync, zipSync } from \"fflate\";\nimport {\n applyChartSeriesFormula,\n buildChartSeriesFormula,\n hydrateWorkbookChartStyles,\n loadWorkbookChartAssets,\n updateWorkbookChartAnchor,\n updateWorkbookChartDefinition,\n type WorkbookChartAssets\n} from \"./charts\";\nimport { resolveWorkbookColor, resolveWorkbookFillStyle } from \"./colors\";\nimport {\n collectWorkbookFormControls,\n dukeDrawingAnchorToXlsxAnchor,\n mergeWorkbookImageAssets,\n parseWorkbookImageAssets,\n pxToSheetColumnWidth,\n rectToImageAnchor,\n refreshWorkbookFormControls,\n resolveContentSheetAxisPixels,\n resolveWorksheetDefaultColumnWidthPixels,\n resolveWorksheetDefaultRowHeightPixels,\n resolveWorksheetHiddenCols,\n resolveWorksheetHiddenRows,\n resolveWorksheetMergeMetadata,\n resolveSheetColumnWidthPixels,\n resolveRenderedSheetAxisPixels,\n resolveSheetRowHeightPixels,\n resizeImageRect,\n revokeWorkbookImageAssets,\n updateWorkbookImageAnchor,\n xlsxAnchorToDukeDrawingAnchor,\n type WorkbookImageAssets,\n type WorkbookImageSheetOrigin\n} from \"./images\";\nimport { safeCalculate, tryRecalculate } from \"./safe-calculate\";\nimport { parseXlsxRange } from \"./range-reference\";\nimport { expandHighlightedRange } from \"./range-highlight\";\nimport { canUseConfiguredWasmSourceInWorker, getSheetsWasmModule } from \"./wasm\";\nimport { XlsxWorkerClient, XlsxWorkerUnavailableError } from \"./worker-client\";\nimport { normalizeWorkbookArrayBuffer } from \"./zip-entry-names\";\nimport type {\n UseXlsxViewerControllerOptions,\n XlsxChart,\n XlsxChartElementSelection,\n XlsxChartsheet,\n XlsxCellAddress,\n XlsxCellRange,\n XlsxMergeMode,\n XlsxCellStyleColorInput,\n XlsxCellStyleInput,\n XlsxClipboardData,\n XlsxConditionalFormatRule,\n XlsxDataValidation,\n XlsxDrawingLayout,\n XlsxFormControl,\n XlsxFormControlCaptionInput,\n XlsxFormControlInput,\n XlsxFormControlPatch,\n XlsxFreezePanes,\n XlsxImage,\n XlsxImageRect,\n XlsxImageResizeHandlePosition,\n XlsxHighlightRangeOptions,\n XlsxRangeHighlight,\n XlsxResolvedCellStyle,\n XlsxShape,\n XlsxSheetData,\n XlsxSheetVisibility,\n XlsxSparkline,\n XlsxThemePalette,\n XlsxTable,\n XlsxTableStyleDefinition,\n XlsxTableSortDirection,\n XlsxTableSortState,\n XlsxViewerController,\n XlsxWorkbookTab\n} from \"./types\";\n\nconst FORMULA_COUNT_THRESHOLD = 1000;\nconst DEFAULT_ROW_HEIGHT = 24;\nconst DEFAULT_COL_WIDTH = 80;\nconst XLSX_MIME_TYPE = \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\";\nconst CSV_MIME_TYPE = \"text/csv;charset=utf-8\";\nconst MIN_COL_WIDTH_PX = 30;\nconst MIN_ROW_HEIGHT_PX = 16;\nconst GRID_HEADER_HEIGHT = 24;\nconst GRID_ROW_HEADER_WIDTH = 40;\nconst HISTORY_LIMIT = 100;\nconst INTERNAL_CLIPBOARD_MIME = \"application/x-react-xlsx-range+json\";\nconst DEFAULT_DEFER_LOADING_ABOVE_BYTES = 0;\nconst DEFAULT_MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;\nconst MAX_INTERACTIVE_WORKSHEET_XML_BYTES = 200 * 1024 * 1024;\nconst MAX_INTERACTIVE_SHARED_STRINGS_BYTES = 50 * 1024 * 1024;\nconst MAX_INTERACTIVE_TOTAL_XML_BYTES = 256 * 1024 * 1024;\nconst EMU_PER_PIXEL = 9525;\nconst DEFAULT_ZOOM_SCALE = 100;\nconst MIN_ZOOM_SCALE = 10;\nconst MAX_ZOOM_SCALE = 400;\nconst ZOOM_STEP = 10;\nconst DEFAULT_ZOOM_TAB_KEY = \"__default__\";\n\nfunction normalizeWorksheetVisibility(value: unknown): XlsxSheetVisibility {\n return value === \"hidden\" || value === \"veryHidden\" ? value : \"visible\";\n}\n\ntype IdleRequestHandle = number;\n\ntype IdleDeadline = {\n didTimeout: boolean;\n timeRemaining: () => number;\n};\n\ntype IdleWindow = Window & {\n cancelIdleCallback?: (handle: IdleRequestHandle) => void;\n requestIdleCallback?: (\n callback: (deadline: IdleDeadline) => void,\n options?: {\n timeout: number;\n }\n ) => IdleRequestHandle;\n};\n\ntype SnapshotHistoryEntry = {\n kind: \"snapshot\";\n activeCell: XlsxCellAddress | null;\n activeSheetIndex: number;\n bytes: Uint8Array;\n selection: XlsxCellRange | null;\n};\n\ntype CellMutationState = {\n formula: string | null;\n style: unknown;\n value: unknown;\n};\n\ntype CellEditHistoryEntry = {\n kind: \"cell-edit\";\n activeCellAfter: XlsxCellAddress | null;\n activeCellBefore: XlsxCellAddress | null;\n after: CellMutationState;\n before: CellMutationState;\n cell: XlsxCellAddress;\n selectionAfter: XlsxCellRange | null;\n selectionBefore: XlsxCellRange | null;\n sheetIndex: number;\n};\n\ntype RangeCellMutation = {\n after: CellMutationState;\n before: CellMutationState;\n cell: XlsxCellAddress;\n};\n\ntype RangeEditHistoryEntry = {\n kind: \"range-edit\";\n activeCellAfter: XlsxCellAddress | null;\n activeCellBefore: XlsxCellAddress | null;\n mutations: RangeCellMutation[];\n selectionAfter: XlsxCellRange | null;\n selectionBefore: XlsxCellRange | null;\n sheetIndex: number;\n};\n\nfunction clampZoomScale(zoomScale: number) {\n if (!Number.isFinite(zoomScale)) {\n return DEFAULT_ZOOM_SCALE;\n }\n\n return Math.min(MAX_ZOOM_SCALE, Math.max(MIN_ZOOM_SCALE, Math.round(zoomScale)));\n}\n\nfunction resolveDefaultZoomScale(activeTab: XlsxWorkbookTab | null, activeSheet: XlsxSheetData | null) {\n if (activeTab?.kind !== \"sheet\") {\n return DEFAULT_ZOOM_SCALE;\n }\n\n return clampZoomScale(activeSheet?.zoomScale ?? DEFAULT_ZOOM_SCALE);\n}\n\nfunction resolveWorksheetZoomScale(\n worksheet: ReturnType<Workbook[\"getSheet\"]>,\n sheetState?: Record<string, unknown> | null\n) {\n const candidates = [\n typeof sheetState?.zoomScale === \"number\" ? sheetState.zoomScale : undefined,\n typeof worksheet.zoomScale === \"number\" ? worksheet.zoomScale : undefined\n ];\n const value = candidates.find((entry): entry is number => typeof entry === \"number\" && Number.isFinite(entry) && entry > 0);\n return clampZoomScale(value ?? DEFAULT_ZOOM_SCALE);\n}\n\nfunction resolveNextZoomScale(currentZoomScale: number, direction: 1 | -1) {\n if (direction > 0) {\n return Math.min(\n MAX_ZOOM_SCALE,\n currentZoomScale % ZOOM_STEP === 0\n ? currentZoomScale + ZOOM_STEP\n : Math.ceil(currentZoomScale / ZOOM_STEP) * ZOOM_STEP\n );\n }\n\n return Math.max(\n MIN_ZOOM_SCALE,\n currentZoomScale % ZOOM_STEP === 0\n ? currentZoomScale - ZOOM_STEP\n : Math.floor(currentZoomScale / ZOOM_STEP) * ZOOM_STEP\n );\n}\n\ntype ZipEntryMetadata = {\n compressedSize: number;\n name: string;\n uncompressedSize: number;\n};\n\ntype WorkbookPreflightResult = {\n largestWorksheetXmlBytes: number;\n sharedStringsBytes: number;\n totalWorksheetXmlBytes: number;\n tooLarge: boolean;\n};\n\nfunction findZipEndOfCentralDirectoryOffset(bytes: Uint8Array) {\n const minLength = 22;\n if (bytes.byteLength < minLength) {\n return -1;\n }\n\n const searchStart = Math.max(0, bytes.byteLength - (0xffff + minLength));\n for (let offset = bytes.byteLength - minLength; offset >= searchStart; offset -= 1) {\n if (\n bytes[offset] === 0x50 &&\n bytes[offset + 1] === 0x4b &&\n bytes[offset + 2] === 0x05 &&\n bytes[offset + 3] === 0x06\n ) {\n return offset;\n }\n }\n\n return -1;\n}\n\nfunction readZipCentralDirectoryEntries(buffer: ArrayBuffer): ZipEntryMetadata[] | null {\n const bytes = new Uint8Array(buffer);\n const eocdOffset = findZipEndOfCentralDirectoryOffset(bytes);\n if (eocdOffset < 0) {\n return null;\n }\n\n const view = new DataView(buffer, bytes.byteOffset, bytes.byteLength);\n const centralDirectorySize = view.getUint32(eocdOffset + 12, true);\n const centralDirectoryOffset = view.getUint32(eocdOffset + 16, true);\n const decoder = new TextDecoder();\n const entries: ZipEntryMetadata[] = [];\n\n let offset = centralDirectoryOffset;\n const endOffset = centralDirectoryOffset + centralDirectorySize;\n while (offset + 46 <= endOffset && offset + 46 <= bytes.byteLength) {\n if (view.getUint32(offset, true) !== 0x02014b50) {\n return null;\n }\n\n const compressedSize = view.getUint32(offset + 20, true);\n const uncompressedSize = view.getUint32(offset + 24, true);\n const fileNameLength = view.getUint16(offset + 28, true);\n const extraLength = view.getUint16(offset + 30, true);\n const commentLength = view.getUint16(offset + 32, true);\n const fileNameStart = offset + 46;\n const fileNameEnd = fileNameStart + fileNameLength;\n if (fileNameEnd > bytes.byteLength) {\n return null;\n }\n\n entries.push({\n compressedSize,\n name: decoder.decode(bytes.subarray(fileNameStart, fileNameEnd)),\n uncompressedSize\n });\n\n offset = fileNameEnd + extraLength + commentLength;\n }\n\n return entries;\n}\n\nfunction preflightWorkbookBuffer(buffer: ArrayBuffer): WorkbookPreflightResult | null {\n const entries = readZipCentralDirectoryEntries(buffer);\n if (!entries) {\n return null;\n }\n\n let largestWorksheetXmlBytes = 0;\n let totalWorksheetXmlBytes = 0;\n let sharedStringsBytes = 0;\n\n for (const entry of entries) {\n if (/^xl\\/worksheets\\/[^/]+\\.xml$/i.test(entry.name)) {\n largestWorksheetXmlBytes = Math.max(largestWorksheetXmlBytes, entry.uncompressedSize);\n totalWorksheetXmlBytes += entry.uncompressedSize;\n continue;\n }\n\n if (entry.name === \"xl/sharedStrings.xml\") {\n sharedStringsBytes = entry.uncompressedSize;\n }\n }\n\n const tooLarge =\n largestWorksheetXmlBytes > MAX_INTERACTIVE_WORKSHEET_XML_BYTES ||\n sharedStringsBytes > MAX_INTERACTIVE_SHARED_STRINGS_BYTES ||\n totalWorksheetXmlBytes + sharedStringsBytes > MAX_INTERACTIVE_TOTAL_XML_BYTES;\n\n return {\n largestWorksheetXmlBytes,\n sharedStringsBytes,\n tooLarge,\n totalWorksheetXmlBytes\n };\n}\n\nfunction createWorkbookTooLargeError(preflight: WorkbookPreflightResult) {\n return new Error(\n `XLSX is too large to preview interactively. `\n + `Largest worksheet XML: ${formatBinaryBytes(preflight.largestWorksheetXmlBytes)}; `\n + `shared strings: ${formatBinaryBytes(preflight.sharedStringsBytes)}.`\n );\n}\n\ntype HistoryEntry = SnapshotHistoryEntry | CellEditHistoryEntry | RangeEditHistoryEntry;\n\ntype ClipboardMatrixCell = {\n colOffset: number;\n formula: string | null;\n rowOffset: number;\n value: string;\n};\n\ntype ClipboardMerge = {\n colSpan: number;\n colOffset: number;\n rowOffset: number;\n rowSpan: number;\n};\n\ntype ClipboardPayload = {\n cells: ClipboardMatrixCell[];\n cols: number;\n merges: ClipboardMerge[];\n rows: number;\n};\n\nfunction resolveDisplayFileName(src?: string, fileName?: string): string {\n if (typeof fileName === \"string\" && fileName.trim().length > 0) {\n return fileName.trim();\n }\n\n if (!src) {\n return \"Workbook.xlsx\";\n }\n\n const pathWithoutQuery = src.split(\"?\")[0] ?? \"\";\n const pathSegments = pathWithoutQuery.split(\"/\");\n const lastSegment = pathSegments[pathSegments.length - 1] ?? \"\";\n\n if (!lastSegment) {\n return \"Workbook.xlsx\";\n }\n\n try {\n return decodeURIComponent(lastSegment);\n } catch {\n return lastSegment;\n }\n}\n\nfunction resolveSheetDisplayUsedRange(\n usedRange: [number, number, number, number],\n sheetState?: {\n maxContentCol?: number;\n maxContentRow?: number;\n maxHorizontalMergeEndCol?: number;\n maxVerticalMergeEndRow?: number;\n minContentCol?: number;\n minContentRow?: number;\n } | null\n): [number, number, number, number] {\n const [minRow, minCol, maxRow, maxCol] = usedRange;\n const maxContentRow = sheetState?.maxContentRow ?? -1;\n const maxContentCol = sheetState?.maxContentCol ?? -1;\n const maxVerticalMergeEndRow = sheetState?.maxVerticalMergeEndRow ?? -1;\n const maxHorizontalMergeEndCol = sheetState?.maxHorizontalMergeEndCol ?? -1;\n const maxMeaningfulRow = Math.max(maxContentRow, maxVerticalMergeEndRow);\n const maxMeaningfulCol = Math.max(maxContentCol, maxHorizontalMergeEndCol);\n\n if (maxMeaningfulRow < 0 && maxMeaningfulCol < 0) {\n return usedRange;\n }\n\n return [\n sheetState?.minContentRow !== undefined && sheetState.minContentRow >= 0 ? Math.min(minRow, sheetState.minContentRow) : minRow,\n sheetState?.minContentCol !== undefined && sheetState.minContentCol >= 0 ? Math.min(minCol, sheetState.minContentCol) : minCol,\n maxMeaningfulRow >= 0\n ? (maxContentRow >= 0 ? Math.min(maxRow, maxMeaningfulRow) : Math.max(maxRow, maxMeaningfulRow))\n : maxRow,\n maxMeaningfulCol >= 0\n ? (maxContentCol >= 0 ? Math.min(maxCol, maxMeaningfulCol) : Math.max(maxCol, maxMeaningfulCol))\n : maxCol\n ];\n}\n\nfunction buildSheetList(\n workbook: Workbook,\n sheetStatesByWorkbookSheetIndex?: Array<{\n cellStyleIds?: Record<string, number>;\n autoFilterRanges?: XlsxCellRange[];\n cachedFormulaValues?: Record<string, string>;\n columnWidthCharacterWidthPx?: number;\n colWidthOverridesPx?: Record<number, number>;\n colStyleIds?: Record<number, number>;\n conditionalFormatRules?: XlsxConditionalFormatRule[];\n officeLayout?: XlsxSheetData[\"officeLayout\"];\n defaultColWidthPx?: number;\n defaultRowHeightPx?: number;\n hasHorizontalMerges?: boolean;\n hasVerticalMerges?: boolean;\n maxHorizontalMergeEndCol?: number;\n maxVerticalMergeEndRow?: number;\n maxContentCol?: number;\n maxContentRow?: number;\n minContentCol?: number;\n minContentRow?: number;\n hiddenCols?: number[];\n hiddenRows?: number[];\n rowHeightOverridesPx?: Record<number, number>;\n rowStyleIds?: Record<number, number>;\n showGridLines: boolean;\n sparklines?: XlsxSparkline[];\n } | null>,\n themePalette?: XlsxThemePalette | null,\n styleById?: Record<number, XlsxResolvedCellStyle> | null,\n namedCellStyleByName?: Record<string, XlsxResolvedCellStyle> | null,\n tableStyleByName?: Record<string, XlsxTableStyleDefinition> | null,\n showHiddenSheets = false\n): XlsxSheetData[] {\n const sheets: XlsxSheetData[] = [];\n\n for (let index = 0; index < workbook.sheetCount; index += 1) {\n const worksheet = workbook.getSheet(index);\n const sheetState = sheetStatesByWorkbookSheetIndex?.[index] ?? null;\n const mergeMetadata = resolveWorksheetMergeMetadata(worksheet);\n const effectiveSheetState = {\n ...sheetState,\n ...mergeMetadata\n };\n const defaultColWidthPx = sheetState?.defaultColWidthPx ?? resolveWorksheetDefaultColumnWidthPixels(\n worksheet,\n sheetState?.columnWidthCharacterWidthPx,\n DEFAULT_COL_WIDTH\n );\n const defaultRowHeightPx = sheetState?.defaultRowHeightPx ?? resolveWorksheetDefaultRowHeightPixels(\n worksheet,\n DEFAULT_ROW_HEIGHT\n );\n const visibility = normalizeWorksheetVisibility(worksheet.visibility);\n if (!showHiddenSheets && visibility !== \"visible\") {\n continue;\n }\n\n const resolveColumnWidthPx = (col: number) => {\n const width = worksheet.getColumnWidth(col);\n if (width !== undefined && width !== null) {\n return resolveSheetColumnWidthPixels(width, sheetState?.columnWidthCharacterWidthPx);\n }\n\n return sheetState?.colWidthOverridesPx?.[col] ?? defaultColWidthPx;\n };\n\n const resolveRowHeightPx = (row: number) => {\n const height = worksheet.getRowHeight(row);\n if (height !== undefined && height !== null) {\n return Math.max(Math.round(height * 1.33), MIN_ROW_HEIGHT_PX);\n }\n\n return sheetState?.rowHeightOverridesPx?.[row] ?? defaultRowHeightPx;\n };\n\n const usedRange = worksheet.usedRange() as [number, number, number, number] | null;\n if (!usedRange) {\n sheets.push({\n autoFilterRanges: sheetState?.autoFilterRanges ?? [],\n cachedFormulaValues: sheetState?.cachedFormulaValues ?? {},\n columnWidthCharacterWidthPx: sheetState?.columnWidthCharacterWidthPx,\n colWidthOverridesPx: sheetState?.colWidthOverridesPx ?? {},\n colStyleIds: sheetState?.colStyleIds ?? {},\n conditionalFormatRules: sheetState?.conditionalFormatRules ?? [],\n dataValidations: parseWorksheetDataValidations(worksheet),\n officeLayout: sheetState?.officeLayout,\n defaultColWidthPx,\n defaultRowHeightPx,\n freezePanes: parseWorksheetFreezePanes(worksheet),\n hasHorizontalMerges: mergeMetadata.hasHorizontalMerges,\n hasVerticalMerges: mergeMetadata.hasVerticalMerges,\n maxHorizontalMergeEndCol: mergeMetadata.maxHorizontalMergeEndCol,\n maxVerticalMergeEndRow: mergeMetadata.maxVerticalMergeEndRow,\n mergedRanges: mergeMetadata.mergedRanges,\n hiddenCols: [],\n hiddenRows: [],\n minUsedCol: -1,\n minUsedRow: -1,\n maxUsedCol: -1,\n maxUsedRow: -1,\n name: worksheet.name,\n visibility,\n namedCellStyleByName: namedCellStyleByName ?? {},\n rowCount: 0,\n colCount: 0,\n rowHeightOverridesPx: sheetState?.rowHeightOverridesPx ?? {},\n rowStyleIds: sheetState?.rowStyleIds ?? {},\n cellStyleIds: sheetState?.cellStyleIds ?? {},\n styleById: styleById ?? {},\n sparklines: sheetState?.sparklines ?? [],\n tableStyleByName: tableStyleByName ?? {},\n visibleRows: [],\n visibleCols: [],\n colWidths: [],\n rowHeights: [],\n showGridLines: sheetState?.showGridLines ?? true,\n themePalette: themePalette ?? { colorsByIndex: {} },\n workbookSheetIndex: index,\n zoomScale: resolveWorksheetZoomScale(worksheet, sheetState)\n });\n continue;\n }\n\n const [minRow, minCol, maxRow, maxCol] = resolveSheetDisplayUsedRange(usedRange, effectiveSheetState);\n let visibleRowsCache: number[] | null = null;\n let visibleColsCache: number[] | null = null;\n let rowHeightsCache: number[] | null = null;\n let colWidthsCache: number[] | null = null;\n\n const getVisibleRows = () => {\n if (visibleRowsCache) {\n return visibleRowsCache;\n }\n\n const nextVisibleRows: number[] = [];\n for (let row = 0; row <= maxRow; row += 1) {\n if (!worksheet.isRowHidden(row)) {\n nextVisibleRows.push(row);\n }\n }\n\n visibleRowsCache = nextVisibleRows;\n return nextVisibleRows;\n };\n\n const getVisibleCols = () => {\n if (visibleColsCache) {\n return visibleColsCache;\n }\n\n const nextVisibleCols: number[] = [];\n for (let col = 0; col <= maxCol; col += 1) {\n if (!worksheet.isColumnHidden(col)) {\n nextVisibleCols.push(col);\n }\n }\n\n visibleColsCache = nextVisibleCols;\n return nextVisibleCols;\n };\n\n const getRowHeights = () => {\n if (rowHeightsCache) {\n return rowHeightsCache;\n }\n\n rowHeightsCache = getVisibleRows().map(resolveRowHeightPx);\n return rowHeightsCache;\n };\n\n const getColWidths = () => {\n if (colWidthsCache) {\n return colWidthsCache;\n }\n\n colWidthsCache = getVisibleCols().map(resolveColumnWidthPx);\n return colWidthsCache;\n };\n\n const sheet: XlsxSheetData = {\n autoFilterRanges: sheetState?.autoFilterRanges ?? [],\n cachedFormulaValues: sheetState?.cachedFormulaValues ?? {},\n columnWidthCharacterWidthPx: sheetState?.columnWidthCharacterWidthPx,\n colWidthOverridesPx: sheetState?.colWidthOverridesPx ?? {},\n colStyleIds: sheetState?.colStyleIds ?? {},\n conditionalFormatRules: sheetState?.conditionalFormatRules ?? [],\n dataValidations: parseWorksheetDataValidations(worksheet),\n officeLayout: sheetState?.officeLayout,\n defaultColWidthPx,\n defaultRowHeightPx,\n freezePanes: parseWorksheetFreezePanes(worksheet),\n hasHorizontalMerges: mergeMetadata.hasHorizontalMerges,\n hasVerticalMerges: mergeMetadata.hasVerticalMerges,\n maxHorizontalMergeEndCol: mergeMetadata.maxHorizontalMergeEndCol,\n maxVerticalMergeEndRow: mergeMetadata.maxVerticalMergeEndRow,\n mergedRanges: mergeMetadata.mergedRanges,\n hiddenCols: resolveWorksheetHiddenCols(worksheet, maxCol),\n hiddenRows: resolveWorksheetHiddenRows(worksheet, maxRow),\n minUsedCol: minCol,\n minUsedRow: minRow,\n maxUsedCol: maxCol,\n maxUsedRow: maxRow,\n name: worksheet.name,\n visibility,\n namedCellStyleByName: namedCellStyleByName ?? {},\n rowHeightOverridesPx: sheetState?.rowHeightOverridesPx ?? {},\n rowStyleIds: sheetState?.rowStyleIds ?? {},\n cellStyleIds: sheetState?.cellStyleIds ?? {},\n showGridLines: sheetState?.showGridLines ?? true,\n styleById: styleById ?? {},\n sparklines: sheetState?.sparklines ?? [],\n tableStyleByName: tableStyleByName ?? {},\n themePalette: themePalette ?? { colorsByIndex: {} },\n workbookSheetIndex: index,\n zoomScale: resolveWorksheetZoomScale(worksheet, sheetState),\n get rowCount() {\n return getVisibleRows().length;\n },\n get colCount() {\n return getVisibleCols().length;\n },\n get visibleRows() {\n return getVisibleRows();\n },\n get visibleCols() {\n return getVisibleCols();\n },\n get colWidths() {\n return getColWidths();\n },\n get rowHeights() {\n return getRowHeights();\n }\n };\n\n sheets.push(sheet);\n }\n\n return sheets;\n}\n\nfunction buildVisibleSheetIndexMap(sheets: XlsxSheetData[]) {\n return new Map(sheets.map((sheet, index) => [sheet.workbookSheetIndex, index]));\n}\n\nfunction resolveInheritedCellStyle(sheet: XlsxSheetData | null | undefined, row: number, col: number) {\n if (!sheet) {\n return null;\n }\n\n const rowStyleId = sheet.rowStyleIds[row];\n if (rowStyleId !== undefined) {\n return sheet.styleById[rowStyleId] ?? null;\n }\n\n const colStyleId = sheet.colStyleIds[col];\n if (colStyleId !== undefined) {\n return sheet.styleById[colStyleId] ?? null;\n }\n\n return null;\n}\n\nfunction columnLabel(col: number): string {\n let label = \"\";\n let nextValue = col;\n\n while (nextValue >= 0) {\n label = String.fromCharCode(65 + (nextValue % 26)) + label;\n nextValue = Math.floor(nextValue / 26) - 1;\n }\n\n return label;\n}\n\nfunction cellAddressToA1(cell: XlsxCellAddress): string {\n return `${columnLabel(cell.col)}${cell.row + 1}`;\n}\n\nfunction parseA1CellReference(reference: string): XlsxCellAddress | null {\n const match = /^([A-Z]+)(\\d+)$/i.exec(reference.trim());\n if (!match) {\n return null;\n }\n\n const [, columnPart, rowPart] = match;\n let col = 0;\n for (const char of columnPart.toUpperCase()) {\n col = col * 26 + (char.charCodeAt(0) - 64);\n }\n\n return {\n col: col - 1,\n row: Number(rowPart) - 1\n };\n}\n\nfunction parseA1RangeReference(reference: string): XlsxCellRange | null {\n const [startRef, endRef = startRef] = reference.split(\":\");\n const start = parseA1CellReference(startRef ?? \"\");\n const end = parseA1CellReference(endRef ?? \"\");\n if (!start || !end) {\n return null;\n }\n\n return normalizeRange({ start, end });\n}\n\ntype ResolvedWorkbookReference = {\n range: XlsxCellRange;\n workbookSheetIndex: number;\n worksheet: ReturnType<Workbook[\"getSheet\"]>;\n};\n\nfunction resolveWorkbookReference(\n workbook: Workbook,\n defaultWorkbookSheetIndex: number,\n rawReference: string,\n resolvingNamedRange = false\n): ResolvedWorkbookReference | null {\n const reference = rawReference.trim().replace(/^=/, \"\");\n if (!reference) {\n return null;\n }\n\n let workbookSheetIndex = defaultWorkbookSheetIndex;\n let rangeReference = reference;\n const bangIndex = reference.lastIndexOf(\"!\");\n if (bangIndex >= 0) {\n let sheetName = reference.slice(0, bangIndex).trim();\n rangeReference = reference.slice(bangIndex + 1).trim();\n if (sheetName.startsWith(\"'\") && sheetName.endsWith(\"'\")) {\n sheetName = sheetName.slice(1, -1).replace(/''/g, \"'\");\n }\n const resolvedSheetIndex = workbook.sheetIndex(sheetName);\n if (resolvedSheetIndex === undefined) {\n return null;\n }\n workbookSheetIndex = resolvedSheetIndex;\n } else if (!resolvingNamedRange) {\n const namedRange = workbook.getNamedRange(reference);\n if (namedRange) {\n return resolveWorkbookReference(workbook, defaultWorkbookSheetIndex, namedRange, true);\n }\n }\n\n const range = parseA1RangeReference(rangeReference.replace(/\\$/g, \"\"));\n if (!range || workbookSheetIndex < 0 || workbookSheetIndex >= workbook.sheetCount) {\n return null;\n }\n\n try {\n return {\n range,\n workbookSheetIndex,\n worksheet: workbook.getSheet(workbookSheetIndex)\n };\n } catch {\n return null;\n }\n}\n\nfunction parseColorHex(hex: string | undefined) {\n const normalized = hex?.replace(/^#/, \"\");\n return normalized && /^[0-9a-f]{6}$/i.test(normalized)\n ? [0, 2, 4].map((offset) => Number.parseInt(normalized.slice(offset, offset + 2), 16))\n : null;\n}\n\nfunction xlsxColorToDukeColor(color: XlsxCellStyleColorInput | undefined): DukeDrawingColor | undefined {\n if (!color) {\n return undefined;\n }\n const normalizedHex = color.hex?.replace(/^#/, \"\");\n const colorType = color.colorType\n ?? (normalizedHex?.length === 8 ? \"argb\" : normalizedHex?.length === 6 ? \"rgb\" : undefined)\n ?? (color.r !== undefined && color.g !== undefined && color.b !== undefined\n ? color.a !== undefined ? \"argb\" : \"rgb\"\n : undefined)\n ?? (color.themeIndex !== undefined ? \"theme\" : color.paletteIndex !== undefined ? \"indexed\" : undefined);\n switch (colorType) {\n case \"auto\":\n return { colorType: \"auto\" };\n case \"rgb\": {\n const channels = color.r !== undefined && color.g !== undefined && color.b !== undefined\n ? [color.r, color.g, color.b]\n : parseColorHex(color.hex);\n return channels ? { b: channels[2]!, colorType: \"rgb\", g: channels[1]!, r: channels[0]! } : undefined;\n }\n case \"argb\": {\n const normalized = color.hex?.replace(/^#/, \"\");\n const channels = normalized && /^[0-9a-f]{8}$/i.test(normalized)\n ? [0, 2, 4, 6].map((offset) => Number.parseInt(normalized.slice(offset, offset + 2), 16))\n : color.a !== undefined && color.r !== undefined && color.g !== undefined && color.b !== undefined\n ? [color.a, color.r, color.g, color.b]\n : null;\n return channels\n ? { a: channels[0]!, b: channels[3]!, colorType: \"argb\", g: channels[2]!, r: channels[1]! }\n : undefined;\n }\n case \"theme\":\n return color.themeIndex === undefined\n ? undefined\n : { colorType: \"theme\", index: color.themeIndex, tint: color.tint ?? 0 };\n case \"indexed\":\n return color.paletteIndex === undefined\n ? undefined\n : { colorType: \"indexed\", index: color.paletteIndex };\n }\n}\n\nfunction xlsxFontToDukeFont(font: NonNullable<Exclude<XlsxFormControlCaptionInput, string>[\"runs\"][number][\"font\"]>): DukeDrawingRunFont {\n return {\n bold: font.bold,\n charset: font.charset,\n color: xlsxColorToDukeColor(font.color),\n family: font.family,\n italic: font.italic,\n name: font.name,\n scheme: font.scheme,\n size: font.size,\n strikethrough: font.strikethrough,\n underline: font.underline,\n verticalAlign: font.verticalAlign\n };\n}\n\nfunction xlsxCaptionToDukeText(caption: XlsxFormControlCaptionInput): DukeDrawingText {\n if (typeof caption === \"string\") {\n return { runs: [{ text: caption }] };\n }\n return {\n horizontalAlignment: caption.horizontalAlignment,\n runs: caption.runs.map((run) => ({\n font: run.font ? xlsxFontToDukeFont(run.font) : undefined,\n text: run.text\n })),\n verticalAlignment: caption.verticalAlignment\n };\n}\n\nfunction xlsxFormControlKindToDukeKind(kind: XlsxFormControlInput[\"kind\"]): DukeFormControlKindInput {\n switch (kind.kind) {\n case \"button\":\n case \"label\":\n return { caption: xlsxCaptionToDukeText(kind.caption), kind: kind.kind };\n case \"checkbox\":\n return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };\n case \"optionButton\":\n return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };\n case \"groupBox\":\n return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };\n case \"editbox\":\n return {\n caption: xlsxCaptionToDukeText(kind.caption),\n kind: \"unknown\",\n legacyObjectType: kind.legacyObjectType,\n objectType: \"EditBox\"\n };\n case \"unknown\":\n return {\n caption: kind.caption ? xlsxCaptionToDukeText(kind.caption) : undefined,\n kind: \"unknown\",\n legacyObjectType: kind.legacyObjectType,\n objectType: kind.objectType\n };\n default:\n return { ...kind };\n }\n}\n\nfunction dukeFormControlKindToInput(kind: DukeFormControlKind): DukeFormControlKindInput {\n if (kind.kind === \"optionButton\") {\n return { ...kind, state: kind.state === \"checked\" ? \"checked\" : \"unchecked\" };\n }\n if (kind.kind === \"unknown\") {\n return {\n caption: kind.caption,\n kind: \"unknown\",\n legacyObjectType: kind.legacyObjectType,\n objectType: kind.objectType\n };\n }\n return { ...kind };\n}\n\nfunction formControlInputFromDukeControl(control: DukeFormControlDrawing): DukeDrawingInput {\n return {\n altText: control.altText,\n ...(control.anchor ? { anchor: control.anchor } : { transform: control.transform! }),\n formControl: {\n kind: dukeFormControlKindToInput(control.formControl.kind),\n macroName: control.formControl.macroName,\n rawClientData: control.formControl.rawClientData,\n rawObj: control.formControl.rawObj,\n rawProperties: control.formControl.rawProperties\n },\n hidden: control.hidden,\n kind: \"formControl\",\n locked: control.locked,\n name: control.name,\n printable: control.printable,\n title: control.title\n };\n}\n\ntype DukeFormControlDrawingInput = Extract<DukeDrawingInput, { kind: \"formControl\" }> & {\n anchor: DukeDrawingAnchor;\n};\n\nfunction xlsxFormControlInputToDukeDrawing(input: XlsxFormControlInput): DukeFormControlDrawingInput {\n return {\n altText: input.altText,\n anchor: xlsxAnchorToDukeDrawingAnchor(input.anchor, input.editAs),\n formControl: {\n kind: xlsxFormControlKindToDukeKind(input.kind),\n macroName: input.macroName,\n rawClientData: input.rawClientData,\n rawObj: input.kind.kind === \"editbox\" || input.kind.kind === \"unknown\" ? input.kind.rawObj : undefined,\n rawProperties: input.kind.kind === \"editbox\" || input.kind.kind === \"unknown\" ? input.kind.rawProperties : undefined\n },\n hidden: input.hidden,\n kind: \"formControl\",\n locked: input.locked,\n name: input.name,\n printable: input.printable,\n title: input.title\n };\n}\n\nfunction reconcileCheckedOptionButtons(worksheet: ReturnType<Workbook[\"getSheet\"]>) {\n const checkedPaths = worksheet.formControls.flatMap((control) => (\n control.formControl.kind.kind === \"optionButton\" && control.formControl.kind.state === \"checked\"\n ? [control.drawingPath]\n : []\n ));\n for (const path of checkedPaths) {\n const current = worksheet.formControls.find((control) => (\n control.drawingPath.length === path.length\n && control.drawingPath.every((segment, index) => segment === path[index])\n ));\n if (current?.formControl.kind.kind === \"optionButton\" && current.formControl.kind.state === \"checked\") {\n worksheet.setFormControlCheckState(current.drawingPath, \"checked\");\n }\n }\n}\n\nfunction formControlInteractionChanged(\n current: DukeFormControlKind,\n next: DukeFormControlKind\n) {\n if (current.kind !== next.kind) {\n return true;\n }\n if (\"cellLink\" in current && \"cellLink\" in next && current.cellLink !== next.cellLink) {\n return true;\n }\n switch (current.kind) {\n case \"checkbox\":\n case \"optionButton\":\n return next.kind === current.kind && current.state !== next.state;\n case \"dropdown\":\n return next.kind === \"dropdown\" && current.selected !== next.selected;\n case \"listBox\":\n return next.kind === \"listBox\" && (\n current.selection !== next.selection\n || current.selected.length !== next.selected.length\n || current.selected.some((value, index) => value !== next.selected[index])\n );\n case \"scrollbar\":\n case \"spinner\":\n return next.kind === current.kind && current.value !== next.value;\n default:\n return false;\n }\n}\n\nfunction parseWorksheetFreezePanes(worksheet: ReturnType<Workbook[\"getSheet\"]>): XlsxFreezePanes | null {\n const rawFreezePanes = worksheet.freezePanes as Record<string, unknown> | null | undefined;\n const row = typeof rawFreezePanes?.row === \"number\" && rawFreezePanes.row >= 0 ? rawFreezePanes.row : null;\n const col = typeof rawFreezePanes?.col === \"number\" && rawFreezePanes.col >= 0 ? rawFreezePanes.col : null;\n if (row === null && col === null) {\n return null;\n }\n\n return {\n col: col ?? 0,\n row: row ?? 0\n };\n}\n\nfunction parseWorksheetDataValidations(worksheet: ReturnType<Workbook[\"getSheet\"]>): XlsxDataValidation[] {\n const rawDataValidations = Array.isArray(worksheet.dataValidations) ? worksheet.dataValidations : [];\n\n return rawDataValidations.flatMap((entry) => {\n if (!entry || typeof entry !== \"object\") {\n return [];\n }\n\n const validation = entry as Record<string, unknown>;\n const ranges = Array.isArray(validation.ranges)\n ? validation.ranges.flatMap((range) => {\n if (typeof range !== \"string\") {\n return [];\n }\n\n const parsedRange = parseA1RangeReference(range);\n return parsedRange ? [parsedRange] : [];\n })\n : [];\n const validationType = typeof validation.validationType === \"string\" ? validation.validationType : null;\n if (!validationType || ranges.length === 0) {\n return [];\n }\n\n return [{\n allowBlank: typeof validation.allowBlank === \"boolean\" ? validation.allowBlank : undefined,\n errorMessage: typeof validation.errorMessage === \"string\" ? validation.errorMessage : undefined,\n errorStyle: typeof validation.errorStyle === \"string\" ? validation.errorStyle : undefined,\n inputMessage: typeof validation.inputMessage === \"string\" ? validation.inputMessage : undefined,\n listSource: typeof validation.listSource === \"string\" ? validation.listSource : undefined,\n ranges,\n showDropdown: typeof validation.showDropdown === \"boolean\" ? validation.showDropdown : undefined,\n showErrorAlert: typeof validation.showErrorAlert === \"boolean\" ? validation.showErrorAlert : undefined,\n showInputMessage: typeof validation.showInputMessage === \"boolean\" ? validation.showInputMessage : undefined,\n validationType\n } satisfies XlsxDataValidation];\n });\n}\n\nfunction normalizeRange(range: XlsxCellRange): XlsxCellRange {\n return {\n start: {\n row: Math.min(range.start.row, range.end.row),\n col: Math.min(range.start.col, range.end.col)\n },\n end: {\n row: Math.max(range.start.row, range.end.row),\n col: Math.max(range.start.col, range.end.col)\n }\n };\n}\n\nfunction rangeToA1(range: XlsxCellRange): string {\n const normalized = normalizeRange(range);\n const start = cellAddressToA1(normalized.start);\n const end = cellAddressToA1(normalized.end);\n return start === end ? start : `${start}:${end}`;\n}\n\nfunction rangeContainsCell(range: XlsxCellRange, cell: XlsxCellAddress): boolean {\n const normalized = normalizeRange(range);\n return (\n cell.row >= normalized.start.row &&\n cell.row <= normalized.end.row &&\n cell.col >= normalized.start.col &&\n cell.col <= normalized.end.col\n );\n}\n\nfunction rangesIntersect(a: XlsxCellRange, b: XlsxCellRange): boolean {\n return a.start.row <= b.end.row && a.end.row >= b.start.row\n && a.start.col <= b.end.col && a.end.col >= b.start.col;\n}\n\nfunction mapWorksheetTables(\n worksheet: ReturnType<Workbook[\"getSheet\"]> | null,\n autoFilterRanges: XlsxCellRange[] = []\n): XlsxTable[] {\n const rawTables = (worksheet?.tables ?? []) as Array<Record<string, unknown>>;\n const mappedTables = rawTables.flatMap((table, index) => {\n const rawColumns = Array.isArray(table.columns) ? table.columns : [];\n const rawName = typeof table.name === \"string\" ? table.name : `Table${index + 1}`;\n const rawDisplayName =\n typeof table.displayName === \"string\"\n ? table.displayName\n : typeof table.name === \"string\"\n ? table.name\n : `Table ${index + 1}`;\n const rawReference = typeof table.reference === \"string\" ? table.reference : \"\";\n const reference = rawReference;\n const parsedRange = parseA1RangeReference(reference);\n if (!parsedRange) {\n return [];\n }\n\n return [{\n columns: rawColumns.map((column, columnIndex) => ({\n id: typeof (column as { id?: unknown }).id === \"number\" ? ((column as { id?: number }).id ?? columnIndex + 1) : columnIndex + 1,\n index: columnIndex,\n name: typeof (column as { name?: unknown }).name === \"string\" ? ((column as { name?: string }).name ?? `Column ${columnIndex + 1}`) : `Column ${columnIndex + 1}`\n })),\n displayName: rawDisplayName,\n end: parsedRange.end,\n headerRowCount: resolveWorkbookTableCount(table.headerRowCount, 1),\n headerRowCellStyle: typeof table.headerRowCellStyle === \"string\" ? table.headerRowCellStyle : undefined,\n name: rawName,\n reference,\n start: parsedRange.start,\n styleInfo: table.styleInfo as XlsxTable[\"styleInfo\"] | undefined,\n totalsRowCount: resolveWorkbookTableCount(table.totalsRowCount, 0),\n totalsRowShown: resolveWorkbookTableBoolean(table.totalsRowShown)\n }];\n });\n\n const existingReferences = new Set(mappedTables.map((table) => table.reference));\n const mappedAutoFilterTables = autoFilterRanges.flatMap((range, index) => {\n const reference = rangeToA1(range);\n if (existingReferences.has(reference)) {\n return [];\n }\n\n const columnCount = Math.max(0, range.end.col - range.start.col + 1);\n const columns = Array.from({ length: columnCount }, (_, columnIndex) => {\n const headerValue = worksheet\n ? decodeHtmlEntities(worksheet.getFormattedValueAt(range.start.row, range.start.col + columnIndex) ?? \"\")\n : \"\";\n return {\n id: columnIndex + 1,\n index: columnIndex,\n name: headerValue.trim().length > 0 ? headerValue : `Column ${columnIndex + 1}`\n };\n });\n\n return [{\n columns,\n displayName: `AutoFilter ${index + 1}`,\n end: range.end,\n headerRowCount: 1,\n name: `__autofilter_${index + 1}_${reference}`,\n reference,\n start: range.start,\n totalsRowCount: 0,\n totalsRowShown: false\n } satisfies XlsxTable];\n });\n\n return [...mappedTables, ...mappedAutoFilterTables];\n}\n\nfunction resolveWorkbookTableCount(value: unknown, fallback: number) {\n if (typeof value === \"number\" && Number.isFinite(value) && value >= 0) {\n return value;\n }\n\n if (typeof value === \"string\") {\n const parsed = Number.parseInt(value, 10);\n if (Number.isFinite(parsed) && parsed >= 0) {\n return parsed;\n }\n }\n\n return fallback;\n}\n\nfunction resolveWorkbookTableBoolean(value: unknown) {\n if (typeof value === \"boolean\") {\n return value;\n }\n\n if (typeof value === \"number\") {\n return value !== 0;\n }\n\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if (normalized === \"0\" || normalized === \"false\" || normalized === \"\") {\n return false;\n }\n if (normalized === \"1\" || normalized === \"true\") {\n return true;\n }\n }\n\n return false;\n}\n\nfunction fileStem(fileName: string): string {\n const normalized = fileName.trim();\n const lastDot = normalized.lastIndexOf(\".\");\n return lastDot > 0 ? normalized.slice(0, lastDot) : normalized;\n}\n\nfunction pxToSheetRowHeight(heightPx: number): number {\n return Math.max(heightPx, MIN_ROW_HEIGHT_PX) / 1.33;\n}\n\nfunction cssColor(color: Record<string, unknown> | undefined): string | null {\n if (!color?.hex) {\n return null;\n }\n\n const hex = String(color.hex);\n const rgb = hex.length === 8 ? hex.slice(2) : hex;\n return `#${rgb}`;\n}\n\nfunction mapBorder(edge: { style: string; color?: { hex?: string } }): string {\n const color = cssColor(edge.color as Record<string, unknown> | undefined) ?? \"#000000\";\n const widthMap: Record<string, string> = {\n dashed: \"1px\",\n dotted: \"1px\",\n double: \"3px\",\n hair: \"1px\",\n medium: \"2px\",\n thick: \"3px\",\n thin: \"1px\"\n };\n const styleMap: Record<string, string> = {\n dashDot: \"dashed\",\n dashDotDot: \"dotted\",\n dashed: \"dashed\",\n dotted: \"dotted\",\n double: \"double\",\n hair: \"solid\",\n medium: \"solid\",\n mediumDashDot: \"dashed\",\n mediumDashDotDot: \"dotted\",\n mediumDashed: \"dashed\",\n slantDashDot: \"dashed\",\n thick: \"solid\",\n thin: \"solid\"\n };\n\n return `${widthMap[edge.style] ?? \"1px\"} ${styleMap[edge.style] ?? \"solid\"} ${color}`;\n}\n\nfunction decodeHtmlEntities(value: string): string {\n return value\n .replace(/"/g, \"\\\"\")\n .replace(/"/g, \"\\\"\")\n .replace(/'/g, \"'\")\n .replace(/'/g, \"'\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/&/g, \"&\");\n}\n\nfunction cloneBytes(bytes: Uint8Array): Uint8Array {\n const nextBytes = new Uint8Array(bytes.byteLength);\n nextBytes.set(bytes);\n return nextBytes;\n}\n\nfunction sanitizeSavedWorkbookBytes(bytes: Uint8Array): Uint8Array {\n try {\n const archive = unzipSync(bytes);\n const stylesEntry = archive[\"xl/styles.xml\"];\n if (stylesEntry) {\n const stylesXml = strFromU8(stylesEntry)\n .replace(/&quot;/g, \""\")\n .replace(/&apos;/g, \"'\");\n archive[\"xl/styles.xml\"] = strToU8(stylesXml);\n }\n\n return zipSync(archive, { level: 6 });\n } catch {\n return cloneBytes(bytes);\n }\n}\n\nfunction pushHistoryEntry(stack: HistoryEntry[], entry: HistoryEntry) {\n stack.push(entry);\n if (stack.length > HISTORY_LIMIT) {\n stack.shift();\n }\n}\n\nfunction normalizeCellValue(value: unknown) {\n return value ?? \"\";\n}\n\nfunction cloneCellStyle(style: unknown): unknown {\n if (!style || typeof style !== \"object\") {\n return style;\n }\n\n if (typeof structuredClone === \"function\") {\n try {\n return structuredClone(style);\n } catch {\n // Fall through to the JSON clone below.\n }\n }\n\n try {\n return JSON.parse(JSON.stringify(style));\n } catch {\n return style;\n }\n}\n\nfunction coerceUserEnteredValue(value: string, locale = \"en-US\"): unknown {\n const trimmed = value.trim();\n if (!trimmed) {\n return \"\";\n }\n\n if (trimmed.startsWith(\"'\")) {\n return trimmed.slice(1);\n }\n\n if (/^(true|false)$/i.test(trimmed)) {\n return trimmed.toLowerCase() === \"true\";\n }\n\n const localizedNumber = parseLocaleNumber(trimmed, locale);\n if (localizedNumber !== undefined) return localizedNumber;\n\n if (/^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)$/.test(trimmed)) {\n const parsed = Number(trimmed);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n\n return value;\n}\n\nfunction applyCellMutationState(\n worksheet: ReturnType<Workbook[\"getSheet\"]>,\n cell: XlsxCellAddress,\n state: CellMutationState\n) {\n if (state.formula) {\n worksheet.setFormula(cellAddressToA1(cell), state.formula);\n } else {\n worksheet.setCell(cellAddressToA1(cell), normalizeCellValue(state.value));\n }\n\n if (state.style && typeof state.style === \"object\") {\n worksheet.setCellStyleAt(cell.row, cell.col, state.style);\n }\n}\n\nfunction escapeHtml(value: string) {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction parseClipboardText(text: string): string[][] {\n const normalized = text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n const rows = normalized.split(\"\\n\");\n\n if (rows.length > 1 && rows[rows.length - 1] === \"\") {\n rows.pop();\n }\n\n return rows.map((row) => row.split(\"\\t\"));\n}\n\nfunction createAbortError() {\n if (typeof DOMException !== \"undefined\") {\n return new DOMException(\"Aborted\", \"AbortError\");\n }\n\n const error = new Error(\"Aborted\");\n error.name = \"AbortError\";\n return error;\n}\n\nfunction isAbortError(error: unknown) {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nasync function resolveWorkbookBuffer(\n { file, src }: UseXlsxViewerControllerOptions,\n signal?: AbortSignal\n): Promise<ArrayBuffer> {\n let buffer: ArrayBuffer;\n\n if (signal?.aborted) {\n throw createAbortError();\n }\n\n if (file) {\n buffer = file;\n } else if (src) {\n let response: Response;\n try {\n response = await fetch(src, { signal });\n } catch (error) {\n if (isAbortError(error)) {\n throw error;\n }\n\n throw new Error(\n \"Failed to fetch workbook. The remote URL may be blocked by CORS, unavailable, or not directly downloadable from the browser.\"\n );\n }\n if (!response.ok) {\n throw new Error(`Failed to fetch workbook (status ${response.status})`);\n }\n buffer = await response.arrayBuffer();\n } else {\n throw new Error(\"Either `file` or `src` must be provided.\");\n }\n\n return buffer;\n}\n\nasync function parseWorkbookBuffer(buffer: ArrayBuffer): Promise<{\n shouldAutoCalculate: boolean;\n workbook: Workbook;\n}> {\n const wasmModule = await getSheetsWasmModule();\n const initialWorkbook = wasmModule.Workbook.fromBytes(new Uint8Array(buffer));\n let totalFormulas = 0;\n\n for (let index = 0; index < initialWorkbook.sheetCount; index += 1) {\n totalFormulas += initialWorkbook.getSheet(index).formulaCount;\n }\n\n const shouldAutoCalculate = totalFormulas <= FORMULA_COUNT_THRESHOLD;\n if (!shouldAutoCalculate) {\n return { shouldAutoCalculate, workbook: initialWorkbook };\n }\n\n const result = safeCalculate(initialWorkbook, {\n reparse: () => wasmModule.Workbook.fromBytes(new Uint8Array(buffer))\n });\n\n return {\n shouldAutoCalculate: result.calculated,\n workbook: result.workbook\n };\n}\n\nfunction scheduleLowPriorityTask(task: () => void) {\n if (typeof window === \"undefined\") {\n const timeoutHandle = setTimeout(task, 0);\n return () => clearTimeout(timeoutHandle);\n }\n\n const idleWindow = window as IdleWindow;\n if (typeof idleWindow.requestIdleCallback === \"function\") {\n const idleHandle = idleWindow.requestIdleCallback(() => {\n task();\n }, { timeout: 120 });\n return () => {\n idleWindow.cancelIdleCallback?.(idleHandle);\n };\n }\n\n const timeoutHandle = window.setTimeout(task, 0);\n return () => window.clearTimeout(timeoutHandle);\n}\n\nfunction asFiniteNumber(value: unknown) {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction inferImageMimeType(source: string) {\n if (source.startsWith(\"data:\")) {\n const separatorIndex = source.indexOf(\";\");\n if (separatorIndex > 5) {\n return source.slice(5, separatorIndex);\n }\n }\n\n const normalized = source.split(\"?\")[0]?.toLowerCase() ?? \"\";\n if (normalized.endsWith(\".gif\")) {\n return \"image/gif\";\n }\n if (normalized.endsWith(\".jpg\") || normalized.endsWith(\".jpeg\")) {\n return \"image/jpeg\";\n }\n if (normalized.endsWith(\".svg\")) {\n return \"image/svg+xml\";\n }\n if (normalized.endsWith(\".webp\")) {\n return \"image/webp\";\n }\n\n return \"image/png\";\n}\n\nfunction inferWorksheetDirectImageMimeType(info: DukeImageDrawing[\"image\"]) {\n const format = info.format;\n if (format === \"gif\") {\n return \"image/gif\";\n }\n if (format === \"jpeg\") {\n return \"image/jpeg\";\n }\n if (format === \"svg\") {\n return \"image/svg+xml\";\n }\n if (format === \"bmp\") {\n return \"image/bmp\";\n }\n if (format === \"tiff\") {\n return \"image/tiff\";\n }\n if (format === \"emf\") {\n return \"image/emf\";\n }\n if (format === \"wmf\") {\n return \"image/wmf\";\n }\n if (format === \"png\") {\n return \"image/png\";\n }\n\n const mediaPath = typeof info.mediaPath === \"string\" ? info.mediaPath : \"\";\n if (mediaPath) {\n return inferImageMimeType(mediaPath);\n }\n\n return \"image/png\";\n}\n\nfunction createWorksheetDirectImageSource(\n data: unknown,\n mimeType: string,\n objectUrls: string[]\n) {\n const bytes = data instanceof Uint8Array\n ? data\n : Array.isArray(data)\n ? Uint8Array.from(data.filter((value): value is number => typeof value === \"number\"))\n : null;\n if (!bytes || bytes.byteLength === 0) {\n return null;\n }\n\n const blobBuffer = new Uint8Array(bytes.byteLength);\n blobBuffer.set(bytes);\n const objectUrl = URL.createObjectURL(new Blob([blobBuffer.buffer], { type: mimeType }));\n objectUrls.push(objectUrl);\n return objectUrl;\n}\n\nfunction buildWorksheetDirectApiImage(\n worksheet: ReturnType<Workbook[\"getSheet\"]>,\n workbookSheetIndex: number,\n drawing: DukeImageDrawing & { anchor: DukeDrawingAnchor },\n objectUrls: string[]\n): XlsxImage | null {\n const hasSvgCompanion = Boolean(drawing.image.svgMediaPath);\n let mimeType = inferWorksheetDirectImageMimeType(drawing.image);\n let data: Uint8Array;\n try {\n const svgData = drawing.image.format === \"svg\" || hasSvgCompanion\n ? worksheet.drawingSvgData(drawing.drawingPath)\n : undefined;\n if (svgData) {\n data = svgData;\n mimeType = \"image/svg+xml\";\n } else {\n data = worksheet.drawingImageData(drawing.drawingPath);\n }\n } catch {\n return null;\n }\n const src = createWorksheetDirectImageSource(data, mimeType, objectUrls);\n if (!src) {\n return null;\n }\n\n const zIndex = (drawing.drawingPath[0] ?? 0) + 1;\n return {\n anchor: dukeDrawingAnchorToXlsxAnchor(drawing.anchor),\n description: drawing.altText,\n editable: false,\n id: `worksheet-image-${workbookSheetIndex}-${drawing.drawingPath.join(\"-\")}`,\n mediaPath: drawing.image.mediaPath || undefined,\n mimeType,\n name: drawing.name,\n sheetIndex: workbookSheetIndex,\n src,\n workbookSheetIndex,\n zIndex\n };\n}\n\nfunction collectWorksheetApiImages(workbook: Workbook, objectUrls: string[]) {\n const directImagesByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, () => [] as XlsxImage[]);\n for (let workbookSheetIndex = 0; workbookSheetIndex < workbook.sheetCount; workbookSheetIndex += 1) {\n const worksheet = workbook.getSheet(workbookSheetIndex);\n const rawImages = worksheet.images;\n if (rawImages.length === 0) {\n continue;\n }\n\n const nextImages = rawImages\n .filter((drawing): drawing is DukeImageDrawing & { anchor: DukeDrawingAnchor } => !drawing.hidden && Boolean(drawing.anchor))\n .map((drawing) => buildWorksheetDirectApiImage(worksheet, workbookSheetIndex, drawing, objectUrls))\n .filter((image): image is XlsxImage => Boolean(image));\n if (nextImages.length > 0) {\n directImagesByWorkbookSheetIndex[workbookSheetIndex] = nextImages;\n }\n }\n\n return directImagesByWorkbookSheetIndex;\n}\n\nfunction imageAnchorKey(anchor: XlsxImage[\"anchor\"]) {\n if (anchor.kind === \"absolute\") {\n return [\n \"absolute\",\n Math.round(anchor.positionEmu.x),\n Math.round(anchor.positionEmu.y),\n Math.round(anchor.sizeEmu.cx),\n Math.round(anchor.sizeEmu.cy)\n ].join(\":\");\n }\n if (anchor.kind === \"one-cell\") {\n return [\n \"one\",\n anchor.from.col,\n anchor.from.row,\n Math.round(anchor.from.colOffsetEmu),\n Math.round(anchor.from.rowOffsetEmu),\n Math.round(anchor.sizeEmu.cx),\n Math.round(anchor.sizeEmu.cy)\n ].join(\":\");\n }\n return [\n \"two\",\n anchor.from.col,\n anchor.from.row,\n Math.round(anchor.from.colOffsetEmu),\n Math.round(anchor.from.rowOffsetEmu),\n anchor.to.col,\n anchor.to.row,\n Math.round(anchor.to.colOffsetEmu),\n Math.round(anchor.to.rowOffsetEmu)\n ].join(\":\");\n}\n\nfunction collectHiddenWorksheetImageAnchors(workbook: Workbook) {\n return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => new Set(\n workbook.getSheet(workbookSheetIndex).images.flatMap((drawing) => (\n drawing.hidden && drawing.anchor\n ? [imageAnchorKey(dukeDrawingAnchorToXlsxAnchor(drawing.anchor))]\n : []\n ))\n ));\n}\n\nfunction mergeParsedAndApiImages(\n parsedImages: XlsxImage[],\n apiImages: XlsxImage[],\n hiddenAnchorKeys: Set<string>\n) {\n const visibleParsedImages = parsedImages.filter((image) => !hiddenAnchorKeys.has(imageAnchorKey(image.anchor)));\n if (visibleParsedImages.length === 0) {\n return apiImages;\n }\n if (apiImages.length === 0) {\n return visibleParsedImages;\n }\n\n const normalizeTextKey = (value: string | undefined) => value?.trim().toLowerCase() ?? \"\";\n const imageKeys = (image: XlsxImage) => {\n const keys = [\n `${normalizeTextKey(image.mediaPath)}|${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,\n `${normalizeTextKey(image.mediaPath)}|${imageAnchorKey(image.anchor)}`,\n `${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,\n `${imageAnchorKey(image.anchor)}`\n ];\n return keys.filter((key, index) => key && keys.indexOf(key) === index);\n };\n\n const apiBuckets = new Map<string, XlsxImage[]>();\n for (const apiImage of apiImages) {\n for (const key of imageKeys(apiImage)) {\n const bucket = apiBuckets.get(key);\n if (bucket) {\n bucket.push(apiImage);\n } else {\n apiBuckets.set(key, [apiImage]);\n }\n }\n }\n\n const usedApiImages = new Set<XlsxImage>();\n const takeApiMatch = (image: XlsxImage) => {\n for (const key of imageKeys(image)) {\n const bucket = apiBuckets.get(key);\n if (!bucket) {\n continue;\n }\n const match = bucket.find((candidate) => !usedApiImages.has(candidate));\n if (match) {\n usedApiImages.add(match);\n return match;\n }\n }\n return null;\n };\n\n const merged = visibleParsedImages.map((image) => {\n const apiImage = takeApiMatch(image);\n if (!apiImage) {\n return image;\n }\n\n return {\n ...image,\n anchor: apiImage.anchor,\n mediaPath: apiImage.mediaPath ?? image.mediaPath,\n mimeType: apiImage.mimeType,\n name: apiImage.name ?? image.name,\n src: apiImage.src,\n zIndex: apiImage.zIndex\n };\n });\n\n return merged;\n}\n\nfunction isZipWorkbook(bytes: Uint8Array) {\n return bytes.byteLength >= 4 && bytes[0] === 0x50 && bytes[1] === 0x4b;\n}\n\nfunction isLegacyXlsWorkbook(bytes: Uint8Array) {\n return bytes.byteLength >= 8\n && bytes[0] === 0xd0\n && bytes[1] === 0xcf\n && bytes[2] === 0x11\n && bytes[3] === 0xe0\n && bytes[4] === 0xa1\n && bytes[5] === 0xb1\n && bytes[6] === 0x1a\n && bytes[7] === 0xe1;\n}\n\nfunction shouldSkipXmlParsingForWorkbook(bytes: Uint8Array, skipXmlParsing = false) {\n return skipXmlParsing || isLegacyXlsWorkbook(bytes);\n}\n\nfunction createBasicWorkbookAssets(workbook: Workbook): WorkbookImageAssets {\n const objectUrls: string[] = [];\n return {\n archive: {},\n dirtyArchivePaths: new Set(),\n formControlsByWorkbookSheetIndex: collectWorkbookFormControls(workbook),\n imageOriginsById: new Map(),\n imagesByWorkbookSheetIndex: collectWorksheetApiImages(workbook, objectUrls),\n namedCellStyleByName: {},\n objectUrls,\n shapesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => []),\n sheetOrigins: Array.from({ length: workbook.sheetCount }, () => null as WorkbookImageSheetOrigin | null),\n sheetStatesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => null),\n styleById: {},\n tableMetadataByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => []),\n tableStyleByName: {},\n themePalette: { colorsByIndex: {} }\n };\n}\n\nfunction loadWorkbookImageAssets(bytes: Uint8Array, workbook: Workbook, skipXmlParsing = false) {\n if (shouldSkipXmlParsingForWorkbook(bytes, skipXmlParsing) || !isZipWorkbook(bytes)) {\n return createBasicWorkbookAssets(workbook);\n }\n\n const parsedAssets = parseWorkbookImageAssets(bytes, workbook);\n const apiImagesByWorkbookSheetIndex = collectWorksheetApiImages(workbook, parsedAssets.objectUrls);\n const hiddenImageAnchorsByWorkbookSheetIndex = collectHiddenWorksheetImageAnchors(workbook);\n\n const imagesByWorkbookSheetIndex = Array.from(\n { length: Math.max(workbook.sheetCount, parsedAssets.imagesByWorkbookSheetIndex.length, apiImagesByWorkbookSheetIndex.length) },\n (_, index) => {\n const parsedImages = parsedAssets.imagesByWorkbookSheetIndex[index] ?? [];\n const apiImages = apiImagesByWorkbookSheetIndex[index] ?? [];\n return mergeParsedAndApiImages(\n parsedImages,\n apiImages,\n hiddenImageAnchorsByWorkbookSheetIndex[index] ?? new Set()\n );\n }\n );\n\n return {\n ...parsedAssets,\n imagesByWorkbookSheetIndex\n };\n}\n\nfunction downloadArrayBuffer(file: ArrayBuffer, fileName: string) {\n const blob = new Blob([file], { type: XLSX_MIME_TYPE });\n const objectUrl = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = objectUrl;\n anchor.download = fileName;\n document.body.append(anchor);\n anchor.click();\n anchor.remove();\n setTimeout(() => URL.revokeObjectURL(objectUrl), 0);\n}\n\nfunction downloadBytes(bytes: Uint8Array, fileName: string, mimeType: string) {\n const normalizedBytes = new Uint8Array(bytes.byteLength);\n normalizedBytes.set(bytes);\n const blob = new Blob([normalizedBytes], { type: mimeType });\n const objectUrl = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = objectUrl;\n anchor.download = fileName;\n document.body.append(anchor);\n anchor.click();\n anchor.remove();\n setTimeout(() => URL.revokeObjectURL(objectUrl), 0);\n}\n\nfunction downloadText(text: string, fileName: string, mimeType: string) {\n const blob = new Blob([text], { type: mimeType });\n const objectUrl = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = objectUrl;\n anchor.download = fileName;\n document.body.append(anchor);\n anchor.click();\n anchor.remove();\n setTimeout(() => URL.revokeObjectURL(objectUrl), 0);\n}\n\nfunction downloadUrl(src: string, fileName: string) {\n const anchor = document.createElement(\"a\");\n anchor.href = src;\n anchor.download = fileName;\n anchor.rel = \"noreferrer\";\n document.body.append(anchor);\n anchor.click();\n anchor.remove();\n}\n\nexport function useXlsxViewerController(options: UseXlsxViewerControllerOptions): XlsxViewerController {\n const {\n allowResizeInReadOnly = false,\n deferLoadingAboveBytes = DEFAULT_DEFER_LOADING_ABOVE_BYTES,\n file,\n fileName,\n locale = \"en-US\",\n maxFileSizeBytes = DEFAULT_MAX_FILE_SIZE_BYTES,\n readOnly: requestedReadOnly = false,\n readOnlyAboveBytes = 0,\n showHiddenSheets = false,\n skipXmlParsing = false,\n src,\n useWorker = true\n } = options;\n const [isLoading, setIsLoading] = React.useState(Boolean(file ?? src));\n const [error, setError] = React.useState<Error | null>(null);\n const [workbook, setWorkbook] = React.useState<Workbook | null>(null);\n const [sheets, setSheets] = React.useState<XlsxSheetData[]>([]);\n const [chartsByWorkbookSheetIndex, setChartsByWorkbookSheetIndex] = React.useState<XlsxChart[][]>([]);\n const [chartsheets, setChartsheets] = React.useState<XlsxChartsheet[]>([]);\n const [tabs, setTabs] = React.useState<XlsxWorkbookTab[]>([]);\n const [isChartsLoading, setIsChartsLoading] = React.useState(false);\n const [workerTablesByWorkbookSheetIndex, setWorkerTablesByWorkbookSheetIndex] = React.useState<XlsxTable[][]>([]);\n const [formControlsByWorkbookSheetIndex, setFormControlsByWorkbookSheetIndex] = React.useState<XlsxFormControl[][]>([]);\n const [imagesByWorkbookSheetIndex, setImagesByWorkbookSheetIndex] = React.useState<XlsxImage[][]>([]);\n const [shapesByWorkbookSheetIndex, setShapesByWorkbookSheetIndex] = React.useState<XlsxShape[][]>([]);\n const [activeSheetIndex, setActiveSheetIndexState] = React.useState(0);\n const [activeTabIndex, setActiveTabIndexState] = React.useState(0);\n const [zoomScaleOverridesByTabId, setZoomScaleOverridesByTabId] = React.useState<Record<string, number>>({});\n const [activeCell, setActiveCell] = React.useState<XlsxCellAddress | null>(null);\n const [selection, setSelection] = React.useState<XlsxCellRange | null>(null);\n const [highlightedRanges, setHighlightedRanges] = React.useState<XlsxRangeHighlight[]>([]);\n const [selectedChartId, setSelectedChartId] = React.useState<string | null>(null);\n const [selectedChartElement, setSelectedChartElement] = React.useState<XlsxChartElementSelection | null>(null);\n const [selectedImageId, setSelectedImageId] = React.useState<string | null>(null);\n const [revision, setRevision] = React.useState(0);\n const selectionAnchorRef = React.useRef<XlsxCellAddress | null>(null);\n const undoStackRef = React.useRef<HistoryEntry[]>([]);\n const redoStackRef = React.useRef<HistoryEntry[]>([]);\n const isApplyingHistoryRef = React.useRef(false);\n const [historyRevision, setHistoryRevision] = React.useState(0);\n const [shouldAutoCalculate, setShouldAutoCalculate] = React.useState(false);\n const [workerCellSnapshotRevision, setWorkerCellSnapshotRevision] = React.useState(0);\n const [isWorkerBacked, setIsWorkerBacked] = React.useState(false);\n const [sortState, setSortState] = React.useState<XlsxTableSortState | null>(null);\n const [forcedReadOnly, setForcedReadOnly] = React.useState(false);\n const deferredBufferRef = React.useRef<ArrayBuffer | null>(null);\n const [deferredLoadFileSize, setDeferredLoadFileSize] = React.useState<number | null>(null);\n const imageAssetsRef = React.useRef<WorkbookImageAssets | null>(null);\n const chartAssetsRef = React.useRef<WorkbookChartAssets | null>(null);\n const chartLoadRequestTokenRef = React.useRef(0);\n const chartDisplayFallbackCleanupRef = React.useRef<(() => void) | null>(null);\n const sheetOriginsRef = React.useRef<Array<WorkbookImageSheetOrigin | null>>([]);\n const workerClientRef = React.useRef<XlsxWorkerClient | null>(null);\n const workerCellSnapshotCacheRef = React.useRef(new Map<string, { displayValue: string; formula: string }>());\n const displayFileName = React.useMemo(() => resolveDisplayFileName(src, fileName), [fileName, src]);\n const shouldDeferLoading = deferLoadingAboveBytes > 0;\n const readOnly = requestedReadOnly || forcedReadOnly;\n const canResizeReadOnly = requestedReadOnly && allowResizeInReadOnly && !forcedReadOnly;\n const workerSupported = useWorker && typeof Worker !== \"undefined\" && canUseConfiguredWasmSourceInWorker();\n const canUseWorkerForRequestedReadOnly = requestedReadOnly;\n const shouldForceReadOnlyForBuffer = React.useCallback((bufferByteLength: number) => (\n !requestedReadOnly && readOnlyAboveBytes > 0 && bufferByteLength > readOnlyAboveBytes\n ), [readOnlyAboveBytes, requestedReadOnly]);\n const shouldUseWorkerForReadOnlyLoad = React.useCallback((willForceReadOnly: boolean) => (\n workerSupported && (willForceReadOnly || canUseWorkerForRequestedReadOnly)\n ), [canUseWorkerForRequestedReadOnly, workerSupported]);\n\n const disposeWorkerClient = React.useCallback(() => {\n workerClientRef.current?.dispose();\n workerClientRef.current = null;\n }, []);\n\n const getWorkerClient = React.useCallback(() => {\n if (!workerClientRef.current) {\n workerClientRef.current = new XlsxWorkerClient();\n }\n\n return workerClientRef.current;\n }, []);\n\n const clearImageAssets = React.useCallback(() => {\n revokeWorkbookImageAssets(imageAssetsRef.current);\n imageAssetsRef.current = null;\n sheetOriginsRef.current = [];\n setFormControlsByWorkbookSheetIndex([]);\n setImagesByWorkbookSheetIndex([]);\n setShapesByWorkbookSheetIndex([]);\n }, []);\n\n const clearChartAssets = React.useCallback(() => {\n chartLoadRequestTokenRef.current += 1;\n chartDisplayFallbackCleanupRef.current?.();\n chartDisplayFallbackCleanupRef.current = null;\n chartAssetsRef.current = null;\n setChartsByWorkbookSheetIndex([]);\n setChartsheets([]);\n setTabs([]);\n setIsChartsLoading(false);\n }, []);\n\n const setImageAssets = React.useCallback((assets: WorkbookImageAssets | null) => {\n revokeWorkbookImageAssets(imageAssetsRef.current);\n imageAssetsRef.current = assets;\n sheetOriginsRef.current = assets?.sheetOrigins.slice() ?? [];\n setFormControlsByWorkbookSheetIndex(assets?.formControlsByWorkbookSheetIndex ?? []);\n setImagesByWorkbookSheetIndex(assets?.imagesByWorkbookSheetIndex ?? []);\n setShapesByWorkbookSheetIndex(assets?.shapesByWorkbookSheetIndex ?? []);\n }, []);\n\n const setChartAssets = React.useCallback((assets: WorkbookChartAssets | null) => {\n chartAssetsRef.current = assets;\n setChartsByWorkbookSheetIndex(assets?.chartsByWorkbookSheetIndex ?? []);\n setChartsheets(assets?.chartsheets ?? []);\n setTabs(assets?.tabs ?? []);\n setIsChartsLoading(false);\n }, []);\n\n const shouldFallbackFromWorkerError = React.useCallback((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error ?? \"\");\n return (\n error instanceof XlsxWorkerUnavailableError\n || message.includes(\"DOMParser is not defined\")\n || message.includes(\"XMLSerializer is not defined\")\n || message.includes(\"Worker chart payload incomplete\")\n );\n }, []);\n\n const hasIncompleteWorkerChartSnapshot = React.useCallback((snapshot: {\n chartsByWorkbookSheetIndex: XlsxChart[][];\n }) => {\n for (const sheetCharts of snapshot.chartsByWorkbookSheetIndex) {\n for (const chart of sheetCharts) {\n if (chart.chartType !== \"Bubble\") {\n continue;\n }\n for (const series of chart.series) {\n const pointCount = Math.max(series.values.length, series.categories.length);\n if (pointCount <= 1) {\n continue;\n }\n const numericBubbleSizes = (series.bubbleSizes ?? []).filter(\n (value): value is number => typeof value === \"number\" && Number.isFinite(value)\n );\n if (numericBubbleSizes.length < pointCount) {\n return true;\n }\n }\n }\n }\n\n return false;\n }, []);\n\n const ensureChartAssetsHydrated = React.useCallback((\n targetWorkbook: Workbook | null,\n targetSheets: XlsxSheetData[]\n ) => {\n const currentAssets = chartAssetsRef.current;\n if (\n currentAssets\n && (\n currentAssets.chartOriginsById.size > 0\n || !targetWorkbook\n || !imageAssetsRef.current\n )\n ) {\n return currentAssets;\n }\n\n if (!targetWorkbook || !imageAssetsRef.current) {\n return chartAssetsRef.current;\n }\n\n const assets = loadWorkbookChartAssets(\n targetWorkbook,\n imageAssetsRef.current,\n buildVisibleSheetIndexMap(targetSheets),\n showHiddenSheets\n );\n chartAssetsRef.current = assets;\n return assets;\n }, [showHiddenSheets]);\n\n const startChartDisplayHydration = React.useCallback((\n buffer: ArrayBuffer,\n targetWorkbook: Workbook,\n targetSheets: XlsxSheetData[]\n ) => {\n const effectiveSkipXmlParsing = shouldSkipXmlParsingForWorkbook(new Uint8Array(buffer), skipXmlParsing);\n const visibleSheetIndexByWorkbookSheetIndex = buildVisibleSheetIndexMap(targetSheets);\n const quickAssets = loadWorkbookChartAssets(targetWorkbook, null, visibleSheetIndexByWorkbookSheetIndex, showHiddenSheets);\n setChartAssets(quickAssets);\n\n if (effectiveSkipXmlParsing) {\n return;\n }\n\n const hasCharts = quickAssets.chartsByWorkbookSheetIndex.some((sheetCharts) => sheetCharts.length > 0);\n if (!hasCharts) {\n setIsChartsLoading(false);\n return;\n }\n\n setIsChartsLoading(true);\n const requestToken = chartLoadRequestTokenRef.current + 1;\n chartLoadRequestTokenRef.current = requestToken;\n chartDisplayFallbackCleanupRef.current?.();\n chartDisplayFallbackCleanupRef.current = null;\n let fallbackTriggered = false;\n const triggerFallback = () => {\n if (fallbackTriggered || requestToken !== chartLoadRequestTokenRef.current) {\n return;\n }\n fallbackTriggered = true;\n runMainThreadFallback();\n };\n const workerTimeoutHandle = typeof window !== \"undefined\"\n ? window.setTimeout(() => {\n triggerFallback();\n }, 1500)\n : null;\n\n const applyWorkerResult = (result: {\n chartsByWorkbookSheetIndex: XlsxChart[][];\n chartsheets: XlsxChartsheet[];\n tabs: XlsxWorkbookTab[];\n }) => {\n if (requestToken !== chartLoadRequestTokenRef.current) {\n return;\n }\n if (imageAssetsRef.current && !effectiveSkipXmlParsing) {\n const chartOriginsById = hydrateWorkbookChartStyles(result.chartsByWorkbookSheetIndex, imageAssetsRef.current, result.chartsheets);\n chartAssetsRef.current = { ...result, chartOriginsById };\n }\n setChartsByWorkbookSheetIndex(result.chartsByWorkbookSheetIndex);\n setChartsheets(result.chartsheets);\n setTabs(result.tabs);\n setIsChartsLoading(false);\n };\n\n const runMainThreadFallback = () => {\n chartDisplayFallbackCleanupRef.current = scheduleLowPriorityTask(() => {\n if (requestToken !== chartLoadRequestTokenRef.current) {\n return;\n }\n try {\n const hydratedAssets = loadWorkbookChartAssets(\n targetWorkbook,\n imageAssetsRef.current,\n visibleSheetIndexByWorkbookSheetIndex,\n showHiddenSheets\n );\n if (requestToken !== chartLoadRequestTokenRef.current) {\n return;\n }\n setChartAssets(hydratedAssets);\n } catch {\n if (requestToken !== chartLoadRequestTokenRef.current) {\n return;\n }\n setChartAssets(quickAssets);\n } finally {\n if (requestToken === chartLoadRequestTokenRef.current) {\n setIsChartsLoading(false);\n }\n }\n });\n };\n\n if (!workerSupported) {\n if (workerTimeoutHandle !== null) window.clearTimeout(workerTimeoutHandle);\n runMainThreadFallback();\n return;\n }\n\n void Promise.resolve()\n .then(() => getWorkerClient().parseCharts(buffer, effectiveSkipXmlParsing, showHiddenSheets))\n .then((result) => {\n if (workerTimeoutHandle !== null) {\n window.clearTimeout(workerTimeoutHandle);\n }\n if (fallbackTriggered) {\n return;\n }\n try {\n if (hasIncompleteWorkerChartSnapshot(result)) {\n triggerFallback();\n return;\n }\n applyWorkerResult(result);\n } catch {\n triggerFallback();\n }\n })\n .catch((error: unknown) => {\n if (workerTimeoutHandle !== null) {\n window.clearTimeout(workerTimeoutHandle);\n }\n if (isAbortError(error)) {\n return;\n }\n triggerFallback();\n });\n }, [getWorkerClient, hasIncompleteWorkerChartSnapshot, setChartAssets, showHiddenSheets, skipXmlParsing, workerSupported]);\n\n const loadWorkbookOnMainThread = React.useCallback(async (buffer: ArrayBuffer) => {\n const nextParsedWorkbook = await parseWorkbookBuffer(buffer);\n const bytes = new Uint8Array(buffer);\n const nextImageAssets = loadWorkbookImageAssets(\n bytes,\n nextParsedWorkbook.workbook,\n shouldSkipXmlParsingForWorkbook(bytes, skipXmlParsing)\n );\n return {\n imageAssets: nextImageAssets,\n parsedWorkbook: nextParsedWorkbook\n };\n }, [skipXmlParsing]);\n\n const refreshWorkbookState = React.useCallback((targetWorkbook: Workbook) => {\n const currentFormControls = imageAssetsRef.current?.formControlsByWorkbookSheetIndex ?? [];\n const nextFormControls = refreshWorkbookFormControls(\n targetWorkbook,\n currentFormControls,\n imageAssetsRef.current?.themePalette\n );\n if (imageAssetsRef.current) {\n imageAssetsRef.current.formControlsByWorkbookSheetIndex = nextFormControls;\n }\n setFormControlsByWorkbookSheetIndex(nextFormControls);\n const nextSheets = buildSheetList(\n targetWorkbook,\n imageAssetsRef.current?.sheetStatesByWorkbookSheetIndex,\n imageAssetsRef.current?.themePalette,\n imageAssetsRef.current?.styleById,\n imageAssetsRef.current?.namedCellStyleByName,\n imageAssetsRef.current?.tableStyleByName,\n showHiddenSheets\n );\n setSheets(nextSheets);\n setChartAssets(\n loadWorkbookChartAssets(\n targetWorkbook,\n imageAssetsRef.current,\n buildVisibleSheetIndexMap(nextSheets),\n showHiddenSheets\n )\n );\n setRevision((current) => current + 1);\n }, [setChartAssets, showHiddenSheets]);\n\n React.useEffect(() => () => {\n chartDisplayFallbackCleanupRef.current?.();\n chartDisplayFallbackCleanupRef.current = null;\n revokeWorkbookImageAssets(imageAssetsRef.current);\n disposeWorkerClient();\n }, [disposeWorkerClient]);\n\n React.useEffect(() => {\n if (!file && !src) {\n disposeWorkerClient();\n setForcedReadOnly(false);\n setWorkbook(null);\n setSheets([]);\n clearChartAssets();\n setWorkerTablesByWorkbookSheetIndex([]);\n clearImageAssets();\n setError(null);\n setIsLoading(false);\n setIsWorkerBacked(false);\n deferredBufferRef.current = null;\n setDeferredLoadFileSize(null);\n setActiveSheetIndexState(0);\n setActiveTabIndexState(0);\n setActiveCell(null);\n setSelection(null);\n setHighlightedRanges([]);\n setSelectedChartId(null);\n setSelectedChartElement(null);\n setSelectedImageId(null);\n selectionAnchorRef.current = null;\n undoStackRef.current = [];\n redoStackRef.current = [];\n setHistoryRevision(0);\n setShouldAutoCalculate(false);\n workerCellSnapshotCacheRef.current.clear();\n setWorkerCellSnapshotRevision(0);\n setSortState(null);\n setZoomScaleOverridesByTabId({});\n setRevision(0);\n return;\n }\n\n let isCurrent = true;\n const abortController = new AbortController();\n setIsLoading(true);\n setError(null);\n clearImageAssets();\n clearChartAssets();\n setWorkerTablesByWorkbookSheetIndex([]);\n setIsWorkerBacked(false);\n deferredBufferRef.current = null;\n setDeferredLoadFileSize(null);\n setActiveSheetIndexState(0);\n setActiveTabIndexState(0);\n setActiveCell(null);\n setSelection(null);\n setHighlightedRanges([]);\n setSelectedChartId(null);\n setSelectedChartElement(null);\n setSelectedImageId(null);\n selectionAnchorRef.current = null;\n undoStackRef.current = [];\n redoStackRef.current = [];\n setHistoryRevision(0);\n setShouldAutoCalculate(false);\n workerCellSnapshotCacheRef.current.clear();\n setWorkerCellSnapshotRevision(0);\n setSortState(null);\n setZoomScaleOverridesByTabId({});\n setRevision(0);\n disposeWorkerClient();\n\n void resolveWorkbookBuffer({ file, src }, abortController.signal)\n .then(async (buffer) => {\n if (!isCurrent || abortController.signal.aborted) {\n return;\n }\n\n if (maxFileSizeBytes > 0 && buffer.byteLength > maxFileSizeBytes) {\n throw new XlsxFileSizeLimitExceededError(buffer.byteLength, maxFileSizeBytes);\n }\n\n await loadSpreadsheetFonts();\n if (!isCurrent || abortController.signal.aborted) {\n return;\n }\n\n buffer = normalizeWorkbookArrayBuffer(buffer);\n\n const preflight = preflightWorkbookBuffer(buffer);\n if (preflight?.tooLarge) {\n throw createWorkbookTooLargeError(preflight);\n }\n\n const shouldForceReadOnly = shouldForceReadOnlyForBuffer(buffer.byteLength);\n setForcedReadOnly(shouldForceReadOnly);\n const shouldUseWorkerForLoad = shouldUseWorkerForReadOnlyLoad(shouldForceReadOnly);\n const effectiveSkipXmlParsing = shouldSkipXmlParsingForWorkbook(new Uint8Array(buffer), skipXmlParsing);\n\n if (shouldDeferLoading && buffer.byteLength > deferLoadingAboveBytes) {\n deferredBufferRef.current = buffer;\n setDeferredLoadFileSize(buffer.byteLength);\n setWorkbook(null);\n setSheets([]);\n clearChartAssets();\n setWorkerTablesByWorkbookSheetIndex([]);\n setIsLoading(false);\n return;\n }\n\n if (shouldUseWorkerForLoad) {\n try {\n const snapshot = await getWorkerClient().loadWorkbook(buffer, effectiveSkipXmlParsing, showHiddenSheets);\n if (!isCurrent || abortController.signal.aborted) {\n return;\n }\n if (!effectiveSkipXmlParsing && hasIncompleteWorkerChartSnapshot(snapshot)) {\n throw new Error(\"Worker chart payload incomplete\");\n }\n\n const workerImageAssets = effectiveSkipXmlParsing\n ? null\n : parseWorkbookImageAssets(new Uint8Array(buffer));\n if (!isCurrent || abortController.signal.aborted) {\n revokeWorkbookImageAssets(workerImageAssets);\n return;\n }\n\n setImageAssets(workerImageAssets);\n const chartOriginsById = workerImageAssets\n ? hydrateWorkbookChartStyles(snapshot.chartsByWorkbookSheetIndex, workerImageAssets, snapshot.chartsheets)\n : new Map();\n setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);\n setWorkbook(null);\n setSheets(snapshot.sheets.map(sheet => ({ ...sheet, officeLayout: workerImageAssets?.sheetStatesByWorkbookSheetIndex[sheet.workbookSheetIndex]?.officeLayout })));\n setChartsByWorkbookSheetIndex(snapshot.chartsByWorkbookSheetIndex);\n setChartsheets(snapshot.chartsheets);\n setTabs(snapshot.tabs);\n chartAssetsRef.current = { ...snapshot, chartOriginsById };\n setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);\n setShouldAutoCalculate(false);\n setIsWorkerBacked(true);\n setSortState(null);\n setIsChartsLoading(false);\n setIsLoading(false);\n return;\n } catch (workerError) {\n if (!isCurrent || isAbortError(workerError)) {\n return;\n }\n if (!shouldFallbackFromWorkerError(workerError)) {\n throw workerError;\n }\n\n disposeWorkerClient();\n }\n }\n\n const { imageAssets: nextImageAssets, parsedWorkbook: nextParsedWorkbook } = await loadWorkbookOnMainThread(buffer);\n if (!isCurrent || abortController.signal.aborted) {\n revokeWorkbookImageAssets(nextImageAssets);\n return;\n }\n\n setImageAssets(nextImageAssets);\n setWorkbook(nextParsedWorkbook.workbook);\n const nextSheets = buildSheetList(\n nextParsedWorkbook.workbook,\n nextImageAssets.sheetStatesByWorkbookSheetIndex,\n nextImageAssets.themePalette,\n nextImageAssets.styleById,\n nextImageAssets.namedCellStyleByName,\n nextImageAssets.tableStyleByName,\n showHiddenSheets\n );\n setSheets(nextSheets);\n startChartDisplayHydration(buffer, nextParsedWorkbook.workbook, nextSheets);\n setShouldAutoCalculate(nextParsedWorkbook.shouldAutoCalculate);\n setWorkerTablesByWorkbookSheetIndex([]);\n setIsWorkerBacked(false);\n setSortState(null);\n setIsLoading(false);\n })\n .catch((nextError: unknown) => {\n if (!isCurrent || isAbortError(nextError)) {\n return;\n }\n\n setWorkbook(null);\n setSheets([]);\n clearChartAssets();\n setWorkerTablesByWorkbookSheetIndex([]);\n clearImageAssets();\n setShouldAutoCalculate(false);\n setIsWorkerBacked(false);\n setSortState(null);\n setError(nextError instanceof Error ? nextError : new Error(\"Could not load workbook.\"));\n setIsLoading(false);\n });\n\n return () => {\n isCurrent = false;\n abortController.abort();\n disposeWorkerClient();\n };\n }, [\n clearChartAssets,\n clearImageAssets,\n deferLoadingAboveBytes,\n disposeWorkerClient,\n file,\n getWorkerClient,\n hasIncompleteWorkerChartSnapshot,\n loadWorkbookOnMainThread,\n maxFileSizeBytes,\n setImageAssets,\n startChartDisplayHydration,\n shouldFallbackFromWorkerError,\n shouldDeferLoading,\n shouldForceReadOnlyForBuffer,\n shouldUseWorkerForReadOnlyLoad,\n src,\n showHiddenSheets\n ]);\n\n const activeTab = tabs[activeTabIndex] ?? null;\n const activeSheet = activeTab?.kind === \"sheet\"\n ? sheets[activeTab.sheetIndex ?? -1] ?? null\n : null;\n const deferredMetadataCell = React.useDeferredValue(activeCell);\n const deferredMetadataSheet = React.useDeferredValue(activeSheet);\n const activeZoomTabKey = activeTab?.id ?? DEFAULT_ZOOM_TAB_KEY;\n const defaultZoomScale = React.useMemo(\n () => resolveDefaultZoomScale(activeTab, activeSheet),\n [activeSheet, activeTab]\n );\n const zoomScale = React.useMemo(\n () => clampZoomScale(zoomScaleOverridesByTabId[activeZoomTabKey] ?? defaultZoomScale),\n [activeZoomTabKey, defaultZoomScale, zoomScaleOverridesByTabId]\n );\n const canZoomIn = zoomScale < MAX_ZOOM_SCALE;\n const canZoomOut = zoomScale > MIN_ZOOM_SCALE;\n\n React.useEffect(() => {\n setActiveCell(null);\n setSelection(null);\n setSelectedChartId(null);\n setSelectedChartElement(null);\n setSelectedImageId(null);\n selectionAnchorRef.current = null;\n setSortState(null);\n }, [activeTabIndex]);\n\n const setActiveSheetIndex = React.useCallback((index: number) => {\n setActiveSheetIndexState((currentIndex) => {\n if (index < 0 || index >= sheets.length) {\n return currentIndex;\n }\n const targetSheet = sheets[index];\n const tabIndex = tabs.findIndex((tab) => tab.kind === \"sheet\" && tab.workbookSheetIndex === targetSheet?.workbookSheetIndex);\n if (tabIndex >= 0) {\n setActiveTabIndexState(tabIndex);\n }\n return index;\n });\n }, [sheets, tabs]);\n\n const setActiveTabIndex = React.useCallback((index: number) => {\n setActiveTabIndexState((currentIndex) => {\n if (index < 0 || index >= tabs.length) {\n return currentIndex;\n }\n\n const targetTab = tabs[index];\n if (targetTab?.kind === \"sheet\" && typeof targetTab.sheetIndex === \"number\") {\n setActiveSheetIndexState(targetTab.sheetIndex);\n }\n return index;\n });\n }, [tabs]);\n\n const highlightRanges = React.useCallback((references: string | readonly string[], options: XlsxHighlightRangeOptions = {}) => {\n const inputs = typeof references === \"string\" ? [references] : references;\n if (!Array.isArray(inputs) || isLoading || error) {\n return false;\n }\n const nextHighlights: XlsxRangeHighlight[] = [];\n let firstSheetIndex = activeSheetIndex;\n for (const reference of inputs) {\n const parsed = parseXlsxRange(reference);\n if (!parsed) {\n return false;\n }\n const requestedSheetName = parsed.sheetName?.toLowerCase();\n const sheetIndex = requestedSheetName === undefined\n ? options.sheetIndex ?? activeSheetIndex\n : sheets.findIndex((sheet) => sheet.name.toLowerCase() === requestedSheetName);\n const sheet = Number.isInteger(sheetIndex) ? sheets[sheetIndex] : undefined;\n if (!sheet) {\n return false;\n }\n if (nextHighlights.length === 0) {\n firstSheetIndex = sheetIndex;\n }\n nextHighlights.push({\n ...parsed,\n autoScroll: nextHighlights.length === 0 && (options.autoScroll ?? true),\n sheetName: sheet.name,\n workbookSheetIndex: sheet.workbookSheetIndex\n });\n }\n setHighlightedRanges(nextHighlights);\n if (nextHighlights[0]?.autoScroll) {\n setActiveSheetIndex(firstSheetIndex);\n }\n return true;\n }, [activeSheetIndex, error, isLoading, setActiveSheetIndex, sheets]);\n\n const clearHighlightedRanges = React.useCallback(() => setHighlightedRanges([]), []);\n\n const setZoomScale = React.useCallback((nextZoomScale: number) => {\n const normalizedZoomScale = clampZoomScale(nextZoomScale);\n setZoomScaleOverridesByTabId((current) => {\n if (current[activeZoomTabKey] === normalizedZoomScale) {\n return current;\n }\n\n return {\n ...current,\n [activeZoomTabKey]: normalizedZoomScale\n };\n });\n }, [activeZoomTabKey]);\n\n const resetZoom = React.useCallback(() => {\n setZoomScaleOverridesByTabId((current) => {\n if (current[activeZoomTabKey] === undefined) {\n return current;\n }\n\n const next = { ...current };\n delete next[activeZoomTabKey];\n return next;\n });\n }, [activeZoomTabKey]);\n\n const zoomIn = React.useCallback(() => {\n setZoomScale(resolveNextZoomScale(zoomScale, 1));\n }, [setZoomScale, zoomScale]);\n\n const zoomOut = React.useCallback(() => {\n setZoomScale(resolveNextZoomScale(zoomScale, -1));\n }, [setZoomScale, zoomScale]);\n\n React.useEffect(() => {\n setActiveTabIndexState((current) => {\n if (tabs.length === 0) {\n return 0;\n }\n return Math.min(current, tabs.length - 1);\n });\n }, [tabs.length]);\n\n const continueDeferredLoad = React.useCallback(() => {\n const deferredBuffer = deferredBufferRef.current;\n if (!deferredBuffer) {\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n if (maxFileSizeBytes > 0 && deferredBuffer.byteLength > maxFileSizeBytes) {\n deferredBufferRef.current = null;\n setDeferredLoadFileSize(null);\n setWorkbook(null);\n setSheets([]);\n clearChartAssets();\n setWorkerTablesByWorkbookSheetIndex([]);\n clearImageAssets();\n setShouldAutoCalculate(false);\n setIsWorkerBacked(false);\n setSortState(null);\n setError(new XlsxFileSizeLimitExceededError(deferredBuffer.byteLength, maxFileSizeBytes));\n setIsLoading(false);\n return;\n }\n\n const preflight = preflightWorkbookBuffer(deferredBuffer);\n if (preflight?.tooLarge) {\n deferredBufferRef.current = null;\n setDeferredLoadFileSize(null);\n setWorkbook(null);\n setSheets([]);\n clearChartAssets();\n setWorkerTablesByWorkbookSheetIndex([]);\n clearImageAssets();\n setShouldAutoCalculate(false);\n setIsWorkerBacked(false);\n setSortState(null);\n setError(createWorkbookTooLargeError(preflight));\n setIsLoading(false);\n return;\n }\n\n const shouldForceReadOnly = shouldForceReadOnlyForBuffer(deferredBuffer.byteLength);\n setForcedReadOnly(shouldForceReadOnly);\n const shouldUseWorkerForLoad = shouldUseWorkerForReadOnlyLoad(shouldForceReadOnly);\n const effectiveSkipXmlParsing = shouldSkipXmlParsingForWorkbook(new Uint8Array(deferredBuffer), skipXmlParsing);\n\n if (shouldUseWorkerForLoad) {\n void Promise.resolve()\n .then(() => getWorkerClient().loadWorkbook(deferredBuffer, effectiveSkipXmlParsing, showHiddenSheets))\n .then((snapshot) => {\n if (!effectiveSkipXmlParsing && hasIncompleteWorkerChartSnapshot(snapshot)) {\n throw new Error(\"Worker chart payload incomplete\");\n }\n const workerImageAssets = effectiveSkipXmlParsing\n ? null\n : parseWorkbookImageAssets(new Uint8Array(deferredBuffer));\n setImageAssets(workerImageAssets);\n const chartOriginsById = workerImageAssets\n ? hydrateWorkbookChartStyles(snapshot.chartsByWorkbookSheetIndex, workerImageAssets, snapshot.chartsheets)\n : new Map();\n deferredBufferRef.current = null;\n setDeferredLoadFileSize(null);\n setWorkbook(null);\n setSheets(snapshot.sheets.map(sheet => ({ ...sheet, officeLayout: workerImageAssets?.sheetStatesByWorkbookSheetIndex[sheet.workbookSheetIndex]?.officeLayout })));\n setChartsByWorkbookSheetIndex(snapshot.chartsByWorkbookSheetIndex);\n setChartsheets(snapshot.chartsheets);\n setTabs(snapshot.tabs);\n chartAssetsRef.current = { ...snapshot, chartOriginsById };\n setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);\n setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);\n setShouldAutoCalculate(false);\n setIsWorkerBacked(true);\n setSortState(null);\n setIsChartsLoading(false);\n setIsLoading(false);\n })\n .catch(async (workerError: unknown) => {\n if (isAbortError(workerError)) {\n return;\n }\n if (!shouldFallbackFromWorkerError(workerError)) {\n throw workerError;\n }\n\n disposeWorkerClient();\n const { imageAssets: nextImageAssets, parsedWorkbook: nextParsedWorkbook } = await loadWorkbookOnMainThread(deferredBuffer);\n deferredBufferRef.current = null;\n setDeferredLoadFileSize(null);\n setImageAssets(nextImageAssets);\n setWorkbook(nextParsedWorkbook.workbook);\n const nextSheets = buildSheetList(\n nextParsedWorkbook.workbook,\n nextImageAssets.sheetStatesByWorkbookSheetIndex,\n nextImageAssets.themePalette,\n nextImageAssets.styleById,\n nextImageAssets.namedCellStyleByName,\n nextImageAssets.tableStyleByName,\n showHiddenSheets\n );\n setSheets(nextSheets);\n startChartDisplayHydration(deferredBuffer, nextParsedWorkbook.workbook, nextSheets);\n setShouldAutoCalculate(nextParsedWorkbook.shouldAutoCalculate);\n setWorkerTablesByWorkbookSheetIndex([]);\n setIsWorkerBacked(false);\n setSortState(null);\n setIsLoading(false);\n })\n .catch((nextError: unknown) => {\n deferredBufferRef.current = null;\n setDeferredLoadFileSize(null);\n setWorkbook(null);\n setSheets([]);\n clearChartAssets();\n setWorkerTablesByWorkbookSheetIndex([]);\n clearImageAssets();\n setShouldAutoCalculate(false);\n setIsWorkerBacked(false);\n setSortState(null);\n setError(nextError instanceof Error ? nextError : new Error(\"Could not load workbook.\"));\n setIsLoading(false);\n });\n return;\n }\n\n void parseWorkbookBuffer(deferredBuffer)\n .then((nextParsedWorkbook) => {\n const bytes = new Uint8Array(deferredBuffer);\n const nextImageAssets = loadWorkbookImageAssets(\n bytes,\n nextParsedWorkbook.workbook,\n shouldSkipXmlParsingForWorkbook(bytes, skipXmlParsing)\n );\n deferredBufferRef.current = null;\n setDeferredLoadFileSize(null);\n setImageAssets(nextImageAssets);\n setWorkbook(nextParsedWorkbook.workbook);\n const nextSheets = buildSheetList(\n nextParsedWorkbook.workbook,\n nextImageAssets.sheetStatesByWorkbookSheetIndex,\n nextImageAssets.themePalette,\n nextImageAssets.styleById,\n nextImageAssets.namedCellStyleByName,\n nextImageAssets.tableStyleByName,\n showHiddenSheets\n );\n setSheets(nextSheets);\n startChartDisplayHydration(deferredBuffer, nextParsedWorkbook.workbook, nextSheets);\n setShouldAutoCalculate(nextParsedWorkbook.shouldAutoCalculate);\n setWorkerTablesByWorkbookSheetIndex([]);\n setIsWorkerBacked(false);\n setSortState(null);\n setIsLoading(false);\n })\n .catch((nextError: unknown) => {\n deferredBufferRef.current = null;\n setDeferredLoadFileSize(null);\n setWorkbook(null);\n setSheets([]);\n clearChartAssets();\n setWorkerTablesByWorkbookSheetIndex([]);\n clearImageAssets();\n setShouldAutoCalculate(false);\n setIsWorkerBacked(false);\n setSortState(null);\n setError(nextError instanceof Error ? nextError : new Error(\"Could not load workbook.\"));\n setIsLoading(false);\n });\n }, [\n clearChartAssets,\n clearImageAssets,\n disposeWorkerClient,\n getWorkerClient,\n loadWorkbookOnMainThread,\n setImageAssets,\n startChartDisplayHydration,\n hasIncompleteWorkerChartSnapshot,\n maxFileSizeBytes,\n shouldFallbackFromWorkerError,\n shouldForceReadOnlyForBuffer,\n shouldUseWorkerForReadOnlyLoad\n ]);\n\n const maybeRecalculateWorkbook = React.useCallback((targetWorkbook: Workbook) => {\n if (!shouldAutoCalculate) {\n return;\n }\n\n const result = tryRecalculate(targetWorkbook);\n if (!result.calculated) {\n setShouldAutoCalculate(false);\n }\n }, [shouldAutoCalculate]);\n\n const getActiveWorksheet = React.useCallback(() => {\n if (!workbook || !activeSheet) {\n return null;\n }\n\n return workbook.getSheet(activeSheet.workbookSheetIndex);\n }, [activeSheet, workbook]);\n\n const getFormControlWorksheet = React.useCallback((sheetIndex = activeSheetIndex) => {\n const targetSheet = sheets[sheetIndex];\n if (!workbook || !targetSheet) {\n return null;\n }\n try {\n return {\n workbookSheetIndex: targetSheet.workbookSheetIndex,\n worksheet: workbook.getSheet(targetSheet.workbookSheetIndex)\n };\n } catch {\n return null;\n }\n }, [activeSheetIndex, sheets, workbook]);\n\n const tables = React.useMemo(\n () => (\n isWorkerBacked\n ? workerTablesByWorkbookSheetIndex[activeSheet?.workbookSheetIndex ?? -1] ?? []\n : mapWorksheetTables(getActiveWorksheet(), activeSheet?.autoFilterRanges ?? [])\n ),\n [activeSheet?.autoFilterRanges, activeSheet?.workbookSheetIndex, getActiveWorksheet, isWorkerBacked, revision, workerTablesByWorkbookSheetIndex]\n );\n\n const getCellSnapshotAsync = React.useCallback((workbookSheetIndex: number, row: number, col: number) => {\n if (!isWorkerBacked) {\n return Promise.resolve({\n displayValue: \"\",\n formula: \"\"\n });\n }\n\n return getWorkerClient().getCellSnapshot(workbookSheetIndex, row, col, locale);\n }, [getWorkerClient, isWorkerBacked, locale]);\n\n const getRowsBatchAsync = React.useCallback((workbookSheetIndex: number, startRow: number, rowCount: number) => {\n if (!isWorkerBacked) {\n return Promise.resolve(null);\n }\n\n return getWorkerClient().getRowsBatch(workbookSheetIndex, startRow, rowCount, locale);\n }, [getWorkerClient, isWorkerBacked, locale]);\n\n const visibleSheetIndexByWorkbookSheetIndex = React.useMemo(\n () => new Map(sheets.map((sheet, index) => [sheet.workbookSheetIndex, index])),\n [sheets]\n );\n\n const mapPublicChart = React.useCallback((chart: XlsxChart) => {\n const visibleSheetIndex = visibleSheetIndexByWorkbookSheetIndex.get(chart.workbookSheetIndex);\n return {\n ...chart,\n sheetIndex: visibleSheetIndex ?? chart.workbookSheetIndex\n };\n }, [visibleSheetIndexByWorkbookSheetIndex]);\n\n const mapPublicImage = React.useCallback((image: XlsxImage) => {\n const visibleSheetIndex = visibleSheetIndexByWorkbookSheetIndex.get(image.workbookSheetIndex);\n return {\n ...image,\n sheetIndex: visibleSheetIndex ?? image.workbookSheetIndex\n };\n }, [visibleSheetIndexByWorkbookSheetIndex]);\n\n const mapPublicFormControl = React.useCallback((control: XlsxFormControl) => {\n const visibleSheetIndex = visibleSheetIndexByWorkbookSheetIndex.get(control.workbookSheetIndex);\n return {\n ...control,\n sheetIndex: visibleSheetIndex ?? control.workbookSheetIndex\n };\n }, [visibleSheetIndexByWorkbookSheetIndex]);\n\n const publicChartsByWorkbookSheetIndex = React.useMemo(\n () => chartsByWorkbookSheetIndex.map((sheetCharts) => sheetCharts.map(mapPublicChart)),\n [chartsByWorkbookSheetIndex, mapPublicChart]\n );\n const publicChartById = React.useMemo(() => {\n const lookup = new Map<string, XlsxChart>();\n for (const sheetCharts of publicChartsByWorkbookSheetIndex) {\n for (const chart of sheetCharts) {\n lookup.set(chart.id, chart);\n }\n }\n return lookup;\n }, [publicChartsByWorkbookSheetIndex]);\n\n const getSheetCharts = React.useCallback((sheetIndex = activeSheetIndex) => {\n const targetSheet = sheets[sheetIndex];\n if (!targetSheet) {\n return [];\n }\n\n return publicChartsByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? [];\n }, [activeSheetIndex, publicChartsByWorkbookSheetIndex, sheets]);\n\n const getChartById = React.useCallback((id: string) => {\n return publicChartById.get(id) ?? null;\n }, [publicChartById]);\n\n const getChartsheetById = React.useCallback((id: string) => (\n chartsheets.find((chartsheet) => chartsheet.id === id) ?? null\n ), [chartsheets]);\n\n const charts = React.useMemo(() => {\n if (activeTab?.kind === \"chartsheet\" && typeof activeTab.chartsheetIndex === \"number\") {\n const chartsheet = chartsheets[activeTab.chartsheetIndex];\n return (chartsheet?.chartIds ?? []).map((id) => getChartById(id)).filter((value): value is XlsxChart => Boolean(value));\n }\n\n return getSheetCharts(activeSheetIndex);\n }, [activeSheetIndex, activeTab, chartsheets, getChartById, getSheetCharts]);\n\n const selectedChart = React.useMemo(\n () => (selectedChartId ? getChartById(selectedChartId) : null),\n [getChartById, selectedChartId]\n );\n\n React.useEffect(() => {\n if (!selectedChartId) {\n if (selectedChartElement) {\n setSelectedChartElement(null);\n }\n return;\n }\n\n if (!selectedChart) {\n setSelectedChartId(null);\n setSelectedChartElement(null);\n return;\n }\n\n if (!selectedChartElement) {\n setSelectedChartElement({ chartId: selectedChartId, kind: \"chart\" });\n return;\n }\n\n if (selectedChartElement.chartId !== selectedChartId) {\n setSelectedChartElement({ chartId: selectedChartId, kind: \"chart\" });\n return;\n }\n\n if (selectedChartElement.kind !== \"chart\") {\n const selectedSeries = selectedChart.series[selectedChartElement.seriesIndex];\n if (!selectedSeries || selectedSeries.id !== selectedChartElement.seriesId) {\n setSelectedChartElement({ chartId: selectedChartId, kind: \"chart\" });\n }\n }\n }, [selectedChart, selectedChartElement, selectedChartId]);\n\n const selectChart = React.useCallback((id: string | null) => {\n setSelectedImageId(null);\n setSelectedChartId(id);\n setSelectedChartElement(id ? { chartId: id, kind: \"chart\" } : null);\n }, []);\n\n const clearSelectedChart = React.useCallback(() => {\n setSelectedChartId(null);\n setSelectedChartElement(null);\n }, []);\n\n const clearSelectedChartElement = React.useCallback(() => {\n setSelectedChartElement(selectedChartId ? { chartId: selectedChartId, kind: \"chart\" } : null);\n }, [selectedChartId]);\n\n const selectChartElement = React.useCallback((selection: XlsxChartElementSelection | null) => {\n setSelectedImageId(null);\n setSelectedChartId(selection?.chartId ?? null);\n setSelectedChartElement(selection);\n }, []);\n\n const getSheetImages = React.useCallback((sheetIndex = activeSheetIndex) => {\n const targetSheet = sheets[sheetIndex];\n if (!targetSheet) {\n return [];\n }\n\n return (imagesByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? []).map(mapPublicImage);\n }, [activeSheetIndex, imagesByWorkbookSheetIndex, mapPublicImage, sheets]);\n\n const images = React.useMemo(() => getSheetImages(activeSheetIndex), [activeSheetIndex, getSheetImages]);\n\n const getSheetFormControls = React.useCallback((sheetIndex = activeSheetIndex) => {\n const targetSheet = sheets[sheetIndex];\n if (!targetSheet) {\n return [];\n }\n\n return (formControlsByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? []).map(mapPublicFormControl);\n }, [activeSheetIndex, formControlsByWorkbookSheetIndex, mapPublicFormControl, sheets]);\n\n const formControls = React.useMemo(() => getSheetFormControls(activeSheetIndex), [activeSheetIndex, getSheetFormControls]);\n\n const getFormControlItems = React.useCallback((controlIndex: number, sheetIndex = activeSheetIndex) => {\n const target = getFormControlWorksheet(sheetIndex);\n if (!workbook || !target) {\n const targetSheet = sheets[sheetIndex];\n if (!targetSheet) {\n return [];\n }\n const control = (formControlsByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? [])\n .find((entry) => entry.controlIndex === controlIndex);\n return control?.items?.slice() ?? [];\n }\n const control = target.worksheet.formControls[controlIndex];\n const kind = control?.formControl.kind;\n const inputRange = kind?.kind === \"listBox\" || kind?.kind === \"dropdown\"\n ? kind.inputRange\n : undefined;\n if (!inputRange) {\n return [];\n }\n const source = resolveWorkbookReference(workbook, target.workbookSheetIndex, inputRange);\n if (!source) {\n return [];\n }\n\n const items: string[] = [];\n for (let row = source.range.start.row; row <= source.range.end.row; row += 1) {\n for (let col = source.range.start.col; col <= source.range.end.col; col += 1) {\n items.push(source.worksheet.getFormattedValueAt(row, col));\n }\n }\n return items;\n }, [activeSheetIndex, formControlsByWorkbookSheetIndex, getFormControlWorksheet, sheets, workbook]);\n\n const getSheetShapes = React.useCallback((sheetIndex = activeSheetIndex) => {\n const targetSheet = sheets[sheetIndex];\n if (!targetSheet) {\n return [];\n }\n\n return (shapesByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? []).map((shape) => {\n const visibleSheetIndex = visibleSheetIndexByWorkbookSheetIndex.get(shape.workbookSheetIndex);\n return {\n ...shape,\n sheetIndex: visibleSheetIndex ?? shape.workbookSheetIndex\n };\n });\n }, [activeSheetIndex, shapesByWorkbookSheetIndex, sheets, visibleSheetIndexByWorkbookSheetIndex]);\n\n const shapes = React.useMemo(() => getSheetShapes(activeSheetIndex), [activeSheetIndex, getSheetShapes]);\n\n const getImageById = React.useCallback((id: string) => {\n for (const sheetImages of imagesByWorkbookSheetIndex) {\n const match = sheetImages?.find((image) => image.id === id);\n if (match) {\n return mapPublicImage(match);\n }\n }\n\n return null;\n }, [imagesByWorkbookSheetIndex, mapPublicImage]);\n\n const selectedImage = React.useMemo(\n () => (selectedImageId ? getImageById(selectedImageId) : null),\n [getImageById, selectedImageId]\n );\n\n const selectImage = React.useCallback((id: string | null) => {\n setSelectedChartId(null);\n setSelectedChartElement(null);\n setSelectedImageId(id);\n }, []);\n\n const clearSelectedImage = React.useCallback(() => {\n setSelectedImageId(null);\n }, []);\n\n const getColumnWidthPx = React.useCallback((worksheet: ReturnType<Workbook[\"getSheet\"]>, col: number) => {\n const sheetState = imageAssetsRef.current?.sheetStatesByWorkbookSheetIndex[activeSheet?.workbookSheetIndex ?? -1] ?? null;\n const width = worksheet.getColumnWidth(col);\n const showGridLines = activeSheet?.showGridLines ?? true;\n if (width !== undefined && width !== null) {\n return resolveRenderedSheetAxisPixels(\n resolveSheetColumnWidthPixels(width, sheetState?.columnWidthCharacterWidthPx),\n showGridLines\n );\n }\n\n return resolveRenderedSheetAxisPixels(\n sheetState?.colWidthOverridesPx?.[col] ?? sheetState?.defaultColWidthPx ?? DEFAULT_COL_WIDTH,\n showGridLines\n );\n }, [activeSheet?.showGridLines, activeSheet?.workbookSheetIndex]);\n\n const getRowHeightPx = React.useCallback((worksheet: ReturnType<Workbook[\"getSheet\"]>, row: number) => {\n const sheetState = imageAssetsRef.current?.sheetStatesByWorkbookSheetIndex[activeSheet?.workbookSheetIndex ?? -1] ?? null;\n const height = worksheet.getRowHeight(row);\n const showGridLines = activeSheet?.showGridLines ?? true;\n if (height !== undefined && height !== null) {\n return resolveRenderedSheetAxisPixels(resolveSheetRowHeightPixels(height), showGridLines);\n }\n\n return resolveRenderedSheetAxisPixels(\n sheetState?.rowHeightOverridesPx?.[row] ?? sheetState?.defaultRowHeightPx ?? DEFAULT_ROW_HEIGHT,\n showGridLines\n );\n }, [activeSheet?.showGridLines, activeSheet?.workbookSheetIndex]);\n\n const getCellDisplayValue = React.useCallback((cell?: XlsxCellAddress | null) => {\n if (cell && activeSheet) {\n const workerSnapshot = workerCellSnapshotCacheRef.current.get(`${locale}:${activeSheet.workbookSheetIndex}:${cell.row}:${cell.col}`);\n if (workerSnapshot) {\n return workerSnapshot.displayValue;\n }\n }\n\n const worksheet = getActiveWorksheet();\n if (!worksheet || !cell) {\n return \"\";\n }\n\n const formula = worksheet.getFormulaAt(cell.row, cell.col);\n const cachedFormulaValue = formula ? activeSheet?.cachedFormulaValues?.[cellAddressToA1(cell)] : undefined;\n const formatted = worksheet.getFormattedValueAt(cell.row, cell.col);\n if (formatted && !(formula && cachedFormulaValue !== undefined && formatted.startsWith(\"#\"))) {\n return localizeCellNumber(worksheet, cell.row, cell.col, decodeHtmlEntities(formatted), locale);\n }\n\n const calculated = worksheet.getCalculatedValueAt(cell.row, cell.col);\n if (formula && cachedFormulaValue !== undefined && calculated.is_error) {\n return cachedFormulaValue;\n }\n if (calculated.is_error) {\n return calculated.asError() ?? \"\";\n }\n if (calculated.is_empty) {\n return \"\";\n }\n\n return calculated.toString();\n }, [activeSheet, getActiveWorksheet, locale]);\n\n const getCellFormula = React.useCallback((cell?: XlsxCellAddress | null) => {\n if (cell && activeSheet) {\n const workerSnapshot = workerCellSnapshotCacheRef.current.get(`${locale}:${activeSheet.workbookSheetIndex}:${cell.row}:${cell.col}`);\n if (workerSnapshot) {\n return workerSnapshot.formula;\n }\n }\n\n const worksheet = getActiveWorksheet();\n if (!worksheet || !cell) {\n return \"\";\n }\n\n return worksheet.getFormulaAt(cell.row, cell.col) ?? \"\";\n }, [activeSheet, getActiveWorksheet, locale]);\n\n const getClipboardData = React.useCallback((): XlsxClipboardData | null => {\n const worksheet = getActiveWorksheet();\n const targetRange = selection ?? (activeCell ? { start: activeCell, end: activeCell } : null);\n if (!worksheet || !targetRange) {\n return null;\n }\n\n const normalized = normalizeRange(targetRange);\n const rows: string[] = [];\n const htmlRows: string[] = [];\n const payload: ClipboardPayload = {\n cells: [],\n cols: normalized.end.col - normalized.start.col + 1,\n merges: [],\n rows: normalized.end.row - normalized.start.row + 1\n };\n\n for (let row = normalized.start.row; row <= normalized.end.row; row += 1) {\n const textCells: string[] = [];\n const htmlCells: string[] = [];\n\n for (let col = normalized.start.col; col <= normalized.end.col; col += 1) {\n if (worksheet.isMergedSecondary(row, col)) {\n textCells.push(\"\");\n continue;\n }\n\n const formula = worksheet.getFormulaAt(row, col) ?? null;\n const value = getCellDisplayValue({ row, col });\n const merge = worksheet.getMergeSpan(row, col) as\n | { colSpan?: number; rowSpan?: number }\n | null\n | undefined;\n const rawStyle = (\n worksheet.getCellStyleAt(row, col) as Record<string, unknown> | null | undefined\n ) ?? resolveInheritedCellStyle(activeSheet, row, col);\n const cellStyles: string[] = [\n \"padding:2px 4px\",\n \"white-space:pre-wrap\",\n \"vertical-align:top\"\n ];\n\n const fill = rawStyle?.fill as Record<string, unknown> | undefined;\n if (fill) {\n const fillStyle = resolveWorkbookFillStyle(fill, activeSheet?.themePalette);\n if (fillStyle.backgroundColor && fillStyle.backgroundColor.toLowerCase() !== \"#ffffff\") {\n cellStyles.push(`background-color:${fillStyle.backgroundColor}`);\n }\n if (fillStyle.backgroundImage) {\n cellStyles.push(`background-image:${fillStyle.backgroundImage}`);\n }\n }\n\n const font = rawStyle?.font as Record<string, unknown> | undefined;\n if (font) {\n if (font.bold) {\n cellStyles.push(\"font-weight:700\");\n }\n if (font.italic) {\n cellStyles.push(\"font-style:italic\");\n }\n if (font.underline && font.underline !== \"none\") {\n cellStyles.push(\"text-decoration:underline\");\n }\n if (font.strikethrough) {\n cellStyles.push(\"text-decoration:line-through\");\n }\n const fontColor = resolveWorkbookColor(font.color as Record<string, unknown> | undefined, activeSheet?.themePalette);\n if (fontColor) {\n cellStyles.push(`color:${fontColor}`);\n }\n if (typeof font.size === \"number\") {\n cellStyles.push(`font-size:${font.size}pt`);\n }\n }\n\n const alignment = rawStyle?.alignment as Record<string, unknown> | undefined;\n if (alignment?.horizontal && alignment.horizontal !== \"general\") {\n cellStyles.push(`text-align:${String(alignment.horizontal)}`);\n }\n if (alignment?.wrapText) {\n cellStyles.push(\"white-space:pre-wrap\");\n cellStyles.push(\"word-break:break-word\");\n }\n\n const border = rawStyle?.border as Record<string, Record<string, unknown>> | undefined;\n if (border?.top?.style && border.top.style !== \"none\") {\n cellStyles.push(`border-top:${mapBorder(border.top as { color?: { hex?: string }; style: string })}`);\n }\n if (border?.right?.style && border.right.style !== \"none\") {\n cellStyles.push(`border-right:${mapBorder(border.right as { color?: { hex?: string }; style: string })}`);\n }\n if (border?.bottom?.style && border.bottom.style !== \"none\") {\n cellStyles.push(`border-bottom:${mapBorder(border.bottom as { color?: { hex?: string }; style: string })}`);\n }\n if (border?.left?.style && border.left.style !== \"none\") {\n cellStyles.push(`border-left:${mapBorder(border.left as { color?: { hex?: string }; style: string })}`);\n }\n\n const rowSpan = Math.min(merge?.rowSpan ?? 1, normalized.end.row - row + 1);\n const colSpan = Math.min(merge?.colSpan ?? 1, normalized.end.col - col + 1);\n\n payload.cells.push({\n colOffset: col - normalized.start.col,\n formula,\n rowOffset: row - normalized.start.row,\n value\n });\n\n if (rowSpan > 1 || colSpan > 1) {\n payload.merges.push({\n colOffset: col - normalized.start.col,\n colSpan,\n rowOffset: row - normalized.start.row,\n rowSpan\n });\n }\n\n textCells.push(value);\n htmlCells.push(\n `<td${rowSpan > 1 ? ` rowspan=\"${rowSpan}\"` : \"\"}${colSpan > 1 ? ` colspan=\"${colSpan}\"` : \"\"} style=\"${escapeHtml(cellStyles.join(\";\"))}\">${escapeHtml(value)}</td>`\n );\n }\n\n rows.push(textCells.join(\"\\t\"));\n htmlRows.push(`<tr>${htmlCells.join(\"\")}</tr>`);\n }\n\n return {\n html: `<table style=\"border-collapse:collapse\">${htmlRows.join(\"\")}</table>`,\n structured: JSON.stringify(payload),\n text: rows.join(\"\\n\")\n };\n }, [activeCell, getActiveWorksheet, getCellDisplayValue, selection]);\n\n React.useEffect(() => {\n if (!isWorkerBacked || !deferredMetadataSheet || !deferredMetadataCell) {\n return;\n }\n\n const cacheKey = `${locale}:${deferredMetadataSheet.workbookSheetIndex}:${deferredMetadataCell.row}:${deferredMetadataCell.col}`;\n if (workerCellSnapshotCacheRef.current.has(cacheKey)) {\n return;\n }\n\n let isCurrent = true;\n void getCellSnapshotAsync(deferredMetadataSheet.workbookSheetIndex, deferredMetadataCell.row, deferredMetadataCell.col)\n .then((snapshot) => {\n if (!isCurrent) {\n return;\n }\n\n workerCellSnapshotCacheRef.current.set(cacheKey, snapshot);\n setWorkerCellSnapshotRevision((current) => current + 1);\n })\n .catch(() => {\n if (!isCurrent) {\n return;\n }\n\n workerCellSnapshotCacheRef.current.set(cacheKey, {\n displayValue: \"\",\n formula: \"\"\n });\n setWorkerCellSnapshotRevision((current) => current + 1);\n });\n\n return () => {\n isCurrent = false;\n };\n }, [deferredMetadataCell, deferredMetadataSheet, getCellSnapshotAsync, isWorkerBacked, locale]);\n\n const activeCellAddress = React.useMemo(() => (activeCell ? cellAddressToA1(activeCell) : null), [activeCell]);\n const selectedRangeAddress = React.useMemo(() => (selection ? rangeToA1(selection) : null), [selection]);\n const selectedValue = React.useMemo(\n () => getCellDisplayValue(deferredMetadataCell),\n [deferredMetadataCell, getCellDisplayValue, revision, workerCellSnapshotRevision]\n );\n const selectedCellFormula = React.useMemo(\n () => getCellFormula(deferredMetadataCell),\n [deferredMetadataCell, getCellFormula, revision, workerCellSnapshotRevision]\n );\n const getChartSeriesFormula = React.useCallback((chartId: string, seriesIndex: number) => (\n buildChartSeriesFormula(getChartById(chartId), seriesIndex)\n ), [getChartById]);\n const selectedChartFormula = React.useMemo(() => {\n if (\n !selectedChartElement\n || selectedChartElement.kind === \"chart\"\n || selectedChartElement.seriesIndex < 0\n ) {\n return null;\n }\n\n return getChartSeriesFormula(selectedChartElement.chartId, selectedChartElement.seriesIndex);\n }, [getChartSeriesFormula, selectedChartElement]);\n const selectedFormulaTarget = React.useMemo(() => {\n if (selectedChartFormula && selectedChartElement && selectedChartElement.kind !== \"chart\") {\n return {\n chartId: selectedChartElement.chartId,\n kind: \"chartSeries\" as const,\n seriesId: selectedChartElement.seriesId,\n seriesIndex: selectedChartElement.seriesIndex\n };\n }\n\n return {\n cell: deferredMetadataCell,\n kind: \"cell\" as const\n };\n }, [deferredMetadataCell, selectedChartElement, selectedChartFormula]);\n const selectedFormula = selectedChartFormula ?? selectedCellFormula;\n const isLoadDeferred = deferredLoadFileSize !== null;\n const canLoadDeferred = !isLoading && isLoadDeferred;\n const canUndo = !readOnly && undoStackRef.current.length > 0;\n const canRedo = !readOnly && redoStackRef.current.length > 0;\n\n const createSavedWorkbookBytes = React.useCallback((targetWorkbook: Workbook) => {\n const sanitizedBytes = sanitizeSavedWorkbookBytes(targetWorkbook.saveXlsxBytes());\n return mergeWorkbookImageAssets(sanitizedBytes, imageAssetsRef.current, sheetOriginsRef.current);\n }, []);\n\n const createHistoryEntry = React.useCallback((): SnapshotHistoryEntry | null => {\n if (!workbook) {\n return null;\n }\n\n return {\n kind: \"snapshot\",\n activeCell,\n activeSheetIndex,\n bytes: createSavedWorkbookBytes(workbook),\n selection\n };\n }, [activeCell, activeSheetIndex, createSavedWorkbookBytes, selection, workbook]);\n\n const captureCellMutationState = React.useCallback((cell: XlsxCellAddress): CellMutationState | null => {\n const worksheet = getActiveWorksheet();\n if (!worksheet) {\n return null;\n }\n\n return {\n formula: worksheet.getFormulaAt(cell.row, cell.col) ?? null,\n style: worksheet.getCellStyleAt(cell.row, cell.col),\n value: worksheet.getCellAt(cell.row, cell.col).toJs()\n };\n }, [getActiveWorksheet]);\n\n const restoreHistoryEntry = React.useCallback(async (entry: SnapshotHistoryEntry) => {\n const wasmModule = await getSheetsWasmModule();\n const nextWorkbook = wasmModule.Workbook.fromBytes(cloneBytes(entry.bytes));\n const nextImageAssets = loadWorkbookImageAssets(entry.bytes, nextWorkbook);\n const nextSheets = buildSheetList(\n nextWorkbook,\n nextImageAssets.sheetStatesByWorkbookSheetIndex,\n nextImageAssets.themePalette,\n nextImageAssets.styleById,\n nextImageAssets.namedCellStyleByName,\n nextImageAssets.tableStyleByName,\n showHiddenSheets\n );\n const nextSheetIndex = Math.max(0, Math.min(entry.activeSheetIndex, Math.max(0, nextSheets.length - 1)));\n\n setError(null);\n setIsLoading(false);\n setImageAssets(nextImageAssets);\n setWorkbook(nextWorkbook);\n setSheets(nextSheets);\n const nextChartAssets = loadWorkbookChartAssets(nextWorkbook, nextImageAssets, buildVisibleSheetIndexMap(nextSheets), showHiddenSheets);\n setChartAssets(nextChartAssets);\n setActiveSheetIndexState(nextSheetIndex);\n const nextTabIndex = nextChartAssets.tabs.findIndex((tab) => tab.kind === \"sheet\" && tab.sheetIndex === nextSheetIndex);\n if (nextTabIndex >= 0) {\n setActiveTabIndexState(nextTabIndex);\n }\n setActiveCell(entry.activeCell);\n setSelection(entry.selection);\n selectionAnchorRef.current = entry.selection ? normalizeRange(entry.selection).start : entry.activeCell;\n setRevision((current) => current + 1);\n }, [setChartAssets, setImageAssets]);\n\n const applyCellEditHistoryEntry = React.useCallback((\n entry: CellEditHistoryEntry,\n direction: \"undo\" | \"redo\"\n ) => {\n if (!workbook) {\n return;\n }\n\n const worksheet = workbook.getSheet(entry.sheetIndex);\n const visibleSheetIndex = sheets.findIndex((sheet) => sheet.workbookSheetIndex === entry.sheetIndex);\n const targetState = direction === \"undo\" ? entry.before : entry.after;\n\n isApplyingHistoryRef.current = true;\n applyCellMutationState(worksheet, entry.cell, targetState);\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n\n const nextActiveCell = direction === \"undo\" ? entry.activeCellBefore : entry.activeCellAfter;\n const nextSelection = direction === \"undo\" ? entry.selectionBefore : entry.selectionAfter;\n if (visibleSheetIndex >= 0) {\n setActiveSheetIndexState(visibleSheetIndex);\n }\n setActiveCell(nextActiveCell);\n setSelection(nextSelection);\n selectionAnchorRef.current = nextSelection ? normalizeRange(nextSelection).start : nextActiveCell;\n isApplyingHistoryRef.current = false;\n }, [maybeRecalculateWorkbook, refreshWorkbookState, sheets, workbook]);\n\n const applyRangeEditHistoryEntry = React.useCallback((\n entry: RangeEditHistoryEntry,\n direction: \"undo\" | \"redo\"\n ) => {\n if (!workbook) {\n return;\n }\n\n const worksheet = workbook.getSheet(entry.sheetIndex);\n const visibleSheetIndex = sheets.findIndex((sheet) => sheet.workbookSheetIndex === entry.sheetIndex);\n\n isApplyingHistoryRef.current = true;\n for (const mutation of entry.mutations) {\n applyCellMutationState(worksheet, mutation.cell, direction === \"undo\" ? mutation.before : mutation.after);\n }\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n\n const nextActiveCell = direction === \"undo\" ? entry.activeCellBefore : entry.activeCellAfter;\n const nextSelection = direction === \"undo\" ? entry.selectionBefore : entry.selectionAfter;\n if (visibleSheetIndex >= 0) {\n setActiveSheetIndexState(visibleSheetIndex);\n }\n setActiveCell(nextActiveCell);\n setSelection(nextSelection);\n selectionAnchorRef.current = nextSelection ? normalizeRange(nextSelection).start : nextActiveCell;\n isApplyingHistoryRef.current = false;\n }, [maybeRecalculateWorkbook, refreshWorkbookState, sheets, workbook]);\n\n const recordHistoryBeforeMutation = React.useCallback(() => {\n if (isApplyingHistoryRef.current) {\n return;\n }\n\n const snapshot = createHistoryEntry();\n if (!snapshot) {\n return;\n }\n\n pushHistoryEntry(undoStackRef.current, snapshot);\n redoStackRef.current = [];\n setHistoryRevision((current) => current + 1);\n }, [createHistoryEntry]);\n\n const addFormControl = React.useCallback((input: XlsxFormControlInput, sheetIndex = activeSheetIndex) => {\n const target = getFormControlWorksheet(sheetIndex);\n if (readOnly || !workbook || !target) {\n return null;\n }\n\n recordHistoryBeforeMutation();\n target.worksheet.addDrawing(xlsxFormControlInputToDukeDrawing(input));\n const controlIndex = target.worksheet.formControlCount - 1;\n const addedControl = target.worksheet.formControls[controlIndex];\n const addedKind = addedControl?.formControl.kind;\n if (addedControl && (addedKind?.kind === \"checkbox\" || addedKind?.kind === \"optionButton\")) {\n target.worksheet.setFormControlCheckState(addedControl.drawingPath, addedKind.state);\n } else if (addedKind?.kind === \"groupBox\") {\n reconcileCheckedOptionButtons(target.worksheet);\n } else if (addedKind && \"cellLink\" in addedKind && addedKind.cellLink) {\n workbook.syncFormControls();\n }\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n return controlIndex;\n }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);\n\n const updateFormControl = React.useCallback((\n controlIndex: number,\n patch: XlsxFormControlPatch,\n sheetIndex = activeSheetIndex\n ) => {\n const target = getFormControlWorksheet(sheetIndex);\n const currentControl = target?.worksheet.formControls[controlIndex];\n if (readOnly || !workbook || !target || !currentControl) {\n return false;\n }\n\n recordHistoryBeforeMutation();\n const currentInput = formControlInputFromDukeControl(currentControl);\n if (currentInput.kind !== \"formControl\" || !currentControl.anchor) {\n return false;\n }\n const nextAnchor = patch.anchor\n ? xlsxAnchorToDukeDrawingAnchor(\n patch.anchor,\n patch.editAs ?? (currentControl.anchor.type === \"twoCell\" ? currentControl.anchor.editAs : undefined)\n )\n : patch.editAs !== undefined && currentControl.anchor.type === \"twoCell\"\n ? { ...currentControl.anchor, editAs: patch.editAs }\n : currentControl.anchor;\n let nextKind = patch.kind\n ? xlsxFormControlKindToDukeKind(patch.kind)\n : dukeFormControlKindToInput(currentControl.formControl.kind);\n if (currentControl.formControl.kind.kind === \"unknown\" && nextKind.kind === \"unknown\") {\n nextKind = {\n ...nextKind,\n legacyObjectType: nextKind.legacyObjectType ?? currentControl.formControl.kind.legacyObjectType\n };\n }\n const patchedRawObj = patch.kind?.kind === \"editbox\" || patch.kind?.kind === \"unknown\"\n ? patch.kind.rawObj\n : undefined;\n const patchedRawProperties = patch.kind?.kind === \"editbox\" || patch.kind?.kind === \"unknown\"\n ? patch.kind.rawProperties\n : undefined;\n const nextInput: DukeFormControlDrawingInput = {\n altText: \"altText\" in patch ? patch.altText : currentControl.altText,\n anchor: nextAnchor,\n formControl: {\n ...currentInput.formControl,\n kind: nextKind,\n macroName: \"macroName\" in patch ? patch.macroName : currentControl.formControl.macroName,\n rawClientData: \"rawClientData\" in patch ? patch.rawClientData : currentControl.formControl.rawClientData,\n rawObj: patchedRawObj ?? currentControl.formControl.rawObj,\n rawProperties: patchedRawProperties ?? currentControl.formControl.rawProperties\n },\n hidden: \"hidden\" in patch ? patch.hidden : currentControl.hidden,\n kind: \"formControl\",\n locked: \"locked\" in patch ? patch.locked : currentControl.locked,\n name: \"name\" in patch ? patch.name : currentControl.name,\n printable: \"printable\" in patch ? patch.printable : currentControl.printable,\n title: \"title\" in patch ? patch.title : currentControl.title\n };\n\n target.worksheet.setDrawing(currentControl.drawingPath, nextInput);\n const updatedControl = target.worksheet.formControls[controlIndex];\n const updatedKind = updatedControl?.formControl.kind;\n const interactionChanged = updatedKind\n ? formControlInteractionChanged(currentControl.formControl.kind, updatedKind)\n : false;\n const groupTopologyChanged = patch.anchor !== undefined || (\n updatedKind !== undefined\n && updatedKind.kind !== currentControl.formControl.kind.kind\n && (updatedKind.kind === \"groupBox\" || currentControl.formControl.kind.kind === \"groupBox\")\n );\n if (interactionChanged && updatedControl && (updatedKind?.kind === \"checkbox\" || updatedKind?.kind === \"optionButton\")) {\n target.worksheet.setFormControlCheckState(updatedControl.drawingPath, updatedKind.state);\n } else if (groupTopologyChanged) {\n reconcileCheckedOptionButtons(target.worksheet);\n } else if (interactionChanged && updatedKind && \"cellLink\" in updatedKind && updatedKind.cellLink) {\n workbook.syncFormControls();\n }\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n return true;\n }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);\n\n const removeFormControl = React.useCallback((controlIndex: number, sheetIndex = activeSheetIndex) => {\n const target = getFormControlWorksheet(sheetIndex);\n if (readOnly || !workbook || !target || !target.worksheet.formControls[controlIndex]) {\n return false;\n }\n\n recordHistoryBeforeMutation();\n const removedControl = target.worksheet.formControls[controlIndex]!;\n const removedKind = removedControl.formControl.kind.kind;\n target.worksheet.removeDrawing(removedControl.drawingPath);\n if (removedKind === \"groupBox\" || removedKind === \"optionButton\") {\n reconcileCheckedOptionButtons(target.worksheet);\n maybeRecalculateWorkbook(workbook);\n }\n refreshWorkbookState(workbook);\n return true;\n }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);\n\n const recordCellEditHistory = React.useCallback((\n cell: XlsxCellAddress,\n before: CellMutationState,\n after: CellMutationState\n ) => {\n if (!activeSheet || isApplyingHistoryRef.current) {\n return;\n }\n\n pushHistoryEntry(undoStackRef.current, {\n kind: \"cell-edit\",\n activeCellAfter: cell,\n activeCellBefore: activeCell,\n after,\n before,\n cell,\n selectionAfter: { start: cell, end: cell },\n selectionBefore: selection,\n sheetIndex: activeSheet.workbookSheetIndex\n });\n redoStackRef.current = [];\n setHistoryRevision((current) => current + 1);\n }, [activeCell, activeSheet, selection]);\n\n const recordRangeEditHistory = React.useCallback((\n mutations: RangeCellMutation[],\n selectionAfter: XlsxCellRange | null,\n activeCellAfter: XlsxCellAddress | null\n ) => {\n if (!activeSheet || isApplyingHistoryRef.current || mutations.length === 0) {\n return;\n }\n\n pushHistoryEntry(undoStackRef.current, {\n kind: \"range-edit\",\n activeCellAfter,\n activeCellBefore: activeCell,\n mutations,\n selectionAfter,\n selectionBefore: selection,\n sheetIndex: activeSheet.workbookSheetIndex\n });\n redoStackRef.current = [];\n setHistoryRevision((current) => current + 1);\n }, [activeCell, activeSheet, selection]);\n\n const sortTable = React.useCallback((tableName: string, columnIndex: number, direction: XlsxTableSortDirection) => {\n const worksheet = getActiveWorksheet();\n const targetTable = tables.find((table) => table.name === tableName || table.displayName === tableName);\n if (!worksheet || !workbook || !activeSheet || !targetTable) {\n return;\n }\n\n const dataStartRow = targetTable.start.row + Math.max(targetTable.headerRowCount, 1);\n const totalsRowOffset = targetTable.totalsRowCount;\n const dataEndRow = targetTable.end.row - totalsRowOffset;\n const startCol = targetTable.start.col;\n const endCol = targetTable.end.col;\n const sortCol = startCol + columnIndex;\n\n if (columnIndex < 0 || sortCol > endCol || dataStartRow > dataEndRow) {\n return;\n }\n\n const rows: Array<{\n cells: CellMutationState[];\n index: number;\n sortBoolean: boolean | undefined;\n sortEmpty: boolean;\n sortNumber: number | undefined;\n sortText: string;\n }> = [];\n\n for (let row = dataStartRow; row <= dataEndRow; row += 1) {\n const cells: CellMutationState[] = [];\n for (let col = startCol; col <= endCol; col += 1) {\n cells.push({\n formula: worksheet.getFormulaAt(row, col) ?? null,\n style: worksheet.getCellStyleAt(row, col),\n value: worksheet.getCellAt(row, col).toJs()\n });\n }\n\n const calculated = worksheet.getCalculatedValueAt(row, sortCol);\n const formatted = decodeHtmlEntities(worksheet.getFormattedValueAt(row, sortCol) ?? \"\");\n rows.push({\n cells,\n index: row,\n sortBoolean: calculated.is_boolean ? calculated.asBoolean() : undefined,\n sortEmpty: calculated.is_empty || formatted.length === 0,\n sortNumber: calculated.is_number ? calculated.asNumber() : undefined,\n sortText: calculated.is_text ? (calculated.asText() ?? formatted) : formatted\n });\n }\n\n const sortedRows = [...rows].sort((left, right) => {\n if (left.sortEmpty !== right.sortEmpty) {\n return left.sortEmpty ? 1 : -1;\n }\n\n if (left.sortNumber !== undefined && right.sortNumber !== undefined) {\n return direction === \"ascending\" ? left.sortNumber - right.sortNumber : right.sortNumber - left.sortNumber;\n }\n\n if (left.sortBoolean !== undefined && right.sortBoolean !== undefined) {\n const leftValue = left.sortBoolean ? 1 : 0;\n const rightValue = right.sortBoolean ? 1 : 0;\n return direction === \"ascending\" ? leftValue - rightValue : rightValue - leftValue;\n }\n\n const comparedText = left.sortText.localeCompare(right.sortText, undefined, { numeric: true, sensitivity: \"base\" });\n return direction === \"ascending\" ? comparedText : -comparedText;\n });\n\n if (sortedRows.every((row, index) => row.index === rows[index]?.index)) {\n setSortState({ columnIndex, direction, tableName: targetTable.name });\n return;\n }\n\n const mutations: RangeCellMutation[] = [];\n for (let rowOffset = 0; rowOffset < rows.length; rowOffset += 1) {\n const targetRow = dataStartRow + rowOffset;\n const sourceRow = sortedRows[rowOffset];\n const beforeRow = rows[rowOffset];\n if (!sourceRow || !beforeRow) {\n continue;\n }\n\n for (let colOffset = 0; colOffset <= endCol - startCol; colOffset += 1) {\n const before = beforeRow.cells[colOffset];\n const after = sourceRow.cells[colOffset];\n if (!before || !after) {\n continue;\n }\n\n const cell = { row: targetRow, col: startCol + colOffset };\n applyCellMutationState(worksheet, cell, after);\n mutations.push({\n after,\n before,\n cell\n });\n }\n }\n\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n setSortState({ columnIndex, direction, tableName: targetTable.name });\n recordRangeEditHistory(mutations, selection, activeCell);\n }, [\n activeCell,\n activeSheet,\n getActiveWorksheet,\n maybeRecalculateWorkbook,\n readOnly,\n recordRangeEditHistory,\n refreshWorkbookState,\n selection,\n tables,\n workbook\n ]);\n\n const download = React.useCallback(() => {\n if (file) {\n downloadArrayBuffer(file, displayFileName);\n return;\n }\n\n if (src) {\n downloadUrl(src, displayFileName);\n }\n }, [displayFileName, file, src]);\n\n const getXlsxBytes = React.useCallback((): Uint8Array | null => {\n if (!workbook) {\n return null;\n }\n\n return createSavedWorkbookBytes(workbook);\n }, [createSavedWorkbookBytes, workbook]);\n\n const exportXlsx = React.useCallback(() => {\n const bytes = getXlsxBytes();\n if (!bytes) {\n return;\n }\n\n downloadBytes(bytes, `${fileStem(displayFileName)}.xlsx`, XLSX_MIME_TYPE);\n }, [displayFileName, getXlsxBytes]);\n\n const exportCsv = React.useCallback(() => {\n if (!workbook) {\n return;\n }\n\n const activeSheetName = activeSheet?.name ?? \"sheet\";\n downloadText(workbook.saveCsvString(), `${fileStem(displayFileName)}-${activeSheetName}.csv`, CSV_MIME_TYPE);\n }, [activeSheet?.name, displayFileName, workbook]);\n\n const recalculate = React.useCallback(() => {\n if (!workbook) {\n return;\n }\n\n const result = tryRecalculate(workbook);\n if (result.calculated) {\n refreshWorkbookState(workbook);\n return;\n }\n\n // Trap poisons the Workbook pointer; skip refreshWorkbookState so we\n // don't crash reading cells from it.\n setShouldAutoCalculate(false);\n }, [refreshWorkbookState, workbook]);\n\n const applyReadOnlyResizeOverride = React.useCallback((\n axis: \"column\" | \"row\",\n actualIndex: number,\n sizePx: number\n ) => {\n if (!activeSheet) {\n return;\n }\n\n const contentSizePx = resolveContentSheetAxisPixels(sizePx, activeSheet.showGridLines);\n const targetWorkbookSheetIndex = activeSheet.workbookSheetIndex;\n setSheets((currentSheets) => currentSheets.map((sheet) => {\n if (sheet.workbookSheetIndex !== targetWorkbookSheetIndex) {\n return sheet;\n }\n\n if (axis === \"column\") {\n const nextColWidthOverridesPx = {\n ...sheet.colWidthOverridesPx,\n [actualIndex]: contentSizePx\n };\n const nextColWidths = [...sheet.colWidths];\n const visibleColIndex = sheet.visibleCols.indexOf(actualIndex);\n if (visibleColIndex >= 0) {\n nextColWidths[visibleColIndex] = contentSizePx;\n }\n\n return {\n ...sheet,\n colWidthOverridesPx: nextColWidthOverridesPx,\n colWidths: nextColWidths\n };\n }\n\n const nextRowHeightOverridesPx = {\n ...sheet.rowHeightOverridesPx,\n [actualIndex]: contentSizePx\n };\n const nextRowHeights = [...sheet.rowHeights];\n const visibleRowIndex = sheet.visibleRows.indexOf(actualIndex);\n if (visibleRowIndex >= 0) {\n nextRowHeights[visibleRowIndex] = contentSizePx;\n }\n\n return {\n ...sheet,\n rowHeightOverridesPx: nextRowHeightOverridesPx,\n rowHeights: nextRowHeights\n };\n }));\n setRevision((current) => current + 1);\n }, [activeSheet]);\n\n const resizeColumn = React.useCallback((col: number, widthPx: number) => {\n if ((readOnly && !canResizeReadOnly) || !activeSheet) {\n return;\n }\n\n if (isWorkerBacked) {\n applyReadOnlyResizeOverride(\"column\", col, widthPx);\n return;\n }\n\n if (!workbook) {\n return;\n }\n\n recordHistoryBeforeMutation();\n const worksheet = workbook.getSheet(activeSheet.workbookSheetIndex);\n worksheet.setColumnWidth(\n col,\n pxToSheetColumnWidth(resolveContentSheetAxisPixels(widthPx, activeSheet.showGridLines))\n );\n refreshWorkbookState(workbook);\n }, [\n activeSheet,\n applyReadOnlyResizeOverride,\n canResizeReadOnly,\n isWorkerBacked,\n readOnly,\n recordHistoryBeforeMutation,\n refreshWorkbookState,\n workbook\n ]);\n\n const resizeRow = React.useCallback((row: number, heightPx: number) => {\n if ((readOnly && !canResizeReadOnly) || !activeSheet) {\n return;\n }\n\n if (isWorkerBacked) {\n applyReadOnlyResizeOverride(\"row\", row, heightPx);\n return;\n }\n\n if (!workbook) {\n return;\n }\n\n recordHistoryBeforeMutation();\n const worksheet = workbook.getSheet(activeSheet.workbookSheetIndex);\n worksheet.setRowHeight(\n row,\n pxToSheetRowHeight(resolveContentSheetAxisPixels(heightPx, activeSheet.showGridLines))\n );\n refreshWorkbookState(workbook);\n }, [\n activeSheet,\n applyReadOnlyResizeOverride,\n canResizeReadOnly,\n isWorkerBacked,\n readOnly,\n recordHistoryBeforeMutation,\n refreshWorkbookState,\n workbook\n ]);\n\n const resolveAnchoredObjectRect = React.useCallback((\n anchor: XlsxImage[\"anchor\"],\n worksheet: ReturnType<Workbook[\"getSheet\"]>\n ): XlsxImageRect => {\n const resolveAxisSum = (\n index: number,\n getSize: (target: number) => number\n ) => {\n let total = 0;\n for (let cursor = 0; cursor < index; cursor += 1) {\n total += getSize(cursor);\n }\n return total;\n };\n\n if (anchor.kind === \"absolute\") {\n return {\n height: anchor.sizeEmu.cy / 9525,\n left: GRID_ROW_HEADER_WIDTH + anchor.positionEmu.x / 9525,\n top: GRID_HEADER_HEIGHT + anchor.positionEmu.y / 9525,\n width: anchor.sizeEmu.cx / 9525\n };\n }\n\n const left = GRID_ROW_HEADER_WIDTH + resolveAxisSum(anchor.from.col, (col) => getColumnWidthPx(worksheet, col)) + anchor.from.colOffsetEmu / 9525;\n const top = GRID_HEADER_HEIGHT + resolveAxisSum(anchor.from.row, (row) => getRowHeightPx(worksheet, row)) + anchor.from.rowOffsetEmu / 9525;\n\n if (anchor.kind === \"one-cell\") {\n return {\n height: anchor.sizeEmu.cy / 9525,\n left,\n top,\n width: anchor.sizeEmu.cx / 9525\n };\n }\n\n const right = GRID_ROW_HEADER_WIDTH + resolveAxisSum(anchor.to.col, (col) => getColumnWidthPx(worksheet, col)) + anchor.to.colOffsetEmu / 9525;\n const bottom = GRID_HEADER_HEIGHT + resolveAxisSum(anchor.to.row, (row) => getRowHeightPx(worksheet, row)) + anchor.to.rowOffsetEmu / 9525;\n\n return {\n height: Math.max(1, bottom - top),\n left,\n top,\n width: Math.max(1, right - left)\n };\n }, [getColumnWidthPx, getRowHeightPx]);\n\n const setChartRect = React.useCallback((id: string, rect: XlsxImageRect, layout?: XlsxDrawingLayout) => {\n const hydratedChartAssets = ensureChartAssetsHydrated(workbook, sheets);\n console.info(\"[react-xlsx debug] setChartRect\", {\n hasActiveSheet: Boolean(activeSheet),\n hasHydratedChartAssets: Boolean(hydratedChartAssets),\n hasImageAssets: Boolean(imageAssetsRef.current),\n hasWorkbook: Boolean(workbook),\n id,\n readOnly,\n rect\n });\n if (readOnly || !workbook || !activeSheet || !imageAssetsRef.current || !hydratedChartAssets) {\n return;\n }\n\n const worksheet = workbook.getSheet(activeSheet.workbookSheetIndex);\n const currentChart = getChartById(id);\n console.info(\"[react-xlsx debug] currentChart\", {\n activeWorkbookSheetIndex: activeSheet.workbookSheetIndex,\n editable: currentChart?.editable,\n found: Boolean(currentChart),\n originCount: hydratedChartAssets.chartOriginsById.size,\n workbookSheetIndex: currentChart?.workbookSheetIndex\n });\n if (!currentChart || currentChart.editable === false || currentChart.workbookSheetIndex !== activeSheet.workbookSheetIndex) {\n return;\n }\n\n const nextAnchor = rectToImageAnchor(rect, currentChart.anchor, {\n contentOffsetLeft: GRID_ROW_HEADER_WIDTH,\n contentOffsetTop: GRID_HEADER_HEIGHT,\n getColumnWidthPx: (col) => layout?.columnWidths[col] ?? getColumnWidthPx(worksheet, col),\n getRowHeightPx: (row) => layout?.rowHeights[row] ?? getRowHeightPx(worksheet, row)\n });\n\n recordHistoryBeforeMutation();\n const didUpdateAnchor = updateWorkbookChartAnchor(imageAssetsRef.current, hydratedChartAssets, id, nextAnchor);\n console.info(\"[react-xlsx debug] updateWorkbookChartAnchor\", { didUpdateAnchor, nextAnchor });\n\n hydratedChartAssets.chartsByWorkbookSheetIndex = hydratedChartAssets.chartsByWorkbookSheetIndex.map((sheetCharts) => (\n sheetCharts.map((chart) => chart.id === id ? { ...chart, anchor: nextAnchor } : chart)\n ));\n\n setChartsByWorkbookSheetIndex((current) => current.map((sheetCharts) => (\n sheetCharts.map((chart) => chart.id === id ? { ...chart, anchor: nextAnchor } : chart)\n )));\n setRevision((current) => current + 1);\n }, [\n activeSheet,\n getChartById,\n getColumnWidthPx,\n getRowHeightPx,\n ensureChartAssetsHydrated,\n readOnly,\n recordHistoryBeforeMutation,\n sheets,\n workbook\n ]);\n\n const setImageRect = React.useCallback((id: string, rect: XlsxImageRect, layout?: XlsxDrawingLayout) => {\n if (readOnly || !workbook || !activeSheet || !imageAssetsRef.current) {\n return;\n }\n\n const worksheet = workbook.getSheet(activeSheet.workbookSheetIndex);\n const currentImage = getImageById(id);\n if (!currentImage || currentImage.editable === false || currentImage.workbookSheetIndex !== activeSheet.workbookSheetIndex) {\n return;\n }\n\n const nextAnchor = rectToImageAnchor(rect, currentImage.anchor, {\n contentOffsetLeft: GRID_ROW_HEADER_WIDTH,\n contentOffsetTop: GRID_HEADER_HEIGHT,\n getColumnWidthPx: (col) => layout?.columnWidths[col] ?? getColumnWidthPx(worksheet, col),\n getRowHeightPx: (row) => layout?.rowHeights[row] ?? getRowHeightPx(worksheet, row)\n });\n\n recordHistoryBeforeMutation();\n if (!updateWorkbookImageAnchor(imageAssetsRef.current, id, nextAnchor)) {\n return;\n }\n\n setImagesByWorkbookSheetIndex([...imageAssetsRef.current.imagesByWorkbookSheetIndex]);\n setRevision((current) => current + 1);\n }, [\n activeSheet,\n getColumnWidthPx,\n getImageById,\n getRowHeightPx,\n readOnly,\n recordHistoryBeforeMutation,\n workbook\n ]);\n\n const moveChartBy = React.useCallback((id: string, deltaX: number, deltaY: number) => {\n const currentChart = getChartById(id);\n if (!currentChart || currentChart.editable === false) {\n return;\n }\n\n const worksheet = getActiveWorksheet();\n if (!worksheet) {\n return;\n }\n\n const currentRect = resolveAnchoredObjectRect(currentChart.anchor, worksheet);\n setChartRect(id, {\n ...currentRect,\n left: currentRect.left + deltaX,\n top: currentRect.top + deltaY\n });\n }, [getActiveWorksheet, getChartById, resolveAnchoredObjectRect, setChartRect]);\n\n const moveImageBy = React.useCallback((id: string, deltaX: number, deltaY: number) => {\n const currentImage = getImageById(id);\n if (!currentImage || currentImage.editable === false) {\n return;\n }\n\n const currentRect = (() => {\n const worksheet = getActiveWorksheet();\n if (!worksheet) {\n return null;\n }\n\n const resolveAxisSum = (\n index: number,\n getSize: (target: number) => number\n ) => {\n let total = 0;\n for (let cursor = 0; cursor < index; cursor += 1) {\n total += getSize(cursor);\n }\n return total;\n };\n\n if (currentImage.anchor.kind === \"absolute\") {\n return {\n height: currentImage.anchor.sizeEmu.cy / 9525,\n left: GRID_ROW_HEADER_WIDTH + currentImage.anchor.positionEmu.x / 9525,\n top: GRID_HEADER_HEIGHT + currentImage.anchor.positionEmu.y / 9525,\n width: currentImage.anchor.sizeEmu.cx / 9525\n };\n }\n\n const left = GRID_ROW_HEADER_WIDTH + resolveAxisSum(currentImage.anchor.from.col, (col) => getColumnWidthPx(worksheet, col)) + currentImage.anchor.from.colOffsetEmu / 9525;\n const top = GRID_HEADER_HEIGHT + resolveAxisSum(currentImage.anchor.from.row, (row) => getRowHeightPx(worksheet, row)) + currentImage.anchor.from.rowOffsetEmu / 9525;\n\n if (currentImage.anchor.kind === \"one-cell\") {\n return {\n height: currentImage.anchor.sizeEmu.cy / 9525,\n left,\n top,\n width: currentImage.anchor.sizeEmu.cx / 9525\n };\n }\n\n const right = GRID_ROW_HEADER_WIDTH + resolveAxisSum(currentImage.anchor.to.col, (col) => getColumnWidthPx(worksheet, col)) + currentImage.anchor.to.colOffsetEmu / 9525;\n const bottom = GRID_HEADER_HEIGHT + resolveAxisSum(currentImage.anchor.to.row, (row) => getRowHeightPx(worksheet, row)) + currentImage.anchor.to.rowOffsetEmu / 9525;\n\n return {\n height: Math.max(1, bottom - top),\n left,\n top,\n width: Math.max(1, right - left)\n };\n })();\n\n if (!currentRect) {\n return;\n }\n\n setImageRect(id, {\n ...currentRect,\n left: currentRect.left + deltaX,\n top: currentRect.top + deltaY\n });\n }, [getActiveWorksheet, getColumnWidthPx, getImageById, getRowHeightPx, setImageRect]);\n\n const resizeChartBy = React.useCallback((\n id: string,\n handle: XlsxImageResizeHandlePosition,\n deltaX: number,\n deltaY: number\n ) => {\n const currentChart = getChartById(id);\n if (!currentChart || currentChart.editable === false) {\n return;\n }\n\n const worksheet = getActiveWorksheet();\n if (!worksheet) {\n return;\n }\n\n const currentRect = resolveAnchoredObjectRect(currentChart.anchor, worksheet);\n setChartRect(id, resizeImageRect(currentRect, handle, deltaX, deltaY, 48));\n }, [getActiveWorksheet, getChartById, resolveAnchoredObjectRect, setChartRect]);\n\n const resizeImageBy = React.useCallback((\n id: string,\n handle: XlsxImageResizeHandlePosition,\n deltaX: number,\n deltaY: number\n ) => {\n const currentImage = getImageById(id);\n if (!currentImage || currentImage.editable === false) {\n return;\n }\n\n const worksheet = getActiveWorksheet();\n if (!worksheet) {\n return;\n }\n\n const resolveAxisSum = (\n index: number,\n getSize: (target: number) => number\n ) => {\n let total = 0;\n for (let cursor = 0; cursor < index; cursor += 1) {\n total += getSize(cursor);\n }\n return total;\n };\n\n const left = currentImage.anchor.kind === \"absolute\"\n ? GRID_ROW_HEADER_WIDTH + currentImage.anchor.positionEmu.x / 9525\n : GRID_ROW_HEADER_WIDTH + resolveAxisSum(currentImage.anchor.from.col, (col) => getColumnWidthPx(worksheet, col)) + currentImage.anchor.from.colOffsetEmu / 9525;\n const top = currentImage.anchor.kind === \"absolute\"\n ? GRID_HEADER_HEIGHT + currentImage.anchor.positionEmu.y / 9525\n : GRID_HEADER_HEIGHT + resolveAxisSum(currentImage.anchor.from.row, (row) => getRowHeightPx(worksheet, row)) + currentImage.anchor.from.rowOffsetEmu / 9525;\n const width = currentImage.anchor.kind === \"two-cell\"\n ? Math.max(\n 1,\n GRID_ROW_HEADER_WIDTH + resolveAxisSum(currentImage.anchor.to.col, (col) => getColumnWidthPx(worksheet, col)) + currentImage.anchor.to.colOffsetEmu / 9525 - left\n )\n : currentImage.anchor.sizeEmu.cx / 9525;\n const height = currentImage.anchor.kind === \"two-cell\"\n ? Math.max(\n 1,\n GRID_HEADER_HEIGHT + resolveAxisSum(currentImage.anchor.to.row, (row) => getRowHeightPx(worksheet, row)) + currentImage.anchor.to.rowOffsetEmu / 9525 - top\n )\n : currentImage.anchor.sizeEmu.cy / 9525;\n\n const nextRect = resizeImageRect({ height, left, top, width }, handle, deltaX, deltaY);\n setImageRect(id, nextRect);\n }, [getActiveWorksheet, getColumnWidthPx, getImageById, getRowHeightPx, setImageRect]);\n\n const updateChart = React.useCallback((id: string, patch: Partial<XlsxChart>) => {\n const currentChart = getChartById(id);\n const hydratedChartAssets = ensureChartAssetsHydrated(workbook, sheets);\n if (readOnly || !currentChart) {\n return;\n }\n\n recordHistoryBeforeMutation();\n if (patch.anchor && imageAssetsRef.current && hydratedChartAssets) {\n updateWorkbookChartAnchor(imageAssetsRef.current, hydratedChartAssets, id, patch.anchor);\n }\n if (imageAssetsRef.current && hydratedChartAssets) {\n updateWorkbookChartDefinition(imageAssetsRef.current, hydratedChartAssets, id, patch);\n }\n\n setChartsByWorkbookSheetIndex((current) => current.map((sheetCharts) => (\n sheetCharts.map((chart) => {\n if (chart.id !== id) return chart;\n const appearanceUnchanged = Object.keys(patch).every((key) => key === \"anchor\" || key === \"title\");\n return { ...chart, renderSource: appearanceUnchanged ? chart.renderSource : undefined, ...patch };\n })\n )));\n setRevision((current) => current + 1);\n }, [ensureChartAssetsHydrated, getChartById, readOnly, recordHistoryBeforeMutation, sheets, workbook]);\n\n const setChartSeriesFormula = React.useCallback((chartId: string, seriesIndex: number, formula: string) => {\n if (readOnly) {\n return false;\n }\n\n const chart = getChartById(chartId);\n if (!chart || chart.editable === false) {\n return false;\n }\n\n const nextChart = applyChartSeriesFormula(chart, seriesIndex, formula, workbook);\n if (!nextChart) {\n return false;\n }\n\n const source = chart.renderSource;\n const indexedCategoryFormulaChanged = source?.indexedXSeries?.[seriesIndex]\n && chart.series[seriesIndex]?.categoriesRef?.formula !== nextChart.series[seriesIndex]?.categoriesRef?.formula;\n updateChart(chartId, {\n series: nextChart.series,\n typeGroups: nextChart.typeGroups,\n renderSource: source?.sourceSeries || indexedCategoryFormulaChanged ? undefined : source\n });\n const selectedSeries = nextChart.series[seriesIndex];\n if (selectedSeries) {\n setSelectedChartElement((current) => (\n current && current.chartId === chartId && current.kind !== \"chart\"\n ? {\n ...current,\n seriesId: selectedSeries.id,\n seriesIndex\n }\n : current\n ));\n }\n return true;\n }, [getChartById, readOnly, updateChart, workbook]);\n\n const selectCell = React.useCallback((cell: XlsxCellAddress, options?: { extend?: boolean }) => {\n setSelectedChartId(null);\n setSelectedChartElement(null);\n setSelectedImageId(null);\n setActiveCell(cell);\n if (options?.extend && selectionAnchorRef.current) {\n setSelection(normalizeRange({ start: selectionAnchorRef.current, end: cell }));\n return;\n }\n\n selectionAnchorRef.current = cell;\n setSelection({ start: cell, end: cell });\n }, []);\n\n const selectRange = React.useCallback((range: XlsxCellRange) => {\n const normalized = normalizeRange(range);\n setSelectedChartId(null);\n setSelectedChartElement(null);\n setSelectedImageId(null);\n selectionAnchorRef.current = normalized.start;\n setActiveCell(normalized.end);\n setSelection(normalized);\n }, []);\n\n const clearSelection = React.useCallback(() => {\n selectionAnchorRef.current = null;\n setActiveCell(null);\n setSelection(null);\n setSelectedChartId(null);\n setSelectedChartElement(null);\n setSelectedImageId(null);\n }, []);\n\n const clearSelectedCells = React.useCallback(() => {\n const worksheet = getActiveWorksheet();\n const targetRange = selection ?? (activeCell ? { start: activeCell, end: activeCell } : null);\n if (readOnly || !worksheet || !workbook || !targetRange) {\n return;\n }\n\n const normalized = normalizeRange(targetRange);\n const mutations: RangeCellMutation[] = [];\n for (let row = normalized.start.row; row <= normalized.end.row; row += 1) {\n for (let col = normalized.start.col; col <= normalized.end.col; col += 1) {\n if (worksheet.isMergedSecondary(row, col)) {\n continue;\n }\n\n const cell = { row, col };\n const before = captureCellMutationState(cell);\n if (!before) {\n continue;\n }\n\n worksheet.setCell(cellAddressToA1({ row, col }), \"\");\n const after = captureCellMutationState(cell);\n if (!after) {\n continue;\n }\n mutations.push({\n after,\n before,\n cell\n });\n }\n }\n\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n recordRangeEditHistory(mutations, normalized, activeCell ?? normalized.start);\n }, [\n activeCell,\n captureCellMutationState,\n getActiveWorksheet,\n maybeRecalculateWorkbook,\n readOnly,\n recordRangeEditHistory,\n refreshWorkbookState,\n selection,\n workbook\n ]);\n\n const setCellValue = React.useCallback((cell: XlsxCellAddress, value: string) => {\n const worksheet = getActiveWorksheet();\n if (readOnly || !worksheet || !workbook) {\n return;\n }\n\n const before = captureCellMutationState(cell);\n if (!before) {\n return;\n }\n\n if (locale !== \"en-US\" && value === getCellDisplayValue(cell)) return;\n const nextValue = coerceUserEnteredValue(value, locale);\n worksheet.setCell(cellAddressToA1(cell), nextValue);\n const after = captureCellMutationState(cell);\n if (!after) {\n return;\n }\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n recordCellEditHistory(cell, before, after);\n }, [captureCellMutationState, getActiveWorksheet, getCellDisplayValue, locale, maybeRecalculateWorkbook, readOnly, recordCellEditHistory, refreshWorkbookState, workbook]);\n\n const setCellFormula = React.useCallback((cell: XlsxCellAddress, formula: string) => {\n const worksheet = getActiveWorksheet();\n if (readOnly || !worksheet || !workbook) {\n return;\n }\n\n const before = captureCellMutationState(cell);\n if (!before) {\n return;\n }\n\n const trimmedFormula = formula.trim();\n if (!formula.trim()) {\n worksheet.setCell(cellAddressToA1(cell), \"\");\n } else {\n worksheet.setFormula(cellAddressToA1(cell), formula);\n }\n const after = captureCellMutationState(cell);\n if (!after) {\n return;\n }\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n recordCellEditHistory(cell, before, after);\n }, [captureCellMutationState, getActiveWorksheet, maybeRecalculateWorkbook, readOnly, recordCellEditHistory, refreshWorkbookState, workbook]);\n\n const setCellStyle = React.useCallback((cell: XlsxCellAddress, style: XlsxCellStyleInput) => {\n const worksheet = getActiveWorksheet();\n if (readOnly || !worksheet || !workbook) {\n return;\n }\n\n const before = captureCellMutationState(cell);\n if (!before) {\n return;\n }\n\n worksheet.setCellStyleAt(cell.row, cell.col, style);\n const after = captureCellMutationState(cell);\n if (!after) {\n return;\n }\n\n refreshWorkbookState(workbook);\n recordCellEditHistory(cell, before, after);\n }, [captureCellMutationState, getActiveWorksheet, readOnly, recordCellEditHistory, refreshWorkbookState, workbook]);\n\n const setSelectedCellValue = React.useCallback((value: string) => {\n if (!activeCell) {\n return;\n }\n\n setCellValue(activeCell, value);\n }, [activeCell, setCellValue]);\n\n const setSelectedCellFormula = React.useCallback((formula: string) => {\n if (!activeCell) {\n return;\n }\n\n setCellFormula(activeCell, formula);\n }, [activeCell, setCellFormula]);\n\n const setSelectedFormula = React.useCallback((formula: string) => {\n if (selectedFormulaTarget?.kind === \"chartSeries\") {\n return setChartSeriesFormula(selectedFormulaTarget.chartId, selectedFormulaTarget.seriesIndex, formula);\n }\n\n if (!activeCell) {\n return false;\n }\n\n setCellFormula(activeCell, formula);\n return true;\n }, [activeCell, selectedFormulaTarget, setCellFormula, setChartSeriesFormula]);\n\n const setSelectedCellStyle = React.useCallback((style: XlsxCellStyleInput) => {\n if (!activeCell) {\n return;\n }\n\n setCellStyle(activeCell, style);\n }, [activeCell, setCellStyle]);\n\n const setRangeStyle = React.useCallback((range: XlsxCellRange, style: XlsxCellStyleInput) => {\n const worksheet = getActiveWorksheet();\n if (readOnly || !worksheet || !workbook) {\n return;\n }\n\n const normalized = normalizeRange(range);\n const beforeStates: Array<{ before: CellMutationState; cell: XlsxCellAddress }> = [];\n for (let row = normalized.start.row; row <= normalized.end.row; row += 1) {\n for (let col = normalized.start.col; col <= normalized.end.col; col += 1) {\n const cell = { row, col };\n const before = captureCellMutationState(cell);\n if (!before) {\n continue;\n }\n beforeStates.push({\n before,\n cell\n });\n }\n }\n\n if (beforeStates.length === 0) {\n return;\n }\n\n worksheet.setRangeStyle(rangeToA1(normalized), style);\n const mutations: RangeCellMutation[] = [];\n for (const mutation of beforeStates) {\n const after = captureCellMutationState(mutation.cell);\n if (!after) {\n continue;\n }\n mutations.push({\n after,\n before: mutation.before,\n cell: mutation.cell\n });\n }\n\n refreshWorkbookState(workbook);\n recordRangeEditHistory(mutations, selection, activeCell);\n }, [activeCell, captureCellMutationState, getActiveWorksheet, readOnly, recordRangeEditHistory, refreshWorkbookState, selection, workbook]);\n\n const fillSelection = React.useCallback((targetRange: XlsxCellRange) => {\n const worksheet = getActiveWorksheet();\n if (readOnly || !worksheet || !workbook || !selection) {\n return;\n }\n\n const sourceRange = normalizeRange(selection);\n const nextRange = normalizeRange(targetRange);\n const sourceHeight = sourceRange.end.row - sourceRange.start.row + 1;\n const sourceWidth = sourceRange.end.col - sourceRange.start.col + 1;\n\n if (sourceHeight <= 0 || sourceWidth <= 0) {\n return;\n }\n\n const mutations: RangeCellMutation[] = [];\n for (let row = nextRange.start.row; row <= nextRange.end.row; row += 1) {\n for (let col = nextRange.start.col; col <= nextRange.end.col; col += 1) {\n if (rangeContainsCell(sourceRange, { row, col })) {\n continue;\n }\n\n const targetCell = { row, col };\n const before = captureCellMutationState(targetCell);\n if (!before) {\n continue;\n }\n\n const sourceRow = sourceRange.start.row + ((row - nextRange.start.row) % sourceHeight);\n const sourceCol = sourceRange.start.col + ((col - nextRange.start.col) % sourceWidth);\n const sourceFormula = worksheet.getFormulaAt(sourceRow, sourceCol);\n const sourceStyle = cloneCellStyle(worksheet.getCellStyleAt(sourceRow, sourceCol));\n\n if (sourceFormula) {\n worksheet.setFormula(cellAddressToA1(targetCell), sourceFormula);\n } else {\n const sourceValue = normalizeCellValue(worksheet.getCellAt(sourceRow, sourceCol).toJs());\n worksheet.setCell(cellAddressToA1(targetCell), sourceValue);\n }\n\n if (sourceStyle && typeof sourceStyle === \"object\") {\n worksheet.setCellStyleAt(targetCell.row, targetCell.col, sourceStyle);\n }\n\n const after = captureCellMutationState(targetCell);\n if (!after) {\n continue;\n }\n mutations.push({\n after,\n before,\n cell: targetCell\n });\n }\n }\n\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n setSelection(nextRange);\n setActiveCell(nextRange.end);\n selectionAnchorRef.current = nextRange.start;\n recordRangeEditHistory(mutations, nextRange, nextRange.end);\n }, [captureCellMutationState, getActiveWorksheet, maybeRecalculateWorkbook, readOnly, recordRangeEditHistory, refreshWorkbookState, selection, workbook]);\n\n const mergeSelection = React.useCallback((mode: XlsxMergeMode = \"cells\") => {\n const worksheet = getActiveWorksheet();\n if (readOnly || !worksheet || !selection || !workbook) {\n return;\n }\n\n const mergedRanges = resolveWorksheetMergeMetadata(worksheet).mergedRanges;\n const range = expandHighlightedRange(normalizeRange(selection), mergedRanges);\n const isSingleCell = range.start.row === range.end.row && range.start.col === range.end.col;\n if ((isSingleCell && mode !== \"center\") || (mode === \"across\" && range.start.col === range.end.col)) {\n return;\n }\n\n recordHistoryBeforeMutation();\n for (const merged of mergedRanges) {\n if (rangesIntersect(range, merged)) {\n worksheet.unmergeCells(rangeToA1(merged));\n }\n }\n if (mode === \"across\") {\n for (let row = range.start.row; row <= range.end.row; row += 1) {\n worksheet.mergeCells(rangeToA1({\n start: { row, col: range.start.col },\n end: { row, col: range.end.col }\n }));\n }\n } else {\n if (!isSingleCell) worksheet.mergeCells(rangeToA1(range));\n if (mode === \"center\") {\n worksheet.setCellStyleAt(range.start.row, range.start.col, { alignment: { horizontal: \"center\" } });\n }\n }\n refreshWorkbookState(workbook);\n selectionAnchorRef.current = range.start;\n setSelection(range);\n setActiveCell(range.start);\n }, [getActiveWorksheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, selection, workbook]);\n\n const unmergeSelection = React.useCallback(() => {\n const worksheet = getActiveWorksheet();\n if (readOnly || !worksheet || !selection || !workbook) {\n return;\n }\n\n const range = normalizeRange(selection);\n const mergedRanges = resolveWorksheetMergeMetadata(worksheet).mergedRanges\n .filter((merged) => rangesIntersect(range, merged));\n if (mergedRanges.length === 0) return;\n\n recordHistoryBeforeMutation();\n for (const merged of mergedRanges) {\n worksheet.unmergeCells(rangeToA1(merged));\n }\n refreshWorkbookState(workbook);\n }, [getActiveWorksheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, selection, workbook]);\n\n const addSheet = React.useCallback((name?: string) => {\n if (readOnly || !workbook) {\n return;\n }\n\n recordHistoryBeforeMutation();\n const baseName = name?.trim() || \"Sheet\";\n let candidate = baseName;\n let counter = 2;\n while (workbook.sheetIndex(candidate) !== undefined) {\n candidate = `${baseName} ${counter}`;\n counter += 1;\n }\n\n workbook.addSheet(candidate);\n sheetOriginsRef.current = [...sheetOriginsRef.current, null];\n setFormControlsByWorkbookSheetIndex((current) => [...current, []]);\n setImagesByWorkbookSheetIndex((current) => [...current, []]);\n setShapesByWorkbookSheetIndex((current) => [...current, []]);\n if (imageAssetsRef.current) {\n imageAssetsRef.current.formControlsByWorkbookSheetIndex = [\n ...imageAssetsRef.current.formControlsByWorkbookSheetIndex,\n []\n ];\n }\n const nextSheets = buildSheetList(\n workbook,\n imageAssetsRef.current?.sheetStatesByWorkbookSheetIndex,\n imageAssetsRef.current?.themePalette,\n imageAssetsRef.current?.styleById,\n imageAssetsRef.current?.namedCellStyleByName,\n imageAssetsRef.current?.tableStyleByName,\n showHiddenSheets\n );\n setSheets(nextSheets);\n const nextChartAssets = imageAssetsRef.current\n ? loadWorkbookChartAssets(workbook, imageAssetsRef.current, buildVisibleSheetIndexMap(nextSheets), showHiddenSheets)\n : null;\n if (imageAssetsRef.current) {\n setChartAssets(nextChartAssets);\n }\n const nextIndex = nextSheets.findIndex((sheet) => sheet.name === candidate);\n setActiveSheetIndexState(nextIndex >= 0 ? nextIndex : 0);\n const nextTabIndex = nextChartAssets?.tabs.findIndex((tab) => tab.kind === \"sheet\" && tab.name === candidate) ?? -1;\n if (nextTabIndex >= 0) {\n setActiveTabIndexState(nextTabIndex);\n }\n setRevision((current) => current + 1);\n }, [readOnly, recordHistoryBeforeMutation, setChartAssets, workbook]);\n\n const removeActiveSheet = React.useCallback(() => {\n if (readOnly || !workbook || !activeSheet) {\n return;\n }\n\n recordHistoryBeforeMutation();\n workbook.removeSheet(activeSheet.workbookSheetIndex);\n sheetOriginsRef.current = sheetOriginsRef.current.filter((_, index) => index !== activeSheet.workbookSheetIndex);\n setFormControlsByWorkbookSheetIndex((current) => current.filter((_, index) => index !== activeSheet.workbookSheetIndex));\n setImagesByWorkbookSheetIndex((current) => current.filter((_, index) => index !== activeSheet.workbookSheetIndex));\n setShapesByWorkbookSheetIndex((current) => current.filter((_, index) => index !== activeSheet.workbookSheetIndex));\n if (imageAssetsRef.current) {\n imageAssetsRef.current.formControlsByWorkbookSheetIndex = imageAssetsRef.current.formControlsByWorkbookSheetIndex.filter(\n (_, index) => index !== activeSheet.workbookSheetIndex\n );\n imageAssetsRef.current.sheetStatesByWorkbookSheetIndex = imageAssetsRef.current.sheetStatesByWorkbookSheetIndex.filter(\n (_, index) => index !== activeSheet.workbookSheetIndex\n );\n }\n const nextSheets = buildSheetList(\n workbook,\n imageAssetsRef.current?.sheetStatesByWorkbookSheetIndex,\n imageAssetsRef.current?.themePalette,\n imageAssetsRef.current?.styleById,\n imageAssetsRef.current?.namedCellStyleByName,\n imageAssetsRef.current?.tableStyleByName,\n showHiddenSheets\n );\n setSheets(nextSheets);\n if (imageAssetsRef.current) {\n setChartAssets(loadWorkbookChartAssets(workbook, imageAssetsRef.current, buildVisibleSheetIndexMap(nextSheets), showHiddenSheets));\n }\n setActiveSheetIndexState((current) => Math.max(0, Math.min(current, nextSheets.length - 1)));\n setRevision((current) => current + 1);\n }, [activeSheet, readOnly, recordHistoryBeforeMutation, setChartAssets, workbook]);\n\n const defineNamedRange = React.useCallback((name: string, range?: XlsxCellRange | null) => {\n if (readOnly || !workbook) {\n return;\n }\n\n const trimmed = name.trim();\n if (!trimmed) {\n return;\n }\n\n const targetRange = range ?? selection;\n if (!targetRange) {\n return;\n }\n\n recordHistoryBeforeMutation();\n workbook.defineName(trimmed, rangeToA1(targetRange));\n setRevision((current) => current + 1);\n }, [readOnly, recordHistoryBeforeMutation, selection, workbook]);\n\n const pasteText = React.useCallback((text: string) => {\n const worksheet = getActiveWorksheet();\n const targetCell = activeCell ?? selection?.start ?? null;\n if (readOnly || !worksheet || !workbook || !targetCell || !text) {\n return false;\n }\n\n const grid = parseClipboardText(text);\n if (grid.length === 0 || grid.every((row) => row.length === 0)) {\n return false;\n }\n\n const mutations: RangeCellMutation[] = [];\n for (let rowIndex = 0; rowIndex < grid.length; rowIndex += 1) {\n const row = grid[rowIndex] ?? [];\n for (let colIndex = 0; colIndex < row.length; colIndex += 1) {\n const rawValue = row[colIndex] ?? \"\";\n const nextCell = {\n col: targetCell.col + colIndex,\n row: targetCell.row + rowIndex\n };\n const before = captureCellMutationState(nextCell);\n if (!before) {\n continue;\n }\n if (rawValue.startsWith(\"=\") && rawValue.length > 1) {\n worksheet.setFormula(cellAddressToA1(nextCell), rawValue);\n const after = captureCellMutationState(nextCell);\n if (!after) {\n continue;\n }\n mutations.push({\n after,\n before,\n cell: nextCell\n });\n } else {\n const nextValue = coerceUserEnteredValue(rawValue, locale);\n worksheet.setCell(cellAddressToA1(nextCell), nextValue);\n const after = captureCellMutationState(nextCell);\n if (!after) {\n continue;\n }\n mutations.push({\n after,\n before,\n cell: nextCell\n });\n }\n }\n }\n\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n const nextRange = normalizeRange({\n start: targetCell,\n end: {\n col: targetCell.col + Math.max(0, Math.max(...grid.map((row) => row.length), 1) - 1),\n row: targetCell.row + grid.length - 1\n }\n });\n setActiveCell(targetCell);\n setSelection(nextRange);\n selectionAnchorRef.current = targetCell;\n recordRangeEditHistory(mutations, nextRange, targetCell);\n return true;\n }, [activeCell, captureCellMutationState, getActiveWorksheet, locale, maybeRecalculateWorkbook, readOnly, recordRangeEditHistory, refreshWorkbookState, selection, workbook]);\n\n const pasteStructuredClipboardData = React.useCallback((serializedPayload: string) => {\n const worksheet = getActiveWorksheet();\n const targetCell = activeCell ?? selection?.start ?? null;\n if (readOnly || !worksheet || !workbook || !targetCell || !serializedPayload) {\n return false;\n }\n\n let payload: ClipboardPayload;\n try {\n payload = JSON.parse(serializedPayload) as ClipboardPayload;\n } catch {\n return false;\n }\n\n if (!Array.isArray(payload.cells) || payload.cells.length === 0) {\n return false;\n }\n\n const hasMergeOperations = Array.isArray(payload.merges) && payload.merges.some((merge) => (merge.rowSpan ?? 1) > 1 || (merge.colSpan ?? 1) > 1);\n const mutations: RangeCellMutation[] = [];\n if (hasMergeOperations) {\n recordHistoryBeforeMutation();\n }\n for (const cell of payload.cells) {\n const nextCell = {\n col: targetCell.col + cell.colOffset,\n row: targetCell.row + cell.rowOffset\n };\n const before = hasMergeOperations ? null : captureCellMutationState(nextCell);\n\n if (cell.formula) {\n worksheet.setFormula(cellAddressToA1(nextCell), cell.formula);\n if (before) {\n const after = captureCellMutationState(nextCell);\n if (!after) {\n continue;\n }\n mutations.push({\n after,\n before,\n cell: nextCell\n });\n }\n } else {\n worksheet.setCell(cellAddressToA1(nextCell), cell.value);\n if (before) {\n const after = captureCellMutationState(nextCell);\n if (!after) {\n continue;\n }\n mutations.push({\n after,\n before,\n cell: nextCell\n });\n }\n }\n }\n\n if (Array.isArray(payload.merges)) {\n for (const merge of payload.merges) {\n if ((merge.rowSpan ?? 1) <= 1 && (merge.colSpan ?? 1) <= 1) {\n continue;\n }\n\n const mergeRange = normalizeRange({\n start: {\n col: targetCell.col + merge.colOffset,\n row: targetCell.row + merge.rowOffset\n },\n end: {\n col: targetCell.col + merge.colOffset + merge.colSpan - 1,\n row: targetCell.row + merge.rowOffset + merge.rowSpan - 1\n }\n });\n worksheet.mergeCells(rangeToA1(mergeRange));\n }\n }\n\n maybeRecalculateWorkbook(workbook);\n refreshWorkbookState(workbook);\n const nextRange = normalizeRange({\n start: targetCell,\n end: {\n col: targetCell.col + Math.max((payload.cols ?? 1) - 1, 0),\n row: targetCell.row + Math.max((payload.rows ?? 1) - 1, 0)\n }\n });\n setActiveCell(targetCell);\n setSelection(nextRange);\n selectionAnchorRef.current = targetCell;\n if (!hasMergeOperations) {\n recordRangeEditHistory(mutations, nextRange, targetCell);\n }\n return true;\n }, [\n activeCell,\n captureCellMutationState,\n getActiveWorksheet,\n maybeRecalculateWorkbook,\n readOnly,\n recordHistoryBeforeMutation,\n recordRangeEditHistory,\n refreshWorkbookState,\n selection,\n workbook\n ]);\n\n const copySelectionToClipboard = React.useCallback(async () => {\n if (typeof navigator === \"undefined\" || !navigator.clipboard) {\n return false;\n }\n\n if (isWorkerBacked && activeSheet && selection) {\n const range = normalizeRange(selection);\n const textPromise = (async () => {\n const lines: string[] = [];\n for (let startRow = range.start.row; startRow <= range.end.row; startRow += 128) {\n const count = Math.min(128, range.end.row - startRow + 1);\n const batch = await getRowsBatchAsync(activeSheet.workbookSheetIndex, startRow, count);\n if (!batch) throw new Error(\"Unable to read the selected cells.\");\n const values = new Map<string, string>();\n for (const entry of batch as Array<{ index: number; cells?: Array<{ col: number; value?: unknown; formula?: string; isMergedSecondary?: boolean }> }>) {\n for (const cell of entry.cells ?? []) {\n if (cell.col < range.start.col || cell.col > range.end.col || cell.isMergedSecondary) continue;\n const value = String(cell.value ?? \"\");\n const cached = cell.formula ? activeSheet.cachedFormulaValues[cellAddressToA1({ row: entry.index, col: cell.col })] : undefined;\n values.set(`${entry.index}:${cell.col}`, decodeHtmlEntities(cached !== undefined && value.startsWith(\"#\") ? cached : value));\n }\n }\n for (let row = startRow; row < startRow + count; row++) {\n const cells: string[] = [];\n for (let col = range.start.col; col <= range.end.col; col++) cells.push(values.get(`${row}:${col}`) ?? \"\");\n lines.push(cells.join(\"\\t\"));\n }\n }\n return lines.join(\"\\n\");\n })();\n if (typeof ClipboardItem === \"function\" && navigator.clipboard.write) {\n await navigator.clipboard.write([new ClipboardItem({\n \"text/plain\": textPromise.then((text) => new Blob([text], { type: \"text/plain\" }))\n })]);\n } else {\n await navigator.clipboard.writeText(await textPromise);\n }\n return true;\n }\n\n const clipboardData = getClipboardData();\n if (!clipboardData) return false;\n\n if (typeof ClipboardItem === \"function\" && navigator.clipboard.write) {\n const customType = `web ${INTERNAL_CLIPBOARD_MIME}`;\n try {\n await navigator.clipboard.write([new ClipboardItem({\n [customType]: new Blob([clipboardData.structured], { type: INTERNAL_CLIPBOARD_MIME }),\n \"text/html\": new Blob([clipboardData.html], { type: \"text/html\" }),\n \"text/plain\": new Blob([clipboardData.text], { type: \"text/plain\" })\n })]);\n return true;\n } catch (error) {\n if (!(error instanceof TypeError) &&\n (!(error instanceof DOMException) || (error.name !== \"NotAllowedError\" && error.name !== \"NotSupportedError\"))) {\n throw error;\n }\n await navigator.clipboard.write([new ClipboardItem({\n \"text/html\": new Blob([clipboardData.html], { type: \"text/html\" }),\n \"text/plain\": new Blob([clipboardData.text], { type: \"text/plain\" })\n })]);\n return true;\n }\n }\n\n await navigator.clipboard.writeText(clipboardData.text);\n return true;\n }, [activeSheet, getClipboardData, getRowsBatchAsync, isWorkerBacked, selection]);\n\n const pasteFromClipboard = React.useCallback(async () => {\n if (readOnly || typeof navigator === \"undefined\" || !navigator.clipboard) {\n return false;\n }\n\n if (navigator.clipboard.read) {\n const items = await navigator.clipboard.read();\n for (const item of items) {\n const type = [`web ${INTERNAL_CLIPBOARD_MIME}`, INTERNAL_CLIPBOARD_MIME].find((type) => item.types.includes(type));\n if (type) {\n const blob = await item.getType(type);\n return pasteStructuredClipboardData(await blob.text());\n }\n }\n\n for (const item of items) {\n if (item.types.includes(\"text/plain\")) {\n const blob = await item.getType(\"text/plain\");\n return pasteText(await blob.text());\n }\n }\n }\n\n return pasteText(await navigator.clipboard.readText());\n }, [pasteStructuredClipboardData, pasteText, readOnly]);\n\n const undo = React.useCallback(() => {\n if (readOnly || !workbook || undoStackRef.current.length === 0) {\n return;\n }\n\n const entry = undoStackRef.current.pop();\n if (!entry) {\n return;\n }\n\n if (entry.kind === \"cell-edit\") {\n pushHistoryEntry(redoStackRef.current, entry);\n setHistoryRevision((current) => current + 1);\n applyCellEditHistoryEntry(entry, \"undo\");\n return;\n }\n\n if (entry.kind === \"range-edit\") {\n pushHistoryEntry(redoStackRef.current, entry);\n setHistoryRevision((current) => current + 1);\n applyRangeEditHistoryEntry(entry, \"undo\");\n return;\n }\n\n const currentSnapshot = createHistoryEntry();\n if (currentSnapshot) {\n pushHistoryEntry(redoStackRef.current, currentSnapshot);\n }\n setHistoryRevision((current) => current + 1);\n void restoreHistoryEntry(entry);\n }, [applyCellEditHistoryEntry, applyRangeEditHistoryEntry, createHistoryEntry, readOnly, restoreHistoryEntry, workbook]);\n\n const redo = React.useCallback(() => {\n if (readOnly || !workbook || redoStackRef.current.length === 0) {\n return;\n }\n\n const entry = redoStackRef.current.pop();\n if (!entry) {\n return;\n }\n\n if (entry.kind === \"cell-edit\") {\n pushHistoryEntry(undoStackRef.current, entry);\n setHistoryRevision((current) => current + 1);\n applyCellEditHistoryEntry(entry, \"redo\");\n return;\n }\n\n if (entry.kind === \"range-edit\") {\n pushHistoryEntry(undoStackRef.current, entry);\n setHistoryRevision((current) => current + 1);\n applyRangeEditHistoryEntry(entry, \"redo\");\n return;\n }\n\n const currentSnapshot = createHistoryEntry();\n if (currentSnapshot) {\n pushHistoryEntry(undoStackRef.current, currentSnapshot);\n }\n setHistoryRevision((current) => current + 1);\n void restoreHistoryEntry(entry);\n }, [applyCellEditHistoryEntry, applyRangeEditHistoryEntry, createHistoryEntry, readOnly, restoreHistoryEntry, workbook]);\n\n return React.useMemo(\n () => ({\n activeCell,\n activeCellAddress,\n activeSheet,\n activeSheetIndex,\n activeTab,\n activeTabIndex,\n addFormControl,\n addSheet,\n canRedo,\n canDownload: Boolean(file ?? src),\n canExport: Boolean(workbook),\n canLoadDeferred,\n canUndo,\n canZoomIn,\n canZoomOut,\n charts,\n chartsheets,\n clearSelectedChart,\n clearSelectedChartElement,\n clearSelectedCells,\n clearSelectedImage,\n clearSelection,\n clearHighlightedRanges,\n highlightRanges,\n highlightedRanges,\n continueDeferredLoad,\n copySelectionToClipboard,\n defaultZoomScale,\n deferredLoadFileSize,\n defineNamedRange,\n displayFileName,\n locale,\n download,\n exportCsv,\n exportXlsx,\n getXlsxBytes,\n error,\n fillSelection,\n formControls,\n getChartById,\n getChartSeriesFormula,\n getChartsheetById,\n getImageById,\n getSheetCharts,\n getSheetFormControls,\n getSheetImages,\n getSheetShapes,\n file,\n getClipboardData,\n getCellDisplayValue,\n getCellFormula,\n getFormControlItems,\n getCellSnapshotAsync: isWorkerBacked ? getCellSnapshotAsync : undefined,\n getActiveWorksheet,\n getRowsBatchAsync: isWorkerBacked ? getRowsBatchAsync : undefined,\n images,\n isLoadDeferred,\n isLoading,\n isChartsLoading,\n isWorkerBacked,\n mergeSelection,\n maxZoomScale: MAX_ZOOM_SCALE,\n minZoomScale: MIN_ZOOM_SCALE,\n moveChartBy,\n moveImageBy,\n pasteFromClipboard,\n pasteStructuredClipboardData,\n pasteText,\n removeActiveSheet,\n removeFormControl,\n readOnly,\n recalculate,\n redo,\n resetZoom,\n revision,\n resizeChartBy,\n resizeImageBy,\n resizeColumn,\n resizeRow,\n setCellFormula,\n setCellStyle,\n setCellValue,\n setRangeStyle,\n setSelectedFormula,\n setZoomScale,\n setChartRect,\n setChartSeriesFormula,\n setImageRect,\n selectedChart,\n selectedChartElement,\n selectedChartFormula,\n selectedChartId,\n selectedCellFormula,\n selectedFormula,\n selectedFormulaTarget,\n selectedImage,\n selectedImageId,\n selectedRangeAddress,\n selectedValue,\n selectCell,\n selectChart,\n selectChartElement,\n selectImage,\n selectRange,\n selection,\n setActiveSheetIndex,\n setActiveTabIndex,\n setSelectedCellFormula,\n setSelectedCellStyle,\n setSelectedCellValue,\n sheets,\n shapes,\n src,\n sortState,\n sortTable,\n tabs,\n tables,\n undo,\n unmergeSelection,\n updateChart,\n updateFormControl,\n workbook,\n zoomIn,\n zoomOut,\n zoomScale\n }),\n [\n activeCell,\n activeCellAddress,\n activeSheet,\n activeSheetIndex,\n activeTab,\n activeTabIndex,\n addFormControl,\n addSheet,\n canLoadDeferred,\n canRedo,\n canUndo,\n canZoomIn,\n canZoomOut,\n charts,\n chartsheets,\n clearSelectedChart,\n clearSelectedChartElement,\n clearSelectedCells,\n clearSelectedImage,\n continueDeferredLoad,\n clearHighlightedRanges,\n highlightRanges,\n highlightedRanges,\n copySelectionToClipboard,\n defaultZoomScale,\n deferredLoadFileSize,\n defineNamedRange,\n displayFileName,\n locale,\n download,\n error,\n exportCsv,\n exportXlsx,\n getXlsxBytes,\n file,\n fillSelection,\n formControls,\n getChartById,\n getChartSeriesFormula,\n getChartsheetById,\n getImageById,\n getSheetCharts,\n getSheetFormControls,\n getSheetImages,\n getSheetShapes,\n getClipboardData,\n getCellDisplayValue,\n getCellFormula,\n getFormControlItems,\n getCellSnapshotAsync,\n getActiveWorksheet,\n historyRevision,\n formControls,\n images,\n isLoadDeferred,\n isLoading,\n isChartsLoading,\n isWorkerBacked,\n mergeSelection,\n resetZoom,\n moveChartBy,\n moveImageBy,\n pasteFromClipboard,\n pasteStructuredClipboardData,\n pasteText,\n removeActiveSheet,\n removeFormControl,\n readOnly,\n recalculate,\n redo,\n revision,\n resizeChartBy,\n resizeImageBy,\n resizeColumn,\n resizeRow,\n setCellFormula,\n setCellStyle,\n setCellValue,\n setRangeStyle,\n setSelectedFormula,\n setZoomScale,\n setChartRect,\n setChartSeriesFormula,\n setImageRect,\n selectedChart,\n selectedChartElement,\n selectedChartFormula,\n selectedChartId,\n selectedCellFormula,\n selectedFormula,\n selectedFormulaTarget,\n selectedImage,\n selectedImageId,\n selectedRangeAddress,\n selectedValue,\n selectCell,\n selectChart,\n selectChartElement,\n selectImage,\n selectRange,\n selection,\n setActiveSheetIndex,\n setActiveTabIndex,\n setSelectedCellFormula,\n setSelectedCellStyle,\n setSelectedCellValue,\n sheets,\n shapes,\n sortState,\n sortTable,\n src,\n getRowsBatchAsync,\n tabs,\n tables,\n undo,\n unmergeSelection,\n updateChart,\n updateFormControl,\n workbook,\n zoomIn,\n zoomOut,\n zoomScale\n ]\n );\n}\n","import { strFromU8 } from \"fflate\";\nimport type { WorkbookImageSheetOrigin } from \"./images\";\nimport type { XlsxChartsheet } from \"./types\";\n\nconst REL_NS = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships\";\n\ntype Relationship = { target: string; type: string };\n\nfunction readXml(archive: Record<string, Uint8Array>, path: string) {\n const bytes = archive[path];\n if (!bytes || typeof DOMParser === \"undefined\") return null;\n const document = new DOMParser().parseFromString(strFromU8(bytes), \"application/xml\");\n return document.getElementsByTagName(\"parsererror\").length > 0 ? null : document;\n}\n\nfunction relationshipPath(partPath: string) {\n const separator = partPath.lastIndexOf(\"/\");\n return `${partPath.slice(0, separator + 1)}_rels/${partPath.slice(separator + 1)}.rels`;\n}\n\nfunction resolveTarget(partPath: string, target: string) {\n if (/^[a-z][a-z0-9+.-]*:/i.test(target) || target.startsWith(\"//\")) return null;\n const segments = target.startsWith(\"/\") ? [] : partPath.split(\"/\").slice(0, -1);\n for (const segment of target.replace(/\\\\/g, \"/\").split(\"/\")) {\n if (!segment || segment === \".\") continue;\n if (segment === \"..\") {\n if (segments.length === 0) return null;\n segments.pop();\n } else {\n segments.push(segment);\n }\n }\n return segments.join(\"/\");\n}\n\nfunction readRelationships(archive: Record<string, Uint8Array>, partPath: string) {\n const relationships = new Map<string, Relationship>();\n const document = readXml(archive, relationshipPath(partPath));\n for (const element of Array.from(document?.getElementsByTagNameNS(\"*\", \"Relationship\") ?? [])) {\n if (element.getAttribute(\"TargetMode\") === \"External\") continue;\n const id = element.getAttribute(\"Id\");\n const rawTarget = element.getAttribute(\"Target\");\n const type = element.getAttribute(\"Type\");\n const target = rawTarget ? resolveTarget(partPath, rawTarget) : null;\n if (id && type && target && archive[target]) relationships.set(id, { target, type });\n }\n return relationships;\n}\n\nexport function collectChartsheetDrawingOrigins(\n archive: Record<string, Uint8Array>,\n chartsheets: XlsxChartsheet[]\n) {\n const origins = new Map<number, WorkbookImageSheetOrigin>();\n if (chartsheets.length === 0) return origins;\n const workbookPath = \"xl/workbook.xml\";\n const workbookDocument = readXml(archive, workbookPath);\n const workbookRelationships = readRelationships(archive, workbookPath);\n for (const sheet of Array.from(workbookDocument?.getElementsByTagNameNS(\"*\", \"sheet\") ?? [])) {\n const relationshipId = sheet.getAttributeNS(REL_NS, \"id\") ?? sheet.getAttribute(\"r:id\");\n const relationship = relationshipId ? workbookRelationships.get(relationshipId) : null;\n if (!relationship?.type.endsWith(\"/chartsheet\")) continue;\n const chartsheet = chartsheets.find((entry) => entry.name === sheet.getAttribute(\"name\"));\n if (!chartsheet || chartsheet.workbookSheetIndex == null) continue;\n const sheetDocument = readXml(archive, relationship.target);\n const sheetRelationships = readRelationships(archive, relationship.target);\n const attachments: WorkbookImageSheetOrigin[\"attachments\"] = [];\n for (const drawing of Array.from(sheetDocument?.getElementsByTagNameNS(\"*\", \"drawing\") ?? [])) {\n const drawingId = drawing.getAttributeNS(REL_NS, \"id\") ?? drawing.getAttribute(\"r:id\");\n const drawingRelationship = drawingId ? sheetRelationships.get(drawingId) : null;\n if (!drawingRelationship?.type.endsWith(\"/drawing\")) continue;\n const drawingRelsPath = relationshipPath(drawingRelationship.target);\n attachments.push({\n drawingPath: drawingRelationship.target,\n drawingRelsPath: archive[drawingRelsPath] ? drawingRelsPath : null,\n mediaPaths: []\n });\n }\n origins.set(chartsheet.index, { attachments, workbookSheetIndex: chartsheet.workbookSheetIndex });\n }\n return origins;\n}\n","import { getBuiltinChartColor, getBuiltinChartSurfaceStyle, resolveBuiltinChartColor } from \"./builtin-chart-styles\";\nimport type { Workbook } from \"@dukelib/sheets-wasm\";\nimport { collectChartsheetDrawingOrigins } from \"./chartsheet-payload\";\nimport { strFromU8, strToU8 } from \"fflate\";\nimport type { WorkbookImageAssets, WorkbookImageSheetOrigin } from \"./images\";\nimport type {\n XlsxChart,\n XlsxChartAxis,\n XlsxChartDataLabels,\n XlsxChartLegend,\n XlsxChartPointDataLabel,\n XlsxChartPointStyle,\n XlsxChartReference,\n XlsxChartSeries,\n XlsxChartTypeGroup,\n XlsxChartWall,\n XlsxChartsheet,\n XlsxImageAnchor,\n XlsxThemePalette,\n XlsxWorkbookTab\n} from \"./types\";\n\nconst CHART_NS = \"http://schemas.openxmlformats.org/drawingml/2006/chart\";\nconst DRAWINGML_NS = \"http://schemas.openxmlformats.org/drawingml/2006/main\";\nconst DRAWING_SPREADSHEET_NS = \"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing\";\nconst PKG_REL_NS = \"http://schemas.openxmlformats.org/package/2006/relationships\";\nconst CHART_REL_TYPE = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart\";\nconst CHART_EX_REL_TYPE = \"http://schemas.microsoft.com/office/2014/relationships/chartEx\";\nconst CHART_STYLE_REL_TYPE = \"http://schemas.microsoft.com/office/2011/relationships/chartStyle\";\nconst CHART_COLOR_STYLE_REL_TYPE = \"http://schemas.microsoft.com/office/2011/relationships/chartColorStyle\";\nconst SERIES_COLORS = [\n \"#4472c4\",\n \"#ed7d31\",\n \"#a5a5a5\",\n \"#ffc000\",\n \"#5b9bd5\",\n \"#70ad47\",\n \"#264478\",\n \"#9e480e\",\n \"#636363\",\n \"#997300\"\n];\n\nexport type ParsedChartSeriesFormula = {\n bubbleSizeFormula?: string;\n categoryFormula: string;\n nameFormula?: string;\n nameLiteral?: string;\n order: number;\n valueFormula: string;\n};\n\nfunction quoteSeriesFormulaString(value: string) {\n return `\"${value.replace(/\"/g, \"\\\"\\\"\")}\"`;\n}\n\nfunction unquoteSeriesFormulaString(value: string) {\n const trimmed = value.trim();\n if (trimmed.length < 2 || !trimmed.startsWith(\"\\\"\") || !trimmed.endsWith(\"\\\"\")) {\n return null;\n }\n\n return trimmed.slice(1, -1).replace(/\"\"/g, \"\\\"\");\n}\n\nfunction splitTopLevelSeriesArguments(value: string) {\n const args: string[] = [];\n let current = \"\";\n let doubleQuoted = false;\n let singleQuoted = false;\n let depth = 0;\n\n for (let index = 0; index < value.length; index += 1) {\n const char = value[index] ?? \"\";\n const next = value[index + 1] ?? \"\";\n\n if (doubleQuoted) {\n current += char;\n if (char === \"\\\"\" && next === \"\\\"\") {\n current += next;\n index += 1;\n } else if (char === \"\\\"\") {\n doubleQuoted = false;\n }\n continue;\n }\n\n if (singleQuoted) {\n current += char;\n if (char === \"'\" && next === \"'\") {\n current += next;\n index += 1;\n } else if (char === \"'\") {\n singleQuoted = false;\n }\n continue;\n }\n\n if (char === \"\\\"\") {\n doubleQuoted = true;\n current += char;\n continue;\n }\n\n if (char === \"'\") {\n singleQuoted = true;\n current += char;\n continue;\n }\n\n if (char === \"(\") {\n depth += 1;\n current += char;\n continue;\n }\n\n if (char === \")\") {\n depth = Math.max(0, depth - 1);\n current += char;\n continue;\n }\n\n if (char === \",\" && depth === 0) {\n args.push(current.trim());\n current = \"\";\n continue;\n }\n\n current += char;\n }\n\n args.push(current.trim());\n return args;\n}\n\nfunction readSeriesNameFormula(series: XlsxChartSeries) {\n const raw = series.raw && typeof series.raw === \"object\" ? series.raw as Record<string, unknown> : null;\n return typeof raw?.name === \"string\" && raw.name.length > 0 ? raw.name : null;\n}\n\nexport function buildChartSeriesFormula(chart: XlsxChart | null | undefined, seriesIndex: number) {\n const series = chart?.series[seriesIndex];\n if (!chart || !series) {\n return \"\";\n }\n\n const nameFormula = readSeriesNameFormula(series);\n const nameArgument = nameFormula ?? quoteSeriesFormulaString(series.name ?? `Series ${seriesIndex + 1}`);\n const categoryArgument = series.categoriesRef?.formula ?? \"\";\n const valueArgument = series.valuesRef?.formula ?? \"\";\n const orderArgument = String(seriesIndex + 1);\n const bubbleArgument = series.bubbleSizeRef?.formula;\n\n return [\n `=SERIES(${nameArgument}`,\n categoryArgument,\n valueArgument,\n orderArgument,\n ...(chart.chartType === \"Bubble\" || bubbleArgument ? [bubbleArgument ?? \"\"] : [])\n ].join(\",\") + \")\";\n}\n\nexport function parseChartSeriesFormula(formula: string, chart: XlsxChart | null | undefined): ParsedChartSeriesFormula | null {\n const trimmed = formula.trim();\n const withoutEquals = trimmed.startsWith(\"=\") ? trimmed.slice(1).trim() : trimmed;\n const match = /^SERIES\\s*\\(([\\s\\S]*)\\)$/i.exec(withoutEquals);\n if (!match) {\n return null;\n }\n\n const args = splitTopLevelSeriesArguments(match[1]);\n const isBubble = chart?.chartType === \"Bubble\";\n if (args.length < 4 || args.length > 5 || (isBubble && args.length !== 5)) {\n return null;\n }\n\n const [nameArg = \"\", categoryFormula = \"\", valueFormula = \"\", orderArg = \"\", bubbleSizeFormula] = args;\n if (!categoryFormula || !valueFormula) {\n return null;\n }\n\n const parsedOrder = Number(orderArg);\n if (!Number.isFinite(parsedOrder)) {\n return null;\n }\n\n const nameLiteral = unquoteSeriesFormulaString(nameArg);\n return {\n bubbleSizeFormula: bubbleSizeFormula && bubbleSizeFormula.length > 0 ? bubbleSizeFormula : undefined,\n categoryFormula,\n nameFormula: nameLiteral == null && nameArg.length > 0 ? nameArg : undefined,\n nameLiteral: nameLiteral ?? undefined,\n order: parsedOrder,\n valueFormula\n };\n}\n\nexport function applyChartSeriesFormula(\n chart: XlsxChart,\n seriesIndex: number,\n formula: string,\n workbook: Workbook | null\n) {\n const parsed = parseChartSeriesFormula(formula, chart);\n const currentSeries = chart.series[seriesIndex];\n if (!parsed || !currentSeries) {\n return null;\n }\n\n const categoriesRef: XlsxChartReference = {\n ...(currentSeries.categoriesRef ?? {}),\n formula: parsed.categoryFormula,\n values: parsed.categoryFormula === currentSeries.categoriesRef?.formula ? currentSeries.categoriesRef.values : undefined\n };\n const valuesRef: XlsxChartReference = {\n ...(currentSeries.valuesRef ?? {}),\n formula: parsed.valueFormula,\n values: parsed.valueFormula === currentSeries.valuesRef?.formula ? currentSeries.valuesRef.values : undefined\n };\n const bubbleSizeRef: XlsxChartReference | null = chart.chartType === \"Bubble\" || parsed.bubbleSizeFormula\n ? {\n ...(currentSeries.bubbleSizeRef ?? {}),\n formula: parsed.bubbleSizeFormula,\n values: parsed.bubbleSizeFormula === currentSeries.bubbleSizeRef?.formula ? currentSeries.bubbleSizeRef?.values : undefined\n }\n : currentSeries.bubbleSizeRef ?? null;\n const isUnresolved = (formula: string | undefined) => Boolean(\n workbook && formula && !resolveReferenceSheet(workbook, chart.workbookSheetIndex, formula).sheet\n );\n const resolveEditedReference = (\n reference: XlsxChartReference | null,\n currentReference: XlsxChartReference | null | undefined,\n currentValues: Array<number | string | null>,\n mode: \"category\" | \"value\"\n ) => {\n if (!workbook) return currentValues;\n if (reference?.formula === currentReference?.formula && isUnresolved(reference?.formula)) return currentValues;\n return resolveReferenceValues(workbook, chart.workbookSheetIndex, reference, mode);\n };\n const raw: Record<string, unknown> = {\n ...(currentSeries.raw ?? {}),\n unresolvedCategoriesReference: isUnresolved(categoriesRef.formula),\n unresolvedValuesReference: isUnresolved(valuesRef.formula),\n unresolvedBubbleSizeReference: isUnresolved(bubbleSizeRef?.formula),\n unresolvedNameReference: isUnresolved(parsed.nameFormula)\n };\n if (parsed.nameFormula) {\n raw.name = parsed.nameFormula;\n } else {\n delete raw.name;\n }\n\n const nextSeries: XlsxChartSeries = {\n ...currentSeries,\n bubbleSizeRef,\n bubbleSizes: resolveEditedReference(bubbleSizeRef, currentSeries.bubbleSizeRef, currentSeries.bubbleSizes ?? [], \"value\")\n .map((value) => typeof value === \"number\" && Number.isFinite(value) ? value : null),\n categories: resolveEditedReference(categoriesRef, currentSeries.categoriesRef, currentSeries.categories, \"category\"),\n categoriesRef,\n name: parsed.nameLiteral ?? (\n parsed.nameFormula === currentSeries.raw?.name && isUnresolved(parsed.nameFormula)\n ? currentSeries.name\n : parsed.nameFormula && workbook\n ? resolveSeriesName(workbook, chart.workbookSheetIndex, parsed.nameFormula)\n : parsed.nameFormula ?? currentSeries.name\n ),\n raw,\n values: resolveEditedReference(valuesRef, currentSeries.valuesRef, currentSeries.values, \"value\")\n .map((value) => typeof value === \"number\" && Number.isFinite(value) ? value : null),\n valuesRef\n };\n\n return {\n ...chart,\n series: chart.series.map((series, index) => index === seriesIndex ? nextSeries : series),\n typeGroups: chart.typeGroups?.map((group) => ({\n ...group,\n series: group.series.map((series) => series.id === currentSeries.id ? nextSeries : series)\n }))\n };\n}\n\nfunction normalizeWorksheetVisibility(value: unknown): \"hidden\" | \"veryHidden\" | \"visible\" {\n return value === \"hidden\" || value === \"veryHidden\" ? value : \"visible\";\n}\nconst EMU_PER_PIXEL = 9525;\nconst THEME_COLOR_INDEX_BY_NAME: Record<string, number> = {\n accent1: 4,\n accent2: 5,\n accent3: 6,\n accent4: 7,\n accent5: 8,\n accent6: 9,\n dk1: 1,\n dk2: 3,\n folHlink: 11,\n hlink: 10,\n lt1: 0,\n lt2: 2,\n tx1: 1,\n tx2: 3,\n bg1: 0,\n bg2: 2\n};\nconst PRIMARY_CHART_TYPE_LOCAL_NAMES = [\n \"barChart\",\n \"lineChart\",\n \"line3DChart\",\n \"stockChart\",\n \"radarChart\",\n \"scatterChart\",\n \"pieChart\",\n \"pie3DChart\",\n \"doughnutChart\",\n \"areaChart\",\n \"area3DChart\",\n \"bar3DChart\",\n \"ofPieChart\",\n \"bubbleChart\",\n \"surfaceChart\",\n \"surface3DChart\"\n] as const;\n\nexport type WorkbookChartOrigin = {\n anchorIndex: number;\n anchor: XlsxImageAnchor | null;\n chartPath: string | null;\n chartKind: \"classic\" | \"modern\";\n drawingPath: string;\n workbookSheetIndex: number;\n};\n\nexport type WorkbookChartAssets = {\n chartOriginsById: Map<string, WorkbookChartOrigin>;\n chartsByWorkbookSheetIndex: XlsxChart[][];\n chartsheets: XlsxChartsheet[];\n tabs: XlsxWorkbookTab[];\n};\n\ntype ChartStyleAppearance = {\n axisLabelColor?: string;\n axisLineColor?: string;\n chartAreaBorderColor?: string;\n chartAreaFillColor?: string;\n chartAreaNoFill?: boolean;\n paletteOffset?: number;\n textColor?: string;\n titleColor?: string;\n};\n\nfunction clampUnitInterval(value: number) {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction isElementNode(node: Node | ChildNode | null | undefined): node is Element {\n return node != null && node.nodeType === 1;\n}\n\nfunction normalizeHexColor(value: string) {\n const hex = value.replace(/^#/, \"\");\n if (hex.length === 8) {\n return `#${hex.slice(2).toLowerCase()}`;\n }\n if (hex.length === 6) {\n return `#${hex.toLowerCase()}`;\n }\n return null;\n}\n\nfunction resolveColorFromXmlFragment(fragment: string, themePalette?: XlsxThemePalette | null) {\n if (!fragment) {\n return undefined;\n }\n\n const srgbMatch = fragment.match(/<a:srgbClr\\b[^>]*\\bval=\"([0-9a-fA-F]{6,8})\"/i);\n if (srgbMatch?.[1]) {\n return normalizeHexColor(srgbMatch[1]) ?? undefined;\n }\n\n const schemeMatch = fragment.match(/<a:schemeClr\\b[^>]*\\bval=\"([^\"]+)\"[^>]*>([\\s\\S]*?)<\\/a:schemeClr>/i)\n ?? fragment.match(/<a:schemeClr\\b[^>]*\\bval=\"([^\"]+)\"[^>]*/i);\n if (!schemeMatch?.[1]) {\n return undefined;\n }\n\n const baseColor = resolveThemeColor(schemeMatch[1], themePalette);\n if (!baseColor) {\n return undefined;\n }\n\n const transforms = schemeMatch[2] ?? \"\";\n let lightnessModifier = 1;\n let lightnessOffset = 0;\n for (const match of transforms.matchAll(/<a:(lumMod|lumOff|tint|shade)\\b[^>]*\\bval=\"(-?\\d+(?:\\.\\d+)?)\"/gi)) {\n const transform = match[1]?.toLowerCase();\n const rawValue = Number(match[2] ?? Number.NaN);\n if (!transform || !Number.isFinite(rawValue)) {\n continue;\n }\n if (transform === \"lummod\") {\n lightnessModifier *= rawValue / 100000;\n } else if (transform === \"lumoff\") {\n lightnessOffset += rawValue / 100000;\n } else if (transform === \"tint\") {\n lightnessOffset += (1 - lightnessOffset) * (rawValue / 100000);\n } else if (transform === \"shade\") {\n lightnessModifier *= rawValue / 100000;\n }\n }\n\n return applyLightnessTransform(baseColor, lightnessModifier, lightnessOffset) ?? undefined;\n}\n\nfunction readHexColorFromXmlFragment(\n fragment: string,\n preferLine = false,\n themePalette?: XlsxThemePalette | null\n) {\n const source = preferLine\n ? fragment.match(/<a:ln\\b[\\s\\S]*?<\\/a:ln>/i)?.[0] ?? \"\"\n : fragment.match(/<a:solidFill\\b[\\s\\S]*?<\\/a:solidFill>/i)?.[0] ?? \"\";\n return resolveColorFromXmlFragment(source, themePalette);\n}\n\ntype FallbackSeriesStyle = {\n color?: string;\n lineColor?: string;\n};\n\nfunction parseFallbackSeriesStylesFromChartXml(\n chartXml: string,\n themePalette?: XlsxThemePalette | null\n): FallbackSeriesStyle[] {\n const seriesBlocks = chartXml.match(/<c:ser\\b[\\s\\S]*?<\\/c:ser>/gi) ?? [];\n if (seriesBlocks.length === 0) {\n return [];\n }\n\n return seriesBlocks.map((seriesBlock) => {\n const shapeBlock = seriesBlock.match(/<c:spPr\\b[\\s\\S]*?<\\/c:spPr>/i)?.[0] ?? \"\";\n return {\n color: readHexColorFromXmlFragment(shapeBlock, false, themePalette),\n lineColor: readHexColorFromXmlFragment(shapeBlock, true, themePalette)\n };\n });\n}\n\nfunction parseFallbackPointStylesFromChartXml(\n chartXml: string,\n themePalette?: XlsxThemePalette | null\n): XlsxChartPointStyle[][] {\n const chartDocument = parseXml(chartXml);\n if (chartDocument) {\n const parsedSeriesStyles = getLocalDescendants(chartDocument, \"ser\").map((seriesNode) => {\n const styles: XlsxChartPointStyle[] = [];\n for (const dataPointNode of getLocalChildren(seriesNode, \"dPt\")) {\n const indexValue = readChartNumericAttribute(dataPointNode, \"idx\");\n if (indexValue === undefined) {\n continue;\n }\n const shapeProperties = getFirstLocalChild(dataPointNode, \"spPr\");\n const lineStyle = resolveChartLineStyle(shapeProperties, themePalette);\n styles.push({\n color: resolveChartFillColor(shapeProperties, themePalette) ?? undefined,\n explosion: readChartNumericAttribute(dataPointNode, \"explosion\"),\n index: indexValue,\n lineColor: lineStyle.color ?? undefined\n });\n }\n return styles;\n });\n if (parsedSeriesStyles.some((styles) => styles.length > 0)) {\n return parsedSeriesStyles;\n }\n }\n\n const seriesBlocks = chartXml.match(/<c:ser\\b[\\s\\S]*?<\\/c:ser>/gi) ?? [];\n if (seriesBlocks.length === 0) {\n return [];\n }\n\n return seriesBlocks.map((seriesBlock) => {\n const pointBlocks = seriesBlock.match(/<c:dPt\\b[\\s\\S]*?<\\/c:dPt>/gi) ?? [];\n if (pointBlocks.length === 0) {\n return [];\n }\n\n const styles: XlsxChartPointStyle[] = [];\n for (const pointBlock of pointBlocks) {\n const indexMatch = pointBlock.match(/<c:idx\\b[^>]*\\bval=\"(-?\\d+)\"/i);\n const index = indexMatch?.[1] ? Number(indexMatch[1]) : Number.NaN;\n if (!Number.isFinite(index)) {\n continue;\n }\n const explosionMatch = pointBlock.match(/<c:explosion\\b[^>]*\\bval=\"(-?\\d+(?:\\.\\d+)?)\"/i);\n const explosionValue = explosionMatch?.[1] ? Number(explosionMatch[1]) : Number.NaN;\n styles.push({\n color: readHexColorFromXmlFragment(pointBlock, false, themePalette),\n explosion: Number.isFinite(explosionValue) ? explosionValue : undefined,\n index,\n lineColor: readHexColorFromXmlFragment(pointBlock, true, themePalette)\n });\n }\n\n return styles;\n });\n}\n\nfunction parseNumericPointCacheFromXmlFragment(fragment: string) {\n const pointMatches = Array.from(fragment.matchAll(/<c:pt\\b[^>]*\\bidx=\"(-?\\d+)\"[^>]*>[\\s\\S]*?<c:v>([^<]*)<\\/c:v>[\\s\\S]*?<\\/c:pt>/gi));\n if (pointMatches.length === 0) {\n return [];\n }\n\n const explicitPointCountMatch = fragment.match(/<c:ptCount\\b[^>]*\\bval=\"(\\d+)\"/i);\n const explicitPointCount = explicitPointCountMatch?.[1] ? Number(explicitPointCountMatch[1]) : Number.NaN;\n const maxIndex = pointMatches.reduce((max, match) => {\n const current = Number(match[1] ?? Number.NaN);\n return Number.isFinite(current) ? Math.max(max, current) : max;\n }, -1);\n const pointCount = Math.max(\n pointMatches.length,\n Number.isFinite(explicitPointCount) ? explicitPointCount : 0,\n maxIndex + 1\n );\n const values = Array.from({ length: pointCount }, () => null as number | null);\n\n for (const match of pointMatches) {\n const index = Number(match[1] ?? Number.NaN);\n const rawValue = (match[2] ?? \"\").trim();\n const numericValue = Number(rawValue);\n if (!Number.isFinite(index) || index < 0 || !Number.isFinite(numericValue)) {\n continue;\n }\n values[index] = numericValue;\n }\n\n return values;\n}\n\nfunction parseFallbackBubbleSizesFromChartXml(chartXml: string): Array<Array<number | null>> {\n const seriesBlocks = chartXml.match(/<c:ser\\b[\\s\\S]*?<\\/c:ser>/gi) ?? [];\n if (seriesBlocks.length === 0) {\n return [];\n }\n\n return seriesBlocks.map((seriesBlock) => {\n const bubbleSizeBlock = seriesBlock.match(/<c:bubbleSize\\b[\\s\\S]*?<\\/c:bubbleSize>/i)?.[0] ?? \"\";\n if (!bubbleSizeBlock) {\n return [];\n }\n\n return parseNumericPointCacheFromXmlFragment(bubbleSizeBlock);\n });\n}\n\nfunction findPrimaryChartTypeNode(plotAreaNode: Element | null) {\n if (!plotAreaNode) {\n return null;\n }\n\n for (const localName of PRIMARY_CHART_TYPE_LOCAL_NAMES) {\n const node = getLocalChildren(plotAreaNode, localName)[0];\n if (node) {\n return node;\n }\n }\n\n return null;\n}\n\nfunction resolveScatterChartType(scatterStyle: string | null | undefined) {\n switch (scatterStyle) {\n case \"line\":\n case \"lineMarker\":\n return \"ScatterLines\";\n case \"smooth\":\n case \"smoothMarker\":\n return \"ScatterSmooth\";\n default:\n return \"Scatter\";\n }\n}\n\nfunction parseHexColor(color: string): [number, number, number] | null {\n const normalized = normalizeHexColor(color);\n if (!normalized) {\n return null;\n }\n const match = /^#([0-9a-f]{6})$/.exec(normalized);\n if (!match) {\n return null;\n }\n return [\n Number.parseInt(match[1].slice(0, 2), 16),\n Number.parseInt(match[1].slice(2, 4), 16),\n Number.parseInt(match[1].slice(4, 6), 16)\n ];\n}\n\nfunction rgbToHsl(red: number, green: number, blue: number): [number, number, number] {\n const normalizedRed = red / 255;\n const normalizedGreen = green / 255;\n const normalizedBlue = blue / 255;\n const max = Math.max(normalizedRed, normalizedGreen, normalizedBlue);\n const min = Math.min(normalizedRed, normalizedGreen, normalizedBlue);\n const lightness = (max + min) / 2;\n\n if (max === min) {\n return [0, 0, lightness];\n }\n\n const delta = max - min;\n const saturation = lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min);\n let hue = 0;\n\n switch (max) {\n case normalizedRed:\n hue = (normalizedGreen - normalizedBlue) / delta + (normalizedGreen < normalizedBlue ? 6 : 0);\n break;\n case normalizedGreen:\n hue = (normalizedBlue - normalizedRed) / delta + 2;\n break;\n default:\n hue = (normalizedRed - normalizedGreen) / delta + 4;\n break;\n }\n\n return [hue / 6, saturation, lightness];\n}\n\nfunction hueToRgb(p: number, q: number, t: number) {\n let nextT = t;\n if (nextT < 0) {\n nextT += 1;\n }\n if (nextT > 1) {\n nextT -= 1;\n }\n if (nextT < 1 / 6) {\n return p + (q - p) * 6 * nextT;\n }\n if (nextT < 1 / 2) {\n return q;\n }\n if (nextT < 2 / 3) {\n return p + (q - p) * (2 / 3 - nextT) * 6;\n }\n return p;\n}\n\nfunction hslToRgb(hue: number, saturation: number, lightness: number): [number, number, number] {\n if (saturation === 0) {\n const gray = Math.round(lightness * 255);\n return [gray, gray, gray];\n }\n\n const q = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation;\n const p = 2 * lightness - q;\n\n return [\n Math.round(hueToRgb(p, q, hue + 1 / 3) * 255),\n Math.round(hueToRgb(p, q, hue) * 255),\n Math.round(hueToRgb(p, q, hue - 1 / 3) * 255)\n ];\n}\n\nfunction rgbToHex(red: number, green: number, blue: number) {\n return `#${[red, green, blue]\n .map((channel) => Math.max(0, Math.min(255, Math.round(channel))).toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\nfunction applyLightnessTransform(baseColor: string, modifier = 1, offset = 0) {\n const rgb = parseHexColor(baseColor);\n if (!rgb) {\n return normalizeHexColor(baseColor);\n }\n\n const [hue, saturation, lightness] = rgbToHsl(rgb[0], rgb[1], rgb[2]);\n const nextLightness = clampUnitInterval(lightness * modifier + offset);\n const [nextRed, nextGreen, nextBlue] = hslToRgb(hue, saturation, nextLightness);\n return rgbToHex(nextRed, nextGreen, nextBlue);\n}\n\nfunction resolveThemeColor(name: string | null, themePalette?: XlsxThemePalette | null) {\n if (!name) {\n return null;\n }\n const index = THEME_COLOR_INDEX_BY_NAME[name];\n return index === undefined ? null : themePalette?.colorsByIndex[index] ?? null;\n}\n\nfunction resolveThemeTypeface(typeface: string | null, themePalette?: XlsxThemePalette | null) {\n if (!typeface) {\n return null;\n }\n if (typeface === \"+mn-lt\" || typeface === \"+mn-ea\" || typeface === \"+mn-cs\") {\n return themePalette?.minorLatinFont ?? null;\n }\n if (typeface === \"+mj-lt\" || typeface === \"+mj-ea\" || typeface === \"+mj-cs\") {\n return themePalette?.majorLatinFont ?? null;\n }\n return typeface;\n}\n\nfunction readChartTextTypeface(textPropertiesNode: Element | null, themePalette?: XlsxThemePalette | null) {\n if (!textPropertiesNode) {\n return null;\n }\n const defaultRunProperties = getFirstLocalDescendant(textPropertiesNode, \"defRPr\")\n ?? getFirstLocalDescendant(textPropertiesNode, \"rPr\");\n if (!defaultRunProperties) {\n return null;\n }\n const typeface = getFirstLocalChild(defaultRunProperties, \"latin\")?.getAttribute(\"typeface\")\n ?? getFirstLocalChild(defaultRunProperties, \"ea\")?.getAttribute(\"typeface\")\n ?? getFirstLocalChild(defaultRunProperties, \"cs\")?.getAttribute(\"typeface\")\n ?? null;\n const resolved = resolveThemeTypeface(typeface, themePalette)?.trim() ?? \"\";\n return resolved.length > 0 ? resolved : null;\n}\n\nfunction resolveChartColorNode(node: Element | null, themePalette?: XlsxThemePalette | null): string | null {\n if (!node) {\n return null;\n }\n\n let baseColor: string | null = null;\n if (node.localName === \"srgbClr\") {\n baseColor = normalizeHexColor(`#${node.getAttribute(\"val\") ?? \"\"}`);\n } else if (node.localName === \"schemeClr\") {\n baseColor = resolveThemeColor(node.getAttribute(\"val\"), themePalette);\n } else if (node.localName === \"sysClr\") {\n baseColor = normalizeHexColor(`#${node.getAttribute(\"lastClr\") ?? \"\"}`);\n }\n\n if (!baseColor) {\n return null;\n }\n\n let lightnessModifier = 1;\n let lightnessOffset = 0;\n for (const transformNode of Array.from(node.childNodes).filter(isElementNode)) {\n const rawValue = Number(transformNode.getAttribute(\"val\") ?? Number.NaN);\n if (!Number.isFinite(rawValue)) {\n continue;\n }\n if (transformNode.localName === \"lumMod\") {\n lightnessModifier *= rawValue / 100000;\n } else if (transformNode.localName === \"lumOff\") {\n lightnessOffset += rawValue / 100000;\n } else if (transformNode.localName === \"tint\") {\n lightnessOffset += (1 - lightnessOffset) * (rawValue / 100000);\n } else if (transformNode.localName === \"shade\") {\n lightnessModifier *= rawValue / 100000;\n }\n }\n\n return applyLightnessTransform(baseColor, lightnessModifier, lightnessOffset);\n}\n\nfunction isChartColorElement(node: Element | null | undefined): node is Element {\n return Boolean(node && (node.localName === \"schemeClr\" || node.localName === \"srgbClr\" || node.localName === \"sysClr\"));\n}\n\nfunction findFirstChartColorElement(node: Element | null) {\n if (!node) {\n return null;\n }\n if (isChartColorElement(node)) {\n return node;\n }\n\n for (const localName of [\"srgbClr\", \"schemeClr\", \"sysClr\"]) {\n for (const candidate of getLocalDescendants(node, localName)) {\n if (isChartColorElement(candidate)) {\n return candidate;\n }\n }\n }\n\n return null;\n}\n\nfunction resolveChartFillColor(shapeNode: Element | null, themePalette?: XlsxThemePalette | null) {\n if (!shapeNode || getFirstLocalChild(shapeNode, \"noFill\")) {\n return null;\n }\n const solidFill = getFirstLocalChild(shapeNode, \"solidFill\");\n if (solidFill) {\n const colorNode = findFirstChartColorElement(Array.from(solidFill.childNodes).find(isElementNode) ?? null);\n return resolveChartColorNode(colorNode, themePalette);\n }\n\n const gradientFill = getFirstLocalChild(shapeNode, \"gradFill\");\n const gradientStops = gradientFill\n ? getLocalDescendants(gradientFill, \"gs\")\n .map((stopNode) => ({\n colorNode: Array.from(stopNode.childNodes).find(isElementNode) ?? null,\n position: Number(stopNode.getAttribute(\"pos\") ?? Number.NaN)\n }))\n .filter((stop) => Boolean(stop.colorNode))\n : [];\n if (gradientStops.length === 0) {\n return null;\n }\n\n gradientStops.sort((left, right) => {\n const leftPos = Number.isFinite(left.position) ? left.position : 0;\n const rightPos = Number.isFinite(right.position) ? right.position : 0;\n return leftPos - rightPos;\n });\n const midpointStop = gradientStops.find((stop) => Number.isFinite(stop.position) && stop.position >= 50000)\n ?? gradientStops[Math.floor(gradientStops.length / 2)]\n ?? gradientStops[0];\n return resolveChartColorNode(midpointStop.colorNode, themePalette);\n}\n\nfunction resolveChartLineStyle(shapeNode: Element | null, themePalette?: XlsxThemePalette | null) {\n const lineNode = shapeNode?.localName === \"ln\" ? shapeNode : (shapeNode ? getFirstLocalChild(shapeNode, \"ln\") : null);\n if (!lineNode) {\n return { color: null, hidden: false, widthPx: undefined };\n }\n if (getFirstLocalChild(lineNode, \"noFill\")) {\n return { color: null, hidden: true, widthPx: undefined };\n }\n\n const solidFill = getFirstLocalChild(lineNode, \"solidFill\");\n const colorNode = solidFill ? findFirstChartColorElement(Array.from(solidFill.childNodes).find(isElementNode) ?? null) : null;\n const widthValue = Number(lineNode.getAttribute(\"w\") ?? Number.NaN);\n return {\n color: resolveChartColorNode(colorNode, themePalette),\n hidden: false,\n widthPx: Number.isFinite(widthValue) ? Math.max(1, widthValue / EMU_PER_PIXEL) : undefined\n };\n}\n\nfunction normalizeLegend(raw: unknown): XlsxChartLegend | null {\n if (!raw || typeof raw !== \"object\") {\n return null;\n }\n const legend = raw as Record<string, unknown>;\n return {\n overlay: typeof legend.overlay === \"boolean\" ? legend.overlay : undefined,\n position: typeof legend.position === \"string\" ? legend.position : undefined,\n raw: legend\n };\n}\n\nfunction normalizeLegendPosition(position: string | undefined) {\n if (!position) {\n return undefined;\n }\n switch (position) {\n case \"bottom\":\n return \"b\";\n case \"left\":\n return \"l\";\n case \"right\":\n return \"r\";\n case \"top\":\n return \"t\";\n default:\n return position;\n }\n}\n\nfunction readChartNumericAttribute(parent: Element | null, localName: string) {\n const node = parent ? getFirstLocalChild(parent, localName) : null;\n const value = Number(node?.getAttribute(\"val\") ?? Number.NaN);\n return Number.isFinite(value) ? value : undefined;\n}\n\nfunction readChartBooleanAttribute(parent: Element | null, localName: string) {\n const node = parent ? getFirstLocalChild(parent, localName) : null;\n if (!node) {\n return undefined;\n }\n const rawValue = node.getAttribute(\"val\");\n if (rawValue == null) {\n return true;\n }\n if (rawValue === \"1\" || rawValue === \"true\") {\n return true;\n }\n if (rawValue === \"0\" || rawValue === \"false\") {\n return false;\n }\n return undefined;\n}\n\nfunction readChartLabelFontSizePt(textPropertiesNode: Element | null) {\n if (!textPropertiesNode) {\n return undefined;\n }\n const runPropertiesNode = getFirstLocalDescendant(textPropertiesNode, \"defRPr\")\n ?? getFirstLocalDescendant(textPropertiesNode, \"rPr\");\n const rawSize = Number(runPropertiesNode?.getAttribute(\"sz\") ?? Number.NaN);\n if (!Number.isFinite(rawSize) || rawSize <= 0) {\n return undefined;\n }\n return rawSize / 100;\n}\n\nfunction parseChartPointDataLabelsFromXml(labelsNode: Element): XlsxChartPointDataLabel[] {\n const fallbackFontSizePt = readChartLabelFontSizePt(getFirstLocalChild(labelsNode, \"txPr\"));\n const labels: XlsxChartPointDataLabel[] = [];\n for (const pointLabelNode of getLocalChildren(labelsNode, \"dLbl\")) {\n const index = readChartNumericAttribute(pointLabelNode, \"idx\");\n if (typeof index !== \"number\" || !Number.isFinite(index)) {\n continue;\n }\n\n const layoutNode = getFirstLocalChild(pointLabelNode, \"layout\");\n const manualLayoutNode = layoutNode ? getFirstLocalChild(layoutNode, \"manualLayout\") : null;\n labels.push({\n deleted: readChartBooleanAttribute(pointLabelNode, \"delete\"),\n fontSizePt: readChartLabelFontSizePt(getFirstLocalChild(pointLabelNode, \"txPr\")) ?? fallbackFontSizePt,\n index,\n showBubbleSize: readChartBooleanAttribute(pointLabelNode, \"showBubbleSize\"),\n showCategoryName: readChartBooleanAttribute(pointLabelNode, \"showCatName\"),\n showPercent: readChartBooleanAttribute(pointLabelNode, \"showPercent\"),\n showSeriesName: readChartBooleanAttribute(pointLabelNode, \"showSerName\"),\n showValue: readChartBooleanAttribute(pointLabelNode, \"showVal\"),\n x: readChartNumericAttribute(manualLayoutNode, \"x\"),\n y: readChartNumericAttribute(manualLayoutNode, \"y\")\n });\n }\n return labels;\n}\n\nfunction parseChartDataLabelsFromXml(labelsNode: Element | null): XlsxChartDataLabels | null {\n if (!labelsNode) {\n return null;\n }\n\n const pointLabels = parseChartPointDataLabelsFromXml(labelsNode);\n const labels: XlsxChartDataLabels = {\n pointLabels: pointLabels.length > 0 ? pointLabels : undefined,\n raw: {},\n showBubbleSize: readChartBooleanAttribute(labelsNode, \"showBubbleSize\"),\n showCategoryName: readChartBooleanAttribute(labelsNode, \"showCatName\"),\n showLegendKey: readChartBooleanAttribute(labelsNode, \"showLegendKey\"),\n showPercent: readChartBooleanAttribute(labelsNode, \"showPercent\"),\n showSeriesName: readChartBooleanAttribute(labelsNode, \"showSerName\"),\n showValue: readChartBooleanAttribute(labelsNode, \"showVal\")\n };\n const hasValue = (\n labels.showBubbleSize !== undefined\n || labels.showCategoryName !== undefined\n || labels.showLegendKey !== undefined\n || labels.showPercent !== undefined\n || (labels.pointLabels?.length ?? 0) > 0\n || labels.showSeriesName !== undefined\n || labels.showValue !== undefined\n );\n return hasValue ? labels : null;\n}\n\nfunction readChartRelationships(\n archive: Record<string, Uint8Array>,\n chartPath: string\n) {\n const relsPath = normalizeArchivePath(`${dirname(chartPath)}/_rels/${chartPath.split(\"/\").pop()}.rels`);\n const relsXml = readArchiveText(archive, relsPath);\n if (!relsXml) {\n return new Map<string, string>();\n }\n\n const relsDocument = parseXml(relsXml);\n if (!relsDocument) {\n return new Map<string, string>();\n }\n\n const relationships = new Map<string, string>();\n for (const relationshipNode of getLocalDescendants(relsDocument, \"Relationship\")) {\n const type = relationshipNode.getAttribute(\"Type\");\n const target = relationshipNode.getAttribute(\"Target\");\n if (!type || !target) {\n continue;\n }\n relationships.set(type, resolveRelationshipPath(relsPath, target));\n }\n\n return relationships;\n}\n\nfunction readChartColorPalette(\n archive: Record<string, Uint8Array>,\n colorStylePath: string | null | undefined,\n themePalette?: XlsxThemePalette | null\n) {\n const colorStyleXml = readArchiveText(archive, colorStylePath);\n if (!colorStyleXml) {\n return [];\n }\n\n const colorStyleDocument = parseXml(colorStyleXml);\n if (!colorStyleDocument?.documentElement) {\n return [];\n }\n\n return Array.from(colorStyleDocument.documentElement.childNodes)\n .filter((child): child is Element => isElementNode(child) && child.localName !== \"variation\")\n .map((child) => (\n resolveChartColorNode(child, themePalette)\n ?? resolveChartColorNode(findFirstChartColorElement(child), themePalette)\n ))\n .filter((color): color is string => typeof color === \"string\");\n}\n\nfunction readChartStyleAppearance(\n archive: Record<string, Uint8Array>,\n stylePath: string | null | undefined,\n themePalette?: XlsxThemePalette | null\n): ChartStyleAppearance {\n const styleXml = readArchiveText(archive, stylePath);\n if (!styleXml) {\n return {};\n }\n\n const styleDocument = parseXml(styleXml);\n if (!styleDocument) {\n return {};\n }\n\n const dataPointNode = getFirstLocalDescendant(styleDocument, \"dataPoint\");\n const fillRefNode = dataPointNode ? getFirstLocalChild(dataPointNode, \"fillRef\") : null;\n const index = Number(fillRefNode?.getAttribute(\"idx\") ?? Number.NaN);\n const chartAreaNode = getFirstLocalDescendant(styleDocument, \"chartArea\");\n const chartAreaShapeProperties = chartAreaNode ? getFirstLocalChild(chartAreaNode, \"spPr\") : null;\n const chartAreaFontRef = chartAreaNode ? getFirstLocalChild(chartAreaNode, \"fontRef\") : null;\n const chartAreaFontColor = chartAreaFontRef\n ? resolveChartColorNode(Array.from(chartAreaFontRef.childNodes).find(isElementNode) ?? null, themePalette)\n : null;\n const titleNode = getFirstLocalDescendant(styleDocument, \"title\");\n const titleFontRef = titleNode ? getFirstLocalChild(titleNode, \"fontRef\") : null;\n const titleColor = titleFontRef\n ? resolveChartColorNode(Array.from(titleFontRef.childNodes).find(isElementNode) ?? null, themePalette)\n : null;\n const axisStyleNode = getFirstLocalDescendant(styleDocument, \"categoryAxis\")\n ?? getFirstLocalDescendant(styleDocument, \"valueAxis\");\n const axisShapeProperties = axisStyleNode ? getFirstLocalChild(axisStyleNode, \"spPr\") : null;\n const axisFontRef = axisStyleNode ? getFirstLocalChild(axisStyleNode, \"fontRef\") : null;\n const chartAreaNoFill = chartAreaShapeProperties ? getFirstLocalChild(chartAreaShapeProperties, \"noFill\") != null : false;\n\n return {\n axisLabelColor: axisFontRef\n ? resolveChartColorNode(Array.from(axisFontRef.childNodes).find(isElementNode) ?? null, themePalette) ?? undefined\n : undefined,\n axisLineColor: resolveChartLineStyle(axisShapeProperties, themePalette).color ?? undefined,\n chartAreaBorderColor: resolveChartLineStyle(chartAreaShapeProperties, themePalette).color ?? undefined,\n chartAreaFillColor: resolveChartFillColor(chartAreaShapeProperties, themePalette) ?? undefined,\n chartAreaNoFill,\n paletteOffset: Number.isFinite(index) ? index : undefined,\n textColor: chartAreaFontColor ?? undefined,\n titleColor: titleColor ?? chartAreaFontColor ?? undefined\n };\n}\n\nfunction buildThemeSeriesPalette(themePalette?: XlsxThemePalette | null) {\n const themeColors = [4, 5, 6, 7, 8, 9]\n .map((index) => themePalette?.colorsByIndex[index] ?? null)\n .filter((color): color is string => Boolean(color));\n return themeColors.length > 0 ? themeColors : SERIES_COLORS;\n}\n\nfunction applyBuiltinSurfaceDefaults(chart: XlsxChart, themePalette?: XlsxThemePalette | null) {\n if (chart.chartType !== \"Surface\") {\n return;\n }\n\n const builtin = chart.raw?.hasCustomChartColorStyle !== true ? getBuiltinChartSurfaceStyle(chart.chartStyleId) : undefined;\n const colors = themePalette?.colorsByIndex ?? {};\n const wallFill = resolveBuiltinChartColor(builtin?.wall.fill, colors) ?? (chart.wireframe ? \"#d0d0d0\" : \"#d9d9df\");\n const floorFill = resolveBuiltinChartColor(builtin?.floor.fill, colors) ?? wallFill;\n const gridlineColor = resolveBuiltinChartColor(builtin?.majorGridline, colors);\n for (const axis of [chart.categoryAxis, chart.valueAxis, chart.seriesAxis]) {\n if (axis && !axis.majorGridlineColor) axis.majorGridlineColor = gridlineColor;\n }\n const wallLine = chart.wireframe ? \"#a6a6a6\" : \"#a8adb7\";\n const previousDefaults = chart.raw?.builtinSurfaceDefaults as Record<string, { fillColor?: string; lineColor?: string }> | undefined;\n const nextDefaults: Record<string, { fillColor?: string; lineColor?: string }> = {};\n for (const key of [\"floor\", \"sideWall\", \"backWall\"] as const) {\n const wall = chart[key] ?? {};\n const previous = previousDefaults?.[key];\n const fillIsDefault = wall.fillColor === undefined || wall.fillColor === previous?.fillColor;\n const lineIsDefault = wall.lineColor === undefined || wall.lineColor === previous?.lineColor;\n const fillColor = fillIsDefault ? (key === \"floor\" ? floorFill : wallFill) : wall.fillColor;\n const lineColor = lineIsDefault ? wallLine : wall.lineColor;\n chart[key] = { ...wall, fillColor, lineColor };\n nextDefaults[key] = {\n fillColor: fillIsDefault ? fillColor : undefined,\n lineColor: lineIsDefault ? lineColor : undefined\n };\n }\n chart.raw = { ...chart.raw, builtinSurfaceDefaults: nextDefaults };\n if (!chart.surfaceMaterial && chart.wireframe !== true) {\n chart.surfaceMaterial = \"matte\";\n }\n}\n\nfunction applyBuiltinChartDefaults(chart: XlsxChart, themePalette?: XlsxThemePalette | null) {\n const darkBuiltInStyle = typeof chart.chartStyleId === \"number\" && chart.chartStyleId >= 140 && chart.chartStyleId < 150;\n const textColor = themePalette?.colorsByIndex[1] ?? themePalette?.colorsByIndex[3] ?? null;\n const minorTypeface = themePalette?.minorLatinFont?.trim() || undefined;\n const derivedAxisColor = textColor ? applyLightnessTransform(textColor, 0.35, 0.55) : null;\n const derivedBorderColor = textColor\n ? applyLightnessTransform(textColor, chart.is3d ? 0.28 : 0.22, chart.is3d ? 0.6 : 0.7)\n : null;\n if (darkBuiltInStyle) {\n chart.chartAreaFillColor = chart.chartAreaFillColor ?? \"#1f1f1f\";\n chart.chartAreaBorderColor = chart.chartAreaBorderColor ?? \"#1f1f1f\";\n chart.textColor = chart.textColor ?? \"#f5f5f5\";\n chart.titleColor = chart.titleColor ?? \"#f5f5f5\";\n chart.axisLabelColor = chart.axisLabelColor ?? \"#d9d9d9\";\n chart.axisLineColor = chart.axisLineColor ?? \"#8c8c8c\";\n }\n chart.chartAreaBorderColor = chart.chartAreaBorderColor ?? derivedBorderColor ?? undefined;\n chart.textColor = chart.textColor ?? textColor ?? undefined;\n chart.titleColor = chart.titleColor ?? textColor ?? undefined;\n chart.axisLabelColor = chart.axisLabelColor ?? derivedAxisColor ?? textColor ?? undefined;\n chart.axisLineColor = chart.axisLineColor ?? derivedAxisColor ?? textColor ?? undefined;\n chart.fontFamily = chart.fontFamily ?? minorTypeface;\n chart.titleFontFamily = chart.titleFontFamily ?? chart.fontFamily ?? minorTypeface;\n\n const themeColors = themePalette?.colorsByIndex;\n const useBuiltin = chart.raw?.hasCustomChartColorStyle !== true && themeColors && getBuiltinChartColor(chart.chartStyleId, 0, 1) !== undefined;\n const colorCount = Math.max(1, chart.series.length, chart.typeGroups?.reduce((total, group) => total + group.series.length, 0) ?? 0);\n const seriesPalette = useBuiltin && chart.chartType !== \"Surface\"\n ? Array.from({ length: colorCount }, (_, index) => resolveBuiltinChartColor(getBuiltinChartColor(chart.chartStyleId, index, colorCount), themeColors) ?? SERIES_COLORS[index % SERIES_COLORS.length])\n : chart.chartColorPalette?.length ? chart.chartColorPalette : buildThemeSeriesPalette(themePalette);\n chart.chartColorPalette = seriesPalette;\n chart.raw = { ...chart.raw, builtinThemeColors: useBuiltin ? themeColors : undefined };\n\n const applySeriesDefaults = (series: XlsxChartSeries, index: number) => {\n const fallbackColor = seriesPalette[index % seriesPalette.length];\n const properties = series.shapeProperties ?? {};\n const previous = properties.builtinDefaultColors as Partial<Record<\"color\" | \"lineColor\" | \"markerColor\" | \"markerLineColor\", string>> | undefined;\n const generated: Partial<Record<\"color\" | \"lineColor\" | \"markerColor\" | \"markerLineColor\", string>> = {};\n const resolveColor = (key: keyof typeof generated, explicit: unknown, fallback: string | undefined) => {\n const value = series[key];\n if (!explicit && (value == null || value === previous?.[key] || ((key === \"color\" || key === \"lineColor\") && useBuiltin && chart.raw?.xmlChartType))) {\n generated[key] = fallback;\n return fallback;\n }\n return value ?? fallback;\n };\n const explicitFill = properties.xmlFillColor ?? properties.solidFillHex ?? properties.xmlFillHidden;\n const explicitLine = properties.xmlLineColor ?? properties.lineColorHex ?? properties.xmlLineHidden;\n const color = explicitFill\n ? series.color ?? series.lineColor ?? fallbackColor\n : explicitLine\n ? series.lineColor ?? series.color ?? fallbackColor\n : resolveColor(\"color\", false, fallbackColor);\n const lineColor = explicitLine\n ? series.lineColor ?? color\n : explicitFill\n ? color\n : resolveColor(\"lineColor\", false, color ?? fallbackColor);\n const markerColor = resolveColor(\"markerColor\", properties.xmlMarkerFillColor ?? properties.xmlMarkerFillHidden, color ?? lineColor ?? fallbackColor);\n const markerLineColor = resolveColor(\"markerLineColor\", properties.xmlMarkerLineColor ?? properties.xmlMarkerLineHidden, lineColor ?? color ?? fallbackColor);\n return {\n ...series,\n color,\n lineColor,\n markerColor,\n markerLineColor,\n shapeProperties: { ...properties, builtinDefaultColors: generated }\n };\n };\n chart.series = chart.series.map(applySeriesDefaults);\n const canonicalSeries = chart.id.startsWith(\"chart-ex-\") ? null : new Map(chart.series.map((series) => [series.id, series]));\n let groupSeriesOffset = 0;\n chart.typeGroups = chart.typeGroups?.map((group) => {\n const series = group.series.map((entry, index) => canonicalSeries?.get(entry.id) ?? applySeriesDefaults(entry, groupSeriesOffset + index));\n groupSeriesOffset += group.series.length;\n return { ...group, series };\n });\n applyBuiltinSurfaceDefaults(chart, themePalette);\n}\n\nfunction parseChartPointStyles(seriesNode: Element, themePalette?: XlsxThemePalette | null): XlsxChartPointStyle[] {\n const pointStyles: XlsxChartPointStyle[] = [];\n\n for (const dataPointNode of getLocalChildren(seriesNode, \"dPt\")) {\n const indexValue = readChartNumericAttribute(dataPointNode, \"idx\");\n if (indexValue === undefined) {\n continue;\n }\n const shapeProperties = getFirstLocalChild(dataPointNode, \"spPr\");\n const lineStyle = resolveChartLineStyle(shapeProperties, themePalette);\n pointStyles.push({\n color: resolveChartFillColor(shapeProperties, themePalette) ?? undefined,\n explosion: readChartNumericAttribute(dataPointNode, \"explosion\"),\n index: indexValue,\n lineColor: lineStyle.color ?? undefined\n });\n }\n\n return pointStyles;\n}\n\nfunction parseInvertNegativeStyle(seriesNode: Element, themePalette?: XlsxThemePalette | null) {\n const invertNode = getFirstLocalDescendant(seriesNode, \"invertSolidFillFmt\");\n const shapeProperties = invertNode ? getFirstLocalChild(invertNode, \"spPr\") : null;\n if (!shapeProperties) {\n return {\n color: undefined,\n lineColor: undefined\n };\n }\n\n const lineStyle = resolveChartLineStyle(shapeProperties, themePalette);\n return {\n color: resolveChartFillColor(shapeProperties, themePalette) ?? undefined,\n lineColor: lineStyle.color ?? undefined\n };\n}\n\nfunction parseChartCacheValues(\n parentNode: Element | null,\n cacheName: \"numCache\" | \"strCache\",\n mode: \"category\" | \"value\"\n): Array<number | string | null> | null {\n if (!parentNode) {\n return null;\n }\n\n const referenceNode = getFirstLocalChild(parentNode, \"numRef\")\n ?? getFirstLocalChild(parentNode, \"strRef\")\n ?? parentNode;\n const cacheNode = getFirstLocalChild(referenceNode, cacheName)\n ?? getFirstLocalChild(parentNode, cacheName === \"numCache\" ? \"numLit\" : \"strLit\");\n if (!cacheNode) {\n return null;\n }\n\n const pointCount = readChartNumericAttribute(cacheNode, \"ptCount\");\n const pointNodes = getLocalChildren(cacheNode, \"pt\")\n .map((pointNode) => {\n const rawIndex = Number(pointNode.getAttribute(\"idx\") ?? Number.NaN);\n return {\n index: Number.isFinite(rawIndex) ? rawIndex : 0,\n value: getFirstLocalChild(pointNode, \"v\")?.textContent ?? \"\"\n };\n })\n .sort((left, right) => left.index - right.index);\n\n if (pointNodes.length === 0) {\n return null;\n }\n\n const maxIndex = pointNodes.reduce((max, point) => Math.max(max, point.index), 0);\n const targetLength = Math.max(\n pointNodes.length,\n Number.isFinite(pointCount ?? Number.NaN) ? Number(pointCount) : 0,\n maxIndex + 1\n );\n const values = Array.from({ length: targetLength }, () => null as number | string | null);\n for (const point of pointNodes) {\n if (mode === \"value\") {\n values[point.index] = cellValueToNumber(point.value);\n } else {\n values[point.index] = point.value.length > 0 ? point.value : null;\n }\n }\n return values;\n}\n\nfunction parseChartMultiLevelCacheValues(\n parentNode: Element | null,\n mode: \"category\" | \"value\"\n): Array<number | string | null> | null {\n if (!parentNode) {\n return null;\n }\n\n const referenceNode = getFirstLocalChild(parentNode, \"multiLvlStrRef\") ?? parentNode;\n const cacheNode = getFirstLocalChild(referenceNode, \"multiLvlStrCache\");\n if (!cacheNode) {\n return null;\n }\n\n const levelNodes = getLocalChildren(cacheNode, \"lvl\");\n if (levelNodes.length === 0) {\n return null;\n }\n\n const pointCount = readChartNumericAttribute(cacheNode, \"ptCount\");\n const primaryLevelNode = mode === \"category\"\n ? levelNodes[levelNodes.length - 1] ?? levelNodes[0]\n : levelNodes[0];\n const pointNodes = getLocalChildren(primaryLevelNode, \"pt\")\n .map((pointNode) => {\n const rawIndex = Number(pointNode.getAttribute(\"idx\") ?? Number.NaN);\n return {\n index: Number.isFinite(rawIndex) ? rawIndex : 0,\n value: getFirstLocalChild(pointNode, \"v\")?.textContent ?? \"\"\n };\n })\n .sort((left, right) => left.index - right.index);\n\n if (pointNodes.length === 0) {\n return null;\n }\n\n const maxIndex = pointNodes.reduce((max, point) => Math.max(max, point.index), 0);\n const targetLength = Math.max(\n pointNodes.length,\n Number.isFinite(pointCount ?? Number.NaN) ? Number(pointCount) : 0,\n maxIndex + 1\n );\n const values = Array.from({ length: targetLength }, () => null as number | string | null);\n for (const point of pointNodes) {\n if (mode === \"value\") {\n values[point.index] = cellValueToNumber(point.value);\n continue;\n }\n values[point.index] = point.value.length > 0 ? point.value : null;\n }\n return values;\n}\n\nfunction applyChartSeriesStyleFromXml(chart: XlsxChart, chartTypeNode: Element, themePalette?: XlsxThemePalette | null) {\n const seriesNodes = getLocalChildren(chartTypeNode, \"ser\");\n chart.series = chart.series.map((series, index) => {\n const seriesNode = seriesNodes[index];\n if (!seriesNode) {\n return series;\n }\n\n const shapeProperties = getFirstLocalChild(seriesNode, \"spPr\");\n const markerNode = getFirstLocalChild(seriesNode, \"marker\");\n const markerShapeProperties = getFirstLocalChild(markerNode ?? chartTypeNode, \"spPr\");\n const lineStyle = resolveChartLineStyle(shapeProperties, themePalette);\n const markerLineStyle = resolveChartLineStyle(markerShapeProperties, themePalette);\n const fillColor = resolveChartFillColor(shapeProperties, themePalette);\n const markerSize = readChartNumericAttribute(markerNode, \"size\");\n const markerSymbolNode = markerNode ? getFirstLocalChild(markerNode, \"symbol\") : null;\n const markerSymbol = markerSymbolNode?.getAttribute(\"val\")\n ?? (markerNode\n || chart.chartType === \"Scatter\"\n || [\"marker\", \"lineMarker\", \"smoothMarker\"].includes(chart.scatterStyle ?? \"\")\n || readChartBooleanAttribute(chartTypeNode, \"marker\") === true\n ? \"auto\"\n : series.markerSymbol);\n const pointStyles = parseChartPointStyles(seriesNode, themePalette);\n const seriesExplosion = readChartNumericAttribute(seriesNode, \"explosion\");\n const invertNegativeStyle = parseInvertNegativeStyle(seriesNode, themePalette);\n const invertIfNegative = readChartBooleanAttribute(seriesNode, \"invertIfNegative\");\n const isScatterChart = (\n chart.chartType === \"Scatter\"\n || chart.chartType === \"ScatterLines\"\n || chart.chartType === \"ScatterSmooth\"\n || chart.chartType === \"Bubble\"\n );\n const cachedCategories = isScatterChart\n ? (\n parseChartCacheValues(getFirstLocalChild(seriesNode, \"xVal\"), \"numCache\", \"value\")\n ?? parseChartMultiLevelCacheValues(getFirstLocalChild(seriesNode, \"xVal\"), \"category\")\n )\n : (\n parseChartCacheValues(getFirstLocalChild(seriesNode, \"cat\"), \"strCache\", \"category\")\n ?? parseChartCacheValues(getFirstLocalChild(seriesNode, \"cat\"), \"numCache\", \"category\")\n ?? parseChartMultiLevelCacheValues(getFirstLocalChild(seriesNode, \"cat\"), \"category\")\n );\n const cachedValues = isScatterChart\n ? parseChartCacheValues(getFirstLocalChild(seriesNode, \"yVal\"), \"numCache\", \"value\")\n : parseChartCacheValues(getFirstLocalChild(seriesNode, \"val\"), \"numCache\", \"value\");\n const cachedBubbleSizes = chart.chartType === \"Bubble\"\n ? parseChartCacheValues(getFirstLocalChild(seriesNode, \"bubbleSize\"), \"numCache\", \"value\")\n : null;\n const existingShapeProperties = series.shapeProperties && typeof series.shapeProperties === \"object\"\n ? series.shapeProperties as Record<string, unknown>\n : null;\n const rawFillColor = typeof existingShapeProperties?.solidFillHex === \"string\"\n ? normalizeHexColor(existingShapeProperties.solidFillHex)\n : null;\n const rawLineColor = typeof existingShapeProperties?.lineColorHex === \"string\"\n ? normalizeHexColor(existingShapeProperties.lineColorHex)\n : null;\n const resolvedLineColor = lineStyle.hidden\n ? undefined\n : rawLineColor ?? lineStyle.color ?? rawFillColor ?? fillColor ?? series.lineColor ?? series.color;\n\n const hasCategoryReference = typeof series.categoriesRef?.formula === \"string\" && series.categoriesRef.formula.length > 0 && !series.raw?.unresolvedCategoriesReference;\n const hasValueReference = typeof series.valuesRef?.formula === \"string\" && series.valuesRef.formula.length > 0 && !series.raw?.unresolvedValuesReference;\n const hasBubbleSizeReference = typeof series.bubbleSizeRef?.formula === \"string\" && series.bubbleSizeRef.formula.length > 0 && !series.raw?.unresolvedBubbleSizeReference;\n\n return {\n ...series,\n name: series.raw?.unresolvedNameReference\n ? parseChartCacheValues(getFirstLocalChild(seriesNode, \"tx\"), \"strCache\", \"category\")?.[0]?.toString() ?? series.name\n : series.name,\n bubbleSizes: !hasBubbleSizeReference && cachedBubbleSizes\n ? cachedBubbleSizes.map((value) => (typeof value === \"number\" && Number.isFinite(value) ? value : null))\n : series.bubbleSizes,\n categories: !hasCategoryReference && cachedCategories ? cachedCategories : series.categories,\n color: rawFillColor ?? rawLineColor ?? fillColor ?? lineStyle.color ?? series.color,\n dataPointStyles: pointStyles.length > 0 ? pointStyles : series.dataPointStyles,\n lineColor: resolvedLineColor,\n lineWidthPx: lineStyle.hidden ? undefined : lineStyle.widthPx ?? series.lineWidthPx,\n markerColor: resolveChartFillColor(markerShapeProperties, themePalette)\n ?? rawFillColor\n ?? rawLineColor\n ?? fillColor\n ?? lineStyle.color\n ?? undefined,\n markerLineColor: markerLineStyle.color\n ?? rawLineColor\n ?? rawFillColor\n ?? lineStyle.color\n ?? fillColor\n ?? undefined,\n markerSize: markerSize ?? series.markerSize,\n markerSymbol,\n smooth: readChartBooleanAttribute(seriesNode, \"smooth\") ?? series.smooth,\n invertIfNegative: invertIfNegative ?? series.invertIfNegative,\n shapeProperties: {\n ...series.shapeProperties,\n xmlExplosion: seriesExplosion ?? undefined,\n xmlFillHidden: getFirstLocalChild(shapeProperties, \"noFill\") ? true : undefined,\n xmlFillColor: fillColor ?? undefined,\n xmlLineHidden: lineStyle.hidden ? true : undefined,\n xmlLineColor: lineStyle.color ?? undefined,\n xmlLineWidthPx: lineStyle.widthPx ?? undefined,\n xmlMarkerFillColor: resolveChartFillColor(markerShapeProperties, themePalette) ?? undefined,\n xmlMarkerFillHidden: getFirstLocalChild(markerShapeProperties, \"noFill\") ? true : undefined,\n xmlMarkerLineColor: markerLineStyle.color ?? undefined,\n xmlMarkerLineHidden: markerLineStyle.hidden ? true : undefined,\n xmlNegativeFillColor: invertNegativeStyle.color ?? undefined,\n xmlNegativeLineColor: invertNegativeStyle.lineColor ?? undefined\n },\n negativeColor: invertNegativeStyle.color ?? series.negativeColor,\n negativeLineColor: invertNegativeStyle.lineColor ?? series.negativeLineColor,\n values: !hasValueReference && cachedValues\n ? cachedValues.map((value) => (typeof value === \"number\" && Number.isFinite(value) ? value : null))\n : series.values\n };\n });\n}\n\nfunction applyChartStyleFromXml(\n chart: XlsxChart,\n chartPath: string | undefined,\n archive: Record<string, Uint8Array>,\n themePalette?: XlsxThemePalette | null\n) {\n const chartXml = readArchiveText(archive, chartPath);\n if (!chartXml) {\n return;\n }\n const relationships = chartPath ? readChartRelationships(archive, chartPath) : new Map<string, string>();\n const fallbackPointStylesBySeries = parseFallbackPointStylesFromChartXml(chartXml, themePalette);\n const fallbackSeriesStyles = parseFallbackSeriesStylesFromChartXml(chartXml, themePalette);\n const fallbackBubbleSizesBySeries = parseFallbackBubbleSizesFromChartXml(chartXml);\n const applyFallbackSeriesStyles = () => {\n if (fallbackBubbleSizesBySeries.length > 0) {\n chart.series = chart.series.map((series, seriesIndex) => {\n const fallbackBubbleSizes = fallbackBubbleSizesBySeries[seriesIndex] ?? [];\n if (fallbackBubbleSizes.length === 0) {\n return series;\n }\n\n const currentNumericPointCount = (series.bubbleSizes ?? []).filter(\n (value): value is number => typeof value === \"number\" && Number.isFinite(value)\n ).length;\n const fallbackNumericPointCount = fallbackBubbleSizes.filter(\n (value): value is number => typeof value === \"number\" && Number.isFinite(value)\n ).length;\n\n if (currentNumericPointCount >= fallbackNumericPointCount) {\n return series;\n }\n\n return {\n ...series,\n bubbleSizes: fallbackBubbleSizes\n };\n });\n }\n\n if (fallbackPointStylesBySeries.length > 0) {\n chart.series = chart.series.map((series, seriesIndex) => {\n const fallbackPointStyles = fallbackPointStylesBySeries[seriesIndex] ?? [];\n if (fallbackPointStyles.length === 0) {\n return series;\n }\n\n const existingByIndex = new Map((series.dataPointStyles ?? []).map((entry) => [entry.index, entry]));\n for (const fallbackStyle of fallbackPointStyles) {\n const existing = existingByIndex.get(fallbackStyle.index);\n existingByIndex.set(fallbackStyle.index, {\n color: existing?.color ?? fallbackStyle.color,\n explosion: existing?.explosion ?? fallbackStyle.explosion,\n index: fallbackStyle.index,\n lineColor: existing?.lineColor ?? fallbackStyle.lineColor\n });\n }\n\n return {\n ...series,\n dataPointStyles: Array.from(existingByIndex.values()).sort((left, right) => left.index - right.index)\n };\n });\n }\n\n if (fallbackSeriesStyles.length > 0) {\n chart.series = chart.series.map((series, seriesIndex) => {\n const fallbackStyle = fallbackSeriesStyles[seriesIndex];\n if (!fallbackStyle) {\n return series;\n }\n const fallbackColor = fallbackStyle.color ?? fallbackStyle.lineColor;\n return {\n ...series,\n color: series.color ?? fallbackColor,\n lineColor: series.lineColor ?? fallbackStyle.lineColor ?? fallbackColor,\n markerColor: series.markerColor ?? fallbackColor ?? series.color,\n markerLineColor: series.markerLineColor ?? fallbackStyle.lineColor ?? fallbackColor ?? series.lineColor\n };\n });\n }\n };\n const applyRelationshipStyles = () => {\n chart.chartColorPalette = readChartColorPalette(archive, relationships.get(CHART_COLOR_STYLE_REL_TYPE), themePalette);\n chart.raw = { ...chart.raw, hasCustomChartColorStyle: Boolean(chart.chartColorPalette?.length) };\n const styleAppearance = readChartStyleAppearance(\n archive,\n relationships.get(CHART_STYLE_REL_TYPE),\n themePalette\n );\n chart.axisLabelColor = styleAppearance.axisLabelColor ?? chart.axisLabelColor;\n chart.axisLineColor = styleAppearance.axisLineColor ?? chart.axisLineColor;\n chart.chartAreaBorderColor = styleAppearance.chartAreaBorderColor ?? chart.chartAreaBorderColor;\n chart.chartAreaFillColor = styleAppearance.chartAreaFillColor ?? chart.chartAreaFillColor;\n chart.chartColorPaletteOffset = styleAppearance.paletteOffset ?? chart.chartColorPaletteOffset;\n chart.textColor = styleAppearance.textColor ?? chart.textColor;\n chart.titleColor = styleAppearance.titleColor ?? chart.titleColor;\n return styleAppearance;\n };\n const applyModernChartExStyles = () => {\n const modernPlotAreaNode = chartDocument?.documentElement\n ? getFirstLocalDescendant(chartDocument.documentElement, \"plotArea\")\n : null;\n if (!modernPlotAreaNode) {\n return;\n }\n\n const parseModernBinning = (seriesNode: Element) => {\n const layoutPrNode = getFirstLocalChild(seriesNode, \"layoutPr\");\n const binningNode = layoutPrNode ? getFirstLocalChild(layoutPrNode, \"binning\") : null;\n if (!binningNode) {\n return null;\n }\n const binning: Record<string, unknown> = {};\n for (const attribute of Array.from(binningNode.attributes)) {\n const rawValue = attribute.value;\n const numeric = Number(rawValue);\n binning[attribute.localName || attribute.name] = Number.isFinite(numeric) && rawValue.trim() !== \"\"\n ? numeric\n : rawValue;\n }\n return Object.keys(binning).length > 0 ? binning : {};\n };\n\n const plotAreaShapeProperties = getFirstLocalChild(modernPlotAreaNode, \"spPr\");\n if (plotAreaShapeProperties) {\n const plotAreaFillColor = resolveChartFillColor(plotAreaShapeProperties, themePalette);\n const plotAreaLineStyle = resolveChartLineStyle(plotAreaShapeProperties, themePalette);\n if (plotAreaFillColor) {\n chart.chartAreaFillColor = chart.chartAreaFillColor ?? plotAreaFillColor;\n }\n if (plotAreaLineStyle.color) {\n chart.chartAreaBorderColor = chart.chartAreaBorderColor ?? plotAreaLineStyle.color;\n }\n }\n\n const modernSeriesNodes = getLocalDescendants(modernPlotAreaNode, \"series\");\n if (modernSeriesNodes.length === 0) {\n return;\n }\n\n chart.series = chart.series.map((series, seriesIndex) => {\n const modernSeriesNode = modernSeriesNodes[seriesIndex] ?? null;\n if (!modernSeriesNode) {\n return series;\n }\n const valueColorsNode = getFirstLocalChild(modernSeriesNode, \"valueColors\");\n const valueColors = valueColorsNode\n ? Array.from(valueColorsNode.childNodes)\n .filter((node): node is Element => node.nodeType === Node.ELEMENT_NODE)\n .map((node) => resolveChartColorNode(findFirstChartColorElement(node) ?? node, themePalette))\n .filter((value): value is string => typeof value === \"string\" && value.length > 0)\n : [];\n const nextRaw = valueColors.length > 0\n ? {\n ...(series.raw && typeof series.raw === \"object\" ? series.raw as Record<string, unknown> : {}),\n valueColors\n }\n : series.raw;\n const seriesShapeProperties = getFirstLocalChild(modernSeriesNode, \"spPr\");\n if (!seriesShapeProperties) {\n return nextRaw === series.raw\n ? series\n : {\n ...series,\n raw: nextRaw\n };\n }\n const fillColor = resolveChartFillColor(seriesShapeProperties, themePalette);\n const lineStyle = resolveChartLineStyle(seriesShapeProperties, themePalette);\n const fallbackColor = fillColor ?? lineStyle.color ?? undefined;\n return {\n ...series,\n color: series.color ?? fallbackColor,\n lineColor: series.lineColor ?? lineStyle.color ?? fillColor ?? fallbackColor,\n lineWidthPx: series.lineWidthPx ?? (typeof lineStyle.widthPx === \"number\" ? lineStyle.widthPx : undefined),\n markerColor: series.markerColor ?? fallbackColor ?? series.color,\n markerLineColor: series.markerLineColor ?? lineStyle.color ?? fallbackColor ?? series.lineColor,\n raw: nextRaw\n };\n });\n\n const seriesLayouts = modernSeriesNodes.map((node) => node.getAttribute(\"layoutId\") ?? node.getAttribute(\"layout\"));\n const clusteredColumnIndex = seriesLayouts.findIndex((layout) => layout === \"clusteredColumn\");\n if (clusteredColumnIndex >= 0) {\n const clusteredNode = modernSeriesNodes[clusteredColumnIndex] ?? null;\n const parsedBinning = clusteredNode ? parseModernBinning(clusteredNode) : null;\n if (parsedBinning) {\n const syntheticRawSeries: Record<string, unknown> = {\n layoutId: \"clusteredColumn\",\n layoutPr: {\n binning: parsedBinning\n }\n };\n const hasParetoLine = seriesLayouts.includes(\"paretoLine\");\n const replaceColumnSeries = (series: XlsxChartSeries | null | undefined) => (\n series ? buildChartExHistogramSeries(series, syntheticRawSeries, hasParetoLine) : null\n );\n\n if (chart.typeGroups && chart.typeGroups.length > 0) {\n const nextTypeGroups = chart.typeGroups.map((group) => ({ ...group, series: [...group.series] }));\n const columnGroupIndex = nextTypeGroups.findIndex((group) => group.chartType === \"ColumnClustered\");\n if (columnGroupIndex >= 0) {\n const originalColumnSeries = nextTypeGroups[columnGroupIndex]?.series[0] ?? null;\n const binnedColumnSeries = replaceColumnSeries(originalColumnSeries);\n if (binnedColumnSeries) {\n nextTypeGroups[columnGroupIndex].series = [binnedColumnSeries];\n const lineGroupIndex = nextTypeGroups.findIndex((group) => group.chartType === \"Line\");\n if (lineGroupIndex >= 0 && nextTypeGroups[lineGroupIndex]?.series[0]) {\n const originalLineSeries = nextTypeGroups[lineGroupIndex].series[0];\n const recomputedLine = buildChartExParetoLineSeries(\n binnedColumnSeries,\n {\n text: originalLineSeries.name,\n ...(originalLineSeries.raw && typeof originalLineSeries.raw === \"object\"\n ? originalLineSeries.raw as Record<string, unknown>\n : {})\n },\n 0\n );\n nextTypeGroups[lineGroupIndex].series = [\n {\n ...originalLineSeries,\n categories: recomputedLine.categories,\n categoriesRef: recomputedLine.categoriesRef,\n raw: recomputedLine.raw,\n values: recomputedLine.values\n }\n ];\n chart.series = [binnedColumnSeries, nextTypeGroups[lineGroupIndex].series[0]];\n } else {\n chart.series = [binnedColumnSeries];\n }\n chart.typeGroups = nextTypeGroups;\n }\n } else if (chart.series[0]) {\n const binnedSeries = replaceColumnSeries(chart.series[0]);\n if (binnedSeries) {\n chart.series = [binnedSeries];\n }\n }\n } else if (chart.series[0]) {\n const binnedSeries = replaceColumnSeries(chart.series[0]);\n if (binnedSeries) {\n chart.series = [binnedSeries];\n }\n }\n }\n }\n };\n\n const chartDocument = parseXml(chartXml);\n const chartNode = chartDocument ? getFirstLocalDescendant(chartDocument, \"chart\") : null;\n const plotAreaNode = chartNode ? getFirstLocalChild(chartNode, \"plotArea\") : null;\n const styleIdNode = chartDocument?.documentElement ? getFirstLocalDescendant(chartDocument.documentElement, \"style\") : null;\n const chartTypeNode = findPrimaryChartTypeNode(plotAreaNode);\n\n if (!chartNode || !chartTypeNode) {\n applyRelationshipStyles();\n const fallbackStyleId = readChartNumericAttribute(styleIdNode, \"style\");\n if (typeof fallbackStyleId === \"number\" && Number.isFinite(fallbackStyleId)) {\n chart.chartStyleId = fallbackStyleId;\n }\n const sourceSeries = chart.raw?.officeSourceSeries as XlsxChartSeries[] | undefined;\n if (sourceSeries && chartDocument?.documentElement.namespaceURI === \"http://schemas.microsoft.com/office/drawing/2014/chartex\") {\n chart.renderSource = {\n kind: chart.chartExLayout ?? \"\",\n seriesTypes: getLocalDescendants(chartNode, \"series\").map(node => node.getAttribute(\"layoutId\") ?? \"\"),\n sourceSeries,\n experimental: true,\n xml: chartXml,\n themeColors: themePalette ? Object.fromEntries(Object.entries(THEME_COLOR_INDEX_BY_NAME).flatMap(([key, index]) => {\n const color = themePalette.colorsByIndex[index];\n return color ? [[key, color]] : [];\n })) : undefined,\n themeOverrideXml: readArchiveText(archive, relationships.get(\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride\")) ?? undefined,\n styleXml: readArchiveText(archive, relationships.get(CHART_STYLE_REL_TYPE)) ?? undefined,\n colorStyleXml: readArchiveText(archive, relationships.get(CHART_COLOR_STYLE_REL_TYPE)) ?? undefined\n };\n }\n applyModernChartExStyles();\n applyFallbackSeriesStyles();\n applyBuiltinChartDefaults(chart, themePalette);\n return;\n }\n const plotArea = plotAreaNode;\n if (!plotArea) {\n applyRelationshipStyles();\n applyFallbackSeriesStyles();\n applyBuiltinChartDefaults(chart, themePalette);\n return;\n }\n\n switch (chartTypeNode.localName) {\n case \"barChart\":\n case \"bar3DChart\": {\n const grouping = getFirstLocalChild(chartTypeNode, \"grouping\")?.getAttribute(\"val\");\n const barDir = getFirstLocalChild(chartTypeNode, \"barDir\")?.getAttribute(\"val\");\n const isHorizontalBar = barDir === \"bar\";\n chart.is3d = chartTypeNode.localName === \"bar3DChart\" ? true : chart.is3d;\n if (grouping === \"percentStacked\") {\n chart.chartType = isHorizontalBar ? \"BarPercentStacked\" : \"ColumnPercentStacked\";\n } else if (grouping === \"stacked\") {\n chart.chartType = isHorizontalBar ? \"BarStacked\" : \"ColumnStacked\";\n } else {\n chart.chartType = isHorizontalBar ? \"BarClustered\" : \"ColumnClustered\";\n }\n break;\n }\n case \"areaChart\":\n case \"area3DChart\": {\n const grouping = getFirstLocalChild(chartTypeNode, \"grouping\")?.getAttribute(\"val\");\n chart.is3d = chartTypeNode.localName === \"area3DChart\" ? true : chart.is3d;\n if (grouping === \"stacked\") {\n chart.chartType = \"AreaStacked\";\n } else if (grouping === \"percentStacked\") {\n chart.chartType = \"AreaPercentStacked\";\n } else {\n chart.chartType = \"Area\";\n }\n break;\n }\n case \"lineChart\":\n case \"line3DChart\": {\n const grouping = getFirstLocalChild(chartTypeNode, \"grouping\")?.getAttribute(\"val\");\n chart.is3d = chartTypeNode.localName === \"line3DChart\" ? true : chart.is3d;\n if (grouping === \"stacked\") {\n chart.chartType = \"LineStacked\";\n } else if (grouping === \"percentStacked\") {\n chart.chartType = \"LinePercentStacked\";\n } else {\n chart.chartType = \"Line\";\n }\n break;\n }\n case \"pieChart\":\n chart.chartType = \"Pie\";\n break;\n case \"pie3DChart\":\n chart.chartType = \"Pie3D\";\n chart.is3d = true;\n break;\n case \"doughnutChart\":\n chart.chartType = \"Doughnut\";\n break;\n case \"ofPieChart\":\n chart.chartType = \"BarOfPie\";\n break;\n case \"scatterChart\":\n chart.chartType = resolveScatterChartType(getFirstLocalChild(chartTypeNode, \"scatterStyle\")?.getAttribute(\"val\"));\n break;\n case \"radarChart\":\n chart.chartType = \"Radar\";\n break;\n case \"surfaceChart\":\n chart.chartType = \"Surface\";\n chart.is3d = false;\n break;\n case \"surface3DChart\":\n chart.chartType = \"Surface\";\n chart.is3d = true;\n break;\n case \"stockChart\":\n chart.chartType = \"Stock\";\n break;\n case \"bubbleChart\":\n chart.chartType = \"Bubble\";\n break;\n default:\n break;\n }\n\n const legendNode = getFirstLocalChild(chartNode, \"legend\");\n const legendPosition = legendNode ? getFirstLocalChild(legendNode, \"legendPos\")?.getAttribute(\"val\") ?? undefined : undefined;\n const legendOverlay = legendNode ? getFirstLocalChild(legendNode, \"overlay\")?.getAttribute(\"val\") : undefined;\n\n chart.legend = legendNode ? {\n overlay: legendOverlay === \"1\",\n position: normalizeLegendPosition(legendPosition),\n raw: chart.legend?.raw\n } : chart.legend;\n const plotVisibleOnly = readChartBooleanAttribute(chartNode, \"plotVisOnly\");\n if (plotVisibleOnly !== undefined) {\n chart.plotVisibleOnly = plotVisibleOnly;\n }\n chart.displayBlanksAs = getFirstLocalChild(chartNode, \"dispBlanksAs\")?.getAttribute(\"val\") ?? chart.displayBlanksAs;\n const styleId = Number(styleIdNode?.getAttribute(\"val\") ?? Number.NaN);\n chart.chartStyleId = Number.isFinite(styleId) ? styleId : chart.chartStyleId;\n chart.firstSliceAngle = readChartNumericAttribute(chartTypeNode, \"firstSliceAng\") ?? chart.firstSliceAngle;\n chart.gapWidth = readChartNumericAttribute(chartTypeNode, \"gapWidth\") ?? chart.gapWidth;\n chart.overlap = readChartNumericAttribute(chartTypeNode, \"overlap\") ?? chart.overlap;\n chart.bubbleScale = readChartNumericAttribute(chartTypeNode, \"bubbleScale\") ?? chart.bubbleScale;\n chart.varyColors = readChartBooleanAttribute(chartTypeNode, \"varyColors\") ?? chart.varyColors;\n const bubble3dNode = getFirstLocalChild(chartTypeNode, \"bubble3D\");\n chart.bubble3d = bubble3dNode\n ? bubble3dNode.getAttribute(\"val\") !== \"0\"\n : chart.bubble3d;\n chart.holeSize = readChartNumericAttribute(chartTypeNode, \"holeSize\") ?? chart.holeSize;\n chart.radarStyle = getFirstLocalChild(chartTypeNode, \"radarStyle\")?.getAttribute(\"val\") ?? chart.radarStyle;\n chart.scatterStyle = getFirstLocalChild(chartTypeNode, \"scatterStyle\")?.getAttribute(\"val\") ?? chart.scatterStyle;\n chart.shape3d = getFirstLocalChild(chartTypeNode, \"shape\")?.getAttribute(\"val\") ?? chart.shape3d;\n const wireframeNode = getFirstLocalChild(chartTypeNode, \"wireframe\");\n chart.wireframe = wireframeNode\n ? wireframeNode.getAttribute(\"val\") !== \"0\"\n : chart.wireframe;\n const chartTypeDataLabels = parseChartDataLabelsFromXml(getFirstLocalChild(chartTypeNode, \"dLbls\"));\n const firstSeriesNode = getLocalChildren(chartTypeNode, \"ser\")[0] ?? null;\n const seriesDataLabels = parseChartDataLabelsFromXml(getFirstLocalChild(firstSeriesNode, \"dLbls\"));\n chart.dataLabels = chartTypeDataLabels ?? seriesDataLabels ?? chart.dataLabels;\n const seriesSp3dNode = firstSeriesNode ? getFirstLocalDescendant(firstSeriesNode, \"sp3d\") : null;\n chart.surfaceMaterial = seriesSp3dNode?.getAttribute(\"prstMaterial\") ?? chart.surfaceMaterial;\n const bandFormatsNode = getLocalChildren(chartTypeNode, \"bandFmts\")[0] ?? null;\n const bandFormatNodes = bandFormatsNode ? getLocalChildren(bandFormatsNode, \"bandFmt\") : [];\n const bandFormatColors: Record<number, string> = Object.create(null);\n const bandFormatLineColors: Record<number, string> = Object.create(null);\n const bandFormatLineWidths: Record<number, number> = Object.create(null);\n for (const bandFormatNode of bandFormatNodes) {\n const index = readChartNumericAttribute(bandFormatNode, \"idx\");\n if (index == null || !Number.isInteger(index) || index < 0 || index > 0xffffffff) {\n continue;\n }\n const shapeProperties = getFirstLocalChild(bandFormatNode, \"spPr\");\n const fillColor = getFirstLocalChild(shapeProperties, \"noFill\") ? \"transparent\" : resolveChartFillColor(shapeProperties, themePalette);\n const lineStyle = resolveChartLineStyle(shapeProperties, themePalette);\n const lineColor = lineStyle.hidden ? \"transparent\" : lineStyle.color;\n const lineWidth = Number(getFirstLocalChild(shapeProperties, \"ln\")?.getAttribute(\"w\") ?? Number.NaN);\n if (fillColor) bandFormatColors[index] = fillColor;\n if (lineColor) bandFormatLineColors[index] = lineColor;\n if (Number.isFinite(lineWidth) && lineWidth >= 0) bandFormatLineWidths[index] = lineWidth / EMU_PER_PIXEL;\n }\n\n chart.raw = {\n ...(chart.raw ?? {}),\n bandFormatColors: Object.keys(bandFormatColors).length > 0 ? bandFormatColors : undefined,\n bandFormatLineColors: Object.keys(bandFormatLineColors).length > 0 ? bandFormatLineColors : undefined,\n bandFormatLineWidths: Object.keys(bandFormatLineWidths).length > 0 ? bandFormatLineWidths : undefined,\n date1904: readChartBooleanAttribute(chartDocument?.documentElement ?? null, \"date1904\"),\n titleOverlay: readChartBooleanAttribute(getFirstLocalChild(chartNode, \"title\"), \"overlay\"),\n bubble3d: chart.bubble3d,\n grouping: getFirstLocalChild(chartTypeNode, \"grouping\")?.getAttribute(\"val\") ?? undefined,\n ofPieType: getFirstLocalChild(chartTypeNode, \"ofPieType\")?.getAttribute(\"val\") ?? undefined,\n shape: getFirstLocalChild(chartTypeNode, \"shape\")?.getAttribute(\"val\") ?? undefined,\n secondPieSize: readChartNumericAttribute(chartTypeNode, \"secondPieSize\"),\n scatterStyle: chart.scatterStyle,\n splitPos: readChartNumericAttribute(chartTypeNode, \"splitPos\"),\n splitType: getFirstLocalChild(chartTypeNode, \"splitType\")?.getAttribute(\"val\") ?? undefined,\n xmlChartType: chartTypeNode.localName\n };\n const view3dNode = getFirstLocalDescendant(chartNode, \"view3D\");\n if (view3dNode) {\n chart.view3d = {\n depthPercent: readChartNumericAttribute(view3dNode, \"depthPercent\"),\n heightPercent: readChartNumericAttribute(view3dNode, \"hPercent\"),\n perspective: readChartNumericAttribute(view3dNode, \"perspective\"),\n rAngAx: getFirstLocalChild(view3dNode, \"rAngAx\")?.getAttribute(\"val\") === \"1\",\n rotX: readChartNumericAttribute(view3dNode, \"rotX\"),\n rotY: readChartNumericAttribute(view3dNode, \"rotY\")\n };\n }\n for (const key of [\"floor\", \"sideWall\", \"backWall\"] as const) {\n const wall = readChartWallFromXml(getFirstLocalChild(chartNode, key), themePalette);\n if (wall) {\n chart[key] = wall;\n const defaults = chart.raw?.builtinSurfaceDefaults as Record<string, unknown> | undefined;\n if (defaults) chart.raw = { ...chart.raw, builtinSurfaceDefaults: { ...defaults, [key]: undefined } };\n }\n }\n\n const styleAppearance = applyRelationshipStyles();\n const chartTextTypeface = readChartTextTypeface(getFirstLocalChild(chartNode, \"txPr\"), themePalette);\n const titleTypeface = readChartTextTypeface(getFirstLocalDescendant(chartNode, \"title\"), themePalette);\n chart.fontFamily = chartTextTypeface ?? chart.fontFamily;\n chart.titleFontFamily = titleTypeface ?? chart.titleFontFamily ?? chart.fontFamily;\n\n const chartAreaShapeProperties = chartDocument?.documentElement\n ? getFirstLocalChild(chartDocument.documentElement, \"spPr\")\n : null;\n const plotAreaShapeProperties = getFirstLocalChild(plotArea, \"spPr\");\n const chartAreaNoFill = chartAreaShapeProperties ? getFirstLocalChild(chartAreaShapeProperties, \"noFill\") != null : false;\n const plotAreaNoFill = plotAreaShapeProperties ? getFirstLocalChild(plotAreaShapeProperties, \"noFill\") != null : false;\n chart.raw = {\n ...(chart.raw ?? {}),\n chartAreaNoFill: styleAppearance.chartAreaNoFill === true || chartAreaNoFill,\n plotAreaNoFill,\n plotAreaFillColor: resolveChartFillColor(plotAreaShapeProperties, themePalette) ?? undefined,\n plotAreaLineColor: resolveChartLineStyle(plotAreaShapeProperties, themePalette).color ?? undefined\n };\n if (chartAreaShapeProperties) {\n const chartAreaFillColor = resolveChartFillColor(chartAreaShapeProperties, themePalette);\n if (chartAreaFillColor) {\n chart.chartAreaFillColor = chartAreaFillColor;\n } else if (getFirstLocalChild(chartAreaShapeProperties, \"noFill\")) {\n chart.chartAreaFillColor = \"transparent\";\n }\n const chartAreaLineStyle = resolveChartLineStyle(chartAreaShapeProperties, themePalette);\n if (chartAreaLineStyle.hidden) {\n chart.chartAreaBorderColor = \"transparent\";\n } else if (chartAreaLineStyle.color) {\n chart.chartAreaBorderColor = chartAreaLineStyle.color;\n }\n }\n if (!chart.chartAreaFillColor && (styleAppearance.chartAreaNoFill === true || plotAreaNoFill)) {\n chart.chartAreaFillColor = \"transparent\";\n }\n const categoryAxisNodes = [\n ...getLocalChildren(plotArea, \"catAx\"),\n ...getLocalChildren(plotArea, \"dateAx\")\n ];\n const valueAxisNodes = getLocalChildren(plotArea, \"valAx\");\n const seriesAxisNode = getLocalChildren(plotArea, \"serAx\")[0] ?? null;\n const isScatterLikeChart = (\n chart.chartType === \"Scatter\"\n || chart.chartType === \"ScatterLines\"\n || chart.chartType === \"ScatterSmooth\"\n || chart.chartType === \"Bubble\"\n );\n let categoryAxisNode = categoryAxisNodes[0] ?? null;\n let valueAxisNode = valueAxisNodes[0] ?? null;\n if (!categoryAxisNode && isScatterLikeChart && valueAxisNodes.length >= 2) {\n categoryAxisNode = valueAxisNodes.find((axisNode) => {\n const position = getFirstLocalChild(axisNode, \"axPos\")?.getAttribute(\"val\");\n return position === \"b\" || position === \"t\";\n }) ?? valueAxisNodes[0];\n valueAxisNode = valueAxisNodes.find((axisNode) => {\n const position = getFirstLocalChild(axisNode, \"axPos\")?.getAttribute(\"val\");\n return position === \"l\" || position === \"r\";\n }) ?? valueAxisNodes[1] ?? valueAxisNodes[0];\n }\n chart.categoryAxis = mergeChartAxis(chart.categoryAxis, readChartAxisFromXml(categoryAxisNode, themePalette));\n chart.valueAxis = mergeChartAxis(chart.valueAxis, readChartAxisFromXml(valueAxisNode, themePalette));\n chart.seriesAxis = mergeChartAxis(chart.seriesAxis, readChartAxisFromXml(seriesAxisNode, themePalette));\n chart.axes = chart.axes.length > 0\n ? chart.axes.map((axis, index) => (\n index === 0 && categoryAxisNode\n ? { ...axis, ...readChartAxisFromXml(categoryAxisNode, themePalette) }\n : index === 1 && valueAxisNode\n ? { ...axis, ...readChartAxisFromXml(valueAxisNode, themePalette) }\n : axis\n ))\n : chart.axes;\n if (seriesAxisNode) {\n const seriesAxis = readChartAxisFromXml(seriesAxisNode, themePalette);\n if (seriesAxis && !chart.axes.some((axis) => axis.id != null && axis.id === seriesAxis.id)) {\n chart.axes = [...chart.axes, seriesAxis as XlsxChartAxis];\n }\n }\n\n const groups = Array.from(plotArea.children).filter((entry) => entry.localName.endsWith(\"Chart\"));\n const seriesTypes = groups.flatMap(group => getLocalChildren(group, \"ser\").map(() => group.localName.slice(0, -5)));\n const seriesNodes = groups.flatMap(group => getLocalChildren(group, \"ser\"));\n const indexedXSeries = seriesNodes.map(series => {\n const x = getFirstLocalChild(series, \"xVal\");\n return Boolean(x && (getFirstLocalChild(x, \"multiLvlStrRef\") || getFirstLocalChild(x, \"strRef\") || getFirstLocalChild(x, \"strLit\")));\n });\n const unsupportedHierarchy = getLocalDescendants(plotArea, \"multiLvlStrRef\").some(node => node.parentElement?.localName !== \"xVal\");\n const kind = chartTypeNode.localName.slice(0, -5);\n const stable = groups.length === 1 && !chart.is3d\n && ((kind === \"line\" && chart.chartType === \"Line\") || (kind === \"bar\" && chart.chartType === \"ColumnClustered\"))\n && !plotArea.querySelector(\"trendline, errBars, dropLines, hiLowLines, upDownBars, dateAx, multiLvlStrRef, pictureOptions\");\n if (seriesTypes.length === chart.series.length\n && !unsupportedHierarchy && !plotArea.querySelector(\"pictureOptions\")) {\n chart.renderSource = {\n kind,\n seriesTypes,\n indexedXSeries,\n experimental: !stable,\n xml: chartXml,\n themeColors: themePalette ? Object.fromEntries(Object.entries(THEME_COLOR_INDEX_BY_NAME).flatMap(([key, index]) => {\n const color = themePalette.colorsByIndex[index];\n return color ? [[key, color]] : [];\n })) : undefined,\n themeOverrideXml: readArchiveText(archive, relationships.get(\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride\")) ?? undefined,\n styleXml: readArchiveText(archive, relationships.get(CHART_STYLE_REL_TYPE)) ?? undefined,\n colorStyleXml: readArchiveText(archive, relationships.get(CHART_COLOR_STYLE_REL_TYPE)) ?? undefined\n };\n }\n\n if (groups.length > 1 && chart.typeGroups?.length === groups.length) {\n let seriesOffset = 0;\n const hydratedSeries: XlsxChartSeries[] = [];\n chart.typeGroups = chart.typeGroups.map((group, index) => {\n const count = getLocalChildren(groups[index], \"ser\").length;\n const groupChart = { ...chart, chartType: group.chartType, series: chart.series.slice(seriesOffset, seriesOffset + count) };\n applyChartSeriesStyleFromXml(groupChart, groups[index], themePalette);\n seriesOffset += count;\n hydratedSeries.push(...groupChart.series);\n return { ...group, series: groupChart.series };\n });\n chart.series = [...hydratedSeries, ...chart.series.slice(seriesOffset)];\n } else {\n applyChartSeriesStyleFromXml(chart, chartTypeNode, themePalette);\n }\n applyFallbackSeriesStyles();\n applyBuiltinChartDefaults(chart, themePalette);\n}\n\nfunction normalizeArchivePath(path: string) {\n return path.replace(/^\\/+/, \"\").replace(/\\\\/g, \"/\");\n}\n\nfunction dirname(path: string) {\n const normalized = normalizeArchivePath(path);\n const index = normalized.lastIndexOf(\"/\");\n return index >= 0 ? normalized.slice(0, index) : \"\";\n}\n\nfunction resolveRelationshipPath(basePath: string, target: string) {\n if (!target) {\n return \"\";\n }\n\n const normalizedTarget = target.replace(/\\\\/g, \"/\");\n if (normalizedTarget.startsWith(\"/\")) {\n return normalizeArchivePath(normalizedTarget);\n }\n const normalizedBasePath = normalizeArchivePath(basePath);\n let baseDirectory = dirname(normalizedBasePath);\n if (normalizedBasePath.endsWith(\".rels\")) {\n const relsMarker = \"/_rels/\";\n const relsMarkerIndex = normalizedBasePath.lastIndexOf(relsMarker);\n if (relsMarkerIndex >= 0) {\n const ownerPrefix = normalizedBasePath.slice(0, relsMarkerIndex);\n const relFileName = normalizedBasePath.slice(relsMarkerIndex + relsMarker.length);\n const ownerFileName = relFileName.endsWith(\".rels\")\n ? relFileName.slice(0, -\".rels\".length)\n : relFileName;\n baseDirectory = dirname(`${ownerPrefix}/${ownerFileName}`);\n }\n }\n\n const segments = [...baseDirectory.split(\"/\").filter(Boolean), ...normalizedTarget.split(\"/\").filter(Boolean)];\n const resolved: string[] = [];\n for (const segment of segments) {\n if (segment === \".\") {\n continue;\n }\n if (segment === \"..\") {\n resolved.pop();\n continue;\n }\n resolved.push(segment);\n }\n\n return resolved.join(\"/\");\n}\n\nfunction readArchiveText(archive: Record<string, Uint8Array>, path: string | null | undefined) {\n if (!path) {\n return null;\n }\n\n const entry = archive[normalizeArchivePath(path)];\n return entry ? strFromU8(entry) : null;\n}\n\nfunction parseXml(xml: string) {\n if (typeof DOMParser === \"undefined\") {\n return null;\n }\n\n try {\n return new DOMParser().parseFromString(xml, \"application/xml\");\n } catch {\n return null;\n }\n}\n\nfunction serializeXml(document: XMLDocument) {\n return new XMLSerializer().serializeToString(document);\n}\n\nfunction getLocalChildren(parent: ParentNode | null | undefined, localName: string) {\n return Array.from(parent?.childNodes ?? []).filter(\n (node): node is Element => node.nodeType === Node.ELEMENT_NODE && (node as Element).localName === localName\n );\n}\n\nfunction removeLocalChildren(parent: ParentNode, localName: string) {\n getLocalChildren(parent, localName).forEach((node) => node.parentNode?.removeChild(node));\n}\n\nfunction getLocalDescendants(parent: ParentNode | null | undefined, localName: string) {\n return Array.from((parent as Element | Document | null | undefined)?.getElementsByTagName(\"*\") ?? []).filter(\n (node) => node.localName === localName\n );\n}\n\nfunction getFirstLocalChild(parent: ParentNode | null | undefined, localName: string) {\n return getLocalChildren(parent, localName)[0] ?? null;\n}\n\nfunction getFirstLocalDescendant(parent: ParentNode | null | undefined, localName: string) {\n return getLocalDescendants(parent, localName)[0] ?? null;\n}\n\nfunction ensureChild(parent: Element, localName: string, namespace = parent.namespaceURI ?? CHART_NS, prefix = \"c\") {\n const existing = getFirstLocalChild(parent, localName);\n if (existing) {\n return existing;\n }\n\n const document = parent.ownerDocument;\n const node = document.createElementNS(namespace, `${prefix}:${localName}`);\n parent.appendChild(node);\n return node;\n}\n\nfunction setLeafValue(parent: Element, localName: string, value: string, namespace = parent.namespaceURI ?? CHART_NS, prefix = \"c\") {\n const node = ensureChild(parent, localName, namespace, prefix);\n node.textContent = value;\n return node;\n}\n\nfunction setBooleanValue(parent: Element, localName: string, value: boolean) {\n const node = ensureChild(parent, localName);\n node.setAttribute(\"val\", value ? \"1\" : \"0\");\n return node;\n}\n\nfunction setNumericValue(parent: Element, localName: string, value: number) {\n const node = ensureChild(parent, localName);\n node.setAttribute(\"val\", String(Math.round(value)));\n return node;\n}\n\nfunction unquoteSheetName(value: string) {\n const trimmed = value.trim();\n if (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\")) {\n return trimmed.slice(1, -1).replace(/''/g, \"'\");\n }\n return trimmed;\n}\n\nfunction splitSheetReference(reference: string) {\n let bangIndex = -1;\n let quoted = false;\n for (let index = 0; index < reference.length; index += 1) {\n const char = reference[index];\n if (char === \"'\") {\n quoted = !quoted;\n } else if (char === \"!\" && !quoted) {\n bangIndex = index;\n break;\n }\n }\n\n if (bangIndex < 0) {\n return null;\n }\n\n return {\n range: reference.slice(bangIndex + 1),\n sheetName: unquoteSheetName(reference.slice(0, bangIndex))\n };\n}\n\nfunction parseA1Cell(reference: string) {\n const match = /^\\$?([A-Z]+)\\$?(\\d+)$/i.exec(reference.trim());\n if (!match) {\n return null;\n }\n\n let col = 0;\n for (const char of match[1].toUpperCase()) {\n col = col * 26 + (char.charCodeAt(0) - 64);\n }\n\n return {\n col: col - 1,\n row: Number(match[2]) - 1\n };\n}\n\nfunction parseA1Range(reference: string) {\n const [startRef, endRef = startRef] = reference.split(\":\");\n const start = parseA1Cell(startRef ?? \"\");\n const end = parseA1Cell(endRef ?? \"\");\n if (!start || !end) {\n return null;\n }\n\n return {\n end: {\n col: Math.max(start.col, end.col),\n row: Math.max(start.row, end.row)\n },\n start: {\n col: Math.min(start.col, end.col),\n row: Math.min(start.row, end.row)\n }\n };\n}\n\nfunction formatA1Column(col: number) {\n let current = col + 1;\n let label = \"\";\n while (current > 0) {\n const remainder = (current - 1) % 26;\n label = String.fromCharCode(65 + remainder) + label;\n current = Math.floor((current - 1) / 26);\n }\n return label;\n}\n\nfunction buildA1RangeFormula(sheetName: string, start: { col: number; row: number }, end: { col: number; row: number }) {\n const escapedSheetName = sheetName.replace(/'/g, \"''\");\n return `'${escapedSheetName}'!$${formatA1Column(start.col)}$${start.row + 1}:$${formatA1Column(end.col)}$${end.row + 1}`;\n}\n\nfunction resolveReferenceSheet(workbook: Workbook, fallbackSheetIndex: number, formula?: string | null) {\n if (!formula) {\n return {\n range: null,\n sheet: fallbackSheetIndex < workbook.sheetCount ? workbook.getSheet(fallbackSheetIndex) : null,\n sheetName: fallbackSheetIndex < workbook.sheetCount ? workbook.getSheet(fallbackSheetIndex)?.name ?? \"\" : \"\"\n };\n }\n\n const trimmedFormula = formula.trim();\n if (trimmedFormula.length > 0 && !trimmedFormula.includes(\"!\")) {\n try {\n const namedRange = workbook.getNamedRange(trimmedFormula);\n if (typeof namedRange === \"string\" && namedRange.length > 0 && namedRange !== trimmedFormula) {\n return resolveReferenceSheet(workbook, fallbackSheetIndex, namedRange);\n }\n } catch {\n // Fall back to direct A1 parsing when the workbook has no matching name.\n }\n }\n\n const split = splitSheetReference(trimmedFormula);\n if (!split) {\n return {\n range: parseA1Range(trimmedFormula),\n sheet: fallbackSheetIndex < workbook.sheetCount ? workbook.getSheet(fallbackSheetIndex) : null,\n sheetName: fallbackSheetIndex < workbook.sheetCount ? workbook.getSheet(fallbackSheetIndex)?.name ?? \"\" : \"\"\n };\n }\n\n try {\n return {\n range: parseA1Range(split.range),\n sheet: workbook.getSheetByName(split.sheetName),\n sheetName: split.sheetName\n };\n } catch {\n return {\n range: parseA1Range(split.range),\n sheet: null,\n sheetName: split.sheetName\n };\n }\n}\n\nfunction resolveChartReferenceLabel(\n workbook: Workbook,\n fallbackSheetIndex: number,\n reference: XlsxChartReference | null | undefined,\n fallbackLabel: string\n) {\n if (!reference?.formula) {\n return fallbackLabel;\n }\n\n const resolved = resolveReferenceSheet(workbook, fallbackSheetIndex, reference.formula);\n if (!resolved.sheet || !resolved.range) {\n return fallbackLabel;\n }\n\n const { start } = resolved.range;\n if (start.row > 0) {\n const headerDisplay = cellValueToDisplay(\n typeof resolved.sheet.getFormattedValueAt === \"function\"\n ? resolved.sheet.getFormattedValueAt(start.row - 1, start.col)\n : null\n );\n if (headerDisplay.length > 0) {\n return headerDisplay;\n }\n }\n\n const firstDisplay = cellValueToDisplay(\n typeof resolved.sheet.getFormattedValueAt === \"function\"\n ? resolved.sheet.getFormattedValueAt(start.row, start.col)\n : null\n );\n return firstDisplay.length > 0 ? firstDisplay : fallbackLabel;\n}\n\nfunction resolveReferenceRowPaths(\n workbook: Workbook,\n fallbackSheetIndex: number,\n reference: XlsxChartReference | null | undefined\n) {\n if (!reference?.formula) {\n return [] as string[][];\n }\n\n const resolved = resolveReferenceSheet(workbook, fallbackSheetIndex, reference.formula);\n if (!resolved.sheet || !resolved.range) {\n return [];\n }\n\n const rows: string[][] = [];\n for (let row = resolved.range.start.row; row <= resolved.range.end.row; row += 1) {\n const parts: string[] = [];\n for (let col = resolved.range.start.col; col <= resolved.range.end.col; col += 1) {\n const calculated = typeof resolved.sheet.getCalculatedValueAt === \"function\"\n ? resolved.sheet.getCalculatedValueAt(row, col)\n : null;\n const formatted = typeof resolved.sheet.getFormattedValueAt === \"function\"\n ? resolved.sheet.getFormattedValueAt(row, col)\n : calculated;\n const display = cellValueToDisplay(formatted ?? calculated);\n const numeric = cellValueToNumber(calculated ?? formatted);\n const label = display.length > 0 ? display : (numeric != null ? String(numeric) : \"\");\n if (label.length > 0) {\n parts.push(label);\n }\n }\n rows.push(parts);\n }\n return rows;\n}\n\nfunction normalizeChartExLegend(raw: unknown): XlsxChartLegend | null {\n if (!raw || typeof raw !== \"object\") {\n return null;\n }\n\n const legend = raw as Record<string, unknown>;\n const position = typeof legend.position === \"string\"\n ? normalizeLegendPosition(String(legend.position))\n : undefined;\n return {\n overlay: typeof legend.overlay === \"boolean\" ? legend.overlay : undefined,\n position,\n raw: legend\n };\n}\n\nfunction humanizeChartExLayoutLabel(layout: string | undefined) {\n if (!layout) {\n return undefined;\n }\n return layout\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .replace(/[-_]+/g, \" \")\n .trim()\n .replace(/\\b\\w/g, (match) => match.toUpperCase());\n}\n\nfunction normalizeChartExAxis(raw: unknown): XlsxChartAxis | null {\n if (!raw || typeof raw !== \"object\") {\n return null;\n }\n\n const axis = raw as Record<string, unknown>;\n const scaling = axis.scaling && typeof axis.scaling === \"object\"\n ? axis.scaling as Record<string, unknown>\n : null;\n const numberFormat = axis.numberFormat && typeof axis.numberFormat === \"object\"\n ? axis.numberFormat as Record<string, unknown>\n : null;\n\n return {\n delete: typeof axis.hidden === \"boolean\" ? axis.hidden : undefined,\n id: typeof axis.id === \"number\" && Number.isFinite(axis.id) ? axis.id : undefined,\n crossId: typeof axis.crossId === \"number\" && Number.isFinite(axis.crossId) ? axis.crossId : undefined,\n majorGridlines: axis.majorGridlines != null ? true : undefined,\n majorUnit: typeof scaling?.majorUnit === \"number\" ? scaling.majorUnit : undefined,\n max: typeof scaling?.max === \"number\" ? scaling.max : undefined,\n min: typeof scaling?.min === \"number\" ? scaling.min : undefined,\n minorGridlines: axis.minorGridlines != null ? true : undefined,\n minorUnit: typeof scaling?.minorUnit === \"number\" ? scaling.minorUnit : undefined,\n numberFormat: numberFormat\n ? {\n formatCode: typeof numberFormat.formatCode === \"string\" ? numberFormat.formatCode : undefined,\n sourceLinked: typeof numberFormat.sourceLinked === \"boolean\" ? numberFormat.sourceLinked : undefined\n }\n : undefined,\n raw: axis,\n position: typeof axis.position === \"string\" ? axis.position : undefined,\n tickLabelSkip: typeof axis.tickLabelSkip === \"number\" ? axis.tickLabelSkip : undefined,\n tickMarkSkip: typeof axis.tickMarkSkip === \"number\" ? axis.tickMarkSkip : undefined\n };\n}\n\nfunction resolveChartExLayoutChartType(layout: string | undefined) {\n switch (layout) {\n case \"boxWhisker\":\n return \"BoxWhisker\";\n case \"clusteredColumn\":\n return \"ColumnClustered\";\n case \"funnel\":\n return \"Funnel\";\n case \"paretoLine\":\n return \"Line\";\n case \"regionMap\":\n return \"RegionMap\";\n case \"sunburst\":\n return \"Sunburst\";\n case \"treemap\":\n return \"Treemap\";\n case \"waterfall\":\n return \"Waterfall\";\n default:\n return layout ? `Unsupported(cx:${layout})` : \"ColumnClustered\";\n }\n}\n\nfunction resolveChartExSeriesLayout(raw: unknown) {\n if (!raw || typeof raw !== \"object\") {\n return undefined;\n }\n const record = raw as Record<string, unknown>;\n return typeof record.layout === \"string\"\n ? record.layout\n : typeof record.layoutId === \"string\"\n ? record.layoutId\n : undefined;\n}\n\nfunction resolveChartExSeriesAxisIds(raw: unknown) {\n if (!raw || typeof raw !== \"object\") {\n return [];\n }\n const record = raw as Record<string, unknown>;\n if (Array.isArray(record.axisIds)) {\n return record.axisIds.filter((value): value is number => typeof value === \"number\" && Number.isFinite(value));\n }\n if (Array.isArray(record.axisId)) {\n return record.axisId.flatMap((value) => {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return [value];\n }\n if (value && typeof value === \"object\" && typeof (value as { val?: unknown }).val === \"number\") {\n return [(value as { val: number }).val];\n }\n return [];\n });\n }\n if (typeof record.axisId === \"number\" && Number.isFinite(record.axisId)) {\n return [record.axisId];\n }\n return [];\n}\n\nfunction niceHistogramStep(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n return 1;\n }\n const exponent = Math.floor(Math.log10(value));\n const scale = 10 ** exponent;\n const normalized = value / scale;\n if (normalized <= 1) {\n return scale;\n }\n if (normalized <= 2) {\n return scale * 2;\n }\n if (normalized <= 5) {\n return scale * 5;\n }\n return scale * 10;\n}\n\nfunction formatHistogramBinLabel(lower: number, upper: number, index: number, closedRight: boolean) {\n const leftBracket = closedRight\n ? (index === 0 ? \"[\" : \"(\")\n : \"[\";\n const rightBracket = closedRight ? \"]\" : \")\";\n return `${leftBracket}${Number(lower.toFixed(6))},${Number(upper.toFixed(6))}${rightBracket}`;\n}\n\ntype ChartExHistogramBin = {\n count: number;\n label: string;\n lower: number;\n upper: number;\n};\n\nfunction buildChartExHistogramBins(values: number[], rawSeries: unknown, sortByFrequency: boolean) {\n if (values.length === 0) {\n return [] as ChartExHistogramBin[];\n }\n\n const rawRecord = rawSeries && typeof rawSeries === \"object\" ? rawSeries as Record<string, unknown> : null;\n const layoutProperties = rawRecord?.layoutProperties && typeof rawRecord.layoutProperties === \"object\"\n ? rawRecord.layoutProperties as Record<string, unknown>\n : null;\n const rawBinning = layoutProperties?.binning && typeof layoutProperties.binning === \"object\"\n ? layoutProperties.binning as Record<string, unknown>\n : null;\n const minValue = Math.min(...values);\n const maxValue = Math.max(...values);\n const explicitWidth = typeof rawBinning?.binSize === \"number\" && Number.isFinite(rawBinning.binSize) && rawBinning.binSize > 0\n ? rawBinning.binSize\n : undefined;\n const explicitCount = typeof rawBinning?.binCount === \"number\" && Number.isFinite(rawBinning.binCount) && rawBinning.binCount > 0\n ? rawBinning.binCount\n : typeof rawBinning?.count === \"number\" && Number.isFinite(rawBinning.count) && rawBinning.count > 0\n ? rawBinning.count\n : undefined;\n const closedRight = rawBinning?.intervalClosed === \"r\" || rawBinning?.intervalClosed === \"right\";\n const mean = values.reduce((sum, value) => sum + value, 0) / values.length;\n const variance = values.reduce((sum, value) => sum + ((value - mean) ** 2), 0) / Math.max(1, values.length);\n const standardDeviation = Math.sqrt(Math.max(0, variance));\n const allIntegers = values.every((value) => Math.abs(value - Math.round(value)) < 1e-9);\n const scottWidth = standardDeviation > 0\n ? (3.49 * standardDeviation) / Math.cbrt(values.length)\n : undefined;\n const fallbackWidth = explicitCount != null\n ? (maxValue - minValue) / Math.max(1, explicitCount)\n : scottWidth ?? ((maxValue - minValue) / Math.max(1, Math.ceil(Math.log2(values.length) + 1)));\n const roughWidth = explicitWidth\n ?? (allIntegers\n ? Math.max(1, Math.ceil(Math.max(fallbackWidth, 1e-6)))\n : niceHistogramStep(Math.max(fallbackWidth, 1e-6)));\n const binWidth = Math.max(roughWidth, 1e-6);\n const start = explicitWidth != null || explicitCount != null\n ? Math.floor(minValue / binWidth) * binWidth\n : minValue;\n const end = Math.max(start + binWidth, start + Math.ceil((maxValue - start) / binWidth) * binWidth);\n const binCount = Math.max(1, Math.ceil((end - start) / binWidth));\n const bins = Array.from({ length: binCount }, (_, index) => {\n const lower = start + binWidth * index;\n const upper = lower + binWidth;\n return {\n count: 0,\n label: formatHistogramBinLabel(lower, upper, index, closedRight),\n lower,\n upper\n } satisfies ChartExHistogramBin;\n });\n\n values.forEach((value) => {\n if (!Number.isFinite(value)) {\n return;\n }\n const offset = (value - start) / binWidth;\n let binIndex = Math.floor(offset);\n if (closedRight && Math.abs(offset - Math.round(offset)) < 1e-9 && value > start) {\n binIndex -= 1;\n }\n if (value >= end) {\n binIndex = bins.length - 1;\n }\n if (value <= start) {\n binIndex = 0;\n }\n const target = bins[Math.max(0, Math.min(bins.length - 1, binIndex))];\n if (target) {\n target.count += 1;\n }\n });\n\n if (sortByFrequency) {\n bins.sort((left, right) => (\n right.count - left.count\n || left.lower - right.lower\n ));\n }\n return bins;\n}\n\nfunction buildChartExHistogramSeries(\n series: XlsxChartSeries,\n rawSeries: unknown,\n sortByFrequency: boolean\n) {\n const layout = resolveChartExSeriesLayout(rawSeries);\n const rawRecord = rawSeries && typeof rawSeries === \"object\" ? rawSeries as Record<string, unknown> : null;\n const hasBinning = Boolean(\n layout === \"clusteredColumn\"\n && rawRecord?.layoutProperties\n && typeof rawRecord.layoutProperties === \"object\"\n && (rawRecord.layoutProperties as Record<string, unknown>).binning != null\n );\n if (!hasBinning) {\n return series;\n }\n\n const numericValues = series.values.filter((value): value is number => typeof value === \"number\" && Number.isFinite(value));\n if (numericValues.length === 0) {\n return series;\n }\n\n const bins = buildChartExHistogramBins(numericValues, rawSeries, sortByFrequency);\n if (bins.length === 0) {\n return series;\n }\n\n return {\n ...series,\n categories: bins.map((bin) => bin.label),\n categoriesRef: null,\n raw: {\n ...series.raw,\n chartExHistogramBins: bins,\n chartExSourceValues: numericValues\n },\n values: bins.map((bin) => bin.count)\n };\n}\n\nfunction buildChartExParetoLineSeries(series: XlsxChartSeries, sourceRaw: unknown, index: number) {\n const counts = series.values.map((value) => (\n typeof value === \"number\" && Number.isFinite(value) ? value : 0\n ));\n const total = counts.reduce((sum, value) => sum + value, 0);\n let running = 0;\n const cumulative = counts.map((value) => {\n running += value;\n return total > 0 ? (running / total) * 100 : 0;\n });\n return {\n ...series,\n id: series.id.replace(/-series-\\d+$/, `-series-${index}`),\n color: undefined,\n lineColor: undefined,\n markerColor: undefined,\n markerLineColor: undefined,\n markerSize: 7,\n markerSymbol: \"circle\",\n name: typeof (sourceRaw as { text?: unknown } | null)?.text === \"string\"\n ? (sourceRaw as { text: string }).text\n : \"Pareto\",\n raw: {\n ...(series.raw ?? {}),\n chartExLayout: \"paretoLine\",\n source: sourceRaw && typeof sourceRaw === \"object\" ? sourceRaw as Record<string, unknown> : undefined\n },\n values: cumulative\n };\n}\n\nfunction resolveChartExTextFormula(raw: unknown) {\n if (typeof raw === \"string\" && raw.length > 0) {\n return raw;\n }\n if (!raw || typeof raw !== \"object\") {\n return undefined;\n }\n const record = raw as Record<string, unknown>;\n if (typeof record.formula === \"string\" && record.formula.length > 0) {\n return record.formula;\n }\n if (typeof record.text === \"string\" && record.text.length > 0) {\n return record.text;\n }\n if (typeof record.value === \"string\" && record.value.length > 0) {\n return record.value;\n }\n return undefined;\n}\n\nfunction resolveChartExTitleText(raw: unknown) {\n if (typeof raw === \"string\" && raw.length > 0) {\n return raw;\n }\n if (!raw || typeof raw !== \"object\") {\n return undefined;\n }\n const record = raw as Record<string, unknown>;\n if (typeof record.text === \"string\" && record.text.length > 0) {\n return record.text;\n }\n const nestedText = record.text && typeof record.text === \"object\"\n ? resolveChartExTextFormula(record.text)\n : undefined;\n if (nestedText) {\n return nestedText;\n }\n return typeof record.value === \"string\" && record.value.length > 0 ? record.value : undefined;\n}\n\nfunction resolveChartExFallbackCategoryReference(\n workbook: Workbook,\n fallbackSheetIndex: number,\n valueFormula: string | undefined\n) {\n if (!valueFormula) {\n return null;\n }\n\n const resolved = resolveReferenceSheet(workbook, fallbackSheetIndex, valueFormula);\n if (!resolved.sheet || !resolved.range || resolved.range.start.col <= 0) {\n return null;\n }\n\n return normalizeChartReference({\n formula: buildA1RangeFormula(\n resolved.sheetName,\n {\n col: resolved.range.start.col - 1,\n row: resolved.range.start.row\n },\n {\n col: resolved.range.start.col - 1,\n row: resolved.range.end.row\n }\n )\n });\n}\n\nfunction normalizeChartExSeries(\n workbook: Workbook,\n workbookSheetIndex: number,\n chartId: string,\n raw: unknown,\n dataById: Map<number, Record<string, unknown>>,\n index: number,\n chartType?: XlsxChart[\"chartType\"]\n): XlsxChartSeries {\n const series = raw && typeof raw === \"object\" ? raw as Record<string, unknown> : {};\n const dataId = typeof series.dataId === \"number\" ? series.dataId : null;\n const dataEntry = dataId != null ? dataById.get(dataId) ?? null : null;\n const dimensions = Array.isArray(dataEntry?.dimensions)\n ? dataEntry.dimensions.filter((value): value is Record<string, unknown> => Boolean(value && typeof value === \"object\"))\n : [];\n const categoryDimension = dimensions.find((dimension) => dimension.dimType === \"cat\")\n ?? dimensions.find((dimension) => dimension.dimType === \"name\")\n ?? null;\n const valueDimension = dimensions.find((dimension) => (\n dimension.dimType === \"val\"\n || dimension.dimType === \"y\"\n || dimension.dimType === \"colorVal\"\n || dimension.dimType === \"size\"\n ))\n ?? dimensions.find((dimension) => dimension !== categoryDimension)\n ?? categoryDimension;\n const categoryDimensionFormula = typeof categoryDimension?.formula === \"string\" ? categoryDimension.formula : undefined;\n const valueDimensionFormula = typeof valueDimension?.formula === \"string\" ? valueDimension.formula : undefined;\n const fallbackCategoryRef = (\n (chartType === \"Sunburst\" || chartType === \"Treemap\")\n && !categoryDimension\n && typeof valueDimensionFormula === \"string\"\n )\n ? resolveChartExFallbackCategoryReference(workbook, workbookSheetIndex, valueDimensionFormula)\n : null;\n const categoriesRef = categoryDimension\n ? normalizeChartReference({\n formula: categoryDimensionFormula\n })\n : fallbackCategoryRef;\n const valuesRef = valueDimension\n ? normalizeChartReference({\n formula: valueDimensionFormula\n })\n : null;\n const isCategoricalMap = chartType === \"RegionMap\" && valueDimension?.dimType === \"colorStr\";\n const resolvedValueCells = resolveReferenceValues(workbook, workbookSheetIndex, valuesRef, isCategoricalMap ? \"category\" : \"value\");\n const values = resolvedValueCells.map((value) => (\n typeof value === \"number\" && Number.isFinite(value) ? value : null\n ));\n const colorStrings = isCategoricalMap\n ? resolvedValueCells.map((value) => {\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return String(value);\n }\n return null;\n })\n : [];\n const categoryRows = chartType === \"RegionMap\"\n ? resolveReferenceRowPaths(workbook, workbookSheetIndex, categoriesRef)\n : [];\n const categories = categoryRows.length === resolvedValueCells.length && categoryRows.length > 0\n ? categoryRows.map((parts) => parts[0] ?? \"\")\n : resolveReferenceValues(workbook, workbookSheetIndex, categoriesRef, \"category\");\n const hierarchyCategories = (\n chartType === \"Sunburst\" || chartType === \"Treemap\"\n )\n ? resolveReferenceRowPaths(workbook, workbookSheetIndex, categoriesRef)\n : [];\n const seriesTextFormula = resolveChartExTextFormula(series.text);\n const shapeProperties = series.shapeProperties && typeof series.shapeProperties === \"object\"\n ? series.shapeProperties as Record<string, unknown>\n : undefined;\n const rawFillColor = typeof shapeProperties?.solidFillHex === \"string\"\n ? normalizeHexColor(shapeProperties.solidFillHex)\n : null;\n const rawLineColor = typeof shapeProperties?.lineColorHex === \"string\"\n ? normalizeHexColor(shapeProperties.lineColorHex)\n : null;\n\n return {\n bubbleSizeRef: null,\n bubbleSizes: [],\n categories,\n categoriesRef,\n color: rawFillColor ?? undefined,\n dataPoints: Array.isArray(series.dataPoints) ? series.dataPoints : [],\n dataPointStyles: undefined,\n formatIdx: typeof series.formatIdx === \"number\" ? series.formatIdx : undefined,\n hidden: typeof series.hidden === \"boolean\" ? series.hidden : undefined,\n id: `${chartId}-series-${index}`,\n invertIfNegative: undefined,\n lineColor: rawLineColor ?? rawFillColor ?? undefined,\n lineWidthPx: typeof shapeProperties?.lineWidth === \"number\"\n ? Math.max(1, Number(shapeProperties.lineWidth) / EMU_PER_PIXEL)\n : undefined,\n marker: undefined,\n markerColor: rawFillColor ?? undefined,\n markerLineColor: rawLineColor ?? rawFillColor ?? undefined,\n markerSize: undefined,\n markerSymbol: undefined,\n name: typeof series.text === \"string\"\n ? series.text\n : seriesTextFormula\n ? resolveSeriesName(workbook, workbookSheetIndex, seriesTextFormula)\n : resolveChartReferenceLabel(workbook, workbookSheetIndex, valuesRef, `Series ${index + 1}`),\n negativeColor: undefined,\n negativeLineColor: undefined,\n raw: {\n ...series,\n chartExColorStrings: colorStrings,\n chartExHierarchyCategories: hierarchyCategories,\n data: dataEntry,\n dimType: typeof valueDimension?.dimType === \"string\" ? valueDimension.dimType : undefined\n },\n shapeProperties,\n smooth: undefined,\n values,\n valuesRef\n };\n}\n\nfunction collapseChartExPointSeries(chartType: XlsxChart[\"chartType\"], series: XlsxChartSeries[]) {\n if (chartType !== \"Funnel\" && chartType !== \"Waterfall\") {\n if (\n (chartType === \"Sunburst\" || chartType === \"Treemap\")\n && series.length > 1\n && series.every((entry) => {\n const raw = entry.raw && typeof entry.raw === \"object\" ? entry.raw as Record<string, unknown> : null;\n return raw?.dimType === \"size\";\n })\n ) {\n const primarySeries = series.find((entry) => entry.hidden !== true) ?? series[0] ?? null;\n if (!primarySeries) {\n return series;\n }\n return [\n {\n ...primarySeries,\n dataPoints: [],\n hidden: false\n }\n ];\n }\n return series;\n }\n\n const primarySeries = series.find((entry) => entry.hidden !== true) ?? series[0] ?? null;\n if (!primarySeries) {\n return series;\n }\n\n return [\n {\n ...primarySeries,\n categories: [],\n categoriesRef: null,\n dataPoints: [],\n hidden: false\n }\n ];\n}\n\nfunction normalizeChartExChart(\n workbook: Workbook,\n workbookSheetIndex: number,\n visibleSheetIndex: number,\n raw: unknown,\n index: number,\n themePalette?: XlsxThemePalette | null\n): XlsxChart {\n const drawing = raw && typeof raw === \"object\" ? raw as Record<string, unknown> : {};\n const chart = drawing.chartEx && typeof drawing.chartEx === \"object\"\n ? drawing.chartEx as Record<string, unknown>\n : {};\n const plotArea = chart.plotArea && typeof chart.plotArea === \"object\"\n ? chart.plotArea as Record<string, unknown>\n : {};\n const rawSeries = Array.isArray(plotArea.series) ? plotArea.series : [];\n const seriesLayouts = rawSeries.map(resolveChartExSeriesLayout);\n const dataEntries = Array.isArray(chart.data) ? chart.data : [];\n const dataById = new Map<number, Record<string, unknown>>();\n dataEntries.forEach((entry) => {\n if (!entry || typeof entry !== \"object\") {\n return;\n }\n const record = entry as Record<string, unknown>;\n if (typeof record.id === \"number\") {\n dataById.set(record.id, record);\n }\n });\n const axes = Array.isArray(plotArea.axes)\n ? plotArea.axes.map(normalizeChartExAxis).filter((value): value is XlsxChartAxis => Boolean(value))\n : [];\n const primaryLayout = typeof chart.layout === \"string\"\n ? chart.layout\n : seriesLayouts.find((value): value is string => typeof value === \"string\" && value.length > 0);\n const fallbackTitle = humanizeChartExLayoutLabel(primaryLayout);\n const chartTitle = resolveChartExTitleText(chart.title) ?? (chart.title != null ? \"Chart Title\" : fallbackTitle);\n const chartType = resolveChartExLayoutChartType(primaryLayout);\n const normalizedSeries = rawSeries.map((entry, seriesIndex) => (\n normalizeChartExSeries(workbook, workbookSheetIndex, `chart-ex-${workbookSheetIndex}-${index}`, entry, dataById, seriesIndex, chartType)\n ));\n const clusteredColumnSeriesIndex = seriesLayouts.findIndex((layout) => layout === \"clusteredColumn\");\n const hasParetoLine = seriesLayouts.includes(\"paretoLine\");\n const clusteredColumnAxisIds = clusteredColumnSeriesIndex >= 0\n ? resolveChartExSeriesAxisIds(rawSeries[clusteredColumnSeriesIndex])\n : [];\n const paretoLineSeriesIndex = seriesLayouts.findIndex((layout) => layout === \"paretoLine\");\n const paretoLineAxisIds = paretoLineSeriesIndex >= 0\n ? resolveChartExSeriesAxisIds(rawSeries[paretoLineSeriesIndex])\n : [];\n const primaryHistogramSeries = clusteredColumnSeriesIndex >= 0\n ? buildChartExHistogramSeries(normalizedSeries[clusteredColumnSeriesIndex] ?? normalizedSeries[0], rawSeries[clusteredColumnSeriesIndex], hasParetoLine)\n : null;\n const synthesizedParetoSeries = (\n hasParetoLine\n && primaryHistogramSeries\n && primaryHistogramSeries.values.length > 0\n )\n ? buildChartExParetoLineSeries(primaryHistogramSeries, rawSeries[paretoLineSeriesIndex], paretoLineSeriesIndex)\n : null;\n const resolvedSeries = synthesizedParetoSeries\n ? [primaryHistogramSeries!, synthesizedParetoSeries]\n : primaryHistogramSeries\n ? [\n primaryHistogramSeries,\n ...normalizedSeries.filter((_, seriesIndex) => seriesIndex !== clusteredColumnSeriesIndex)\n ]\n : collapseChartExPointSeries(chartType, normalizedSeries);\n const resolvedChartType = primaryHistogramSeries ? \"ColumnClustered\" : chartType;\n const resolvedGapWidth = primaryHistogramSeries ? 0 : undefined;\n const typeGroups = synthesizedParetoSeries\n ? [\n {\n axisIds: clusteredColumnAxisIds,\n chartType: \"ColumnClustered\",\n gapWidth: 0,\n raw: {\n gapWidth: 0,\n layout: \"clusteredColumn\"\n },\n series: [primaryHistogramSeries!]\n },\n {\n axisIds: paretoLineAxisIds,\n chartType: \"Line\",\n raw: {\n layout: \"paretoLine\"\n },\n series: [synthesizedParetoSeries]\n }\n ]\n : [];\n const normalizedChart: XlsxChart = {\n anchor: normalizeChartAnchor(drawing.anchor),\n autoTitleDeleted: undefined,\n axes,\n axisLabelColor: undefined,\n axisLineColor: undefined,\n categoryAxis: axes[0] ?? null,\n chartAreaBorderColor: undefined,\n chartAreaFillColor: undefined,\n chartColorPalette: undefined,\n chartColorPaletteOffset: undefined,\n chartExLayout: primaryLayout,\n chartPath: undefined,\n chartStyleId: undefined,\n chartType: resolvedChartType,\n dataLabels: rawSeries.length > 0 && rawSeries[0] && typeof rawSeries[0] === \"object\"\n ? normalizeChartDataLabels((rawSeries[0] as Record<string, unknown>).dataLabels)\n : null,\n displayBlanksAs: undefined,\n editable: true,\n firstSliceAngle: undefined,\n fontFamily: undefined,\n gapWidth: resolvedGapWidth,\n holeSize: undefined,\n id: `chart-ex-${workbookSheetIndex}-${index}`,\n is3d: undefined,\n legend: normalizeChartExLegend(chart.legend),\n name: typeof drawing.name === \"string\" ? drawing.name : chartTitle,\n overlap: undefined,\n plotVisibleOnly: undefined,\n raw: { ...chart, officeSourceSeries: normalizedSeries },\n radarStyle: undefined,\n scatterStyle: undefined,\n roundedCorners: undefined,\n shape3d: undefined,\n seriesAxis: null,\n series: resolvedSeries,\n sheetIndex: visibleSheetIndex,\n showDlblsOverMax: undefined,\n sideWall: null,\n backWall: null,\n bubbleScale: undefined,\n bubble3d: undefined,\n floor: null,\n surfaceMaterial: undefined,\n textColor: undefined,\n title: chartTitle,\n titleColor: undefined,\n titleFontFamily: undefined,\n typeGroups,\n valueAxis: axes.find((axis) => axis.numberFormat || axis.majorGridlines) ?? axes[1] ?? null,\n varyColors: typeof chart.valueColors === \"boolean\" ? chart.valueColors : undefined,\n view3d: undefined,\n wireframe: undefined,\n workbookSheetIndex,\n zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === \"number\"\n ? drawing.drawingPath[0] + 1\n : index + 1\n };\n\n applyBuiltinChartDefaults(normalizedChart, themePalette);\n return normalizedChart;\n}\n\nfunction cellValueToNumber(value: unknown) {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n if (value && typeof value === \"object\") {\n if ((value as { is_empty?: boolean }).is_empty) {\n return null;\n }\n const candidates: unknown[] = [];\n if (typeof (value as { asNumber?: () => unknown }).asNumber === \"function\") {\n candidates.push((value as { asNumber: () => unknown }).asNumber());\n }\n if (typeof (value as { toJs?: () => unknown }).toJs === \"function\") {\n candidates.push((value as { toJs: () => unknown }).toJs());\n }\n if (typeof (value as { asText?: () => unknown }).asText === \"function\") {\n candidates.push((value as { asText: () => unknown }).asText());\n }\n if (typeof (value as { toString?: () => unknown }).toString === \"function\") {\n candidates.push((value as { toString: () => unknown }).toString());\n }\n\n for (const candidate of candidates) {\n if (typeof candidate === \"number\" && Number.isFinite(candidate)) {\n return candidate;\n }\n if (typeof candidate === \"string\") {\n const parsed = Number(candidate.replace(/,/g, \"\"));\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n }\n }\n if (typeof value === \"string\") {\n const parsed = Number(value.replace(/,/g, \"\"));\n return Number.isFinite(parsed) ? parsed : null;\n }\n return null;\n}\n\nfunction cellValueToDisplay(value: unknown) {\n if (value === null || value === undefined) {\n return \"\";\n }\n if (typeof value === \"string\") {\n return value;\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value && typeof value === \"object\") {\n if ((value as { is_empty?: boolean }).is_empty) {\n return \"\";\n }\n const candidates: unknown[] = [];\n if (typeof (value as { asText?: () => unknown }).asText === \"function\") {\n candidates.push((value as { asText: () => unknown }).asText());\n }\n if (typeof (value as { toJs?: () => unknown }).toJs === \"function\") {\n candidates.push((value as { toJs: () => unknown }).toJs());\n }\n if (typeof (value as { toString?: () => unknown }).toString === \"function\") {\n candidates.push((value as { toString: () => unknown }).toString());\n }\n\n for (const candidate of candidates) {\n if (candidate === null || candidate === undefined) {\n continue;\n }\n if (typeof candidate === \"string\") {\n return candidate;\n }\n return String(candidate);\n }\n }\n return String(value);\n}\n\nfunction resolveReferenceValues(\n workbook: Workbook,\n fallbackSheetIndex: number,\n reference: XlsxChartReference | null | undefined,\n mode: \"category\" | \"value\"\n): Array<number | string | null> {\n if (!reference?.formula) {\n return reference?.values ?? [];\n }\n\n const resolved = resolveReferenceSheet(workbook, fallbackSheetIndex, reference.formula);\n if (!resolved.sheet || !resolved.range) {\n return reference.values ?? [];\n }\n\n const values: Array<number | string | null> = [];\n for (let row = resolved.range.start.row; row <= resolved.range.end.row; row += 1) {\n for (let col = resolved.range.start.col; col <= resolved.range.end.col; col += 1) {\n const calculated = typeof resolved.sheet.getCalculatedValueAt === \"function\"\n ? resolved.sheet.getCalculatedValueAt(row, col)\n : null;\n const formatted = typeof resolved.sheet.getFormattedValueAt === \"function\"\n ? resolved.sheet.getFormattedValueAt(row, col)\n : calculated;\n if (mode === \"value\") {\n values.push(cellValueToNumber(calculated ?? formatted));\n } else {\n const display = cellValueToDisplay(formatted ?? calculated);\n const numeric = cellValueToNumber(calculated ?? formatted);\n values.push(display.length > 0 ? display : (numeric !== null ? numeric : null));\n }\n }\n }\n\n return values;\n}\n\nfunction resolveSeriesName(workbook: Workbook, fallbackSheetIndex: number, rawName: unknown) {\n if (typeof rawName !== \"string\" || !rawName) {\n return undefined;\n }\n\n const resolved = resolveReferenceSheet(workbook, fallbackSheetIndex, rawName);\n if (!resolved.sheet || !resolved.range) {\n return rawName;\n }\n\n const value = typeof resolved.sheet.getFormattedValueAt === \"function\"\n ? resolved.sheet.getFormattedValueAt(resolved.range.start.row, resolved.range.start.col)\n : null;\n const display = cellValueToDisplay(value);\n return display || rawName;\n}\n\nfunction normalizeChartReference(raw: unknown): XlsxChartReference | null {\n if (!raw || typeof raw !== \"object\") {\n return null;\n }\n\n const record = raw as Record<string, unknown>;\n const values = Array.isArray(record.numbers)\n ? record.numbers\n : Array.isArray(record.strings)\n ? record.strings\n : undefined;\n return {\n formula: typeof record.formula === \"string\" ? record.formula : undefined,\n refType: typeof record.refType === \"string\" ? record.refType : undefined,\n values: values as Array<number | string | null> | undefined\n };\n}\n\nfunction normalizeChartAxis(raw: unknown): XlsxChartAxis | null {\n if (!raw || typeof raw !== \"object\") {\n return null;\n }\n\n const rawAxis = raw as Record<string, unknown>;\n const axis = rawAxis.axis && typeof rawAxis.axis === \"object\"\n ? rawAxis.axis as Record<string, unknown>\n : rawAxis;\n const numberFormat = axis.numberFormat && typeof axis.numberFormat === \"object\"\n ? axis.numberFormat as Record<string, unknown>\n : null;\n\n return {\n crossId: typeof rawAxis.crossId === \"number\" && Number.isFinite(rawAxis.crossId) ? rawAxis.crossId : undefined,\n crosses: typeof axis.crosses === \"string\" ? axis.crosses : undefined,\n crossBetween: typeof axis.crossBetween === \"string\" ? axis.crossBetween : undefined,\n delete: typeof axis.delete === \"boolean\" ? axis.delete : undefined,\n id: typeof rawAxis.id === \"number\" && Number.isFinite(rawAxis.id) ? rawAxis.id : undefined,\n labelPosition: typeof axis.labelPosition === \"string\" ? axis.labelPosition : undefined,\n logBase: typeof axis.logBase === \"number\" ? axis.logBase : undefined,\n orientation: typeof axis.orientation === \"string\" ? axis.orientation : undefined,\n majorUnit: typeof axis.majorUnit === \"number\" ? axis.majorUnit : undefined,\n max: typeof axis.maximum === \"number\" ? axis.maximum : undefined,\n min: typeof axis.minimum === \"number\" ? axis.minimum : undefined,\n majorGridlines: typeof axis.majorGridlines === \"boolean\" ? axis.majorGridlines : undefined,\n majorTickMark: typeof axis.majorTickMark === \"string\" ? axis.majorTickMark : undefined,\n minorUnit: typeof axis.minorUnit === \"number\" ? axis.minorUnit : undefined,\n minorGridlines: typeof axis.minorGridlines === \"boolean\" ? axis.minorGridlines : undefined,\n minorTickMark: typeof axis.minorTickMark === \"string\" ? axis.minorTickMark : undefined,\n numberFormat: numberFormat ? {\n formatCode: typeof numberFormat.formatCode === \"string\" ? numberFormat.formatCode : undefined,\n sourceLinked: typeof numberFormat.sourceLinked === \"boolean\" ? numberFormat.sourceLinked : undefined\n } : undefined,\n position: typeof axis.position === \"string\" ? axis.position : undefined,\n raw: axis,\n shapeProperties: axis.shapeProperties && typeof axis.shapeProperties === \"object\"\n ? axis.shapeProperties as Record<string, unknown>\n : undefined,\n tickLabelSkip: typeof axis.tickLabelSkip === \"number\" && Number.isFinite(axis.tickLabelSkip) ? axis.tickLabelSkip : undefined,\n tickMarkSkip: typeof axis.tickMarkSkip === \"number\" && Number.isFinite(axis.tickMarkSkip) ? axis.tickMarkSkip : undefined\n };\n}\n\nfunction mergeChartAxis(target: XlsxChartAxis | null | undefined, patch: Partial<XlsxChartAxis> | null | undefined) {\n if (!patch) {\n return target ?? null;\n }\n return {\n ...(target ?? {}),\n ...patch\n };\n}\n\nfunction readChartAxisFromXml(axisNode: Element | null, themePalette?: XlsxThemePalette | null): Partial<XlsxChartAxis> | null {\n if (!axisNode) {\n return null;\n }\n\n const numFmt = getFirstLocalChild(axisNode, \"numFmt\");\n const scalingNode = getFirstLocalChild(axisNode, \"scaling\");\n return {\n crossId: readChartNumericAttribute(axisNode, \"crossAx\"),\n crosses: getFirstLocalChild(axisNode, \"crosses\")?.getAttribute(\"val\") ?? undefined,\n crossBetween: getFirstLocalChild(axisNode, \"crossBetween\")?.getAttribute(\"val\") ?? undefined,\n delete: getFirstLocalChild(axisNode, \"delete\")?.getAttribute(\"val\") === \"1\"\n ? true\n : getFirstLocalChild(axisNode, \"delete\")?.getAttribute(\"val\") === \"0\"\n ? false\n : undefined,\n id: readChartNumericAttribute(axisNode, \"axId\"),\n labelPosition: getFirstLocalChild(axisNode, \"tickLblPos\")?.getAttribute(\"val\") ?? undefined,\n logBase: readChartNumericAttribute(getFirstLocalChild(axisNode, \"scaling\"), \"logBase\"),\n orientation: getFirstLocalChild(scalingNode ?? axisNode, \"orientation\")?.getAttribute(\"val\") ?? undefined,\n majorGridlines: Boolean(getFirstLocalChild(axisNode, \"majorGridlines\")),\n majorGridlineColor: (() => {\n const style = resolveChartLineStyle(getFirstLocalChild(getFirstLocalChild(axisNode, \"majorGridlines\"), \"spPr\"), themePalette);\n return style.hidden ? \"transparent\" : style.color ?? undefined;\n })(),\n majorTickMark: getFirstLocalChild(axisNode, \"majorTickMark\")?.getAttribute(\"val\") ?? undefined,\n majorUnit: readChartNumericAttribute(axisNode, \"majorUnit\"),\n max: readChartNumericAttribute(scalingNode, \"max\"),\n min: readChartNumericAttribute(scalingNode, \"min\"),\n minorGridlines: Boolean(getFirstLocalChild(axisNode, \"minorGridlines\")),\n minorTickMark: getFirstLocalChild(axisNode, \"minorTickMark\")?.getAttribute(\"val\") ?? undefined,\n minorUnit: readChartNumericAttribute(axisNode, \"minorUnit\"),\n numberFormat: numFmt\n ? {\n formatCode: numFmt.getAttribute(\"formatCode\") ?? undefined,\n sourceLinked: numFmt.getAttribute(\"sourceLinked\") === \"1\"\n ? true\n : numFmt.getAttribute(\"sourceLinked\") === \"0\"\n ? false\n : undefined\n }\n : undefined,\n position: getFirstLocalChild(axisNode, \"axPos\")?.getAttribute(\"val\") ?? undefined,\n tickLabelSkip: readChartNumericAttribute(axisNode, \"tickLblSkip\"),\n tickMarkSkip: readChartNumericAttribute(axisNode, \"tickMarkSkip\")\n };\n}\n\nfunction readChartWallFromXml(wallNode: Element | null, themePalette?: XlsxThemePalette | null): XlsxChartWall | null {\n if (!wallNode) {\n return null;\n }\n const shapeProperties = getFirstLocalChild(wallNode, \"spPr\");\n const lineStyle = resolveChartLineStyle(shapeProperties, themePalette);\n return {\n fillColor: resolveChartFillColor(shapeProperties, themePalette) ?? undefined,\n hidden: shapeProperties ? getFirstLocalChild(shapeProperties, \"noFill\") != null : undefined,\n lineColor: lineStyle.color ?? undefined,\n thickness: readChartNumericAttribute(wallNode, \"thickness\")\n };\n}\n\nfunction normalizeChartDataLabels(raw: unknown): XlsxChartDataLabels | null {\n if (!raw || typeof raw !== \"object\") {\n return null;\n }\n\n const labels = raw as Record<string, unknown>;\n const pointLabels = Array.isArray(labels.pointLabels)\n ? (() => {\n const normalized: XlsxChartPointDataLabel[] = [];\n for (const entry of labels.pointLabels) {\n if (!entry || typeof entry !== \"object\") {\n continue;\n }\n const point = entry as Record<string, unknown>;\n const index = typeof point.index === \"number\" && Number.isFinite(point.index)\n ? point.index\n : null;\n if (index == null) {\n continue;\n }\n\n const nextPoint: XlsxChartPointDataLabel = { index };\n if (typeof point.deleted === \"boolean\") {\n nextPoint.deleted = point.deleted;\n }\n if (typeof point.fontSizePt === \"number\" && Number.isFinite(point.fontSizePt)) {\n nextPoint.fontSizePt = point.fontSizePt;\n }\n if (typeof point.showBubbleSize === \"boolean\") {\n nextPoint.showBubbleSize = point.showBubbleSize;\n }\n if (typeof point.showCategoryName === \"boolean\") {\n nextPoint.showCategoryName = point.showCategoryName;\n }\n if (typeof point.showPercent === \"boolean\") {\n nextPoint.showPercent = point.showPercent;\n }\n if (typeof point.showSeriesName === \"boolean\") {\n nextPoint.showSeriesName = point.showSeriesName;\n }\n if (typeof point.showValue === \"boolean\") {\n nextPoint.showValue = point.showValue;\n }\n if (typeof point.x === \"number\" && Number.isFinite(point.x)) {\n nextPoint.x = point.x;\n }\n if (typeof point.y === \"number\" && Number.isFinite(point.y)) {\n nextPoint.y = point.y;\n }\n normalized.push(nextPoint);\n }\n return normalized;\n })()\n : undefined;\n return {\n pointLabels: pointLabels && pointLabels.length > 0 ? pointLabels : undefined,\n raw: labels,\n showBubbleSize: typeof labels.showBubbleSize === \"boolean\" ? labels.showBubbleSize : undefined,\n showCategoryName: typeof (labels.showCategoryName ?? labels.visibilityCategoryName) === \"boolean\"\n ? Boolean(labels.showCategoryName ?? labels.visibilityCategoryName)\n : undefined,\n showLegendKey: typeof labels.showLegendKey === \"boolean\" ? labels.showLegendKey : undefined,\n showPercent: typeof labels.showPercent === \"boolean\" ? labels.showPercent : undefined,\n showSeriesName: typeof (labels.showSeriesName ?? labels.visibilitySeriesName) === \"boolean\"\n ? Boolean(labels.showSeriesName ?? labels.visibilitySeriesName)\n : undefined,\n showValue: typeof (labels.showValue ?? labels.visibilityValue) === \"boolean\"\n ? Boolean(labels.showValue ?? labels.visibilityValue)\n : undefined\n };\n}\n\nfunction normalizeChartAnchor(raw: unknown): XlsxImageAnchor {\n if (!raw || typeof raw !== \"object\") {\n return {\n kind: \"two-cell\",\n from: { col: 0, colOffsetEmu: 0, row: 0, rowOffsetEmu: 0 },\n to: { col: 8, colOffsetEmu: 0, row: 15, rowOffsetEmu: 0 }\n };\n }\n\n const anchor = raw as Record<string, unknown>;\n const from = anchor.from && typeof anchor.from === \"object\"\n ? anchor.from as Record<string, unknown>\n : null;\n\n if (anchor.type === \"oneCell\") {\n return {\n from: {\n col: typeof from?.col === \"number\" ? from.col : 0,\n colOffsetEmu: typeof from?.colOffsetEmu === \"number\" ? from.colOffsetEmu : 0,\n row: typeof from?.row === \"number\" ? from.row : 0,\n rowOffsetEmu: typeof from?.rowOffsetEmu === \"number\" ? from.rowOffsetEmu : 0\n },\n kind: \"one-cell\",\n sizeEmu: {\n cx: typeof anchor.widthEmu === \"number\" ? anchor.widthEmu : 0,\n cy: typeof anchor.heightEmu === \"number\" ? anchor.heightEmu : 0\n }\n };\n }\n\n if (anchor.type === \"absolute\") {\n return {\n kind: \"absolute\",\n positionEmu: {\n x: typeof anchor.xEmu === \"number\" ? anchor.xEmu : 0,\n y: typeof anchor.yEmu === \"number\" ? anchor.yEmu : 0\n },\n sizeEmu: {\n cx: typeof anchor.widthEmu === \"number\" ? anchor.widthEmu : 0,\n cy: typeof anchor.heightEmu === \"number\" ? anchor.heightEmu : 0\n }\n };\n }\n\n const to = anchor.to && typeof anchor.to === \"object\"\n ? anchor.to as Record<string, unknown>\n : null;\n const fromColValue = from?.col;\n const fromColOffsetValue = from?.colOffsetEmu;\n const fromRowValue = from?.row;\n const fromRowOffsetValue = from?.rowOffsetEmu;\n const toColValue = to?.col;\n const toColOffsetValue = to?.colOffsetEmu;\n const toRowValue = to?.row;\n const toRowOffsetValue = to?.rowOffsetEmu;\n const fromCol = typeof fromColValue === \"number\" ? fromColValue : 0;\n const fromColOffsetEmu = typeof fromColOffsetValue === \"number\" ? fromColOffsetValue : 0;\n const fromRow = typeof fromRowValue === \"number\" ? fromRowValue : 0;\n const fromRowOffsetEmu = typeof fromRowOffsetValue === \"number\" ? fromRowOffsetValue : 0;\n const rawToCol = typeof toColValue === \"number\" ? toColValue : 0;\n const rawToColOffsetEmu = typeof toColOffsetValue === \"number\" ? toColOffsetValue : 0;\n const rawToRow = typeof toRowValue === \"number\" ? toRowValue : 0;\n const rawToRowOffsetEmu = typeof toRowOffsetValue === \"number\" ? toRowOffsetValue : 0;\n return {\n kind: \"two-cell\",\n from: {\n col: fromCol,\n colOffsetEmu: fromColOffsetEmu,\n row: fromRow,\n rowOffsetEmu: fromRowOffsetEmu\n },\n to: {\n col: rawToCol,\n colOffsetEmu: rawToColOffsetEmu,\n row: rawToRow,\n rowOffsetEmu: rawToRowOffsetEmu\n }\n };\n}\n\nfunction parseMarkerNode(node: Element | null) {\n if (!node) {\n return null;\n }\n\n const col = Number(getFirstLocalChild(node, \"col\")?.textContent ?? Number.NaN);\n const row = Number(getFirstLocalChild(node, \"row\")?.textContent ?? Number.NaN);\n const colOffsetEmu = Number(getFirstLocalChild(node, \"colOff\")?.textContent ?? 0);\n const rowOffsetEmu = Number(getFirstLocalChild(node, \"rowOff\")?.textContent ?? 0);\n\n if (!Number.isFinite(col) || !Number.isFinite(row)) {\n return null;\n }\n\n return {\n col: Math.max(0, Math.round(col)),\n colOffsetEmu: Number.isFinite(colOffsetEmu) ? Math.max(0, Math.round(colOffsetEmu)) : 0,\n row: Math.max(0, Math.round(row)),\n rowOffsetEmu: Number.isFinite(rowOffsetEmu) ? Math.max(0, Math.round(rowOffsetEmu)) : 0\n };\n}\n\nfunction parseChartAnchorNode(anchorNode: Element): XlsxImageAnchor | null {\n if (anchorNode.localName === \"twoCellAnchor\") {\n const from = parseMarkerNode(getFirstLocalChild(anchorNode, \"from\"));\n const to = parseMarkerNode(getFirstLocalChild(anchorNode, \"to\"));\n return from && to ? { from, kind: \"two-cell\", to } : null;\n }\n\n if (anchorNode.localName === \"oneCellAnchor\") {\n const from = parseMarkerNode(getFirstLocalChild(anchorNode, \"from\"));\n const ext = getFirstLocalChild(anchorNode, \"ext\");\n const cx = Number(ext?.getAttribute(\"cx\") ?? Number.NaN);\n const cy = Number(ext?.getAttribute(\"cy\") ?? Number.NaN);\n return from && Number.isFinite(cx) && Number.isFinite(cy)\n ? {\n from,\n kind: \"one-cell\",\n sizeEmu: {\n cx: Math.max(0, Math.round(cx)),\n cy: Math.max(0, Math.round(cy))\n }\n }\n : null;\n }\n\n const pos = getFirstLocalChild(anchorNode, \"pos\");\n const ext = getFirstLocalChild(anchorNode, \"ext\");\n const x = Number(pos?.getAttribute(\"x\") ?? Number.NaN);\n const y = Number(pos?.getAttribute(\"y\") ?? Number.NaN);\n const cx = Number(ext?.getAttribute(\"cx\") ?? Number.NaN);\n const cy = Number(ext?.getAttribute(\"cy\") ?? Number.NaN);\n return Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(cx) && Number.isFinite(cy)\n ? {\n kind: \"absolute\",\n positionEmu: {\n x: Math.round(x),\n y: Math.round(y)\n },\n sizeEmu: {\n cx: Math.max(0, Math.round(cx)),\n cy: Math.max(0, Math.round(cy))\n }\n }\n : null;\n}\n\nfunction isCollapsedChartAnchor(anchor: XlsxImageAnchor) {\n if (anchor.kind !== \"two-cell\") {\n return false;\n }\n\n const collapsedWidth = anchor.to.col < anchor.from.col\n || (anchor.to.col === anchor.from.col && anchor.to.colOffsetEmu <= anchor.from.colOffsetEmu);\n const collapsedHeight = anchor.to.row < anchor.from.row\n || (anchor.to.row === anchor.from.row && anchor.to.rowOffsetEmu <= anchor.from.rowOffsetEmu);\n return collapsedWidth || collapsedHeight;\n}\n\nfunction normalizeChartSeries(\n workbook: Workbook,\n workbookSheetIndex: number,\n chartId: string,\n raw: unknown,\n index: number\n): XlsxChartSeries {\n const series = raw && typeof raw === \"object\" ? raw as Record<string, unknown> : {};\n const categoriesRef = normalizeChartReference(series.categories);\n const valuesRef = normalizeChartReference(series.values);\n const shapeProperties = series.shapeProperties && typeof series.shapeProperties === \"object\"\n ? series.shapeProperties as Record<string, unknown>\n : undefined;\n const rawFillColor = typeof shapeProperties?.solidFillHex === \"string\"\n ? normalizeHexColor(shapeProperties.solidFillHex)\n : null;\n const rawLineColor = typeof shapeProperties?.lineColorHex === \"string\"\n ? normalizeHexColor(shapeProperties.lineColorHex)\n : null;\n const bubbleSizeRef = normalizeChartReference(series.bubbleSize ?? series.bubbleSizes ?? series.bubbles);\n\n return {\n bubbleSizeRef,\n bubbleSizes: resolveReferenceValues(workbook, workbookSheetIndex, bubbleSizeRef, \"value\").map((value) => (\n typeof value === \"number\" && Number.isFinite(value) ? value : null\n )),\n categories: resolveReferenceValues(workbook, workbookSheetIndex, categoriesRef, \"category\"),\n categoriesRef,\n color: rawFillColor ?? undefined,\n dataPoints: Array.isArray(series.dataPoints) ? series.dataPoints : [],\n dataPointStyles: undefined,\n id: `${chartId}-series-${index}`,\n invertIfNegative: typeof series.invertIfNegative === \"boolean\" ? series.invertIfNegative : undefined,\n lineColor: rawLineColor ?? rawFillColor ?? undefined,\n lineWidthPx: typeof shapeProperties?.lineWidth === \"number\"\n ? Math.max(1, Number(shapeProperties.lineWidth) / EMU_PER_PIXEL)\n : undefined,\n marker: series.marker && typeof series.marker === \"object\" ? series.marker as Record<string, unknown> : undefined,\n markerColor: undefined,\n markerLineColor: undefined,\n markerSize: series.marker && typeof series.marker === \"object\" && typeof (series.marker as Record<string, unknown>).size === \"number\"\n ? Number((series.marker as Record<string, unknown>).size)\n : undefined,\n markerSymbol: series.marker && typeof series.marker === \"object\" && typeof (series.marker as Record<string, unknown>).symbol === \"string\"\n ? String((series.marker as Record<string, unknown>).symbol)\n : undefined,\n name: resolveSeriesName(workbook, workbookSheetIndex, series.name),\n negativeColor: undefined,\n negativeLineColor: undefined,\n raw: {\n ...series,\n unresolvedCategoriesReference: Boolean(categoriesRef?.formula && !resolveReferenceSheet(workbook, workbookSheetIndex, categoriesRef.formula).sheet),\n unresolvedValuesReference: Boolean(valuesRef?.formula && !resolveReferenceSheet(workbook, workbookSheetIndex, valuesRef.formula).sheet),\n unresolvedBubbleSizeReference: Boolean(bubbleSizeRef?.formula && !resolveReferenceSheet(workbook, workbookSheetIndex, bubbleSizeRef.formula).sheet),\n unresolvedNameReference: typeof series.name === \"string\" && series.name.includes(\"!\")\n && !resolveReferenceSheet(workbook, workbookSheetIndex, series.name).sheet\n },\n shapeProperties,\n smooth: typeof series.smooth === \"boolean\" ? series.smooth : undefined,\n values: resolveReferenceValues(workbook, workbookSheetIndex, valuesRef, \"value\").map((value) => (\n typeof value === \"number\" && Number.isFinite(value) ? value : null\n )),\n valuesRef\n };\n}\n\nfunction normalizeChartTypeGroup(\n workbook: Workbook,\n workbookSheetIndex: number,\n chartId: string,\n raw: unknown,\n index: number\n): XlsxChartTypeGroup | null {\n if (!raw || typeof raw !== \"object\") {\n return null;\n }\n\n const group = raw as Record<string, unknown>;\n const rawSeries = Array.isArray(group.series) ? group.series : [];\n return {\n axisIds: Array.isArray(group.axisIds)\n ? group.axisIds.filter((value): value is number => typeof value === \"number\" && Number.isFinite(value))\n : undefined,\n chartType: typeof group.chartType === \"string\" ? group.chartType : \"ColumnClustered\",\n dataLabels: normalizeChartDataLabels(group.dataLabels),\n gapWidth: typeof group.gapWidth === \"number\" && Number.isFinite(group.gapWidth) ? group.gapWidth : undefined,\n is3d: typeof group.is3D === \"boolean\" ? group.is3D : undefined,\n overlap: typeof group.overlap === \"number\" && Number.isFinite(group.overlap) ? group.overlap : undefined,\n raw: group,\n series: rawSeries.map((entry, seriesIndex) => (\n normalizeChartSeries(workbook, workbookSheetIndex, `${chartId}-group-${index}`, entry, seriesIndex)\n )),\n varyColors: typeof group.varyColors === \"boolean\" ? group.varyColors : undefined\n };\n}\n\nfunction normalizeChartsheet(raw: unknown, index: number): XlsxChartsheet {\n const chartsheet = raw && typeof raw === \"object\" ? raw as Record<string, unknown> : {};\n return {\n chartIds: Array.isArray(chartsheet.chartIds) ? chartsheet.chartIds.filter((value): value is string => typeof value === \"string\") : [],\n chartPath: typeof chartsheet.chartPath === \"string\" ? chartsheet.chartPath : undefined,\n id: `chartsheet-${index}`,\n index,\n name: typeof chartsheet.name === \"string\" ? chartsheet.name : `Chart ${index + 1}`,\n raw: chartsheet,\n workbookSheetIndex: typeof chartsheet.workbookSheetIndex === \"number\" ? chartsheet.workbookSheetIndex : undefined\n };\n}\n\nfunction buildTabs(\n workbook: Workbook,\n chartsheets: XlsxChartsheet[],\n visibleSheetIndexByWorkbookSheetIndex: Map<number, number>,\n showHiddenSheets = false\n): XlsxWorkbookTab[] {\n const rawOrder = Array.isArray(workbook.sheetOrder) ? workbook.sheetOrder as Array<Record<string, unknown>> : [];\n if (rawOrder.length === 0) {\n return workbook.sheetNames.flatMap((name, index) => {\n const worksheet = workbook.getSheet(index);\n const visibility = normalizeWorksheetVisibility(worksheet.visibility);\n if (!showHiddenSheets && visibility !== \"visible\") {\n return [];\n }\n\n return [{\n id: `sheet-${index}`,\n index,\n kind: \"sheet\" as const,\n name,\n sheetIndex: visibleSheetIndexByWorkbookSheetIndex.get(index) ?? index,\n visibility,\n workbookSheetIndex: index\n }];\n });\n }\n\n return rawOrder.flatMap<XlsxWorkbookTab>((entry, index) => {\n const slotType = typeof entry.slotType === \"string\" ? entry.slotType : \"worksheet\";\n const slotIndex = typeof entry.index === \"number\" ? entry.index : index;\n if (slotType === \"chartsheet\") {\n const chartsheet = chartsheets[slotIndex];\n const visibility = normalizeWorksheetVisibility(chartsheet?.raw?.visibility);\n if (!showHiddenSheets && visibility !== \"visible\") return [];\n return chartsheet ? [{\n chartsheetIndex: slotIndex,\n id: `chartsheet-${slotIndex}`,\n index,\n kind: \"chartsheet\" as const,\n name: chartsheet.name,\n visibility\n }] : [];\n }\n\n const worksheet = workbook.getSheet(slotIndex);\n const visibility = normalizeWorksheetVisibility(worksheet.visibility);\n if (!showHiddenSheets && visibility !== \"visible\") {\n return [];\n }\n\n return [{\n id: `sheet-${slotIndex}`,\n index,\n kind: \"sheet\" as const,\n name: worksheet.name,\n sheetIndex: visibleSheetIndexByWorkbookSheetIndex.get(slotIndex) ?? slotIndex,\n visibility,\n workbookSheetIndex: slotIndex\n }];\n });\n}\n\nfunction collectChartOriginsForSheet(\n archive: Record<string, Uint8Array>,\n origin: WorkbookImageSheetOrigin | null\n) {\n if (!origin) {\n return [] as WorkbookChartOrigin[];\n }\n\n const chartOrigins: WorkbookChartOrigin[] = [];\n\n for (const attachment of origin.attachments) {\n const drawingXml = readArchiveText(archive, attachment.drawingPath);\n const relsXml = readArchiveText(archive, attachment.drawingRelsPath);\n if (!drawingXml || !relsXml) {\n continue;\n }\n\n const drawingDocument = parseXml(drawingXml);\n const relsDocument = parseXml(relsXml);\n if (!drawingDocument || !relsDocument) {\n continue;\n }\n\n const relationships = new Map<string, { target: string; type: string | null }>();\n for (const node of getLocalDescendants(relsDocument, \"Relationship\")) {\n const id = node.getAttribute(\"Id\");\n const target = node.getAttribute(\"Target\");\n const type = node.getAttribute(\"Type\");\n if (id && target) {\n relationships.set(id, {\n target: resolveRelationshipPath(attachment.drawingRelsPath ?? attachment.drawingPath, target),\n type\n });\n }\n }\n\n const anchorNodes = Array.from(drawingDocument.documentElement.childNodes).filter(\n (node): node is Element => (\n node.nodeType === Node.ELEMENT_NODE\n && (\n (node as Element).localName === \"twoCellAnchor\"\n || (node as Element).localName === \"oneCellAnchor\"\n || (node as Element).localName === \"absoluteAnchor\"\n )\n )\n );\n\n let chartAnchorIndex = 0;\n for (const anchorNode of anchorNodes) {\n const graphicFrame = getFirstLocalDescendant(anchorNode, \"graphicFrame\");\n const chartNode = graphicFrame ? getFirstLocalDescendant(graphicFrame, \"chart\") : null;\n const relationshipId = chartNode?.getAttributeNS(\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\", \"id\")\n ?? chartNode?.getAttribute(\"r:id\")\n ?? chartNode?.getAttribute(\"id\");\n if (!relationshipId) {\n continue;\n }\n const relationship = relationships.get(relationshipId);\n if (!relationship || (relationship.type !== CHART_REL_TYPE && relationship.type !== CHART_EX_REL_TYPE)) {\n continue;\n }\n\n chartOrigins.push({\n anchorIndex: chartAnchorIndex,\n anchor: parseChartAnchorNode(anchorNode),\n chartKind: relationship.type === CHART_EX_REL_TYPE ? \"modern\" : \"classic\",\n chartPath: relationship.target,\n drawingPath: attachment.drawingPath,\n workbookSheetIndex: origin.workbookSheetIndex\n });\n chartAnchorIndex += 1;\n }\n }\n\n return chartOrigins;\n}\n\nfunction applyChartOrigins(\n chartsByWorkbookSheetIndex: XlsxChart[][],\n chartOriginsById: Map<string, WorkbookChartOrigin>,\n archive: Record<string, Uint8Array>,\n sheetOrigins: Array<WorkbookImageSheetOrigin | null>\n) {\n for (let workbookSheetIndex = 0; workbookSheetIndex < chartsByWorkbookSheetIndex.length; workbookSheetIndex += 1) {\n const charts = chartsByWorkbookSheetIndex[workbookSheetIndex] ?? [];\n const origins = collectChartOriginsForSheet(archive, sheetOrigins[workbookSheetIndex] ?? null);\n const originsByKind = {\n classic: origins.filter((origin) => origin.chartKind === \"classic\"),\n modern: origins.filter((origin) => origin.chartKind === \"modern\")\n };\n const chartIndexByKind = {\n classic: 0,\n modern: 0\n };\n\n charts.forEach((chart) => {\n const chartKind = chart.id.startsWith(\"chart-ex-\") ? \"modern\" : \"classic\";\n const origin = originsByKind[chartKind][chartIndexByKind[chartKind]];\n chartIndexByKind[chartKind] += 1;\n if (!origin) {\n return;\n }\n if (origin.anchor && isCollapsedChartAnchor(chart.anchor)) {\n chart.anchor = origin.anchor;\n } else if (origin.anchor && chart.anchor.kind === \"two-cell\" && chart.anchor.from.col === 0 && chart.anchor.from.row === 0) {\n chart.anchor = origin.anchor;\n }\n chart.chartPath = origin.chartPath ?? undefined;\n chartOriginsById.set(chart.id, origin);\n });\n }\n}\n\nexport function hydrateWorkbookChartStyles(\n chartsByWorkbookSheetIndex: XlsxChart[][],\n imageAssets: Pick<WorkbookImageAssets, \"archive\" | \"sheetOrigins\" | \"themePalette\">,\n chartsheets: XlsxChartsheet[] = []\n) {\n const chartOriginsById = new Map<string, WorkbookChartOrigin>();\n applyChartOrigins(chartsByWorkbookSheetIndex, chartOriginsById, imageAssets.archive, imageAssets.sheetOrigins);\n const chartsheetOrigins = collectChartsheetDrawingOrigins(imageAssets.archive, chartsheets);\n for (const chartsheet of chartsheets) {\n if (chartsheet.workbookSheetIndex == null) continue;\n const charts = chartsByWorkbookSheetIndex[chartsheet.workbookSheetIndex] ?? [];\n const origins = collectChartOriginsForSheet(imageAssets.archive, chartsheetOrigins.get(chartsheet.index) ?? null);\n for (const chart of charts) {\n const kind = chart.id.startsWith(\"chart-ex-\") ? \"modern\" : \"classic\";\n const origin = origins.find((entry) => entry.chartKind === kind);\n if (!origin) continue;\n if (origin.anchor) chart.anchor = origin.anchor;\n chart.chartPath = origin.chartPath ?? undefined;\n chartsheet.chartPath = chart.chartPath;\n chartOriginsById.set(chart.id, origin);\n }\n }\n for (const charts of chartsByWorkbookSheetIndex) {\n for (const chart of charts) {\n applyChartStyleFromXml(chart, chart.chartPath, imageAssets.archive, imageAssets.themePalette);\n applyBuiltinChartDefaults(chart, imageAssets.themePalette);\n }\n }\n return chartOriginsById;\n}\n\nfunction normalizeClassicChartDrawing(\n workbook: Workbook,\n workbookSheetIndex: number,\n visibleSheetIndex: number,\n rawChart: unknown,\n chartIndex: number,\n chartId = `chart-${workbookSheetIndex}-${chartIndex}`\n): XlsxChart {\n const drawing = rawChart && typeof rawChart === \"object\" ? rawChart as Record<string, unknown> : {};\n const chart = drawing.chart && typeof drawing.chart === \"object\"\n ? drawing.chart as Record<string, unknown>\n : {};\n const rawView3d = chart.view3D && typeof chart.view3D === \"object\"\n ? chart.view3D as Record<string, unknown>\n : null;\n const rawSeries = Array.isArray(chart.series) ? chart.series : [];\n const typeGroups = Array.isArray(chart.typeGroups)\n ? chart.typeGroups\n .map((entry, groupIndex) => normalizeChartTypeGroup(workbook, workbookSheetIndex, chartId, entry, groupIndex))\n .filter((value): value is XlsxChartTypeGroup => value != null)\n : [];\n let seriesOrdinal = 0;\n const series = typeGroups.some((group) => group.series.length > 0)\n ? typeGroups.flatMap((group) => {\n group.series = group.series.map((entry) => ({ ...entry, id: `${chartId}-series-${seriesOrdinal++}` }));\n return group.series;\n })\n : rawSeries.map((entry, seriesIndex) => normalizeChartSeries(workbook, workbookSheetIndex, chartId, entry, seriesIndex));\n const chartLevelDataLabels = normalizeChartDataLabels(chart.dataLabels);\n const firstSeriesDataLabels = rawSeries.length > 0 && rawSeries[0] && typeof rawSeries[0] === \"object\"\n ? normalizeChartDataLabels((rawSeries[0] as Record<string, unknown>).dataLabels)\n : null;\n return {\n anchor: normalizeChartAnchor(drawing.anchor),\n autoTitleDeleted: typeof chart.autoTitleDeleted === \"boolean\" ? chart.autoTitleDeleted : undefined,\n axes: Array.isArray(chart.axes) ? chart.axes.map(normalizeChartAxis).filter((value): value is XlsxChartAxis => Boolean(value)) : [],\n axisLabelColor: undefined,\n axisLineColor: undefined,\n categoryAxis: normalizeChartAxis(chart.categoryAxis),\n chartAreaBorderColor: undefined,\n chartAreaFillColor: undefined,\n chartColorPalette: undefined,\n chartColorPaletteOffset: undefined,\n chartPath: undefined,\n chartStyleId: undefined,\n chartType: typeof chart.chartType === \"string\" ? chart.chartType : \"ColumnClustered\",\n dataLabels: chartLevelDataLabels ?? firstSeriesDataLabels,\n displayBlanksAs: typeof chart.displayBlanksAs === \"string\" ? chart.displayBlanksAs : undefined,\n editable: true,\n firstSliceAngle: typeof chart.firstSliceAngle === \"number\" ? chart.firstSliceAngle : undefined,\n fontFamily: undefined,\n gapWidth: typeof chart.gapWidth === \"number\" ? chart.gapWidth : undefined,\n holeSize: typeof chart.holeSize === \"number\" ? chart.holeSize : undefined,\n id: chartId,\n is3d: typeof chart.is3D === \"boolean\" ? chart.is3D : typeof chart.is3d === \"boolean\" ? chart.is3d : undefined,\n legend: normalizeLegend(chart.legend)\n ? {\n ...normalizeLegend(chart.legend),\n position: normalizeLegendPosition(normalizeLegend(chart.legend)?.position)\n }\n : null,\n name: typeof drawing.name === \"string\" ? drawing.name : undefined,\n overlap: typeof chart.overlap === \"number\" ? chart.overlap : undefined,\n plotVisibleOnly: typeof chart.plotVisibleOnly === \"boolean\" ? chart.plotVisibleOnly : undefined,\n raw: chart,\n radarStyle: typeof chart.radarStyle === \"string\" ? chart.radarStyle : undefined,\n scatterStyle: typeof chart.scatterStyle === \"string\" ? chart.scatterStyle : undefined,\n roundedCorners: typeof chart.roundedCorners === \"boolean\" ? chart.roundedCorners : undefined,\n shape3d: typeof chart.shape === \"string\"\n ? chart.shape\n : typeof chart.shape3d === \"string\"\n ? chart.shape3d\n : undefined,\n seriesAxis: null,\n series,\n sheetIndex: visibleSheetIndex,\n showDlblsOverMax: typeof chart.showDlblsOverMax === \"boolean\" ? chart.showDlblsOverMax : undefined,\n sideWall: null,\n backWall: null,\n bubbleScale: typeof chart.bubbleScale === \"number\" ? chart.bubbleScale : undefined,\n bubble3d: typeof chart.bubble3d === \"boolean\" ? chart.bubble3d : undefined,\n floor: null,\n surfaceMaterial: undefined,\n textColor: undefined,\n title: typeof chart.title === \"string\" ? chart.title : undefined,\n titleColor: undefined,\n titleFontFamily: undefined,\n typeGroups,\n valueAxis: normalizeChartAxis(chart.valueAxis),\n varyColors: typeof chart.varyColors === \"boolean\" ? chart.varyColors : undefined,\n view3d: rawView3d\n ? {\n depthPercent: typeof rawView3d.depthPercent === \"number\" ? rawView3d.depthPercent : undefined,\n heightPercent: typeof rawView3d.heightPercent === \"number\" ? rawView3d.heightPercent : undefined,\n perspective: typeof rawView3d.perspective === \"number\" ? rawView3d.perspective : undefined,\n rAngAx: typeof rawView3d.rAngAx === \"boolean\"\n ? rawView3d.rAngAx\n : typeof rawView3d.rightAngleAxes === \"boolean\"\n ? rawView3d.rightAngleAxes\n : undefined,\n rotX: typeof rawView3d.rotX === \"number\"\n ? rawView3d.rotX\n : typeof rawView3d.rotateX === \"number\"\n ? rawView3d.rotateX\n : undefined,\n rotY: typeof rawView3d.rotY === \"number\"\n ? rawView3d.rotY\n : typeof rawView3d.rotateY === \"number\"\n ? rawView3d.rotateY\n : undefined\n }\n : undefined,\n wireframe: typeof chart.wireframe === \"boolean\" ? chart.wireframe : undefined,\n workbookSheetIndex,\n zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === \"number\"\n ? drawing.drawingPath[0] + 1\n : chartIndex + 1\n } satisfies XlsxChart;\n}\n\nexport function loadWorkbookChartAssets(\n workbook: Workbook,\n imageAssets: Pick<WorkbookImageAssets, \"archive\" | \"sheetOrigins\" | \"themePalette\"> | null,\n visibleSheetIndexByWorkbookSheetIndex: Map<number, number>,\n showHiddenSheets = false\n): WorkbookChartAssets {\n const excludedChartIds = new Set<string>();\n const chartsByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {\n const worksheet = workbook.getSheet(workbookSheetIndex);\n const rawCharts = worksheet.charts;\n const rawChartsEx = worksheet.chartsEx;\n const visibleSheetIndex = visibleSheetIndexByWorkbookSheetIndex.get(workbookSheetIndex) ?? workbookSheetIndex;\n\n const classicCharts = rawCharts.map((rawChart, chartIndex) => {\n const chartId = `chart-${workbookSheetIndex}-${chartIndex}`;\n if (rawChart.hidden || !rawChart.anchor) {\n excludedChartIds.add(chartId);\n }\n return normalizeClassicChartDrawing(workbook, workbookSheetIndex, visibleSheetIndex, rawChart, chartIndex, chartId);\n });\n\n const modernCharts = rawChartsEx.map((rawChartEx, chartExIndex) => {\n const chartId = `chart-ex-${workbookSheetIndex}-${chartExIndex}`;\n if (rawChartEx.hidden || !rawChartEx.anchor) {\n excludedChartIds.add(chartId);\n }\n return normalizeChartExChart(\n workbook,\n workbookSheetIndex,\n visibleSheetIndex,\n rawChartEx,\n chartExIndex,\n imageAssets?.themePalette ?? null\n );\n });\n\n return [...classicCharts, ...modernCharts];\n });\n\n const chartsheets = Array.isArray(workbook.chartsheets)\n ? workbook.chartsheets.map((entry, index) => normalizeChartsheet(entry, index))\n : [];\n for (const chartsheet of chartsheets) {\n const workbookSheetIndex = workbook.sheetCount + chartsheet.index;\n const raw = chartsheet.raw;\n const charts: XlsxChart[] = [];\n if (raw?.chart && typeof raw.chart === \"object\") {\n charts.push(normalizeClassicChartDrawing(\n workbook, workbookSheetIndex, workbookSheetIndex, raw, 0, `${chartsheet.id}-chart-0`\n ));\n }\n if (raw?.chartEx && typeof raw.chartEx === \"object\") {\n charts.push(normalizeChartExChart(\n workbook, workbookSheetIndex, workbookSheetIndex, raw, 0, imageAssets?.themePalette ?? null\n ));\n }\n chartsByWorkbookSheetIndex[workbookSheetIndex] = charts;\n chartsheet.chartIds = charts.map((chart) => chart.id);\n chartsheet.workbookSheetIndex = workbookSheetIndex;\n }\n const tabs = buildTabs(workbook, chartsheets, visibleSheetIndexByWorkbookSheetIndex, showHiddenSheets);\n const chartOriginsById = imageAssets\n ? hydrateWorkbookChartStyles(chartsByWorkbookSheetIndex, imageAssets, chartsheets)\n : new Map<string, WorkbookChartOrigin>();\n\n if (imageAssets) {\n for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {\n chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? [])\n .filter((chart) => !excludedChartIds.has(chart.id));\n }\n for (const id of excludedChartIds) {\n chartOriginsById.delete(id);\n }\n } else {\n for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {\n chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? [])\n .filter((chart) => !excludedChartIds.has(chart.id));\n }\n for (const charts of chartsByWorkbookSheetIndex) {\n for (const chart of charts) {\n applyBuiltinChartDefaults(chart, null);\n }\n }\n }\n\n return {\n chartOriginsById,\n chartsByWorkbookSheetIndex,\n chartsheets,\n tabs\n };\n}\n\nfunction getChartAnchorNodes(drawingDocument: XMLDocument) {\n return Array.from(drawingDocument.documentElement.childNodes).filter(\n (node): node is Element => (\n node.nodeType === Node.ELEMENT_NODE\n && (\n (node as Element).localName === \"twoCellAnchor\"\n || (node as Element).localName === \"oneCellAnchor\"\n || (node as Element).localName === \"absoluteAnchor\"\n )\n )\n )\n .filter((anchorNode) => {\n const graphicFrame = getFirstLocalChild(anchorNode, \"graphicFrame\");\n return Boolean(graphicFrame && getFirstLocalDescendant(graphicFrame, \"chart\"));\n });\n}\n\nfunction updateMarkerNode(markerNode: Element | null, marker: { col: number; colOffsetEmu: number; row: number; rowOffsetEmu: number }) {\n if (!markerNode) {\n return;\n }\n\n setLeafValue(markerNode, \"col\", String(Math.max(0, Math.round(marker.col))));\n setLeafValue(markerNode, \"colOff\", String(Math.max(0, Math.round(marker.colOffsetEmu))));\n setLeafValue(markerNode, \"row\", String(Math.max(0, Math.round(marker.row))));\n setLeafValue(markerNode, \"rowOff\", String(Math.max(0, Math.round(marker.rowOffsetEmu))));\n}\n\nfunction updateAnchorNode(anchorNode: Element, anchor: XlsxImageAnchor) {\n if (anchor.kind === \"two-cell\") {\n updateMarkerNode(getFirstLocalChild(anchorNode, \"from\"), anchor.from);\n updateMarkerNode(getFirstLocalChild(anchorNode, \"to\"), anchor.to);\n return;\n }\n\n if (anchor.kind === \"one-cell\") {\n updateMarkerNode(getFirstLocalChild(anchorNode, \"from\"), anchor.from);\n const ext = getFirstLocalChild(anchorNode, \"ext\");\n if (ext) {\n ext.setAttribute(\"cx\", String(Math.max(0, Math.round(anchor.sizeEmu.cx))));\n ext.setAttribute(\"cy\", String(Math.max(0, Math.round(anchor.sizeEmu.cy))));\n }\n return;\n }\n\n const pos = getFirstLocalChild(anchorNode, \"pos\");\n if (pos) {\n pos.setAttribute(\"x\", String(Math.max(0, Math.round(anchor.positionEmu.x))));\n pos.setAttribute(\"y\", String(Math.max(0, Math.round(anchor.positionEmu.y))));\n }\n const ext = getFirstLocalChild(anchorNode, \"ext\");\n if (ext) {\n ext.setAttribute(\"cx\", String(Math.max(0, Math.round(anchor.sizeEmu.cx))));\n ext.setAttribute(\"cy\", String(Math.max(0, Math.round(anchor.sizeEmu.cy))));\n }\n}\n\nfunction setChartTitle(chartNode: Element, value: string | undefined) {\n const existing = getFirstLocalChild(chartNode, \"title\");\n if (!value) {\n existing?.remove();\n return;\n }\n\n const titleNode = existing ?? chartNode.insertBefore(\n chartNode.ownerDocument.createElementNS(CHART_NS, \"c:title\"),\n chartNode.firstChild\n );\n while (titleNode.firstChild) {\n titleNode.removeChild(titleNode.firstChild);\n }\n const tx = titleNode.ownerDocument.createElementNS(CHART_NS, \"c:tx\");\n const rich = titleNode.ownerDocument.createElementNS(CHART_NS, \"c:rich\");\n const bodyPr = titleNode.ownerDocument.createElementNS(DRAWINGML_NS, \"a:bodyPr\");\n const lstStyle = titleNode.ownerDocument.createElementNS(DRAWINGML_NS, \"a:lstStyle\");\n const p = titleNode.ownerDocument.createElementNS(DRAWINGML_NS, \"a:p\");\n const r = titleNode.ownerDocument.createElementNS(DRAWINGML_NS, \"a:r\");\n const t = titleNode.ownerDocument.createElementNS(DRAWINGML_NS, \"a:t\");\n t.textContent = value;\n r.appendChild(t);\n p.appendChild(r);\n rich.append(bodyPr, lstStyle, p);\n tx.appendChild(rich);\n titleNode.appendChild(tx);\n}\n\nfunction setRefFormula(parent: Element, refNodeName: string, formula: string | undefined) {\n if (!formula) {\n removeLocalChildren(parent, refNodeName);\n return;\n }\n\n const refNode = ensureChild(parent, refNodeName);\n setLeafValue(refNode, \"f\", formula);\n}\n\nfunction setSeriesText(seriesNode: Element, series: XlsxChartSeries) {\n const raw = series.raw && typeof series.raw === \"object\" ? series.raw as Record<string, unknown> : null;\n const nameFormula = typeof raw?.name === \"string\" && raw.name.length > 0 ? raw.name : undefined;\n if (!nameFormula && series.name === undefined) {\n return;\n }\n\n const tx = ensureChild(seriesNode, \"tx\");\n removeLocalChildren(tx, \"strRef\");\n removeLocalChildren(tx, \"v\");\n if (nameFormula) {\n const strRef = ensureChild(tx, \"strRef\");\n setLeafValue(strRef, \"f\", nameFormula);\n return;\n }\n\n setLeafValue(tx, \"v\", series.name ?? \"\");\n}\n\nfunction updateSeriesNodes(plotAreaNode: Element, chart: Partial<XlsxChart>) {\n if (!chart.series) {\n return;\n }\n\n const seriesNodes = getLocalDescendants(plotAreaNode, \"ser\");\n chart.series.forEach((series, index) => {\n const seriesNode = seriesNodes[index];\n if (!seriesNode) {\n return;\n }\n\n setSeriesText(seriesNode, series);\n if (series.categoriesRef?.formula) {\n const target = getFirstLocalChild(seriesNode, \"xVal\")\n ?? getFirstLocalChild(seriesNode, \"cat\")\n ?? (\n chart.chartType === \"Scatter\" || chart.chartType === \"ScatterLines\" || chart.chartType === \"ScatterSmooth\" || chart.chartType === \"Bubble\"\n ? ensureChild(seriesNode, \"xVal\")\n : ensureChild(seriesNode, \"cat\")\n );\n const categoryRefName = target.localName === \"xVal\" || getFirstLocalChild(target, \"numRef\") ? \"numRef\" : \"strRef\";\n setRefFormula(target, categoryRefName, series.categoriesRef.formula);\n }\n if (series.valuesRef?.formula) {\n const target = getFirstLocalChild(seriesNode, \"yVal\")\n ?? getFirstLocalChild(seriesNode, \"val\")\n ?? (\n chart.chartType === \"Scatter\" || chart.chartType === \"ScatterLines\" || chart.chartType === \"ScatterSmooth\" || chart.chartType === \"Bubble\"\n ? ensureChild(seriesNode, \"yVal\")\n : ensureChild(seriesNode, \"val\")\n );\n setRefFormula(target, \"numRef\", series.valuesRef.formula);\n }\n if (series.bubbleSizeRef) {\n const target = getFirstLocalChild(seriesNode, \"bubbleSize\") ?? ensureChild(seriesNode, \"bubbleSize\");\n setRefFormula(target, \"numRef\", series.bubbleSizeRef.formula);\n }\n if (series.invertIfNegative !== undefined) {\n setBooleanValue(seriesNode, \"invertIfNegative\", series.invertIfNegative);\n }\n if (series.smooth !== undefined) {\n setBooleanValue(seriesNode, \"smooth\", series.smooth);\n }\n });\n}\n\nfunction updateAxisNode(axisNode: Element | null, axis: XlsxChartAxis | null | undefined) {\n if (!axisNode || !axis) {\n return;\n }\n\n if (axis.position) {\n setLeafValue(ensureChild(axisNode, \"axPos\"), \"val\", axis.position);\n getFirstLocalChild(axisNode, \"axPos\")?.setAttribute(\"val\", axis.position);\n }\n if (axis.majorGridlines !== undefined) {\n const gridlines = getFirstLocalChild(axisNode, \"majorGridlines\");\n if (axis.majorGridlines && !gridlines) {\n axisNode.appendChild(axisNode.ownerDocument.createElementNS(CHART_NS, \"c:majorGridlines\"));\n } else if (!axis.majorGridlines) {\n gridlines?.remove();\n }\n }\n if (axis.minorGridlines !== undefined) {\n const gridlines = getFirstLocalChild(axisNode, \"minorGridlines\");\n if (axis.minorGridlines && !gridlines) {\n axisNode.appendChild(axisNode.ownerDocument.createElementNS(CHART_NS, \"c:minorGridlines\"));\n } else if (!axis.minorGridlines) {\n gridlines?.remove();\n }\n }\n if (axis.majorTickMark) {\n getFirstLocalChild(axisNode, \"majorTickMark\")?.setAttribute(\"val\", axis.majorTickMark)\n ?? setBooleanValue(axisNode, \"majorTickMark\", false).setAttribute(\"val\", axis.majorTickMark);\n }\n if (axis.minorTickMark) {\n getFirstLocalChild(axisNode, \"minorTickMark\")?.setAttribute(\"val\", axis.minorTickMark)\n ?? setBooleanValue(axisNode, \"minorTickMark\", false).setAttribute(\"val\", axis.minorTickMark);\n }\n if (axis.labelPosition) {\n getFirstLocalChild(axisNode, \"tickLblPos\")?.setAttribute(\"val\", axis.labelPosition)\n ?? setBooleanValue(axisNode, \"tickLblPos\", false).setAttribute(\"val\", axis.labelPosition);\n }\n if (axis.crosses) {\n getFirstLocalChild(axisNode, \"crosses\")?.setAttribute(\"val\", axis.crosses)\n ?? setBooleanValue(axisNode, \"crosses\", false).setAttribute(\"val\", axis.crosses);\n }\n if (axis.crossBetween) {\n getFirstLocalChild(axisNode, \"crossBetween\")?.setAttribute(\"val\", axis.crossBetween)\n ?? setBooleanValue(axisNode, \"crossBetween\", false).setAttribute(\"val\", axis.crossBetween);\n }\n if (axis.delete !== undefined) {\n setBooleanValue(axisNode, \"delete\", axis.delete);\n }\n if (axis.numberFormat?.formatCode) {\n const numFmt = ensureChild(axisNode, \"numFmt\");\n numFmt.setAttribute(\"formatCode\", axis.numberFormat.formatCode);\n if (axis.numberFormat.sourceLinked !== undefined) {\n numFmt.setAttribute(\"sourceLinked\", axis.numberFormat.sourceLinked ? \"1\" : \"0\");\n }\n }\n}\n\nfunction updateDataLabels(chartTypeNode: Element, labels: XlsxChartDataLabels | null | undefined) {\n if (!labels) {\n return;\n }\n\n const labelsNode = ensureChild(chartTypeNode, \"dLbls\");\n if (labels.showLegendKey !== undefined) {\n setBooleanValue(labelsNode, \"showLegendKey\", labels.showLegendKey);\n }\n if (labels.showValue !== undefined) {\n setBooleanValue(labelsNode, \"showVal\", labels.showValue);\n }\n if (labels.showCategoryName !== undefined) {\n setBooleanValue(labelsNode, \"showCatName\", labels.showCategoryName);\n }\n if (labels.showSeriesName !== undefined) {\n setBooleanValue(labelsNode, \"showSerName\", labels.showSeriesName);\n }\n if (labels.showPercent !== undefined) {\n setBooleanValue(labelsNode, \"showPercent\", labels.showPercent);\n }\n if (labels.showBubbleSize !== undefined) {\n setBooleanValue(labelsNode, \"showBubbleSize\", labels.showBubbleSize);\n }\n}\n\nexport function updateWorkbookChartAnchor(\n imageAssets: Pick<WorkbookImageAssets, \"archive\" | \"dirtyArchivePaths\">,\n chartAssets: WorkbookChartAssets,\n chartId: string,\n anchor: XlsxImageAnchor\n) {\n const origin = chartAssets.chartOriginsById.get(chartId);\n if (!origin) {\n return false;\n }\n\n const drawingXml = readArchiveText(imageAssets.archive, origin.drawingPath);\n if (!drawingXml) {\n return false;\n }\n\n const drawingDocument = parseXml(drawingXml);\n if (!drawingDocument) {\n return false;\n }\n\n const anchorNode = getChartAnchorNodes(drawingDocument)[origin.anchorIndex];\n if (!anchorNode) {\n return false;\n }\n\n updateAnchorNode(anchorNode, anchor);\n imageAssets.archive[normalizeArchivePath(origin.drawingPath)] = strToU8(serializeXml(drawingDocument));\n imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.drawingPath));\n return true;\n}\n\nexport function updateWorkbookChartDefinition(\n imageAssets: Pick<WorkbookImageAssets, \"archive\" | \"dirtyArchivePaths\">,\n chartAssets: WorkbookChartAssets,\n chartId: string,\n patch: Partial<XlsxChart>\n) {\n const origin = chartAssets.chartOriginsById.get(chartId);\n if (!origin?.chartPath) {\n return false;\n }\n\n const chartXml = readArchiveText(imageAssets.archive, origin.chartPath);\n if (!chartXml) {\n return false;\n }\n\n const chartDocument = parseXml(chartXml);\n if (!chartDocument) {\n return false;\n }\n\n const chartNode = getFirstLocalDescendant(chartDocument, \"chart\");\n const plotAreaNode = chartNode ? getFirstLocalChild(chartNode, \"plotArea\") : null;\n const chartTypeNode = findPrimaryChartTypeNode(plotAreaNode);\n if (!chartNode || !plotAreaNode || !chartTypeNode) {\n return false;\n }\n\n if (patch.title !== undefined) {\n setChartTitle(chartNode, patch.title);\n }\n if (patch.displayBlanksAs) {\n const node = ensureChild(chartNode, \"dispBlanksAs\");\n node.setAttribute(\"val\", patch.displayBlanksAs);\n }\n if (patch.roundedCorners !== undefined) {\n setBooleanValue(chartNode, \"roundedCorners\", patch.roundedCorners);\n }\n if (patch.showDlblsOverMax !== undefined) {\n setBooleanValue(chartNode, \"showDLblsOverMax\", patch.showDlblsOverMax);\n }\n if (patch.varyColors !== undefined) {\n setBooleanValue(chartTypeNode, \"varyColors\", patch.varyColors);\n }\n if (patch.gapWidth !== undefined) {\n setNumericValue(chartTypeNode, \"gapWidth\", patch.gapWidth);\n }\n if (patch.overlap !== undefined) {\n const overlapNode = ensureChild(chartTypeNode, \"overlap\");\n overlapNode.setAttribute(\"val\", String(Math.round(patch.overlap)));\n }\n if (patch.dataLabels) {\n updateDataLabels(chartTypeNode, patch.dataLabels);\n }\n updateSeriesNodes(plotAreaNode, patch);\n updateAxisNode(\n getLocalChildren(plotAreaNode, \"catAx\")[0]\n ?? getLocalChildren(plotAreaNode, \"dateAx\")[0]\n ?? getLocalChildren(plotAreaNode, \"serAx\")[0]\n ?? null,\n patch.categoryAxis\n );\n updateAxisNode(getLocalChildren(plotAreaNode, \"valAx\")[0] ?? null, patch.valueAxis);\n\n imageAssets.archive[normalizeArchivePath(origin.chartPath)] = strToU8(serializeXml(chartDocument));\n imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.chartPath));\n return true;\n}\n","import type { XlsxThemePalette } from \"./types\";\n\nfunction normalizeHexColor(value: string) {\n const hex = value.replace(/^#/, \"\");\n if (hex.length === 8) {\n return `#${hex.slice(2).toLowerCase()}`;\n }\n if (hex.length === 6) {\n return `#${hex.toLowerCase()}`;\n }\n return null;\n}\n\nfunction parseHexColor(color: string): [number, number, number] | null {\n const normalized = normalizeHexColor(color);\n const match = normalized ? /^#([0-9a-f]{6})$/.exec(normalized) : null;\n if (!match) {\n return null;\n }\n\n const hex = match[1];\n return [\n Number.parseInt(hex.slice(0, 2), 16),\n Number.parseInt(hex.slice(2, 4), 16),\n Number.parseInt(hex.slice(4, 6), 16)\n ];\n}\n\nfunction rgbToHsl(red: number, green: number, blue: number): [number, number, number] {\n const normalizedRed = red / 255;\n const normalizedGreen = green / 255;\n const normalizedBlue = blue / 255;\n const max = Math.max(normalizedRed, normalizedGreen, normalizedBlue);\n const min = Math.min(normalizedRed, normalizedGreen, normalizedBlue);\n const lightness = (max + min) / 2;\n\n if (max === min) {\n return [0, 0, lightness];\n }\n\n const delta = max - min;\n const saturation = lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min);\n let hue = 0;\n\n switch (max) {\n case normalizedRed:\n hue = (normalizedGreen - normalizedBlue) / delta + (normalizedGreen < normalizedBlue ? 6 : 0);\n break;\n case normalizedGreen:\n hue = (normalizedBlue - normalizedRed) / delta + 2;\n break;\n default:\n hue = (normalizedRed - normalizedGreen) / delta + 4;\n break;\n }\n\n return [hue / 6, saturation, lightness];\n}\n\nfunction hueToRgb(p: number, q: number, t: number) {\n let nextT = t;\n if (nextT < 0) {\n nextT += 1;\n }\n if (nextT > 1) {\n nextT -= 1;\n }\n if (nextT < 1 / 6) {\n return p + (q - p) * 6 * nextT;\n }\n if (nextT < 1 / 2) {\n return q;\n }\n if (nextT < 2 / 3) {\n return p + (q - p) * (2 / 3 - nextT) * 6;\n }\n return p;\n}\n\nfunction hslToRgb(hue: number, saturation: number, lightness: number): [number, number, number] {\n if (saturation === 0) {\n const gray = Math.round(lightness * 255);\n return [gray, gray, gray];\n }\n\n const q = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation;\n const p = 2 * lightness - q;\n\n return [\n Math.round(hueToRgb(p, q, hue + 1 / 3) * 255),\n Math.round(hueToRgb(p, q, hue) * 255),\n Math.round(hueToRgb(p, q, hue - 1 / 3) * 255)\n ];\n}\n\nfunction rgbToHex(red: number, green: number, blue: number) {\n return `#${[red, green, blue]\n .map((channel) => Math.max(0, Math.min(255, channel)).toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\nfunction applyExcelTint(baseColor: string, tint: number) {\n const rgb = parseHexColor(baseColor);\n if (!rgb || !Number.isFinite(tint) || tint === 0) {\n return normalizeHexColor(baseColor);\n }\n\n const [hue, saturation, lightness] = rgbToHsl(rgb[0], rgb[1], rgb[2]);\n const nextLightness = tint < 0\n ? lightness * (1 + tint)\n : lightness * (1 - tint) + tint;\n const [nextRed, nextGreen, nextBlue] = hslToRgb(hue, saturation, Math.max(0, Math.min(1, nextLightness)));\n return rgbToHex(nextRed, nextGreen, nextBlue);\n}\n\nexport function buildThemePaletteFromHex(hexColors: readonly string[]): XlsxThemePalette {\n const colorsByIndex: Record<number, string> = {};\n hexColors.forEach((hex, index) => {\n const color = normalizeHexColor(hex);\n if (color) {\n colorsByIndex[index] = color;\n }\n });\n return { colorsByIndex };\n}\n\nexport function resolveWorkbookColor(\n color: Record<string, unknown> | undefined,\n themePalette?: XlsxThemePalette | null\n): string | null {\n if (!color) {\n return null;\n }\n\n const directHex = [\"hex\", \"rgb\", \"argb\"]\n .map((key) => color[key])\n .find((value): value is string => typeof value === \"string\" && value.trim().length > 0);\n if (directHex) {\n return normalizeHexColor(directHex);\n }\n\n const themeValue = color.theme ?? color.themeIndex;\n const numericTheme = typeof themeValue === \"number\"\n ? themeValue\n : typeof themeValue === \"string\" && themeValue.trim().length > 0\n ? Number(themeValue)\n : Number.NaN;\n const themeColor = Number.isFinite(numericTheme) ? themePalette?.colorsByIndex[numericTheme] ?? null : null;\n if (!themeColor) {\n return null;\n }\n\n const tintValue = color.tint;\n const tint = typeof tintValue === \"number\"\n ? tintValue\n : typeof tintValue === \"string\" && tintValue.trim().length > 0\n ? Number(tintValue)\n : Number.NaN;\n\n return Number.isFinite(tint) ? applyExcelTint(themeColor, tint) : themeColor;\n}\n\nexport function resolveWorkbookFillColor(\n fill: Record<string, unknown> | undefined,\n themePalette?: XlsxThemePalette | null\n): string | null {\n if (!fill) {\n return null;\n }\n\n if (fill.fillType === \"solid\") {\n return resolveWorkbookColor(\n (fill.color as Record<string, unknown> | undefined)\n ?? (fill.foreground as Record<string, unknown> | undefined)\n ?? (fill.background as Record<string, unknown> | undefined),\n themePalette\n );\n }\n\n if (fill.fillType === \"pattern\") {\n return resolveWorkbookColor(\n (fill.foreground as Record<string, unknown> | undefined)\n ?? (fill.color as Record<string, unknown> | undefined)\n ?? (fill.background as Record<string, unknown> | undefined),\n themePalette\n );\n }\n\n return null;\n}\n\nexport function resolveWorkbookFillStyle(\n fill: Record<string, unknown> | undefined,\n themePalette?: XlsxThemePalette | null\n) {\n if (!fill) {\n return {\n backgroundColor: null,\n backgroundImage: null\n };\n }\n\n if (fill.fillType === \"gradient\") {\n const rawStops = Array.isArray(fill.stops) ? fill.stops as Array<Record<string, unknown>> : [];\n const stops = rawStops\n .map((stop) => ({\n color: resolveWorkbookColor(stop.color as Record<string, unknown> | undefined, themePalette),\n position: typeof stop.position === \"number\" ? stop.position : Number(stop.position)\n }))\n .filter((stop): stop is { color: string; position: number } => Boolean(stop.color) && Number.isFinite(stop.position))\n .sort((left, right) => left.position - right.position);\n if (stops.length > 0) {\n const gradientType = typeof fill.gradientType === \"string\" ? fill.gradientType : \"linear\";\n if (gradientType === \"path\") {\n const backgroundColor = stops[stops.length - 1]?.color ?? null;\n return {\n backgroundColor,\n backgroundImage: `radial-gradient(circle at center, ${stops.map((stop) => `${stop.color} ${Math.max(0, Math.min(100, stop.position * 100))}%`).join(\", \")})`\n };\n }\n\n const degree = typeof fill.degree === \"number\" ? fill.degree : Number(fill.degree ?? 0);\n const cssAngle = Number.isFinite(degree) ? 90 - degree : 90;\n return {\n backgroundColor: stops[stops.length - 1]?.color ?? null,\n backgroundImage: `linear-gradient(${cssAngle}deg, ${stops.map((stop) => `${stop.color} ${Math.max(0, Math.min(100, stop.position * 100))}%`).join(\", \")})`\n };\n }\n }\n\n return {\n backgroundColor: resolveWorkbookFillColor(fill, themePalette),\n backgroundImage: null\n };\n}\n","import type {\n DrawingAnchor as DukeDrawingAnchor,\n DrawingColor as DukeDrawingColor,\n DrawingRunFont as DukeDrawingRunFont,\n DrawingText as DukeDrawingText,\n FormControlDrawing as DukeFormControlDrawing,\n FormControlKind as DukeFormControlKind,\n Workbook\n} from \"@dukelib/sheets-wasm\";\nimport { strFromU8, strToU8, unzipSync, zipSync } from \"fflate\";\nimport { resolveWorkbookColor } from \"./colors\";\nimport type {\n XlsxOfficeLayout,\n XlsxCellAddress,\n XlsxConditionalDataBarRule,\n XlsxConditionalFormatIcon,\n XlsxConditionalFormatRule,\n XlsxConditionalFormatValueObject,\n XlsxConditionalIconSetRule,\n XlsxCellRange,\n XlsxCellStyleColorInput,\n XlsxFormControl,\n XlsxFormControlCaption,\n XlsxImage,\n XlsxImageAnchor,\n XlsxImageRect,\n XlsxImageResizeHandlePosition,\n XlsxResolvedCellStyle,\n XlsxShape,\n XlsxSparkline,\n XlsxTableStyleDefinition,\n XlsxThemePalette\n} from \"./types\";\n\nconst REL_NS = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships\";\nconst PKG_REL_NS = \"http://schemas.openxmlformats.org/package/2006/relationships\";\nconst SPREADSHEET_NS = \"http://schemas.openxmlformats.org/spreadsheetml/2006/main\";\nconst CONTENT_TYPES_NS = \"http://schemas.openxmlformats.org/package/2006/content-types\";\nconst DRAWING_REL_TYPE = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing\";\nconst IMAGE_REL_TYPE = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\";\nconst HYPERLINK_REL_TYPE = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\";\nconst DRAWING_CONTENT_TYPE = \"application/vnd.openxmlformats-officedocument.drawing+xml\";\nconst EMU_PER_PIXEL = 9525;\nconst MIN_COL_WIDTH_PX = 30;\nconst MIN_ROW_HEIGHT_PX = 16;\nconst DEFAULT_COL_WIDTH_EMU = 64 * EMU_PER_PIXEL;\nconst DEFAULT_ROW_HEIGHT_EMU = 20 * EMU_PER_PIXEL;\nconst DEFAULT_COLUMN_CHARACTER_WIDTH_PX = 7;\nconst columnCharacterWidthCache = new Map<string, number>();\ntype DukeWorksheet = ReturnType<Workbook[\"getSheet\"]>;\n\nfunction resolveDeviceGridlineThicknessPx() {\n if (typeof window === \"undefined\") {\n return 1;\n }\n\n const devicePixelRatio = window.devicePixelRatio;\n if (!Number.isFinite(devicePixelRatio) || devicePixelRatio <= 0) {\n return 1;\n }\n\n return 1 / devicePixelRatio;\n}\n\nfunction measureColumnCharacterWidthPx(fontFamily?: string | null, fontSizePt?: number | null) {\n const normalizedFamily = typeof fontFamily === \"string\" && fontFamily.trim().length > 0\n ? fontFamily.trim()\n : \"Calibri\";\n const normalizedSizePt = typeof fontSizePt === \"number\" && Number.isFinite(fontSizePt) && fontSizePt > 0\n ? fontSizePt\n : 11;\n const cacheKey = `${normalizedFamily}|${normalizedSizePt}`;\n const cached = columnCharacterWidthCache.get(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const fontSizePx = normalizedSizePt * (96 / 72);\n const family = JSON.stringify(normalizedFamily);\n const fallback = normalizedFamily.toLowerCase() === \"calibri\" || normalizedFamily.toLowerCase() === \"carlito\"\n ? ', \"Carlito\", \"React XLSX Carlito\"'\n : \"\";\n const font = `${fontSizePx}px ${family}${fallback}`;\n let width = DEFAULT_COLUMN_CHARACTER_WIDTH_PX;\n\n try {\n const context = typeof document !== \"undefined\"\n ? document.createElement(\"canvas\").getContext(\"2d\")\n : typeof OffscreenCanvas !== \"undefined\"\n ? new OffscreenCanvas(32, 32).getContext(\"2d\")\n : null;\n if (context) {\n context.font = font;\n width = Math.max(1, context.measureText(\"0\").width);\n }\n } catch {\n width = DEFAULT_COLUMN_CHARACTER_WIDTH_PX;\n }\n\n columnCharacterWidthCache.set(cacheKey, width);\n return width;\n}\n\nfunction measureOfficeDigitWidthPt(fontFamily?: string | null, fontSizePt?: number | null) {\n const family = fontFamily?.trim() || \"Calibri\";\n const size = typeof fontSizePt === \"number\" && fontSizePt > 0 ? fontSizePt : 11;\n const key = `office|${family}|${size}`;\n const cached = columnCharacterWidthCache.get(key);\n if (cached !== undefined) return cached;\n const context = typeof document !== \"undefined\" ? document.createElement(\"canvas\").getContext(\"2d\") : null;\n if (!context) return 6;\n context.font = `${size}px ${JSON.stringify(family)}, \"React XLSX Carlito\"`;\n const width = Math.max(1, Math.round(Math.max(...Array.from(\"0123456789\", digit => context.measureText(digit).width))));\n columnCharacterWidthCache.set(key, width);\n return width;\n}\n\nfunction sheetColumnWidthToPixels(width: number, columnCharacterWidthPx = DEFAULT_COLUMN_CHARACTER_WIDTH_PX) {\n if (!Number.isFinite(width) || width <= 0) {\n return MIN_COL_WIDTH_PX;\n }\n\n const digitWidth = Math.max(1, columnCharacterWidthPx);\n const pixels = width < 1\n ? Math.floor(width * (digitWidth + 5) + 0.5)\n : Math.floor(((256 * width + Math.floor(128 / digitWidth)) / 256) * digitWidth);\n return Math.max(MIN_COL_WIDTH_PX, pixels);\n}\n\nexport function resolveWorksheetDefaultColumnWidthPixels(\n worksheet: DukeWorksheet,\n columnCharacterWidthPx = DEFAULT_COLUMN_CHARACTER_WIDTH_PX,\n fallbackPx = sheetColumnWidthToPixels(8.43, columnCharacterWidthPx)\n) {\n const width = typeof worksheet.defaultColumnWidth === \"number\" ? worksheet.defaultColumnWidth : Number.NaN;\n return Number.isFinite(width) && width > 0\n ? sheetColumnWidthToPixels(width, columnCharacterWidthPx)\n : fallbackPx;\n}\n\nexport function resolveWorksheetDefaultRowHeightPixels(\n worksheet: DukeWorksheet,\n fallbackPx = Math.max(MIN_ROW_HEIGHT_PX, Math.round(15 * 1.33))\n) {\n const height = typeof worksheet.defaultRowHeight === \"number\" ? worksheet.defaultRowHeight : Number.NaN;\n return Number.isFinite(height) && height > 0\n ? Math.max(MIN_ROW_HEIGHT_PX, Math.round(height * 1.33))\n : fallbackPx;\n}\n\nexport function resolveWorksheetHiddenRows(worksheet: DukeWorksheet, maxRow: number) {\n if (!Number.isFinite(maxRow) || maxRow < 0 || typeof worksheet.isRowHidden !== \"function\") {\n return [] as number[];\n }\n\n const hiddenRows: number[] = [];\n for (let row = 0; row <= maxRow; row += 1) {\n if (worksheet.isRowHidden(row)) {\n hiddenRows.push(row);\n }\n }\n return hiddenRows;\n}\n\nexport function resolveWorksheetHiddenCols(worksheet: DukeWorksheet, maxCol: number) {\n if (!Number.isFinite(maxCol) || maxCol < 0 || typeof worksheet.isColumnHidden !== \"function\") {\n return [] as number[];\n }\n\n const hiddenCols: number[] = [];\n for (let col = 0; col <= maxCol; col += 1) {\n if (worksheet.isColumnHidden(col)) {\n hiddenCols.push(col);\n }\n }\n return hiddenCols;\n}\n\nexport function resolveWorksheetMergeMetadata(worksheet: DukeWorksheet) {\n const mergeMetadata = {\n hasHorizontalMerges: false,\n hasVerticalMerges: false,\n maxHorizontalMergeEndCol: -1,\n maxVerticalMergeEndRow: -1,\n mergedRanges: [] as XlsxCellRange[]\n };\n const mergedRegions = Array.isArray(worksheet.mergedRegions) ? worksheet.mergedRegions : [];\n\n for (const rawRegion of mergedRegions) {\n let range: XlsxCellRange | null = null;\n if (typeof rawRegion === \"string\") {\n range = parseA1RangeReference(rawRegion);\n } else if (rawRegion && typeof rawRegion === \"object\") {\n const region = rawRegion as Record<string, unknown>;\n const startRow = typeof region.startRow === \"number\" ? region.startRow : Number.NaN;\n const startCol = typeof region.startCol === \"number\" ? region.startCol : Number.NaN;\n const endRow = typeof region.endRow === \"number\" ? region.endRow : Number.NaN;\n const endCol = typeof region.endCol === \"number\" ? region.endCol : Number.NaN;\n if ([startRow, startCol, endRow, endCol].every((value) => Number.isFinite(value) && value >= 0)) {\n range = {\n end: {\n col: Math.max(startCol, endCol),\n row: Math.max(startRow, endRow)\n },\n start: {\n col: Math.min(startCol, endCol),\n row: Math.min(startRow, endRow)\n }\n };\n } else if (typeof region.range === \"string\") {\n range = parseA1RangeReference(region.range);\n }\n }\n\n if (!range) {\n continue;\n }\n\n mergeMetadata.mergedRanges.push(range);\n if (range.end.col > range.start.col) {\n mergeMetadata.hasHorizontalMerges = true;\n mergeMetadata.maxHorizontalMergeEndCol = Math.max(mergeMetadata.maxHorizontalMergeEndCol, range.end.col);\n }\n if (range.end.row > range.start.row) {\n mergeMetadata.hasVerticalMerges = true;\n mergeMetadata.maxVerticalMergeEndRow = Math.max(mergeMetadata.maxVerticalMergeEndRow, range.end.row);\n }\n }\n\n return mergeMetadata;\n}\n\ntype ArchiveEntries = Record<string, Uint8Array>;\n\ntype ContentTypesState = {\n defaultEntries: Map<string, string>;\n overrideEntries: Map<string, string>;\n};\n\ntype RelationshipRecord = {\n id: string;\n target: string;\n targetMode: string | null;\n type: string;\n};\n\ntype WorkbookSheetInfo = {\n name: string;\n path: string;\n};\n\ntype WorkbookSheetState = {\n officeLayout?: XlsxOfficeLayout;\n cellStyleIds: Record<string, number>;\n autoFilterRanges: XlsxCellRange[];\n cachedFormulaValues: Record<string, string>;\n columnWidthCharacterWidthPx?: number;\n colWidthOverridesPx: Record<number, number>;\n colStyleIds: Record<number, number>;\n conditionalFormatRules: XlsxConditionalFormatRule[];\n defaultColWidthPx: number;\n defaultRowHeightPx: number;\n hasHorizontalMerges: boolean;\n hasVerticalMerges: boolean;\n maxHorizontalMergeEndCol: number;\n maxVerticalMergeEndRow: number;\n maxContentCol: number;\n maxContentRow: number;\n minContentCol: number;\n minContentRow: number;\n hiddenCols: number[];\n hiddenRows: number[];\n rowHeightOverridesPx: Record<number, number>;\n rowStyleIds: Record<number, number>;\n showGridLines: boolean;\n sparklines: XlsxSparkline[];\n zoomScale: number;\n};\n\ntype ParseWorkbookStructureOptions = {\n includeCachedFormulaValues?: boolean;\n themePalette?: XlsxThemePalette | null;\n};\n\ntype ThemeState = {\n colors: Map<string, string>;\n majorLatinFont: string | null;\n minorLatinFont: string | null;\n};\n\ntype DrawingColor = {\n color: string;\n opacity: number;\n};\n\ntype DrawingRectEmu = {\n cx: number;\n cy: number;\n x: number;\n y: number;\n};\n\ntype ShapeVectorPath = {\n path: string;\n viewBox: {\n height: number;\n width: number;\n };\n};\n\ntype GroupTransform = {\n chCx: number;\n chCy: number;\n chX: number;\n chY: number;\n cx: number;\n cy: number;\n scaleX: number;\n scaleY: number;\n x: number;\n y: number;\n};\n\ntype XlsxImageAttachment = {\n drawingPath: string;\n drawingRelsPath: string | null;\n mediaPaths: string[];\n};\n\ntype WorkbookImageOrigin = {\n anchorIndex: number;\n drawingPath: string;\n picturePath: number[];\n workbookSheetIndex: number;\n};\n\nexport type WorkbookImageSheetOrigin = {\n attachments: XlsxImageAttachment[];\n workbookSheetIndex: number;\n};\n\nexport type WorkbookTableMetadata = {\n displayName?: string;\n headerRowCount?: number;\n headerRowCellStyle?: string;\n name?: string;\n reference?: string;\n totalsRowCount?: number;\n totalsRowShown?: boolean;\n};\n\nexport type WorkbookImageAssets = {\n archive: ArchiveEntries;\n dirtyArchivePaths: Set<string>;\n formControlsByWorkbookSheetIndex: XlsxFormControl[][];\n imageOriginsById: Map<string, WorkbookImageOrigin>;\n imagesByWorkbookSheetIndex: XlsxImage[][];\n namedCellStyleByName: Record<string, XlsxResolvedCellStyle>;\n objectUrls: string[];\n shapesByWorkbookSheetIndex: XlsxShape[][];\n sheetStatesByWorkbookSheetIndex: Array<WorkbookSheetState | null>;\n sheetOrigins: Array<WorkbookImageSheetOrigin | null>;\n styleById: Record<number, XlsxResolvedCellStyle>;\n tableMetadataByWorkbookSheetIndex: WorkbookTableMetadata[][];\n tableStyleByName: Record<string, XlsxTableStyleDefinition>;\n themePalette: XlsxThemePalette;\n};\n\nexport type WorkbookStructureAssets = Pick<\n WorkbookImageAssets,\n | \"namedCellStyleByName\"\n | \"sheetStatesByWorkbookSheetIndex\"\n | \"styleById\"\n | \"tableMetadataByWorkbookSheetIndex\"\n | \"tableStyleByName\"\n | \"themePalette\"\n>;\n\nexport type WorkbookChartStyleAssets = Pick<\n WorkbookImageAssets,\n | \"archive\"\n | \"sheetOrigins\"\n | \"themePalette\"\n>;\n\nfunction buildThemePalette(theme: ThemeState): XlsxThemePalette {\n const themeOrder = [\"lt1\", \"dk1\", \"lt2\", \"dk2\", \"accent1\", \"accent2\", \"accent3\", \"accent4\", \"accent5\", \"accent6\", \"hlink\", \"folHlink\"];\n const colorsByIndex: Record<number, string> = {};\n\n themeOrder.forEach((key, index) => {\n const color = theme.colors.get(key);\n if (color) {\n colorsByIndex[index] = color;\n }\n });\n\n return {\n colorsByIndex,\n majorLatinFont: theme.majorLatinFont ?? undefined,\n minorLatinFont: theme.minorLatinFont ?? undefined\n };\n}\n\nfunction cloneBytes(bytes: Uint8Array) {\n const nextBytes = new Uint8Array(bytes.byteLength);\n nextBytes.set(bytes);\n return nextBytes;\n}\n\nfunction normalizeArchivePath(path: string) {\n return path.replace(/\\\\/g, \"/\").replace(/^\\/+/, \"\");\n}\n\nfunction joinArchivePath(...parts: string[]) {\n return normalizeArchivePath(parts.join(\"/\"));\n}\n\nfunction dirname(path: string) {\n const normalized = normalizeArchivePath(path);\n const lastSlash = normalized.lastIndexOf(\"/\");\n return lastSlash >= 0 ? normalized.slice(0, lastSlash) : \"\";\n}\n\nfunction resolveArchiveTarget(baseDocumentPath: string, target: string) {\n if (!target) {\n return normalizeArchivePath(baseDocumentPath);\n }\n\n if (target.startsWith(\"#\")) {\n return target;\n }\n\n if (target.startsWith(\"/\")) {\n return normalizeArchivePath(target);\n }\n\n const baseParts = dirname(baseDocumentPath).split(\"/\").filter(Boolean);\n for (const segment of target.split(\"/\")) {\n if (!segment || segment === \".\") {\n continue;\n }\n if (segment === \"..\") {\n baseParts.pop();\n continue;\n }\n baseParts.push(segment);\n }\n\n return normalizeArchivePath(baseParts.join(\"/\"));\n}\n\nfunction relativeArchivePath(fromDocumentPath: string, toPath: string) {\n const fromParts = dirname(fromDocumentPath).split(\"/\").filter(Boolean);\n const toParts = normalizeArchivePath(toPath).split(\"/\").filter(Boolean);\n let shared = 0;\n while (shared < fromParts.length && shared < toParts.length && fromParts[shared] === toParts[shared]) {\n shared += 1;\n }\n\n const upSegments = fromParts.slice(shared).map(() => \"..\");\n const downSegments = toParts.slice(shared);\n return [...upSegments, ...downSegments].join(\"/\") || \".\";\n}\n\nfunction relsPathForDocument(documentPath: string) {\n const baseName = documentPath.split(\"/\").pop();\n const parentDir = dirname(documentPath);\n return joinArchivePath(parentDir, \"_rels\", `${baseName}.rels`);\n}\n\nfunction parseXml(xml: string) {\n const parser = new DOMParser();\n const document = parser.parseFromString(xml, \"application/xml\");\n if (document.querySelector(\"parsererror\")) {\n return null;\n }\n return document;\n}\n\nfunction serializeXml(document: XMLDocument) {\n return new XMLSerializer().serializeToString(document);\n}\n\nfunction readArchiveText(archive: ArchiveEntries, path: string) {\n const entry = archive[path];\n return entry ? strFromU8(entry) : null;\n}\n\nfunction parseColumnReference(reference: string) {\n let value = 0;\n for (const character of reference.toUpperCase()) {\n if (character < \"A\" || character > \"Z\") {\n return null;\n }\n value = value * 26 + (character.charCodeAt(0) - 64);\n }\n return value > 0 ? value - 1 : null;\n}\n\nfunction parseA1CellReference(reference: string) {\n const match = /^\\$?([A-Za-z]+)\\$?(\\d+)$/.exec(reference.trim());\n if (!match) {\n return null;\n }\n\n const col = parseColumnReference(match[1] ?? \"\");\n const row = Number(match[2] ?? Number.NaN) - 1;\n if (col === null || !Number.isFinite(row) || row < 0) {\n return null;\n }\n\n return { col, row };\n}\n\nfunction parseA1RangeReference(reference: string) {\n const [startRef, endRef] = reference.split(\":\");\n const start = parseA1CellReference(startRef ?? \"\");\n const end = parseA1CellReference(endRef ?? startRef ?? \"\");\n return start && end ? { end, start } : null;\n}\n\nfunction stripSheetNameFromFormulaReference(reference: string) {\n const trimmed = reference.trim();\n const bangIndex = trimmed.lastIndexOf(\"!\");\n return bangIndex >= 0 ? trimmed.slice(bangIndex + 1) : trimmed;\n}\n\nfunction parseFormulaCellReference(reference: string) {\n const normalized = stripSheetNameFromFormulaReference(reference).split(/\\s+/)[0] ?? \"\";\n return parseA1CellReference(normalized);\n}\n\nfunction parseFormulaRangeReference(reference: string) {\n return parseA1RangeReference(stripSheetNameFromFormulaReference(reference));\n}\n\nfunction isElementNode(node: Node | null | undefined): node is Element {\n return Boolean(node && node.nodeType === 1);\n}\n\nfunction getLocalElements(parent: Document | Element, localName: string) {\n return Array.from(parent.getElementsByTagName(\"*\")).filter((node): node is Element => isElementNode(node) && node.localName === localName);\n}\n\nfunction getChildElements(parent: Element, localName: string) {\n return Array.from(parent.childNodes).filter((node): node is Element => isElementNode(node) && node.localName === localName);\n}\n\nfunction getFirstChild(parent: Element, localName: string) {\n return getChildElements(parent, localName)[0] ?? null;\n}\n\nfunction getFirstDescendant(parent: Document | Element, localName: string) {\n return getLocalElements(parent, localName)[0] ?? null;\n}\n\nfunction readFeaturePropertyBagCheckboxComplements(archive: ArchiveEntries) {\n const xml = readArchiveText(archive, \"xl/featurePropertyBag/featurePropertyBag.xml\");\n if (!xml) {\n return new Set<number>();\n }\n\n const document = parseXml(xml);\n if (!document?.documentElement) {\n return new Set<number>();\n }\n\n const bagNodes = getChildElements(document.documentElement, \"bag\");\n const bagTypeById = bagNodes.map((node) => node.getAttribute(\"type\") ?? \"\");\n const checkboxComplementIndices = new Set<number>();\n\n const xfComplementsBag = bagNodes.find((node) => node.getAttribute(\"type\") === \"XFComplements\") ?? null;\n const mappedBagIds = xfComplementsBag\n ? getLocalElements(xfComplementsBag, \"bagId\")\n .map((node) => Number(node.textContent ?? Number.NaN))\n .filter((value) => Number.isFinite(value))\n : [];\n\n mappedBagIds.forEach((bagId, complementIndex) => {\n const xfComplementBag = bagNodes[bagId];\n if (!xfComplementBag || bagTypeById[bagId] !== \"XFComplement\") {\n return;\n }\n\n const xfControlsBagId = getLocalElements(xfComplementBag, \"bagId\")\n .map((node) => Number(node.textContent ?? Number.NaN))\n .find((value) => Number.isFinite(value));\n if (xfControlsBagId === undefined) {\n return;\n }\n\n const xfControlsBag = bagNodes[xfControlsBagId];\n if (!xfControlsBag || bagTypeById[xfControlsBagId] !== \"XFControls\") {\n return;\n }\n\n const cellControlBagId = getLocalElements(xfControlsBag, \"bagId\")\n .map((node) => Number(node.textContent ?? Number.NaN))\n .find((value) => Number.isFinite(value));\n if (cellControlBagId === undefined) {\n return;\n }\n\n if (bagTypeById[cellControlBagId] === \"Checkbox\") {\n checkboxComplementIndices.add(complementIndex);\n }\n });\n\n return checkboxComplementIndices;\n}\n\nfunction getRelationshipId(element: Element) {\n return element.getAttributeNS(REL_NS, \"id\") ?? element.getAttribute(\"r:id\") ?? element.getAttribute(\"id\");\n}\n\nfunction getEmbeddedRelationshipId(element: Element) {\n return element.getAttributeNS(REL_NS, \"embed\") ?? element.getAttribute(\"r:embed\") ?? element.getAttribute(\"embed\");\n}\n\nfunction setChildText(parent: Element, localName: string, value: string) {\n const child = getFirstChild(parent, localName);\n if (child) {\n child.textContent = value;\n }\n}\n\nfunction updateMarkerElement(element: Element | null, marker: { col: number; colOffsetEmu: number; row: number; rowOffsetEmu: number }) {\n if (!element) {\n return;\n }\n\n setChildText(element, \"col\", String(Math.max(0, marker.col)));\n setChildText(element, \"colOff\", String(Math.max(0, Math.round(marker.colOffsetEmu))));\n setChildText(element, \"row\", String(Math.max(0, marker.row)));\n setChildText(element, \"rowOff\", String(Math.max(0, Math.round(marker.rowOffsetEmu))));\n}\n\nfunction parseContentTypes(archive: ArchiveEntries): ContentTypesState {\n const xml = readArchiveText(archive, \"[Content_Types].xml\");\n const defaultEntries = new Map<string, string>();\n const overrideEntries = new Map<string, string>();\n if (!xml) {\n return { defaultEntries, overrideEntries };\n }\n\n const document = parseXml(xml);\n if (!document) {\n return { defaultEntries, overrideEntries };\n }\n\n for (const defaultNode of getLocalElements(document, \"Default\")) {\n const extension = defaultNode.getAttribute(\"Extension\");\n const contentType = defaultNode.getAttribute(\"ContentType\");\n if (extension && contentType) {\n defaultEntries.set(extension.toLowerCase(), contentType);\n }\n }\n\n for (const overrideNode of getLocalElements(document, \"Override\")) {\n const partName = overrideNode.getAttribute(\"PartName\");\n const contentType = overrideNode.getAttribute(\"ContentType\");\n if (partName && contentType) {\n overrideEntries.set(normalizeArchivePath(partName), contentType);\n }\n }\n\n return { defaultEntries, overrideEntries };\n}\n\nfunction resolveContentType(contentTypes: ContentTypesState, path: string) {\n const normalized = normalizeArchivePath(path);\n const override = contentTypes.overrideEntries.get(normalized);\n if (override) {\n return override;\n }\n\n const extension = normalized.split(\".\").pop()?.toLowerCase();\n if (!extension) {\n return \"application/octet-stream\";\n }\n\n return contentTypes.defaultEntries.get(extension) ?? \"application/octet-stream\";\n}\n\nfunction parseRelationships(archive: ArchiveEntries, relsPath: string, baseDocumentPath: string) {\n const xml = readArchiveText(archive, relsPath);\n const relationships = new Map<string, RelationshipRecord>();\n if (!xml) {\n return relationships;\n }\n\n const document = parseXml(xml);\n if (!document) {\n return relationships;\n }\n\n for (const relationshipNode of getLocalElements(document, \"Relationship\")) {\n const id = relationshipNode.getAttribute(\"Id\");\n const target = relationshipNode.getAttribute(\"Target\");\n const type = relationshipNode.getAttribute(\"Type\");\n if (!id || !target || !type) {\n continue;\n }\n\n relationships.set(id, {\n id,\n target: resolveArchiveTarget(baseDocumentPath, target),\n targetMode: relationshipNode.getAttribute(\"TargetMode\"),\n type\n });\n }\n\n return relationships;\n}\n\nfunction parseWorkbookSheets(archive: ArchiveEntries) {\n const workbookXml = readArchiveText(archive, \"xl/workbook.xml\");\n if (!workbookXml) {\n return [];\n }\n\n const workbookDocument = parseXml(workbookXml);\n if (!workbookDocument) {\n return [];\n }\n\n const workbookRelationships = parseRelationships(archive, \"xl/_rels/workbook.xml.rels\", \"xl/workbook.xml\");\n const sheets: WorkbookSheetInfo[] = [];\n\n for (const sheetNode of getLocalElements(workbookDocument, \"sheet\")) {\n const relationshipId = getRelationshipId(sheetNode);\n if (!relationshipId) {\n continue;\n }\n\n const relationship = workbookRelationships.get(relationshipId);\n if (!relationship) {\n continue;\n }\n\n sheets.push({\n name: sheetNode.getAttribute(\"name\") ?? `Sheet ${sheets.length + 1}`,\n path: relationship.target\n });\n }\n\n return sheets;\n}\n\nfunction parseWorkbookTheme(archive: ArchiveEntries): ThemeState {\n const defaultTheme: ThemeState = {\n colors: new Map([\n [\"accent1\", \"#5b9bd5\"],\n [\"accent2\", \"#ed7d31\"],\n [\"accent3\", \"#a5a5a5\"],\n [\"accent4\", \"#ffc000\"],\n [\"accent5\", \"#4472c4\"],\n [\"accent6\", \"#70ad47\"],\n [\"bg1\", \"#ffffff\"],\n [\"bg2\", \"#e7e6e6\"],\n [\"dk1\", \"#000000\"],\n [\"dk2\", \"#6e747a\"],\n [\"folHlink\", \"#993366\"],\n [\"hlink\", \"#085296\"],\n [\"lt1\", \"#ffffff\"],\n [\"lt2\", \"#e7e6e6\"],\n [\"tx1\", \"#000000\"],\n [\"tx2\", \"#6e747a\"]\n ]),\n majorLatinFont: null,\n minorLatinFont: null\n };\n\n const themeXml = readArchiveText(archive, \"xl/theme/theme1.xml\");\n if (!themeXml) {\n return defaultTheme;\n }\n\n const themeDocument = parseXml(themeXml);\n if (!themeDocument) {\n return defaultTheme;\n }\n\n const colors = new Map(defaultTheme.colors);\n const colorSchemeNode = getLocalElements(themeDocument, \"clrScheme\")[0] ?? null;\n if (colorSchemeNode) {\n for (const colorNode of Array.from(colorSchemeNode.childNodes).filter(isElementNode)) {\n const key = colorNode.localName;\n const srgbNode = getFirstChild(colorNode, \"srgbClr\");\n const sysNode = getFirstChild(colorNode, \"sysClr\");\n const hex = srgbNode?.getAttribute(\"val\") ?? sysNode?.getAttribute(\"lastClr\");\n if (hex) {\n colors.set(key, normalizeHexColor(hex));\n }\n }\n }\n\n const fontSchemeNode = getLocalElements(themeDocument, \"fontScheme\")[0] ?? null;\n const majorLatinFont = getFirstChild(getFirstChild(fontSchemeNode, \"majorFont\"), \"latin\")?.getAttribute(\"typeface\") ?? null;\n const minorLatinFont = getFirstChild(getFirstChild(fontSchemeNode, \"minorFont\"), \"latin\")?.getAttribute(\"typeface\") ?? null;\n\n colors.set(\"bg1\", colors.get(\"lt1\") ?? defaultTheme.colors.get(\"bg1\") ?? \"#ffffff\");\n colors.set(\"tx1\", colors.get(\"dk1\") ?? defaultTheme.colors.get(\"tx1\") ?? \"#000000\");\n colors.set(\"bg2\", colors.get(\"lt2\") ?? defaultTheme.colors.get(\"bg2\") ?? \"#e7e6e6\");\n colors.set(\"tx2\", colors.get(\"dk2\") ?? defaultTheme.colors.get(\"tx2\") ?? \"#6e747a\");\n\n return {\n colors,\n majorLatinFont,\n minorLatinFont\n };\n}\n\nfunction parseSpreadsheetColor(node: Element | null) {\n if (!node) {\n return undefined;\n }\n\n const color: Record<string, unknown> = {};\n const rgb = node.getAttribute(\"rgb\");\n const theme = node.getAttribute(\"theme\");\n const tint = node.getAttribute(\"tint\");\n const indexed = node.getAttribute(\"indexed\");\n if (rgb) {\n color.rgb = normalizeHexColor(rgb);\n }\n if (theme !== null) {\n color.theme = Number(theme);\n }\n if (tint !== null) {\n color.tint = Number(tint);\n }\n if (indexed !== null) {\n color.indexed = Number(indexed);\n }\n\n return Object.keys(color).length > 0 ? color : undefined;\n}\n\nfunction hasEnabledSpreadsheetFlag(node: Element | null) {\n if (!node) {\n return false;\n }\n\n const value = node.getAttribute(\"val\");\n return value === null || (value !== \"0\" && value !== \"false\");\n}\n\nfunction parseSheetSparklines(\n document: XMLDocument,\n themePalette?: XlsxThemePalette | null\n) {\n const sparklines: XlsxSparkline[] = [];\n\n for (const groupNode of getLocalElements(document, \"sparklineGroup\")) {\n const rawType = groupNode.getAttribute(\"type\");\n const sparklineType: XlsxSparkline[\"type\"] = rawType === \"column\"\n ? \"column\"\n : rawType === \"stacked\"\n ? \"winLoss\"\n : \"line\";\n\n const markersNode = getFirstChild(groupNode, \"markers\");\n const negativeNode = getFirstChild(groupNode, \"negative\");\n const colorSeries = resolveWorkbookColor(parseSpreadsheetColor(getFirstChild(groupNode, \"colorSeries\")), themePalette);\n const colorNegative = resolveWorkbookColor(parseSpreadsheetColor(getFirstChild(groupNode, \"colorNegative\")), themePalette);\n const colorMarkers = resolveWorkbookColor(parseSpreadsheetColor(getFirstChild(groupNode, \"colorMarkers\")), themePalette);\n const colorFirst = resolveWorkbookColor(parseSpreadsheetColor(getFirstChild(groupNode, \"colorFirst\")), themePalette);\n const colorLast = resolveWorkbookColor(parseSpreadsheetColor(getFirstChild(groupNode, \"colorLast\")), themePalette);\n const colorHigh = resolveWorkbookColor(parseSpreadsheetColor(getFirstChild(groupNode, \"colorHigh\")), themePalette);\n const colorLow = resolveWorkbookColor(parseSpreadsheetColor(getFirstChild(groupNode, \"colorLow\")), themePalette);\n const sparklineCollectionNode = getFirstChild(groupNode, \"sparklines\");\n if (!sparklineCollectionNode) {\n continue;\n }\n\n for (const sparklineNode of getChildElements(sparklineCollectionNode, \"sparkline\")) {\n const formula = getFirstChild(sparklineNode, \"f\")?.textContent ?? \"\";\n const targetReference = getFirstChild(sparklineNode, \"sqref\")?.textContent ?? \"\";\n const range = parseFormulaRangeReference(formula);\n const target = parseFormulaCellReference(targetReference);\n if (!range || !target) {\n continue;\n }\n\n sparklines.push({\n color: colorSeries ?? undefined,\n firstColor: colorFirst ?? undefined,\n highColor: colorHigh ?? undefined,\n lastColor: colorLast ?? undefined,\n lowColor: colorLow ?? undefined,\n markerColor: colorMarkers ?? undefined,\n markers: hasEnabledSpreadsheetFlag(markersNode),\n negative: hasEnabledSpreadsheetFlag(negativeNode),\n negativeColor: colorNegative ?? undefined,\n range,\n target,\n type: sparklineType\n });\n }\n }\n\n return sparklines;\n}\n\nfunction parseSpreadsheetFont(node: Element | null): XlsxResolvedCellStyle[\"font\"] {\n if (!node) {\n return undefined;\n }\n\n const font: Record<string, unknown> = {};\n for (const [tag, property] of [[\"b\", \"bold\"], [\"i\", \"italic\"], [\"strike\", \"strikethrough\"]]) {\n const flag = getFirstChild(node, tag);\n if (flag) {\n font[property] = hasEnabledSpreadsheetFlag(flag);\n }\n }\n const size = getFirstChild(node, \"sz\")?.getAttribute(\"val\");\n const name = getFirstChild(node, \"name\")?.getAttribute(\"val\");\n const family = getFirstChild(node, \"family\")?.getAttribute(\"val\");\n const scheme = getFirstChild(node, \"scheme\")?.getAttribute(\"val\");\n const charset = getFirstChild(node, \"charset\")?.getAttribute(\"val\");\n const verticalAlign = getFirstChild(node, \"vertAlign\")?.getAttribute(\"val\");\n const color = parseSpreadsheetColor(getFirstChild(node, \"color\"));\n if (getFirstChild(node, \"u\")) {\n font.underline = getFirstChild(node, \"u\")?.getAttribute(\"val\") ?? \"single\";\n }\n if (size !== null && size !== undefined) {\n font.size = Number(size);\n }\n if (name) {\n font.name = name;\n }\n if (family !== null && family !== undefined) {\n font.family = Number(family);\n }\n if (scheme) {\n font.scheme = scheme;\n }\n if (charset !== null && charset !== undefined) {\n font.charset = Number(charset);\n }\n if (verticalAlign) {\n font.verticalAlign = verticalAlign;\n }\n if (hasEnabledSpreadsheetFlag(getFirstChild(node, \"shadow\"))) {\n font.shadow = true;\n }\n if (hasEnabledSpreadsheetFlag(getFirstChild(node, \"outline\"))) {\n font.outline = true;\n }\n if (hasEnabledSpreadsheetFlag(getFirstChild(node, \"condense\"))) {\n font.condense = true;\n }\n if (hasEnabledSpreadsheetFlag(getFirstChild(node, \"extend\"))) {\n font.extend = true;\n }\n if (color) {\n font.color = color;\n }\n\n return Object.keys(font).length > 0 ? font : undefined;\n}\n\nfunction parseSpreadsheetFill(node: Element | null): XlsxResolvedCellStyle[\"fill\"] {\n if (!node) {\n return undefined;\n }\n\n const gradientFill = getFirstChild(node, \"gradientFill\");\n if (gradientFill) {\n const stops = Array.from(gradientFill.childNodes)\n .filter(isElementNode)\n .filter((child) => child.localName === \"stop\")\n .map((stopNode) => ({\n color: parseSpreadsheetColor(Array.from(stopNode.childNodes).find(isElementNode) ?? null),\n position: Number(stopNode.getAttribute(\"position\") ?? Number.NaN)\n }))\n .filter((stop) => stop.color && Number.isFinite(stop.position));\n if (stops.length > 0) {\n return {\n degree: Number(gradientFill.getAttribute(\"degree\") ?? 0),\n fillType: \"gradient\",\n gradientType: gradientFill.getAttribute(\"type\") ?? \"linear\",\n stops\n };\n }\n }\n\n const patternFill = getFirstChild(node, \"patternFill\");\n if (!patternFill) {\n return undefined;\n }\n\n const patternType = patternFill.getAttribute(\"patternType\") ?? \"none\";\n const foreground = parseSpreadsheetColor(getFirstChild(patternFill, \"fgColor\"));\n const background = parseSpreadsheetColor(getFirstChild(patternFill, \"bgColor\"));\n const solidColor = foreground ?? background;\n if (patternType === \"solid\" && solidColor) {\n return {\n color: solidColor,\n fillType: \"solid\"\n };\n }\n // Differential table styles sometimes omit patternType and only set bgColor.\n // Preserve those fills so header/table overrides are not dropped during import.\n if ((patternType === \"none\" || patternType === \"gray125\") && (foreground || background)) {\n return {\n background,\n fillType: \"pattern\",\n foreground,\n patternType\n };\n }\n if (patternType !== \"none\" && patternType !== \"gray125\" && (foreground || background)) {\n return {\n background,\n fillType: \"pattern\",\n foreground,\n patternType\n };\n }\n\n return undefined;\n}\n\nfunction parseSpreadsheetBorderEdge(node: Element | null) {\n if (!node) {\n return undefined;\n }\n\n const style = node.getAttribute(\"style\");\n const color = parseSpreadsheetColor(getFirstChild(node, \"color\"));\n if (!style || style === \"none\") {\n return undefined;\n }\n\n return {\n color,\n style\n };\n}\n\nfunction parseSpreadsheetBorder(node: Element | null): XlsxResolvedCellStyle[\"border\"] {\n if (!node) {\n return undefined;\n }\n\n const border: Record<string, Record<string, unknown>> = {};\n ([\"top\", \"right\", \"bottom\", \"left\", \"horizontal\", \"vertical\"] as const).forEach((edge) => {\n const parsedEdge = parseSpreadsheetBorderEdge(getFirstChild(node, edge));\n if (parsedEdge) {\n border[edge] = parsedEdge;\n }\n });\n\n return Object.keys(border).length > 0 ? border : undefined;\n}\n\nfunction parseSpreadsheetAlignment(node: Element | null): XlsxResolvedCellStyle[\"alignment\"] {\n if (!node) {\n return undefined;\n }\n\n const alignment: Record<string, unknown> = {};\n const horizontal = node.getAttribute(\"horizontal\");\n const vertical = node.getAttribute(\"vertical\");\n const wrapText = node.getAttribute(\"wrapText\");\n const indent = node.getAttribute(\"indent\");\n const shrinkToFit = node.getAttribute(\"shrinkToFit\");\n const textRotation = node.getAttribute(\"textRotation\");\n if (horizontal) {\n alignment.horizontal = horizontal;\n }\n if (vertical) {\n alignment.vertical = vertical;\n }\n if (wrapText !== null) {\n alignment.wrapText = wrapText === \"1\";\n }\n if (shrinkToFit !== null) {\n alignment.shrinkToFit = shrinkToFit === \"1\";\n }\n if (indent !== null) {\n alignment.indent = Number(indent);\n }\n if (textRotation !== null) {\n const parsedRotation = Number(textRotation);\n if (Number.isFinite(parsedRotation)) {\n alignment.rotation = parsedRotation;\n }\n }\n\n return Object.keys(alignment).length > 0 ? alignment : undefined;\n}\n\nfunction parseDifferentialStyle(node: Element | null): XlsxResolvedCellStyle {\n if (!node) {\n return {};\n }\n\n const style: XlsxResolvedCellStyle = {};\n const font = parseSpreadsheetFont(getFirstChild(node, \"font\"));\n const fill = parseSpreadsheetFill(getFirstChild(node, \"fill\"));\n const border = parseSpreadsheetBorder(getFirstChild(node, \"border\"));\n const alignment = parseSpreadsheetAlignment(getFirstChild(node, \"alignment\"));\n\n if (font) {\n style.font = font;\n }\n if (fill) {\n style.fill = fill;\n }\n if (border) {\n style.border = border;\n }\n if (alignment) {\n style.alignment = alignment;\n }\n\n return style;\n}\n\nfunction parseResolvedXfStyle(\n xfNode: Element,\n fonts: Array<XlsxResolvedCellStyle[\"font\"]>,\n fills: Array<XlsxResolvedCellStyle[\"fill\"]>,\n borders: Array<XlsxResolvedCellStyle[\"border\"]>,\n checkboxComplementIndices?: Set<number>\n) {\n const style: XlsxResolvedCellStyle = {};\n const fontId = Number(xfNode.getAttribute(\"fontId\") ?? Number.NaN);\n const fillId = Number(xfNode.getAttribute(\"fillId\") ?? Number.NaN);\n const borderId = Number(xfNode.getAttribute(\"borderId\") ?? Number.NaN);\n const alignment = parseSpreadsheetAlignment(getFirstChild(xfNode, \"alignment\"));\n\n if (Number.isFinite(fontId) && fonts[fontId]) {\n style.font = fonts[fontId];\n }\n if (Number.isFinite(fillId) && fills[fillId]) {\n style.fill = fills[fillId];\n }\n if (Number.isFinite(borderId) && borders[borderId]) {\n style.border = borders[borderId];\n }\n if (alignment) {\n style.alignment = alignment;\n }\n const xfComplementNode = getFirstDescendant(xfNode, \"xfComplement\");\n const xfComplementIndex = Number(xfComplementNode?.getAttribute(\"i\") ?? Number.NaN);\n if (Number.isFinite(xfComplementIndex) && checkboxComplementIndices?.has(xfComplementIndex)) {\n style.cellControl = { kind: \"checkbox\" };\n }\n\n return style;\n}\n\nfunction parseWorkbookStyles(archive: ArchiveEntries) {\n const xml = readArchiveText(archive, \"xl/styles.xml\");\n if (!xml) {\n return {\n differentialStyles: [] as XlsxResolvedCellStyle[],\n defaultFont: null,\n namedCellStyleByName: {},\n styleById: {},\n tableStyleByName: {}\n };\n }\n\n const document = parseXml(xml);\n if (!document) {\n return {\n differentialStyles: [] as XlsxResolvedCellStyle[],\n defaultFont: null,\n namedCellStyleByName: {},\n styleById: {},\n tableStyleByName: {}\n };\n }\n\n const fontsNode = getFirstDescendant(document, \"fonts\");\n const fillsNode = getFirstDescendant(document, \"fills\");\n const bordersNode = getFirstDescendant(document, \"borders\");\n const cellStyleXfsNode = getFirstDescendant(document, \"cellStyleXfs\");\n const cellStylesNode = getFirstDescendant(document, \"cellStyles\");\n const cellXfsNode = getFirstDescendant(document, \"cellXfs\");\n const dxfsNode = getFirstDescendant(document, \"dxfs\");\n const tableStylesNode = getFirstDescendant(document, \"tableStyles\");\n if (!cellXfsNode) {\n return {\n differentialStyles: [] as XlsxResolvedCellStyle[],\n defaultFont: null,\n namedCellStyleByName: {},\n styleById: {},\n tableStyleByName: {}\n };\n }\n\n const checkboxComplementIndices = readFeaturePropertyBagCheckboxComplements(archive);\n const fonts = getChildElements(fontsNode ?? document.documentElement, \"font\").map((node) => parseSpreadsheetFont(node));\n const fills = getChildElements(fillsNode ?? document.documentElement, \"fill\").map((node) => parseSpreadsheetFill(node));\n const borders = getChildElements(bordersNode ?? document.documentElement, \"border\").map((node) => parseSpreadsheetBorder(node));\n const differentialStyles = getChildElements(dxfsNode ?? document.documentElement, \"dxf\").map((node) => parseDifferentialStyle(node));\n const cellStyleXfs = getChildElements(cellStyleXfsNode ?? document.documentElement, \"xf\").map(\n (node) => parseResolvedXfStyle(node, fonts, fills, borders, checkboxComplementIndices)\n );\n const namedCellStyleByName: Record<string, XlsxResolvedCellStyle> = {};\n const styleById: Record<number, XlsxResolvedCellStyle> = {};\n const tableStyleByName: Record<string, XlsxTableStyleDefinition> = {};\n\n getChildElements(cellXfsNode, \"xf\").forEach((xfNode, index) => {\n const style = parseResolvedXfStyle(xfNode, fonts, fills, borders, checkboxComplementIndices);\n const overrides: XlsxResolvedCellStyle = {};\n for (const component of [\"font\", \"fill\", \"border\"] as const) {\n if (Number(xfNode.getAttribute(`${component}Id`) ?? 0) > 0\n && xfNode.getAttribute(`apply${component[0].toUpperCase()}${component.slice(1)}`) !== \"0\") {\n Object.assign(overrides, { [component]: style[component] });\n }\n }\n styleById[index] = { ...style, tableStyleOverrides: overrides };\n });\n\n getChildElements(cellStylesNode ?? document.documentElement, \"cellStyle\").forEach((cellStyleNode) => {\n const name = cellStyleNode.getAttribute(\"name\");\n const xfId = Number(cellStyleNode.getAttribute(\"xfId\") ?? Number.NaN);\n if (!name || !Number.isFinite(xfId)) {\n return;\n }\n\n const resolvedStyle = cellStyleXfs[xfId];\n if (resolvedStyle) {\n namedCellStyleByName[name] = resolvedStyle;\n }\n });\n\n getChildElements(tableStylesNode ?? document.documentElement, \"tableStyle\").forEach((tableStyleNode) => {\n const name = tableStyleNode.getAttribute(\"name\");\n if (!name) {\n return;\n }\n\n const elements: XlsxTableStyleDefinition = {};\n getChildElements(tableStyleNode, \"tableStyleElement\").forEach((elementNode) => {\n const type = elementNode.getAttribute(\"type\");\n const dxfId = Number(elementNode.getAttribute(\"dxfId\") ?? Number.NaN);\n if (!type || !Number.isFinite(dxfId)) {\n return;\n }\n\n const differentialStyle = differentialStyles[dxfId];\n if (differentialStyle) {\n elements[type] = differentialStyle;\n }\n });\n tableStyleByName[name] = elements;\n });\n\n const normalFont = (namedCellStyleByName.Normal?.font ?? styleById[0]?.font ?? fonts[0]) as Record<string, unknown> | undefined;\n const defaultFont = normalFont ? {\n family: typeof normalFont.name === \"string\" ? normalFont.name : undefined,\n sizePt: typeof normalFont.size === \"number\" ? normalFont.size : undefined\n } : null;\n\n return {\n differentialStyles,\n defaultFont,\n namedCellStyleByName,\n styleById,\n tableStyleByName\n };\n}\n\nfunction parseSqrefRanges(sqref: string | null | undefined): XlsxCellRange[] {\n if (!sqref) {\n return [];\n }\n\n return sqref\n .trim()\n .split(/\\s+/)\n .flatMap((reference) => {\n const range = parseA1RangeReference(reference);\n return range ? [range] : [];\n });\n}\n\nfunction parseConditionalFormatValueObject(node: Element | null): XlsxConditionalFormatValueObject | null {\n if (!node) {\n return null;\n }\n\n const type = node.getAttribute(\"type\");\n if (!type) {\n return null;\n }\n\n const rawValue = node.getAttribute(\"val\") ?? getFirstChild(node, \"f\")?.textContent ?? undefined;\n const numericValue = rawValue !== undefined ? Number(rawValue) : Number.NaN;\n return {\n type,\n value: Number.isFinite(numericValue) ? numericValue : undefined\n };\n}\n\nfunction parseSpreadsheetBooleanAttribute(node: Element | null, name: string) {\n if (!node) {\n return undefined;\n }\n\n const value = node.getAttribute(name);\n if (value === null) {\n return undefined;\n }\n\n return value !== \"0\" && value !== \"false\";\n}\n\nfunction parseStandardConditionalFormatRule(\n cfRuleNode: Element,\n ranges: XlsxCellRange[],\n differentialStyles: XlsxResolvedCellStyle[] = []\n): (XlsxConditionalFormatRule & { id?: string }) | null {\n const type = cfRuleNode.getAttribute(\"type\");\n const rawPriority = Number(cfRuleNode.getAttribute(\"priority\") ?? Number.NaN);\n const priority = Number.isFinite(rawPriority) ? rawPriority : Number.MAX_SAFE_INTEGER;\n const formulas = getChildElements(cfRuleNode, \"formula\")\n .map((formulaNode) => (formulaNode.textContent ?? \"\").trim())\n .filter((formula) => formula.length > 0);\n\n if (type === \"colorScale\") {\n const colorScaleNode = getFirstChild(cfRuleNode, \"colorScale\");\n if (!colorScaleNode) {\n return null;\n }\n\n const cfvos = getChildElements(colorScaleNode, \"cfvo\")\n .map((node) => parseConditionalFormatValueObject(node))\n .filter((value): value is XlsxConditionalFormatValueObject => Boolean(value));\n const colors = getChildElements(colorScaleNode, \"color\")\n .map((node) => parseSpreadsheetColor(node))\n .filter((value): value is Record<string, unknown> => Boolean(value));\n if (cfvos.length === 0 || colors.length === 0) {\n return null;\n }\n\n return {\n cfvos,\n colors,\n kind: \"colorScale\",\n priority,\n ranges\n };\n }\n\n if (type === \"dataBar\") {\n const dataBarNode = getFirstChild(cfRuleNode, \"dataBar\");\n if (!dataBarNode) {\n return null;\n }\n\n const cfvos = getChildElements(dataBarNode, \"cfvo\")\n .map((node) => parseConditionalFormatValueObject(node))\n .filter((value): value is XlsxConditionalFormatValueObject => Boolean(value));\n if (cfvos.length === 0) {\n return null;\n }\n\n const extId = getFirstDescendant(cfRuleNode, \"id\")?.textContent?.trim() || undefined;\n return {\n cfvos,\n color: parseSpreadsheetColor(getFirstChild(dataBarNode, \"color\")),\n kind: \"dataBar\",\n priority,\n ranges,\n id: extId\n };\n }\n\n if (type === \"iconSet\") {\n const iconSetNode = getFirstChild(cfRuleNode, \"iconSet\");\n if (!iconSetNode) {\n return null;\n }\n\n const iconSetName = iconSetNode.getAttribute(\"iconSet\");\n const cfvos = getChildElements(iconSetNode, \"cfvo\")\n .map((node) => parseConditionalFormatValueObject(node))\n .filter((value): value is XlsxConditionalFormatValueObject => Boolean(value));\n if (!iconSetName || cfvos.length === 0) {\n return null;\n }\n\n return {\n cfvos,\n icons: cfvos.map((_, index) => ({\n iconId: index,\n iconSet: iconSetName\n })),\n kind: \"iconSet\",\n priority,\n ranges,\n reverse: parseSpreadsheetBooleanAttribute(iconSetNode, \"reverse\"),\n showValue: parseSpreadsheetBooleanAttribute(iconSetNode, \"showValue\")\n };\n }\n\n const rawDxfId = Number(cfRuleNode.getAttribute(\"dxfId\") ?? Number.NaN);\n if (!type || !Number.isFinite(rawDxfId)) {\n return null;\n }\n\n const style = differentialStyles[rawDxfId];\n if (!style) {\n return null;\n }\n\n const rawRank = Number(cfRuleNode.getAttribute(\"rank\") ?? Number.NaN);\n const rawStdDev = Number(cfRuleNode.getAttribute(\"stdDev\") ?? Number.NaN);\n return {\n aboveAverage: parseSpreadsheetBooleanAttribute(cfRuleNode, \"aboveAverage\"),\n bottom: parseSpreadsheetBooleanAttribute(cfRuleNode, \"bottom\"),\n equalAverage: parseSpreadsheetBooleanAttribute(cfRuleNode, \"equalAverage\"),\n formulas,\n kind: \"styled\",\n operator: cfRuleNode.getAttribute(\"operator\") ?? undefined,\n percent: parseSpreadsheetBooleanAttribute(cfRuleNode, \"percent\"),\n priority,\n rank: Number.isFinite(rawRank) ? rawRank : undefined,\n ranges,\n ruleType: type,\n stdDev: Number.isFinite(rawStdDev) ? rawStdDev : undefined,\n stopIfTrue: parseSpreadsheetBooleanAttribute(cfRuleNode, \"stopIfTrue\"),\n style,\n text: cfRuleNode.getAttribute(\"text\") ?? undefined,\n timePeriod: cfRuleNode.getAttribute(\"timePeriod\") ?? undefined\n };\n}\n\nfunction parseExtendedConditionalFormatRule(\n cfRuleNode: Element,\n ranges: XlsxCellRange[]\n): (XlsxConditionalFormatRule & { id?: string }) | null {\n const type = cfRuleNode.getAttribute(\"type\");\n const ruleId = cfRuleNode.getAttribute(\"id\") ?? undefined;\n const rawPriority = Number(cfRuleNode.getAttribute(\"priority\") ?? Number.NaN);\n const priority = Number.isFinite(rawPriority) ? rawPriority : Number.MAX_SAFE_INTEGER;\n\n if (type === \"dataBar\") {\n const dataBarNode = getFirstChild(cfRuleNode, \"dataBar\");\n if (!dataBarNode) {\n return null;\n }\n\n const cfvos = getChildElements(dataBarNode, \"cfvo\")\n .map((node) => parseConditionalFormatValueObject(node))\n .filter((value): value is XlsxConditionalFormatValueObject => Boolean(value));\n if (cfvos.length === 0) {\n return null;\n }\n\n return {\n axisColor: parseSpreadsheetColor(getFirstChild(dataBarNode, \"axisColor\")),\n border: parseSpreadsheetBooleanAttribute(dataBarNode, \"border\"),\n borderColor: parseSpreadsheetColor(getFirstChild(dataBarNode, \"borderColor\")),\n cfvos,\n color: parseSpreadsheetColor(getFirstChild(dataBarNode, \"fillColor\")),\n gradient: parseSpreadsheetBooleanAttribute(dataBarNode, \"gradient\"),\n kind: \"dataBar\",\n maxLength: Number(dataBarNode.getAttribute(\"maxLength\") ?? Number.NaN),\n minLength: Number(dataBarNode.getAttribute(\"minLength\") ?? Number.NaN),\n negativeBarBorderColorSameAsPositive: parseSpreadsheetBooleanAttribute(dataBarNode, \"negativeBarBorderColorSameAsPositive\"),\n negativeBorderColor: parseSpreadsheetColor(getFirstChild(dataBarNode, \"negativeBorderColor\")),\n negativeFillColor: parseSpreadsheetColor(getFirstChild(dataBarNode, \"negativeFillColor\")),\n priority,\n ranges,\n showValue: parseSpreadsheetBooleanAttribute(dataBarNode, \"showValue\"),\n id: ruleId\n };\n }\n\n if (type === \"iconSet\") {\n const iconSetNode = getFirstChild(cfRuleNode, \"iconSet\");\n if (!iconSetNode) {\n return null;\n }\n\n const cfvos = getChildElements(iconSetNode, \"cfvo\")\n .map((node) => parseConditionalFormatValueObject(node))\n .filter((value): value is XlsxConditionalFormatValueObject => Boolean(value));\n const icons = getChildElements(iconSetNode, \"cfIcon\")\n .map((iconNode) => {\n const iconSet = iconNode.getAttribute(\"iconSet\");\n const rawIconId = Number(iconNode.getAttribute(\"iconId\") ?? Number.NaN);\n if (!iconSet || !Number.isFinite(rawIconId)) {\n return null;\n }\n\n return {\n iconId: rawIconId,\n iconSet\n } satisfies XlsxConditionalFormatIcon;\n })\n .filter((icon): icon is XlsxConditionalFormatIcon => Boolean(icon));\n\n if (cfvos.length === 0 || icons.length === 0) {\n return null;\n }\n\n return {\n cfvos,\n icons,\n kind: \"iconSet\",\n priority,\n ranges,\n reverse: parseSpreadsheetBooleanAttribute(iconSetNode, \"reverse\"),\n showValue: parseSpreadsheetBooleanAttribute(iconSetNode, \"showValue\"),\n id: ruleId\n };\n }\n\n return null;\n}\n\nfunction mergeConditionalFormatRule(\n baseRule: XlsxConditionalFormatRule & { id?: string },\n extendedRule: XlsxConditionalFormatRule & { id?: string }\n) {\n if (baseRule.kind !== extendedRule.kind) {\n return baseRule;\n }\n\n if (baseRule.kind === \"colorScale\" && extendedRule.kind === \"colorScale\") {\n return {\n ...baseRule,\n ...extendedRule,\n cfvos: extendedRule.cfvos.length > 0 ? extendedRule.cfvos : baseRule.cfvos,\n colors: extendedRule.colors.length > 0 ? extendedRule.colors : baseRule.colors,\n priority: Number.isFinite(extendedRule.priority) ? extendedRule.priority : baseRule.priority,\n ranges: extendedRule.ranges.length > 0 ? extendedRule.ranges : baseRule.ranges\n };\n }\n\n if (baseRule.kind === \"dataBar\" && extendedRule.kind === \"dataBar\") {\n const merged: XlsxConditionalDataBarRule & { id?: string } = {\n ...baseRule,\n ...extendedRule,\n axisColor: extendedRule.axisColor ?? baseRule.axisColor,\n border: extendedRule.border ?? baseRule.border,\n cfvos: extendedRule.cfvos.length > 0 ? extendedRule.cfvos : baseRule.cfvos,\n color: extendedRule.color ?? baseRule.color,\n negativeBarBorderColorSameAsPositive: extendedRule.negativeBarBorderColorSameAsPositive ?? baseRule.negativeBarBorderColorSameAsPositive,\n negativeBorderColor: extendedRule.negativeBorderColor ?? baseRule.negativeBorderColor,\n negativeFillColor: extendedRule.negativeFillColor ?? baseRule.negativeFillColor,\n priority: Number.isFinite(extendedRule.priority) ? extendedRule.priority : baseRule.priority,\n ranges: extendedRule.ranges.length > 0 ? extendedRule.ranges : baseRule.ranges\n };\n return merged;\n }\n\n if (baseRule.kind === \"iconSet\" && extendedRule.kind === \"iconSet\") {\n const merged: XlsxConditionalIconSetRule & { id?: string } = {\n ...baseRule,\n ...extendedRule,\n cfvos: extendedRule.cfvos.length > 0 ? extendedRule.cfvos : baseRule.cfvos,\n icons: extendedRule.icons.length > 0 ? extendedRule.icons : baseRule.icons,\n priority: Number.isFinite(extendedRule.priority) ? extendedRule.priority : baseRule.priority,\n ranges: extendedRule.ranges.length > 0 ? extendedRule.ranges : baseRule.ranges\n };\n return merged;\n }\n\n return baseRule;\n}\n\nfunction parseConditionalFormatRules(document: Document, differentialStyles: XlsxResolvedCellStyle[] = []) {\n const standardRules: Array<XlsxConditionalFormatRule & { id?: string }> = [];\n const extendedRules: Array<XlsxConditionalFormatRule & { id?: string }> = [];\n\n getLocalElements(document, \"conditionalFormatting\").forEach((conditionalFormattingNode) => {\n const isExtended = conditionalFormattingNode.namespaceURI !== SPREADSHEET_NS;\n const ranges = isExtended\n ? parseSqrefRanges(getFirstChild(conditionalFormattingNode, \"sqref\")?.textContent ?? \"\")\n : parseSqrefRanges(conditionalFormattingNode.getAttribute(\"sqref\"));\n\n getChildElements(conditionalFormattingNode, \"cfRule\").forEach((cfRuleNode) => {\n const parsedRule = isExtended\n ? parseExtendedConditionalFormatRule(cfRuleNode, ranges)\n : parseStandardConditionalFormatRule(cfRuleNode, ranges, differentialStyles);\n if (parsedRule) {\n if (isExtended) {\n extendedRules.push(parsedRule);\n } else {\n standardRules.push(parsedRule);\n }\n }\n });\n });\n\n const mergedRules: XlsxConditionalFormatRule[] = [];\n const usedExtendedRuleIds = new Set<string>();\n const extendedRulesById = new Map(\n extendedRules\n .filter((rule) => typeof rule.id === \"string\" && rule.id.length > 0)\n .map((rule) => [rule.id as string, rule])\n );\n\n standardRules.forEach((rule) => {\n const matchingExtendedRule = rule.id ? extendedRulesById.get(rule.id) : undefined;\n if (matchingExtendedRule) {\n usedExtendedRuleIds.add(rule.id as string);\n mergedRules.push(mergeConditionalFormatRule(rule, matchingExtendedRule));\n return;\n }\n\n mergedRules.push(rule);\n });\n\n extendedRules.forEach((rule) => {\n if (rule.id && usedExtendedRuleIds.has(rule.id)) {\n return;\n }\n\n mergedRules.push(rule);\n });\n\n return mergedRules\n .map((rule) => {\n const nextRule = { ...rule } as XlsxConditionalFormatRule & { id?: string };\n delete nextRule.id;\n return nextRule;\n })\n .filter((rule) => rule.ranges.length > 0)\n .sort((left, right) => left.priority - right.priority);\n}\n\nfunction parseSheetState(\n archive: ArchiveEntries,\n path: string,\n options?: ParseWorkbookStructureOptions & {\n differentialStyles?: XlsxResolvedCellStyle[];\n defaultFont?: {\n family?: string;\n sizePt?: number;\n } | null;\n }\n): WorkbookSheetState | null {\n const xml = readArchiveText(archive, path);\n if (!xml) {\n return null;\n }\n\n const document = parseXml(xml);\n if (!document) {\n return null;\n }\n\n const includeCachedFormulaValues = options?.includeCachedFormulaValues ?? true;\n const autoFilterRanges = parseSqrefRanges(getLocalElements(document, \"autoFilter\")[0]?.getAttribute(\"ref\"));\n const cachedFormulaValues: Record<string, string> = {};\n const conditionalFormatRules = parseConditionalFormatRules(document, options?.differentialStyles ?? []);\n const sparklines = parseSheetSparklines(document, options?.themePalette);\n const sheetFormatNode = getLocalElements(document, \"sheetFormatPr\")[0] ?? null;\n const sheetViewNode = getLocalElements(document, \"sheetView\")[0] ?? null;\n const rowHeightOverridesPx: Record<number, number> = {};\n const colWidthOverridesPx: Record<number, number> = {};\n const rowStyleIds: Record<number, number> = {};\n const cellStyleIds: Record<string, number> = {};\n const colStyleIds: Record<number, number> = {};\n let minContentCol = Number.POSITIVE_INFINITY;\n let minContentRow = Number.POSITIVE_INFINITY;\n let maxContentCol = -1;\n let maxContentRow = -1;\n const columnWidthCharacterWidthPx = measureColumnCharacterWidthPx(\n options?.defaultFont?.family,\n options?.defaultFont?.sizePt\n );\n\n const defaultRowHeight = Number(sheetFormatNode?.getAttribute(\"defaultRowHeight\") ?? 15);\n const digitWidthPt = measureOfficeDigitWidthPt(options?.defaultFont?.family, options?.defaultFont?.sizePt);\n const storedColumnPixels = (width: number) => Math.max(0, Math.floor(\n ((256 * width + Math.floor(128 / digitWidthPt)) / 256) * digitWidthPt\n ) * (4 / 3));\n const explicitDefaultWidth = Number(sheetFormatNode?.getAttribute(\"defaultColWidth\") ?? Number.NaN);\n const baseWidth = Number(sheetFormatNode?.getAttribute(\"baseColWidth\") ?? 8);\n const officeLayout: XlsxOfficeLayout = {\n defaultColumnPx: Number.isFinite(explicitDefaultWidth) && explicitDefaultWidth > 0\n ? storedColumnPixels(explicitDefaultWidth)\n : ((Number.isFinite(baseWidth) && baseWidth > 0 ? baseWidth : 8) * digitWidthPt + 5) * (4 / 3),\n defaultRowPx: (Number.isFinite(defaultRowHeight) && defaultRowHeight > 0 ? defaultRowHeight : 15) * (4 / 3),\n columns: {},\n rows: {}\n };\n const defaultColWidth = Number(\n sheetFormatNode?.getAttribute(\"defaultColWidth\")\n ?? sheetFormatNode?.getAttribute(\"baseColWidth\")\n ?? 8.43\n );\n const rawZoomScale = Number(\n sheetViewNode?.getAttribute(\"zoomScale\")\n ?? sheetViewNode?.getAttribute(\"zoomScaleNormal\")\n ?? Number.NaN\n );\n const zoomScale = Number.isFinite(rawZoomScale) && rawZoomScale > 0\n ? rawZoomScale\n : 100;\n const trackContentCell = (cellRef: string | null) => {\n if (!cellRef) {\n return;\n }\n\n const cell = parseA1CellReference(cellRef);\n if (!cell) {\n return;\n }\n\n minContentCol = Math.min(minContentCol, cell.col);\n minContentRow = Math.min(minContentRow, cell.row);\n maxContentCol = Math.max(maxContentCol, cell.col);\n maxContentRow = Math.max(maxContentRow, cell.row);\n };\n const isMeaningfulCellNode = (cellNode: Element) => {\n if (getFirstChild(cellNode, \"f\") || getFirstChild(cellNode, \"is\")) {\n return true;\n }\n\n const valueNode = getFirstChild(cellNode, \"v\");\n return Boolean(valueNode && (valueNode.textContent ?? \"\").length > 0);\n };\n\n getLocalElements(document, \"row\").forEach((rowNode) => {\n const rowIndex = Number(rowNode.getAttribute(\"r\") ?? 0) - 1;\n const height = Number(rowNode.getAttribute(\"ht\") ?? Number.NaN);\n const styleId = Number(rowNode.getAttribute(\"s\") ?? Number.NaN);\n if (rowIndex >= 0 && Number.isFinite(height)) {\n rowHeightOverridesPx[rowIndex] = Math.max(MIN_ROW_HEIGHT_PX, Math.round(height * 1.33));\n officeLayout.rows[rowIndex] = { sourcePx: rowHeightOverridesPx[rowIndex], sizePx: Math.max(0, height * (4 / 3)) };\n }\n if (rowIndex >= 0 && Number.isFinite(styleId)) {\n rowStyleIds[rowIndex] = styleId;\n }\n\n getChildElements(rowNode, \"c\").forEach((cellNode) => {\n const cellRef = cellNode.getAttribute(\"r\");\n const cellStyleId = Number(cellNode.getAttribute(\"s\") ?? Number.NaN);\n if (cellRef && Number.isFinite(cellStyleId)) {\n cellStyleIds[cellRef] = cellStyleId;\n }\n if (isMeaningfulCellNode(cellNode)) {\n trackContentCell(cellRef);\n }\n\n if (includeCachedFormulaValues) {\n const formulaNode = getFirstChild(cellNode, \"f\");\n const valueNode = getFirstChild(cellNode, \"v\");\n if (formulaNode && valueNode && cellRef) {\n cachedFormulaValues[cellRef] = valueNode.textContent ?? \"\";\n }\n }\n });\n });\n\n const maxMetadataCol = Math.max(maxContentCol, 0) + 256;\n getLocalElements(document, \"col\").forEach((colNode) => {\n const min = Number(colNode.getAttribute(\"min\") ?? 0) - 1;\n const max = Number(colNode.getAttribute(\"max\") ?? 0) - 1;\n const width = Number(colNode.getAttribute(\"width\") ?? Number.NaN);\n const styleId = Number(colNode.getAttribute(\"style\") ?? Number.NaN);\n if (!Number.isFinite(width)) {\n if (!Number.isFinite(styleId)) {\n return;\n }\n }\n\n for (let col = min; col <= Math.min(max, maxMetadataCol); col += 1) {\n if (col >= 0) {\n if (Number.isFinite(width)) {\n const widthPx = sheetColumnWidthToPixels(width, columnWidthCharacterWidthPx);\n colWidthOverridesPx[col] = widthPx;\n officeLayout.columns[col] = { sourcePx: widthPx, sizePx: storedColumnPixels(width) };\n }\n if (Number.isFinite(styleId)) {\n colStyleIds[col] = styleId;\n }\n }\n }\n });\n\n return {\n autoFilterRanges,\n cachedFormulaValues,\n cellStyleIds,\n columnWidthCharacterWidthPx,\n colWidthOverridesPx,\n colStyleIds,\n conditionalFormatRules,\n defaultColWidthPx: sheetColumnWidthToPixels(defaultColWidth, columnWidthCharacterWidthPx),\n officeLayout,\n defaultRowHeightPx: Math.max(MIN_ROW_HEIGHT_PX, Math.round(defaultRowHeight * 1.33)),\n hasHorizontalMerges: false,\n hasVerticalMerges: false,\n maxHorizontalMergeEndCol: -1,\n maxVerticalMergeEndRow: -1,\n maxContentCol,\n maxContentRow,\n minContentCol: Number.isFinite(minContentCol) ? minContentCol : -1,\n minContentRow: Number.isFinite(minContentRow) ? minContentRow : -1,\n hiddenCols: [],\n hiddenRows: [],\n rowHeightOverridesPx,\n rowStyleIds,\n showGridLines: (sheetViewNode?.getAttribute(\"showGridLines\") ?? \"1\") !== \"0\",\n sparklines,\n zoomScale\n };\n}\n\nfunction clampChannel(value: number) {\n return Math.max(0, Math.min(255, Math.round(value)));\n}\n\nfunction normalizeHexColor(value: string) {\n const hex = value.replace(/^#/, \"\");\n if (hex.length === 8) {\n return `#${hex.slice(2).toLowerCase()}`;\n }\n if (hex.length === 6) {\n return `#${hex.toLowerCase()}`;\n }\n return \"#000000\";\n}\n\nfunction hexToRgb(hex: string) {\n const normalized = normalizeHexColor(hex);\n return {\n b: Number.parseInt(normalized.slice(5, 7), 16),\n g: Number.parseInt(normalized.slice(3, 5), 16),\n r: Number.parseInt(normalized.slice(1, 3), 16)\n };\n}\n\nfunction rgbToHex(red: number, green: number, blue: number) {\n return `#${[red, green, blue]\n .map((value) => clampChannel(value).toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\nfunction applyDrawingColorTransforms(\n baseColor: string,\n transformNodes: Element[]\n): DrawingColor {\n let { r, g, b } = hexToRgb(baseColor);\n let opacity = 1;\n let lumMod = 1;\n let lumOff = 0;\n\n transformNodes.forEach((node) => {\n const rawValue = Number(node.getAttribute(\"val\") ?? 0);\n const value = Number.isFinite(rawValue) ? rawValue / 100000 : 0;\n switch (node.localName) {\n case \"alpha\":\n opacity *= value;\n break;\n case \"lumMod\":\n lumMod *= value;\n break;\n case \"lumOff\":\n lumOff += value;\n break;\n case \"shade\":\n r *= value;\n g *= value;\n b *= value;\n break;\n case \"tint\":\n r = r + (255 - r) * value;\n g = g + (255 - g) * value;\n b = b + (255 - b) * value;\n break;\n default:\n break;\n }\n });\n\n if (lumMod !== 1 || lumOff !== 0) {\n r = r * lumMod + 255 * lumOff;\n g = g * lumMod + 255 * lumOff;\n b = b * lumMod + 255 * lumOff;\n }\n\n return {\n color: rgbToHex(r, g, b),\n opacity: Math.max(0, Math.min(1, opacity))\n };\n}\n\nfunction resolveThemeColorName(theme: ThemeState, name: string | null) {\n if (!name) {\n return null;\n }\n\n const aliases: Record<string, string> = {\n bg1: \"bg1\",\n bg2: \"bg2\",\n tx1: \"tx1\",\n tx2: \"tx2\"\n };\n const key = aliases[name] ?? name;\n return theme.colors.get(key) ?? null;\n}\n\nfunction resolveColorValue(colorNode: Element | null, theme: ThemeState): DrawingColor | null {\n if (!colorNode) {\n return null;\n }\n\n let baseColor: string | null = null;\n if (colorNode.localName === \"srgbClr\") {\n baseColor = normalizeHexColor(colorNode.getAttribute(\"val\") ?? \"\");\n } else if (colorNode.localName === \"schemeClr\") {\n baseColor = resolveThemeColorName(theme, colorNode.getAttribute(\"val\"));\n } else if (colorNode.localName === \"scrgbClr\") {\n const r = Number(colorNode.getAttribute(\"r\") ?? 0) * 255 / 100000;\n const g = Number(colorNode.getAttribute(\"g\") ?? 0) * 255 / 100000;\n const b = Number(colorNode.getAttribute(\"b\") ?? 0) * 255 / 100000;\n baseColor = rgbToHex(r, g, b);\n } else if (colorNode.localName === \"sysClr\") {\n baseColor = normalizeHexColor(colorNode.getAttribute(\"lastClr\") ?? \"\");\n }\n\n if (!baseColor) {\n return null;\n }\n\n return applyDrawingColorTransforms(baseColor, Array.from(colorNode.childNodes).filter(isElementNode));\n}\n\nfunction resolveFillColor(fillParent: Element | null, theme: ThemeState): DrawingColor | null {\n if (!fillParent) {\n return null;\n }\n\n const solidFillNode = getFirstChild(fillParent, \"solidFill\");\n if (!solidFillNode) {\n return null;\n }\n\n return resolveColorValue(Array.from(solidFillNode.childNodes).filter(isElementNode)[0] ?? null, theme);\n}\n\nfunction resolveTextTypeface(typeface: string | null, theme: ThemeState) {\n if (!typeface) {\n return undefined;\n }\n\n if (typeface === \"+mn-lt\" || typeface === \"+mn-ea\" || typeface === \"+mn-cs\") {\n return theme.minorLatinFont ?? undefined;\n }\n if (typeface === \"+mj-lt\" || typeface === \"+mj-ea\" || typeface === \"+mj-cs\") {\n return theme.majorLatinFont ?? undefined;\n }\n\n return typeface;\n}\n\nfunction isThemeTypeface(typeface: string | null | undefined) {\n return Boolean(typeface && typeface.startsWith(\"+\"));\n}\n\nfunction resolvePreferredTextTypeface(node: Element | null, theme: ThemeState) {\n if (!node) {\n return undefined;\n }\n\n const latin = getFirstChild(node, \"latin\")?.getAttribute(\"typeface\") ?? null;\n const eastAsian = getFirstChild(node, \"ea\")?.getAttribute(\"typeface\") ?? null;\n const complexScript = getFirstChild(node, \"cs\")?.getAttribute(\"typeface\") ?? null;\n const candidates = [latin, eastAsian, complexScript];\n const explicit = candidates.find((candidate) => candidate && !isThemeTypeface(candidate));\n if (explicit) {\n return explicit;\n }\n\n return resolveTextTypeface(candidates.find(Boolean) ?? null, theme);\n}\n\ntype ShapeTextStyle = {\n bold?: boolean;\n color?: string;\n fontFamily?: string;\n fontSizePt?: number;\n italic?: boolean;\n underline?: boolean;\n};\n\nfunction parseShapeTextStyle(node: Element | null, theme: ThemeState, fallbackColor?: DrawingColor | null): ShapeTextStyle {\n if (!node) {\n return {\n color: fallbackColor?.color\n };\n }\n\n const fillColor = resolveFillColor(node, theme) ?? fallbackColor ?? null;\n const underlineValue = node.getAttribute(\"u\");\n return {\n bold: node.getAttribute(\"b\") === \"1\" || undefined,\n color: fillColor?.color,\n fontFamily: resolvePreferredTextTypeface(node, theme),\n fontSizePt: node.getAttribute(\"sz\") ? Number(node.getAttribute(\"sz\")) / 100 : undefined,\n italic: node.getAttribute(\"i\") === \"1\" || undefined,\n underline: underlineValue && underlineValue !== \"none\" ? true : undefined\n };\n}\n\nfunction mergeShapeTextStyles(...styles: Array<ShapeTextStyle | undefined>): ShapeTextStyle {\n return styles.reduce<ShapeTextStyle>((acc, style) => {\n if (!style) {\n return acc;\n }\n return {\n bold: style.bold ?? acc.bold,\n color: style.color ?? acc.color,\n fontFamily: style.fontFamily ?? acc.fontFamily,\n fontSizePt: style.fontSizePt ?? acc.fontSizePt,\n italic: style.italic ?? acc.italic,\n underline: style.underline ?? acc.underline\n };\n }, {});\n}\n\nfunction parseMarker(node: Element | null) {\n if (!node) {\n return null;\n }\n\n const col = Number(getFirstChild(node, \"col\")?.textContent ?? 0);\n const row = Number(getFirstChild(node, \"row\")?.textContent ?? 0);\n const colOffsetEmu = Number(getFirstChild(node, \"colOff\")?.textContent ?? 0);\n const rowOffsetEmu = Number(getFirstChild(node, \"rowOff\")?.textContent ?? 0);\n\n return {\n col,\n colOffsetEmu,\n row,\n rowOffsetEmu\n };\n}\n\nexport function dukeDrawingAnchorToXlsxAnchor(anchor: DukeDrawingAnchor): XlsxImageAnchor {\n if (anchor.type === \"absolute\") {\n return {\n kind: \"absolute\",\n positionEmu: { x: anchor.xEmu, y: anchor.yEmu },\n sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }\n };\n }\n\n const from = {\n col: anchor.from.col,\n colOffsetEmu: anchor.from.colOffsetEmu ?? 0,\n row: anchor.from.row,\n rowOffsetEmu: anchor.from.rowOffsetEmu ?? 0\n };\n if (anchor.type === \"oneCell\") {\n return {\n from,\n kind: \"one-cell\",\n sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }\n };\n }\n\n return {\n from,\n kind: \"two-cell\",\n to: {\n col: anchor.to.col,\n colOffsetEmu: anchor.to.colOffsetEmu ?? 0,\n row: anchor.to.row,\n rowOffsetEmu: anchor.to.rowOffsetEmu ?? 0\n }\n };\n}\n\nexport function xlsxAnchorToDukeDrawingAnchor(\n anchor: XlsxImageAnchor,\n editAs?: \"twoCell\" | \"oneCell\" | \"absolute\"\n): DukeDrawingAnchor {\n if (anchor.kind === \"absolute\") {\n return {\n heightEmu: anchor.sizeEmu.cy,\n type: \"absolute\",\n widthEmu: anchor.sizeEmu.cx,\n xEmu: anchor.positionEmu.x,\n yEmu: anchor.positionEmu.y\n };\n }\n\n const from = {\n col: anchor.from.col,\n colOffsetEmu: anchor.from.colOffsetEmu,\n row: anchor.from.row,\n rowOffsetEmu: anchor.from.rowOffsetEmu\n };\n if (anchor.kind === \"one-cell\") {\n return {\n from,\n heightEmu: anchor.sizeEmu.cy,\n type: \"oneCell\",\n widthEmu: anchor.sizeEmu.cx\n };\n }\n\n return {\n editAs,\n from,\n to: {\n col: anchor.to.col,\n colOffsetEmu: anchor.to.colOffsetEmu,\n row: anchor.to.row,\n rowOffsetEmu: anchor.to.rowOffsetEmu\n },\n type: \"twoCell\"\n };\n}\n\nfunction mapDukeDrawingColor(color: DukeDrawingColor | undefined): XlsxCellStyleColorInput | undefined {\n if (!color) {\n return undefined;\n }\n switch (color.colorType) {\n case \"auto\":\n return { colorType: \"auto\" };\n case \"rgb\":\n return { b: color.b, colorType: \"rgb\", g: color.g, r: color.r };\n case \"argb\":\n return { a: color.a, b: color.b, colorType: \"argb\", g: color.g, r: color.r };\n case \"theme\":\n return { colorType: \"theme\", themeIndex: color.index, tint: color.tint };\n case \"indexed\":\n return { colorType: \"indexed\", paletteIndex: color.index };\n }\n}\n\nfunction mapDukeDrawingFont(font: DukeDrawingRunFont | undefined) {\n if (!font) {\n return undefined;\n }\n return {\n bold: font.bold,\n charset: font.charset,\n color: mapDukeDrawingColor(font.color),\n family: font.family,\n italic: font.italic,\n name: font.name,\n scheme: font.scheme,\n size: font.size,\n strikethrough: font.strikethrough,\n underline: font.underline,\n verticalAlign: font.verticalAlign\n };\n}\n\nfunction mapDukeDrawingText(text: DukeDrawingText): XlsxFormControlCaption {\n return {\n horizontalAlignment: text.horizontalAlignment,\n runs: text.runs.map((run) => ({ font: mapDukeDrawingFont(run.font), text: run.text })),\n verticalAlignment: text.verticalAlignment\n };\n}\n\nfunction resolveDukeDrawingColor(color: DukeDrawingColor | undefined, themePalette?: XlsxThemePalette | null) {\n if (!color) {\n return undefined;\n }\n switch (color.colorType) {\n case \"rgb\":\n return resolveWorkbookColor({ rgb: [color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, \"0\")).join(\"\") }) ?? undefined;\n case \"argb\":\n return resolveWorkbookColor({ argb: [color.a, color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, \"0\")).join(\"\") }) ?? undefined;\n case \"theme\":\n return resolveWorkbookColor({ theme: color.index, tint: color.tint }, themePalette) ?? undefined;\n default:\n return undefined;\n }\n}\n\nfunction resolveDukeDrawingText(\n text: DukeDrawingText,\n themePalette?: XlsxThemePalette | null\n) {\n const caption = mapDukeDrawingText(text);\n const label = normalizeControlLabel(text.runs.map((run) => run.text).join(\"\"));\n const firstStyledRun = text.runs.find((run) => run.font)?.font;\n const horizontalAlignment = text.horizontalAlignment;\n return {\n caption,\n fontFamily: firstStyledRun?.name,\n fontSizePt: firstStyledRun?.size,\n label,\n textAlign: horizontalAlignment === \"left\" || horizontalAlignment === \"center\" || horizontalAlignment === \"right\"\n ? horizontalAlignment\n : undefined,\n textColor: resolveDukeDrawingColor(firstStyledRun?.color, themePalette)\n };\n}\n\nfunction mapDukeFormControlKind(\n kind: DukeFormControlKind,\n themePalette?: XlsxThemePalette | null\n): Pick<XlsxFormControl, \"kind\"> & Partial<XlsxFormControl> {\n switch (kind.kind) {\n case \"button\":\n return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: \"button\" };\n case \"checkbox\":\n return {\n ...resolveDukeDrawingText(kind.caption, themePalette),\n checked: kind.state === \"checked\",\n kind: \"checkbox\",\n linkedCell: kind.cellLink,\n no3D: kind.no3D,\n state: kind.state\n };\n case \"optionButton\":\n return {\n ...resolveDukeDrawingText(kind.caption, themePalette),\n checked: kind.state === \"checked\",\n firstInGroup: kind.firstInGroup,\n kind: \"radio\",\n linkedCell: kind.cellLink,\n no3D: kind.no3D,\n state: kind.state\n };\n case \"label\":\n return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: \"label\" };\n case \"groupBox\":\n return {\n ...resolveDukeDrawingText(kind.caption, themePalette),\n kind: \"group-box\",\n no3D: kind.no3D\n };\n case \"listBox\":\n return {\n inputRange: kind.inputRange,\n kind: \"listbox\",\n linkedCell: kind.cellLink,\n no3D: kind.no3D,\n selected: [...kind.selected],\n selection: kind.selection\n };\n case \"dropdown\":\n return {\n inputRange: kind.inputRange,\n kind: \"dropdown\",\n lines: kind.lines,\n linkedCell: kind.cellLink,\n no3D: kind.no3D,\n selected: kind.selected\n };\n case \"scrollbar\":\n return {\n horizontal: kind.horizontal,\n increment: kind.increment,\n kind: \"scrollbar\",\n linkedCell: kind.cellLink,\n max: kind.max,\n min: kind.min,\n page: kind.page,\n value: kind.value\n };\n case \"spinner\":\n return {\n increment: kind.increment,\n kind: \"spinner\",\n linkedCell: kind.cellLink,\n max: kind.max,\n min: kind.min,\n value: kind.value\n };\n case \"unknown\":\n return {\n ...resolveDukeDrawingText(kind.caption, themePalette),\n kind: kind.objectType.toLowerCase() === \"editbox\" ? \"editbox\" : \"unknown\",\n legacyObjectType: kind.legacyObjectType,\n objectType: kind.objectType\n };\n }\n}\n\nfunction mapDukeFormControl(\n control: DukeFormControlDrawing & { anchor: DukeDrawingAnchor },\n controlIndex: number,\n workbookSheetIndex: number,\n themePalette?: XlsxThemePalette | null\n): XlsxFormControl {\n return {\n altText: control.altText,\n anchor: dukeDrawingAnchorToXlsxAnchor(control.anchor),\n controlIndex,\n editAs: control.anchor.type === \"twoCell\" ? control.anchor.editAs : undefined,\n hidden: control.hidden,\n id: `form-control-${workbookSheetIndex}-${controlIndex}`,\n locked: control.locked,\n macroName: control.formControl.macroName,\n name: control.name,\n printable: control.printable,\n rawClientData: control.formControl.rawClientData,\n rawObj: control.formControl.rawObj,\n rawProperties: control.formControl.rawProperties,\n sheetIndex: workbookSheetIndex,\n title: control.title,\n workbookSheetIndex,\n zIndex: (control.drawingPath[0] ?? controlIndex) + 1,\n ...mapDukeFormControlKind(control.formControl.kind, themePalette)\n };\n}\n\nexport function collectWorkbookFormControls(\n workbook: Workbook,\n themePalette?: XlsxThemePalette | null\n): XlsxFormControl[][] {\n return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {\n try {\n const controls = workbook.getSheet(workbookSheetIndex).formControls;\n return Array.isArray(controls)\n ? controls.flatMap((control, controlIndex) => control.anchor\n ? [mapDukeFormControl(control, controlIndex, workbookSheetIndex, themePalette)]\n : [])\n : [];\n } catch {\n return [];\n }\n });\n}\n\nfunction normalizeControlLabel(label: string | null | undefined) {\n if (!label) {\n return undefined;\n }\n\n const normalized = label\n .replace(/\\u00a0/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction parseAnchor(anchorNode: Element) {\n if (anchorNode.localName === \"anchor\") {\n const from = parseMarker(getFirstChild(anchorNode, \"from\"));\n const to = parseMarker(getFirstChild(anchorNode, \"to\"));\n return from && to ? { from, kind: \"two-cell\" as const, to } : null;\n }\n\n if (anchorNode.localName === \"twoCellAnchor\") {\n const from = parseMarker(getFirstChild(anchorNode, \"from\"));\n const to = parseMarker(getFirstChild(anchorNode, \"to\"));\n return from && to ? { from, kind: \"two-cell\" as const, to } : null;\n }\n\n if (anchorNode.localName === \"oneCellAnchor\") {\n const from = parseMarker(getFirstChild(anchorNode, \"from\"));\n const extNode = getFirstChild(anchorNode, \"ext\");\n return from && extNode\n ? {\n from,\n kind: \"one-cell\" as const,\n sizeEmu: {\n cx: Number(extNode.getAttribute(\"cx\") ?? 0),\n cy: Number(extNode.getAttribute(\"cy\") ?? 0)\n }\n }\n : null;\n }\n\n const positionNode = getFirstChild(anchorNode, \"pos\");\n const extNode = getFirstChild(anchorNode, \"ext\");\n return positionNode && extNode\n ? {\n kind: \"absolute\" as const,\n positionEmu: {\n x: Number(positionNode.getAttribute(\"x\") ?? 0),\n y: Number(positionNode.getAttribute(\"y\") ?? 0)\n },\n sizeEmu: {\n cx: Number(extNode.getAttribute(\"cx\") ?? 0),\n cy: Number(extNode.getAttribute(\"cy\") ?? 0)\n }\n }\n : null;\n}\n\nfunction anchorToRect(anchor: XlsxImage[\"anchor\"]): DrawingRectEmu {\n if (anchor.kind === \"absolute\") {\n return {\n cx: anchor.sizeEmu.cx,\n cy: anchor.sizeEmu.cy,\n x: anchor.positionEmu.x,\n y: anchor.positionEmu.y\n };\n }\n\n if (anchor.kind === \"one-cell\") {\n return {\n cx: anchor.sizeEmu.cx,\n cy: anchor.sizeEmu.cy,\n x: anchor.from.colOffsetEmu,\n y: anchor.from.rowOffsetEmu\n };\n }\n\n return {\n cx: Math.max(0, (anchor.to.col - anchor.from.col) * DEFAULT_COL_WIDTH_EMU + anchor.to.colOffsetEmu - anchor.from.colOffsetEmu),\n cy: Math.max(0, (anchor.to.row - anchor.from.row) * DEFAULT_ROW_HEIGHT_EMU + anchor.to.rowOffsetEmu - anchor.from.rowOffsetEmu),\n x: anchor.from.colOffsetEmu,\n y: anchor.from.rowOffsetEmu\n };\n}\n\nfunction resolveSheetColumnWidthPx(sheetState: WorkbookSheetState | null, col: number) {\n if (col < 0) {\n return 0;\n }\n return sheetState?.colWidthOverridesPx[col] ?? sheetState?.defaultColWidthPx ?? emuToPixels(DEFAULT_COL_WIDTH_EMU);\n}\n\nfunction resolveSheetRowHeightPx(sheetState: WorkbookSheetState | null, row: number) {\n if (row < 0) {\n return 0;\n }\n return sheetState?.rowHeightOverridesPx[row] ?? sheetState?.defaultRowHeightPx ?? emuToPixels(DEFAULT_ROW_HEIGHT_EMU);\n}\n\nfunction sumSheetColumnWidthsEmu(sheetState: WorkbookSheetState | null, beforeCol: number) {\n let total = 0;\n for (let col = 0; col < beforeCol; col += 1) {\n total += pixelsToEmu(resolveSheetColumnWidthPx(sheetState, col));\n }\n return total;\n}\n\nfunction sumSheetRowHeightsEmu(sheetState: WorkbookSheetState | null, beforeRow: number) {\n let total = 0;\n for (let row = 0; row < beforeRow; row += 1) {\n total += pixelsToEmu(resolveSheetRowHeightPx(sheetState, row));\n }\n return total;\n}\n\nfunction anchorToAbsoluteRect(anchor: XlsxImage[\"anchor\"], sheetState: WorkbookSheetState | null): DrawingRectEmu {\n if (anchor.kind === \"absolute\") {\n return anchorToRect(anchor);\n }\n\n if (anchor.kind === \"one-cell\") {\n return {\n cx: anchor.sizeEmu.cx,\n cy: anchor.sizeEmu.cy,\n x: sumSheetColumnWidthsEmu(sheetState, anchor.from.col) + anchor.from.colOffsetEmu,\n y: sumSheetRowHeightsEmu(sheetState, anchor.from.row) + anchor.from.rowOffsetEmu\n };\n }\n\n const left = sumSheetColumnWidthsEmu(sheetState, anchor.from.col) + anchor.from.colOffsetEmu;\n const top = sumSheetRowHeightsEmu(sheetState, anchor.from.row) + anchor.from.rowOffsetEmu;\n const right = sumSheetColumnWidthsEmu(sheetState, anchor.to.col) + anchor.to.colOffsetEmu;\n const bottom = sumSheetRowHeightsEmu(sheetState, anchor.to.row) + anchor.to.rowOffsetEmu;\n\n return {\n cx: Math.max(0, right - left),\n cy: Math.max(0, bottom - top),\n x: left,\n y: top\n };\n}\n\nfunction rectToAbsoluteAnchor(rect: DrawingRectEmu): XlsxImage[\"anchor\"] {\n return {\n kind: \"absolute\",\n positionEmu: {\n x: rect.x,\n y: rect.y\n },\n sizeEmu: {\n cx: rect.cx,\n cy: rect.cy\n }\n };\n}\n\nfunction parseTransformRect(xfrmNode: Element | null) {\n if (!xfrmNode) {\n return null;\n }\n\n const offNode = getFirstChild(xfrmNode, \"off\");\n const extNode = getFirstChild(xfrmNode, \"ext\");\n if (!offNode || !extNode) {\n return null;\n }\n\n return {\n cx: Number(extNode.getAttribute(\"cx\") ?? 0),\n cy: Number(extNode.getAttribute(\"cy\") ?? 0),\n flipH: xfrmNode.getAttribute(\"flipH\") === \"1\",\n flipV: xfrmNode.getAttribute(\"flipV\") === \"1\",\n rot: Number(xfrmNode.getAttribute(\"rot\") ?? 0) / 60000,\n x: Number(offNode.getAttribute(\"x\") ?? 0),\n y: Number(offNode.getAttribute(\"y\") ?? 0)\n };\n}\n\nfunction applyGroupTransform(rect: DrawingRectEmu, group: GroupTransform): DrawingRectEmu {\n return {\n cx: rect.cx * group.scaleX,\n cy: rect.cy * group.scaleY,\n x: group.x + (rect.x - group.chX) * group.scaleX,\n y: group.y + (rect.y - group.chY) * group.scaleY\n };\n}\n\nfunction parseGroupTransform(\n groupNode: Element,\n parentGroup: GroupTransform | null,\n fallbackAnchor: XlsxImage[\"anchor\"],\n sheetState: WorkbookSheetState | null\n) {\n const xfrmNode = getFirstDescendant(getFirstChild(groupNode, \"grpSpPr\") ?? groupNode, \"xfrm\");\n const anchorRect = anchorToAbsoluteRect(fallbackAnchor, sheetState);\n const rect = parseTransformRect(xfrmNode) ?? anchorRect;\n const rootRectMatchesAnchorOrigin = Math.abs(rect.x - anchorRect.x) <= EMU_PER_PIXEL\n && Math.abs(rect.y - anchorRect.y) <= EMU_PER_PIXEL;\n const chOffNode = getFirstChild(xfrmNode ?? groupNode, \"chOff\");\n const chExtNode = getFirstChild(xfrmNode ?? groupNode, \"chExt\");\n const rootScaleX = rect.cx !== 0 ? anchorRect.cx / rect.cx : 1;\n const rootScaleY = rect.cy !== 0 ? anchorRect.cy / rect.cy : 1;\n const useRectFrameForRoot = !parentGroup && (\n rootScaleX < 0.85\n || rootScaleX > 1.15\n || rootScaleY < 0.85\n || rootScaleY > 1.15\n );\n const absoluteRect = parentGroup\n ? applyGroupTransform(rect, parentGroup)\n : rootRectMatchesAnchorOrigin\n ? rect\n : anchorRect;\n const childRectX = parentGroup\n ? Number(chOffNode?.getAttribute(\"x\") ?? 0)\n : useRectFrameForRoot\n ? rect.x\n : Number(chOffNode?.getAttribute(\"x\") ?? 0);\n const childRectY = parentGroup\n ? Number(chOffNode?.getAttribute(\"y\") ?? 0)\n : useRectFrameForRoot\n ? rect.y\n : Number(chOffNode?.getAttribute(\"y\") ?? 0);\n const childRectCx = parentGroup\n ? Number(chExtNode?.getAttribute(\"cx\") ?? rect.cx)\n : useRectFrameForRoot\n ? rect.cx\n : Number(chExtNode?.getAttribute(\"cx\") ?? rect.cx);\n const childRectCy = parentGroup\n ? Number(chExtNode?.getAttribute(\"cy\") ?? rect.cy)\n : useRectFrameForRoot\n ? rect.cy\n : Number(chExtNode?.getAttribute(\"cy\") ?? rect.cy);\n return {\n chCx: childRectCx,\n chCy: childRectCy,\n chX: childRectX,\n chY: childRectY,\n cx: absoluteRect.cx,\n cy: absoluteRect.cy,\n scaleX: childRectCx !== 0 ? absoluteRect.cx / childRectCx : (parentGroup?.scaleX ?? 1),\n scaleY: childRectCy !== 0 ? absoluteRect.cy / childRectCy : (parentGroup?.scaleY ?? 1),\n x: absoluteRect.x,\n y: absoluteRect.y\n };\n}\n\nfunction anchorFromNodeOrFallback(\n node: Element,\n fallbackAnchor: XlsxImage[\"anchor\"],\n parentGroup: GroupTransform | null\n) {\n const xfrmNode = getFirstDescendant(node, \"xfrm\");\n const rect = parseTransformRect(xfrmNode);\n if (rect) {\n const scaleX = parentGroup?.scaleX ?? 1;\n const scaleY = parentGroup?.scaleY ?? 1;\n return {\n anchor: parentGroup ? rectToAbsoluteAnchor(applyGroupTransform(rect, parentGroup)) : fallbackAnchor,\n flipH: rect.flipH,\n flipV: rect.flipV,\n rotationDeg: rect.rot,\n scaleX,\n scaleY\n };\n }\n\n return {\n anchor: fallbackAnchor,\n flipH: false,\n flipV: false,\n rotationDeg: 0,\n scaleX: parentGroup?.scaleX ?? 1,\n scaleY: parentGroup?.scaleY ?? 1\n };\n}\n\nfunction mapParagraphAlign(value: string | null | undefined): XlsxShape[\"paragraphs\"][number][\"align\"] | undefined {\n switch (value) {\n case \"ctr\":\n return \"center\";\n case \"just\":\n return \"justify\";\n case \"r\":\n return \"right\";\n case \"l\":\n return \"left\";\n default:\n return undefined;\n }\n}\n\nfunction mapVerticalAnchor(value: string | null | undefined): \"bottom\" | \"middle\" | \"top\" {\n switch (value) {\n case \"b\":\n return \"bottom\";\n case \"ctr\":\n return \"middle\";\n default:\n return \"top\";\n }\n}\n\nfunction createImageAnchorNodes(document: XMLDocument) {\n return document.documentElement\n ? Array.from(document.documentElement.childNodes).filter(\n (node): node is Element =>\n isElementNode(node) &&\n (node.localName === \"twoCellAnchor\" || node.localName === \"oneCellAnchor\" || node.localName === \"absoluteAnchor\")\n )\n : [];\n}\n\nfunction createImageSource(bytes: Uint8Array, mimeType: string, objectUrls: string[]) {\n if (typeof URL !== \"undefined\" && typeof Blob !== \"undefined\") {\n const objectUrl = URL.createObjectURL(new Blob([cloneBytes(bytes)], { type: mimeType }));\n objectUrls.push(objectUrl);\n return objectUrl;\n }\n\n let binary = \"\";\n for (const value of bytes) {\n binary += String.fromCharCode(value);\n }\n const base64 = typeof btoa === \"function\" ? btoa(binary) : \"\";\n return `data:${mimeType};base64,${base64}`;\n}\n\nfunction isStrokeOnlyGeometry(geometry: string) {\n return geometry === \"line\" || geometry === \"arc\" || geometry === \"leftBrace\";\n}\n\nfunction parseShapeStroke(node: Element, styleNode: Element | null, theme: ThemeState): XlsxShape[\"stroke\"] | undefined {\n const lineNode = getFirstDescendant(node, \"ln\");\n const lineRefNode = styleNode ? getFirstChild(styleNode, \"lnRef\") : null;\n if (!lineNode && !lineRefNode) {\n return undefined;\n }\n\n if (lineNode && getFirstChild(lineNode, \"noFill\")) {\n return { none: true };\n }\n\n const color = resolveFillColor(lineNode, theme)\n ?? resolveColorValue(Array.from(lineRefNode?.childNodes ?? []).filter(isElementNode)[0] ?? null, theme);\n const widthEmu = Number(lineNode?.getAttribute(\"w\") ?? 0);\n return {\n color: color?.color,\n dash: getFirstChild(lineNode ?? node, \"prstDash\")?.getAttribute(\"val\") ?? undefined,\n headEndType: getFirstChild(lineNode ?? node, \"headEnd\")?.getAttribute(\"type\") ?? undefined,\n none: false,\n opacity: color?.opacity,\n tailEndType: getFirstChild(lineNode ?? node, \"tailEnd\")?.getAttribute(\"type\") ?? undefined,\n widthPx: widthEmu > 0 ? emuToPixels(widthEmu) : undefined\n };\n}\n\nfunction parseShapeFill(\n node: Element,\n styleNode: Element | null,\n theme: ThemeState,\n geometry: string\n): XlsxShape[\"fill\"] | undefined {\n const shapePropsNode = getFirstChild(node, \"spPr\") ?? node;\n const noFillNode = getFirstChild(shapePropsNode, \"noFill\");\n if (noFillNode) {\n return { none: true };\n }\n\n if (isStrokeOnlyGeometry(geometry)) {\n return { none: true };\n }\n\n const fillRefNode = styleNode ? getFirstChild(styleNode, \"fillRef\") : null;\n const fillColor = resolveFillColor(shapePropsNode, theme)\n ?? resolveColorValue(Array.from(fillRefNode?.childNodes ?? []).filter(isElementNode)[0] ?? null, theme);\n if (!fillColor) {\n return undefined;\n }\n\n return {\n color: fillColor.color,\n none: false,\n opacity: fillColor.opacity\n };\n}\n\nfunction parsePointNode(pointNode: Element | null) {\n if (!pointNode) {\n return null;\n }\n\n const x = Number(pointNode.getAttribute(\"x\") ?? Number.NaN);\n const y = Number(pointNode.getAttribute(\"y\") ?? Number.NaN);\n return Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;\n}\n\nfunction parseCustomGeometryPath(shapeNode: Element): ShapeVectorPath | undefined {\n const pathNode = getFirstDescendant(shapeNode, \"path\");\n if (!pathNode) {\n return undefined;\n }\n\n const width = Number(pathNode.getAttribute(\"w\") ?? Number.NaN);\n const height = Number(pathNode.getAttribute(\"h\") ?? Number.NaN);\n if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {\n return undefined;\n }\n\n const commands: string[] = [];\n let lastPoint: { x: number; y: number } | null = null;\n for (const child of Array.from(pathNode.childNodes).filter(isElementNode)) {\n if (child.localName === \"moveTo\") {\n const point = parsePointNode(getFirstChild(child, \"pt\"));\n if (point) {\n commands.push(`M ${point.x} ${point.y}`);\n lastPoint = point;\n }\n continue;\n }\n\n if (child.localName === \"lnTo\") {\n const point = parsePointNode(getFirstChild(child, \"pt\"));\n if (point && (!lastPoint || point.x !== lastPoint.x || point.y !== lastPoint.y)) {\n commands.push(`L ${point.x} ${point.y}`);\n lastPoint = point;\n }\n continue;\n }\n\n if (child.localName === \"cubicBezTo\") {\n const points = getChildElements(child, \"pt\").map(parsePointNode).filter((point): point is { x: number; y: number } => point !== null);\n if (points.length === 3) {\n commands.push(`C ${points[0].x} ${points[0].y} ${points[1].x} ${points[1].y} ${points[2].x} ${points[2].y}`);\n lastPoint = points[2];\n }\n continue;\n }\n\n if (child.localName === \"close\") {\n commands.push(\"Z\");\n lastPoint = null;\n }\n }\n\n if (commands.length === 0) {\n return undefined;\n }\n\n return {\n path: commands.join(\" \"),\n viewBox: {\n height,\n width\n }\n };\n}\n\nfunction parseShapeGeometryAdjustments(shapeNode: Element) {\n const presetGeometry = getFirstDescendant(shapeNode, \"prstGeom\");\n if (!presetGeometry) {\n return undefined;\n }\n\n const adjustments: Record<string, number> = {};\n getChildElements(getFirstChild(presetGeometry, \"avLst\"), \"gd\").forEach((adjustmentNode) => {\n const name = adjustmentNode.getAttribute(\"name\");\n const formula = adjustmentNode.getAttribute(\"fmla\") ?? \"\";\n const match = formula.match(/^val\\s+(-?\\d+(?:\\.\\d+)?)$/);\n if (!name || !match) {\n return;\n }\n\n const value = Number.parseFloat(match[1] ?? \"\");\n if (Number.isFinite(value)) {\n adjustments[name] = value;\n }\n });\n\n return Object.keys(adjustments).length > 0 ? adjustments : undefined;\n}\n\nfunction parseShapeParagraphs(\n shapeNode: Element,\n styleNode: Element | null,\n theme: ThemeState\n): XlsxShape[\"paragraphs\"] {\n const txBodyNode = getFirstChild(shapeNode, \"txBody\");\n if (!txBodyNode) {\n return [];\n }\n\n const defaultFontRef = styleNode ? getFirstChild(styleNode, \"fontRef\") : null;\n const defaultFontColor = resolveColorValue(Array.from(defaultFontRef?.childNodes ?? []).filter(isElementNode)[0] ?? null, theme);\n const listStyleNode = getFirstChild(txBodyNode, \"lstStyle\");\n const paragraphs: XlsxShape[\"paragraphs\"] = [];\n\n getChildElements(txBodyNode, \"p\").forEach((paragraphNode) => {\n const paragraphProps = getFirstChild(paragraphNode, \"pPr\");\n const paragraphLevel = Number(paragraphProps?.getAttribute(\"lvl\") ?? 0);\n const listLevelProps = getFirstChild(listStyleNode ?? txBodyNode, `lvl${paragraphLevel + 1}pPr`);\n const inheritedStyle = mergeShapeTextStyles(\n parseShapeTextStyle(getFirstChild(listLevelProps ?? txBodyNode, \"defRPr\"), theme, defaultFontColor),\n parseShapeTextStyle(getFirstChild(paragraphProps ?? paragraphNode, \"defRPr\"), theme, defaultFontColor)\n );\n const runs: XlsxShape[\"paragraphs\"][number][\"runs\"] = [];\n let sawRenderableChild = false;\n\n Array.from(paragraphNode.childNodes).filter(isElementNode).forEach((child) => {\n if (child.localName === \"br\") {\n sawRenderableChild = true;\n runs.push({ text: \"\\n\" });\n return;\n }\n if (child.localName !== \"r\") {\n return;\n }\n\n sawRenderableChild = true;\n const text = getFirstChild(child, \"t\")?.textContent ?? \"\";\n const runProps = getFirstChild(child, \"rPr\");\n const runStyle = mergeShapeTextStyles(\n inheritedStyle,\n parseShapeTextStyle(runProps, theme, defaultFontColor)\n );\n\n runs.push({\n bold: runStyle.bold,\n color: runStyle.color,\n fontFamily: runStyle.fontFamily,\n fontSizePt: runStyle.fontSizePt,\n italic: runStyle.italic,\n text,\n underline: runStyle.underline\n });\n });\n\n if (runs.length === 0) {\n if (!sawRenderableChild && !getFirstChild(paragraphNode, \"endParaRPr\")) {\n return;\n }\n\n runs.push({\n bold: inheritedStyle.bold,\n color: inheritedStyle.color,\n fontFamily: inheritedStyle.fontFamily,\n fontSizePt: inheritedStyle.fontSizePt,\n italic: inheritedStyle.italic,\n text: \" \",\n underline: inheritedStyle.underline\n });\n }\n\n paragraphs.push({\n align: mapParagraphAlign(paragraphProps?.getAttribute(\"algn\")),\n runs\n });\n });\n\n return paragraphs;\n}\n\nfunction parseTextBox(shapeNode: Element): XlsxShape[\"textBox\"] | undefined {\n const txBodyNode = getFirstChild(shapeNode, \"txBody\");\n if (!txBodyNode) {\n return undefined;\n }\n\n const bodyProps = getFirstChild(txBodyNode, \"bodyPr\");\n const leftInset = emuToPixels(Number(bodyProps?.getAttribute(\"lIns\") ?? 91440));\n const rightInset = emuToPixels(Number(bodyProps?.getAttribute(\"rIns\") ?? 91440));\n const topInset = emuToPixels(Number(bodyProps?.getAttribute(\"tIns\") ?? 45720));\n const bottomInset = emuToPixels(Number(bodyProps?.getAttribute(\"bIns\") ?? 45720));\n\n return {\n horizontalAlign: bodyProps?.getAttribute(\"anchorCtr\") === \"1\" ? \"center\" : \"left\",\n insetPx: {\n bottom: bottomInset,\n left: leftInset,\n right: rightInset,\n top: topInset\n },\n verticalAlign: mapVerticalAnchor(bodyProps?.getAttribute(\"anchor\"))\n };\n}\n\nfunction getHyperlinkTarget(\n node: Element | null,\n drawingRelationships: Map<string, RelationshipRecord>\n) {\n const hyperlinkNode = node ? getFirstDescendant(node, \"hlinkClick\") : null;\n const hyperlinkTargetNode = hyperlinkNode ?? node;\n const hyperlinkId = hyperlinkTargetNode ? getRelationshipId(hyperlinkTargetNode) : null;\n return hyperlinkId ? drawingRelationships.get(hyperlinkId)?.target ?? undefined : undefined;\n}\n\nfunction parsePictureNode(\n pictureNode: Element,\n fallbackAnchor: XlsxImage[\"anchor\"],\n drawingRelationships: Map<string, RelationshipRecord>,\n archive: ArchiveEntries,\n contentTypes: ContentTypesState,\n objectUrls: string[],\n workbookSheetIndex: number,\n imageId: string,\n zIndex: number,\n parentGroup: GroupTransform | null\n) {\n const blipNode = getFirstDescendant(pictureNode, \"blip\");\n const svgBlipNode = blipNode ? getFirstDescendant(blipNode, \"svgBlip\") : null;\n const embedId = (svgBlipNode ? getEmbeddedRelationshipId(svgBlipNode) : null) ?? (blipNode ? getEmbeddedRelationshipId(blipNode) : null);\n if (!embedId) {\n return null;\n }\n\n const mediaRelationship = drawingRelationships.get(embedId);\n if (!mediaRelationship || mediaRelationship.type !== IMAGE_REL_TYPE) {\n return null;\n }\n\n const mediaBytes = archive[mediaRelationship.target];\n if (!mediaBytes) {\n return null;\n }\n\n const nonVisualProps = getFirstDescendant(pictureNode, \"cNvPr\");\n const transform = anchorFromNodeOrFallback(pictureNode, fallbackAnchor, parentGroup);\n return {\n image: {\n anchor: transform.anchor,\n description: nonVisualProps?.getAttribute(\"descr\") ?? undefined,\n hyperlink: getHyperlinkTarget(nonVisualProps ?? pictureNode, drawingRelationships),\n id: imageId,\n mediaPath: mediaRelationship.target,\n mimeType: resolveContentType(contentTypes, mediaRelationship.target),\n name: nonVisualProps?.getAttribute(\"name\") ?? undefined,\n sheetIndex: workbookSheetIndex,\n src: createImageSource(mediaBytes, resolveContentType(contentTypes, mediaRelationship.target), objectUrls),\n workbookSheetIndex,\n zIndex\n } satisfies XlsxImage,\n mediaPath: mediaRelationship.target\n };\n}\n\nfunction parseShapeNode(\n shapeNode: Element,\n fallbackAnchor: XlsxImage[\"anchor\"],\n drawingRelationships: Map<string, RelationshipRecord>,\n theme: ThemeState,\n workbookSheetIndex: number,\n shapeId: string,\n zIndex: number,\n parentGroup: GroupTransform | null\n) {\n const nonVisualProps = getFirstDescendant(shapeNode, \"cNvPr\");\n const styleNode = getFirstChild(shapeNode, \"style\");\n const transform = anchorFromNodeOrFallback(shapeNode, fallbackAnchor, parentGroup);\n const geometry = getFirstDescendant(shapeNode, \"prstGeom\")?.getAttribute(\"prst\")\n ?? (getFirstDescendant(shapeNode, \"custGeom\") ? \"custom\" : \"rect\");\n const customPath = geometry === \"custom\" ? parseCustomGeometryPath(shapeNode) : undefined;\n const geometryAdjustments = parseShapeGeometryAdjustments(shapeNode);\n return {\n anchor: transform.anchor,\n description: nonVisualProps?.getAttribute(\"descr\") ?? undefined,\n fill: parseShapeFill(shapeNode, styleNode, theme, geometry),\n flipH: transform.flipH,\n flipV: transform.flipV,\n geometry,\n geometryAdjustments,\n hidden: nonVisualProps?.getAttribute(\"hidden\") === \"1\" || undefined,\n hyperlink: getHyperlinkTarget(nonVisualProps ?? shapeNode, drawingRelationships),\n id: shapeId,\n name: nonVisualProps?.getAttribute(\"name\") ?? undefined,\n paragraphs: parseShapeParagraphs(shapeNode, styleNode, theme),\n rotationDeg: transform.rotationDeg,\n scaleX: transform.scaleX,\n scaleY: transform.scaleY,\n sheetIndex: workbookSheetIndex,\n svgPath: customPath?.path,\n svgViewBox: customPath?.viewBox,\n stroke: parseShapeStroke(shapeNode, styleNode, theme),\n textBox: parseTextBox(shapeNode),\n workbookSheetIndex,\n zIndex\n } satisfies XlsxShape;\n}\n\nfunction parseAnchorContents(\n anchorNode: Element,\n fallbackAnchor: XlsxImage[\"anchor\"],\n drawingRelationships: Map<string, RelationshipRecord>,\n archive: ArchiveEntries,\n contentTypes: ContentTypesState,\n objectUrls: string[],\n workbookSheetIndex: number,\n theme: ThemeState,\n ids: { image: number; shape: number; z: number },\n imageOriginsById: Map<string, WorkbookImageOrigin>,\n anchorIndex: number,\n sheetState: WorkbookSheetState | null,\n drawingPath: string,\n parentGroup: GroupTransform | null = null,\n picturePath: number[] = []\n) {\n const images: XlsxImage[] = [];\n const shapes: XlsxShape[] = [];\n const mediaPaths = new Set<string>();\n\n Array.from(anchorNode.childNodes).filter(isElementNode).forEach((child, childIndex) => {\n if (child.localName === \"pic\") {\n const imageId = `sheet-${workbookSheetIndex}-${ids.image}`;\n const parsed = parsePictureNode(\n child,\n fallbackAnchor,\n drawingRelationships,\n archive,\n contentTypes,\n objectUrls,\n workbookSheetIndex,\n imageId,\n ids.z++,\n parentGroup\n );\n ids.image += 1;\n if (parsed) {\n images.push(parsed.image);\n mediaPaths.add(parsed.mediaPath);\n imageOriginsById.set(imageId, {\n anchorIndex,\n drawingPath,\n picturePath: [...picturePath, childIndex],\n workbookSheetIndex\n });\n }\n return;\n }\n\n if (child.localName === \"sp\") {\n shapes.push(parseShapeNode(\n child,\n fallbackAnchor,\n drawingRelationships,\n theme,\n workbookSheetIndex,\n `shape-${workbookSheetIndex}-${ids.shape++}`,\n ids.z++,\n parentGroup\n ));\n return;\n }\n\n if (child.localName === \"cxnSp\") {\n shapes.push(parseShapeNode(\n child,\n fallbackAnchor,\n drawingRelationships,\n theme,\n workbookSheetIndex,\n `shape-${workbookSheetIndex}-${ids.shape++}`,\n ids.z++,\n parentGroup\n ));\n return;\n }\n\n if (child.localName !== \"grpSp\") {\n return;\n }\n\n const nextGroup = parseGroupTransform(\n child,\n parentGroup,\n fallbackAnchor,\n sheetState\n );\n const groupFallbackAnchor = rectToAbsoluteAnchor({\n cx: nextGroup.cx,\n cy: nextGroup.cy,\n x: nextGroup.x,\n y: nextGroup.y\n });\n const parsedGroup = parseAnchorContents(\n child,\n groupFallbackAnchor,\n drawingRelationships,\n archive,\n contentTypes,\n objectUrls,\n workbookSheetIndex,\n theme,\n ids,\n imageOriginsById,\n anchorIndex,\n sheetState,\n drawingPath,\n nextGroup,\n [...picturePath, childIndex]\n );\n parsedGroup.images.forEach((image) => images.push(image));\n parsedGroup.shapes.forEach((shape) => shapes.push(shape));\n parsedGroup.mediaPaths.forEach((path) => mediaPaths.add(path));\n });\n\n return {\n images,\n mediaPaths,\n shapes\n };\n}\n\nfunction parseDrawingObjects(\n archive: ArchiveEntries,\n contentTypes: ContentTypesState,\n drawingPath: string,\n objectUrls: string[],\n workbookSheetIndex: number,\n zIndexBase: number,\n theme: ThemeState,\n sheetState: WorkbookSheetState | null,\n imageOriginsById: Map<string, WorkbookImageOrigin>\n) {\n const drawingXml = readArchiveText(archive, drawingPath);\n if (!drawingXml) {\n return {\n images: [] as XlsxImage[],\n mediaPaths: [] as string[],\n shapes: [] as XlsxShape[]\n };\n }\n\n const drawingDocument = parseXml(drawingXml);\n if (!drawingDocument) {\n return {\n images: [] as XlsxImage[],\n mediaPaths: [] as string[],\n shapes: [] as XlsxShape[]\n };\n }\n\n const drawingRelationships = parseRelationships(archive, relsPathForDocument(drawingPath), drawingPath);\n const images: XlsxImage[] = [];\n const shapes: XlsxShape[] = [];\n const mediaPaths = new Set<string>();\n const anchorNodes = createImageAnchorNodes(drawingDocument);\n const ids = {\n image: zIndexBase,\n shape: zIndexBase,\n z: zIndexBase\n };\n\n anchorNodes.forEach((anchorNode, anchorIndex) => {\n const anchor = parseAnchor(anchorNode);\n if (!anchor) {\n return;\n }\n\n const parsed = parseAnchorContents(\n anchorNode,\n anchor,\n drawingRelationships,\n archive,\n contentTypes,\n objectUrls,\n workbookSheetIndex,\n theme,\n ids,\n imageOriginsById,\n anchorIndex,\n sheetState,\n drawingPath\n );\n parsed.images.forEach((image) => images.push(image));\n parsed.shapes.forEach((shape) => shapes.push(shape));\n parsed.mediaPaths.forEach((path) => mediaPaths.add(path));\n });\n\n return {\n images,\n mediaPaths: [...mediaPaths],\n shapes\n };\n}\n\nexport function refreshWorkbookFormControls(\n workbook: Workbook,\n _currentControlsByWorkbookSheetIndex: XlsxFormControl[][],\n themePalette?: XlsxThemePalette | null\n) {\n return collectWorkbookFormControls(workbook, themePalette);\n}\n\nexport function revokeWorkbookImageAssets(assets: WorkbookImageAssets | null) {\n if (!assets) {\n return;\n }\n\n for (const objectUrl of assets.objectUrls) {\n if (objectUrl.startsWith(\"blob:\")) {\n URL.revokeObjectURL(objectUrl);\n }\n }\n}\n\nfunction parseWorkbookStructureAssetsFromArchive(\n archive: ArchiveEntries,\n options?: ParseWorkbookStructureOptions\n): WorkbookStructureAssets & {\n contentTypes: ContentTypesState;\n theme: ThemeState;\n workbookSheets: WorkbookSheetInfo[];\n} {\n const contentTypes = parseContentTypes(archive);\n const workbookSheets = parseWorkbookSheets(archive);\n const theme = parseWorkbookTheme(archive);\n const themePalette = buildThemePalette(theme);\n const { defaultFont, differentialStyles, namedCellStyleByName, styleById, tableStyleByName } = parseWorkbookStyles(archive);\n return {\n contentTypes,\n namedCellStyleByName,\n sheetStatesByWorkbookSheetIndex: workbookSheets.map((sheet) => parseSheetState(archive, sheet.path, {\n ...options,\n differentialStyles,\n defaultFont,\n themePalette\n })),\n styleById,\n tableMetadataByWorkbookSheetIndex: workbookSheets.map(() => [] as WorkbookTableMetadata[]),\n tableStyleByName,\n theme,\n themePalette,\n workbookSheets\n };\n}\n\nexport function parseWorkbookStructureAssets(\n bytes: Uint8Array,\n options?: ParseWorkbookStructureOptions\n): WorkbookStructureAssets {\n const archive = unzipSync(bytes);\n const {\n namedCellStyleByName,\n sheetStatesByWorkbookSheetIndex,\n styleById,\n tableMetadataByWorkbookSheetIndex,\n tableStyleByName,\n themePalette\n } = parseWorkbookStructureAssetsFromArchive(archive, options);\n\n return {\n namedCellStyleByName,\n sheetStatesByWorkbookSheetIndex,\n styleById,\n tableMetadataByWorkbookSheetIndex,\n tableStyleByName,\n themePalette\n };\n}\n\nexport function parseWorkbookChartStyleAssets(bytes: Uint8Array): WorkbookChartStyleAssets {\n const archive = unzipSync(bytes);\n const {\n themePalette,\n workbookSheets\n } = parseWorkbookStructureAssetsFromArchive(archive);\n const sheetOrigins: Array<WorkbookImageSheetOrigin | null> = [];\n\n workbookSheets.forEach((sheet, workbookSheetIndex) => {\n const sheetRelationships = parseRelationships(archive, relsPathForDocument(sheet.path), sheet.path);\n const attachments: XlsxImageAttachment[] = [];\n\n for (const relationship of sheetRelationships.values()) {\n if (relationship.type !== DRAWING_REL_TYPE) {\n continue;\n }\n\n const drawingPath = relationship.target;\n const drawingRelsPath = relsPathForDocument(drawingPath);\n attachments.push({\n drawingPath,\n drawingRelsPath: archive[drawingRelsPath] ? drawingRelsPath : null,\n mediaPaths: []\n });\n }\n\n sheetOrigins[workbookSheetIndex] = attachments.length > 0\n ? {\n attachments,\n workbookSheetIndex\n }\n : null;\n });\n\n return {\n archive,\n sheetOrigins,\n themePalette\n };\n}\n\nexport function parseWorkbookImageAssets(\n bytes: Uint8Array,\n workbook?: Workbook\n): WorkbookImageAssets {\n const archive = unzipSync(bytes);\n const {\n contentTypes,\n namedCellStyleByName,\n sheetStatesByWorkbookSheetIndex,\n styleById,\n tableMetadataByWorkbookSheetIndex,\n tableStyleByName,\n theme,\n themePalette,\n workbookSheets\n } = parseWorkbookStructureAssetsFromArchive(archive);\n const objectUrls: string[] = [];\n const formControlsByWorkbookSheetIndex: XlsxFormControl[][] = [];\n const imagesByWorkbookSheetIndex: XlsxImage[][] = [];\n const shapesByWorkbookSheetIndex: XlsxShape[][] = [];\n const sheetOrigins: Array<WorkbookImageSheetOrigin | null> = [];\n const imageOriginsById = new Map<string, WorkbookImageOrigin>();\n const formControls = workbook\n ? collectWorkbookFormControls(workbook, themePalette)\n : [];\n\n workbookSheets.forEach((sheet, workbookSheetIndex) => {\n const sheetRelationships = parseRelationships(archive, relsPathForDocument(sheet.path), sheet.path);\n const attachments: XlsxImageAttachment[] = [];\n const imageList: XlsxImage[] = [];\n const shapeList: XlsxShape[] = [];\n let zIndexBase = 1;\n\n for (const relationship of sheetRelationships.values()) {\n if (relationship.type !== DRAWING_REL_TYPE) {\n continue;\n }\n\n const drawingPath = relationship.target;\n const drawingRelsPath = relsPathForDocument(drawingPath);\n const drawingImages = parseDrawingObjects(\n archive,\n contentTypes,\n drawingPath,\n objectUrls,\n workbookSheetIndex,\n zIndexBase,\n theme,\n sheetStatesByWorkbookSheetIndex[workbookSheetIndex] ?? null,\n imageOriginsById\n );\n imageList.push(...drawingImages.images);\n shapeList.push(...drawingImages.shapes);\n zIndexBase += drawingImages.images.length + drawingImages.shapes.length + 10;\n attachments.push({\n drawingPath,\n drawingRelsPath: archive[drawingRelsPath] ? drawingRelsPath : null,\n mediaPaths: drawingImages.mediaPaths\n });\n }\n\n const visibleShapeList = shapeList.filter((shape) => !shape.hidden);\n formControlsByWorkbookSheetIndex[workbookSheetIndex] = formControls[workbookSheetIndex] ?? [];\n imagesByWorkbookSheetIndex[workbookSheetIndex] = imageList;\n shapesByWorkbookSheetIndex[workbookSheetIndex] = visibleShapeList;\n sheetOrigins[workbookSheetIndex] = attachments.length > 0\n ? {\n attachments,\n workbookSheetIndex\n }\n : null;\n });\n\n return {\n archive,\n dirtyArchivePaths: new Set(),\n formControlsByWorkbookSheetIndex,\n imageOriginsById,\n imagesByWorkbookSheetIndex,\n namedCellStyleByName,\n objectUrls,\n shapesByWorkbookSheetIndex,\n sheetOrigins,\n sheetStatesByWorkbookSheetIndex,\n styleById,\n tableMetadataByWorkbookSheetIndex,\n tableStyleByName,\n themePalette\n };\n}\n\nfunction updateAnchorNode(anchorNode: Element, anchor: XlsxImage[\"anchor\"]) {\n if (anchor.kind === \"two-cell\") {\n updateMarkerElement(getFirstChild(anchorNode, \"from\"), anchor.from);\n updateMarkerElement(getFirstChild(anchorNode, \"to\"), anchor.to);\n return;\n }\n\n if (anchor.kind === \"one-cell\") {\n updateMarkerElement(getFirstChild(anchorNode, \"from\"), anchor.from);\n const extNode = getFirstChild(anchorNode, \"ext\");\n if (extNode) {\n extNode.setAttribute(\"cx\", String(Math.max(0, Math.round(anchor.sizeEmu.cx))));\n extNode.setAttribute(\"cy\", String(Math.max(0, Math.round(anchor.sizeEmu.cy))));\n }\n return;\n }\n\n const positionNode = getFirstChild(anchorNode, \"pos\");\n if (positionNode) {\n positionNode.setAttribute(\"x\", String(Math.max(0, Math.round(anchor.positionEmu.x))));\n positionNode.setAttribute(\"y\", String(Math.max(0, Math.round(anchor.positionEmu.y))));\n }\n const extNode = getFirstChild(anchorNode, \"ext\");\n if (extNode) {\n extNode.setAttribute(\"cx\", String(Math.max(0, Math.round(anchor.sizeEmu.cx))));\n extNode.setAttribute(\"cy\", String(Math.max(0, Math.round(anchor.sizeEmu.cy))));\n }\n}\n\nexport function updateWorkbookImageAnchor(\n assets: WorkbookImageAssets,\n imageId: string,\n anchor: XlsxImage[\"anchor\"]\n) {\n const origin = assets.imageOriginsById.get(imageId);\n if (!origin) {\n return false;\n }\n\n const attachments = assets.sheetOrigins[origin.workbookSheetIndex]?.attachments ?? [];\n for (const attachment of attachments) {\n if (attachment.drawingPath !== origin.drawingPath) {\n continue;\n }\n const drawingXml = readArchiveText(assets.archive, attachment.drawingPath);\n if (!drawingXml) {\n continue;\n }\n\n const drawingDocument = parseXml(drawingXml);\n if (!drawingDocument) {\n continue;\n }\n\n const anchorNodes = createImageAnchorNodes(drawingDocument);\n const anchorNode = anchorNodes[origin.anchorIndex];\n if (!anchorNode) {\n continue;\n }\n\n let pictureNode = anchorNode;\n let parentGroup: GroupTransform | null = null;\n let groupAnchor = parseAnchor(anchorNode);\n for (const childIndex of origin.picturePath) {\n const child = Array.from(pictureNode.childNodes).filter(isElementNode)[childIndex];\n if (!child || !groupAnchor) {\n break;\n }\n pictureNode = child;\n if (child.localName === \"grpSp\") {\n parentGroup = parseGroupTransform(\n child,\n parentGroup,\n groupAnchor,\n assets.sheetStatesByWorkbookSheetIndex[origin.workbookSheetIndex] ?? null\n );\n groupAnchor = rectToAbsoluteAnchor(parentGroup);\n }\n }\n if (pictureNode.localName !== \"pic\") {\n continue;\n }\n\n if (parentGroup) {\n const transform = getFirstDescendant(pictureNode, \"xfrm\");\n const off = transform ? getFirstChild(transform, \"off\") : null;\n const ext = transform ? getFirstChild(transform, \"ext\") : null;\n if (anchor.kind !== \"absolute\" || !off || !ext || !parentGroup.scaleX || !parentGroup.scaleY) {\n continue;\n }\n off.setAttribute(\"x\", String(Math.round(parentGroup.chX + (anchor.positionEmu.x - parentGroup.x) / parentGroup.scaleX)));\n off.setAttribute(\"y\", String(Math.round(parentGroup.chY + (anchor.positionEmu.y - parentGroup.y) / parentGroup.scaleY)));\n ext.setAttribute(\"cx\", String(Math.max(0, Math.round(anchor.sizeEmu.cx / parentGroup.scaleX))));\n ext.setAttribute(\"cy\", String(Math.max(0, Math.round(anchor.sizeEmu.cy / parentGroup.scaleY))));\n } else {\n updateAnchorNode(anchorNode, anchor);\n }\n assets.archive[attachment.drawingPath] = strToU8(serializeXml(drawingDocument));\n assets.dirtyArchivePaths.add(normalizeArchivePath(attachment.drawingPath));\n const imageList = assets.imagesByWorkbookSheetIndex[origin.workbookSheetIndex] ?? [];\n const imageIndex = imageList.findIndex((image) => image.id === imageId);\n if (imageIndex >= 0) {\n imageList[imageIndex] = {\n ...imageList[imageIndex],\n anchor\n };\n }\n return true;\n }\n\n return false;\n}\n\nfunction ensureRelationshipsDocument(archive: ArchiveEntries, relsPath: string) {\n const existing = readArchiveText(archive, relsPath);\n if (existing) {\n const parsed = parseXml(existing);\n if (parsed) {\n return parsed;\n }\n }\n\n return parseXml(\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Relationships xmlns=\"${PKG_REL_NS}\"></Relationships>`\n );\n}\n\nfunction ensureContentTypesDocument(archive: ArchiveEntries) {\n const existing = readArchiveText(archive, \"[Content_Types].xml\");\n if (existing) {\n const parsed = parseXml(existing);\n if (parsed) {\n return parsed;\n }\n }\n\n return parseXml(\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Types xmlns=\"${CONTENT_TYPES_NS}\"></Types>`\n );\n}\n\nfunction mergeContentTypeForPath(\n targetDocument: XMLDocument,\n originalDocument: XMLDocument | null,\n partPath: string\n) {\n const normalizedPartName = `/${normalizeArchivePath(partPath)}`;\n const targetRoot = targetDocument.documentElement;\n if (!targetRoot) {\n return;\n }\n\n const existingOverride = getLocalElements(targetDocument, \"Override\").find(\n (node) => node.getAttribute(\"PartName\") === normalizedPartName\n );\n if (!existingOverride && originalDocument) {\n const sourceOverride = getLocalElements(originalDocument, \"Override\").find(\n (node) => node.getAttribute(\"PartName\") === normalizedPartName\n );\n if (sourceOverride) {\n targetRoot.appendChild(sourceOverride.cloneNode(true));\n return;\n }\n }\n\n const extension = normalizedPartName.split(\".\").pop()?.toLowerCase();\n if (!extension) {\n return;\n }\n\n const existingDefault = getLocalElements(targetDocument, \"Default\").find(\n (node) => (node.getAttribute(\"Extension\") ?? \"\").toLowerCase() === extension\n );\n if (existingDefault) {\n return;\n }\n\n if (originalDocument) {\n const sourceDefault = getLocalElements(originalDocument, \"Default\").find(\n (node) => (node.getAttribute(\"Extension\") ?? \"\").toLowerCase() === extension\n );\n if (sourceDefault) {\n targetRoot.appendChild(sourceDefault.cloneNode(true));\n return;\n }\n }\n\n if (extension === \"xml\") {\n const defaultNode = targetDocument.createElementNS(CONTENT_TYPES_NS, \"Default\");\n defaultNode.setAttribute(\"Extension\", extension);\n defaultNode.setAttribute(\"ContentType\", \"application/xml\");\n targetRoot.appendChild(defaultNode);\n return;\n }\n\n if (extension === \"rels\") {\n const defaultNode = targetDocument.createElementNS(CONTENT_TYPES_NS, \"Default\");\n defaultNode.setAttribute(\"Extension\", extension);\n defaultNode.setAttribute(\"ContentType\", \"application/vnd.openxmlformats-package.relationships+xml\");\n targetRoot.appendChild(defaultNode);\n }\n}\n\nfunction removeDrawingReferences(sheetDocument: XMLDocument, relDocument: XMLDocument) {\n getLocalElements(sheetDocument, \"drawing\").forEach((node) => node.remove());\n getLocalElements(relDocument, \"Relationship\")\n .filter((node) => node.getAttribute(\"Type\") === DRAWING_REL_TYPE)\n .forEach((node) => node.remove());\n}\n\nfunction nextRelationshipId(relDocument: XMLDocument) {\n const existingIds = new Set(\n getLocalElements(relDocument, \"Relationship\")\n .map((node) => node.getAttribute(\"Id\"))\n .filter((value): value is string => Boolean(value))\n );\n\n let index = 1;\n while (existingIds.has(`rIdReactXlsxImage${index}`)) {\n index += 1;\n }\n\n return `rIdReactXlsxImage${index}`;\n}\n\nfunction appendSheetDrawingReference(\n sheetDocument: XMLDocument,\n relationshipId: string\n) {\n const worksheet = sheetDocument.documentElement;\n if (!worksheet) {\n return;\n }\n\n const drawingNode = sheetDocument.createElementNS(SPREADSHEET_NS, \"drawing\");\n drawingNode.setAttributeNS(REL_NS, \"r:id\", relationshipId);\n\n const successorNames = new Set([\n \"legacyDrawing\",\n \"legacyDrawingHF\",\n \"picture\",\n \"oleObjects\",\n \"controls\",\n \"webPublishItems\",\n \"tableParts\",\n \"extLst\"\n ]);\n const successor = Array.from(worksheet.childNodes).find((node) => (\n isElementNode(node) && node.namespaceURI === SPREADSHEET_NS && successorNames.has(node.localName)\n ));\n worksheet.insertBefore(drawingNode, successor ?? null);\n}\n\nexport function mergeWorkbookImageAssets(\n savedBytes: Uint8Array,\n sourceAssets: WorkbookImageAssets | null,\n sheetOrigins: Array<WorkbookImageSheetOrigin | null>\n) {\n if (!sourceAssets) {\n return cloneBytes(savedBytes);\n }\n\n try {\n const archive = unzipSync(savedBytes);\n const workbookSheets = parseWorkbookSheets(archive);\n const originalContentTypesDocument = parseXml(readArchiveText(sourceAssets.archive, \"[Content_Types].xml\") ?? \"\");\n const targetContentTypesDocument = ensureContentTypesDocument(archive);\n if (!targetContentTypesDocument) {\n return cloneBytes(savedBytes);\n }\n\n const copiedParts = new Set<string>();\n const copyDrawingPart = (path: string) => {\n const normalizedPath = normalizeArchivePath(path);\n const bytes = sourceAssets.archive[normalizedPath];\n if (!bytes || copiedParts.has(normalizedPath)) {\n return;\n }\n copiedParts.add(normalizedPath);\n archive[normalizedPath] = cloneBytes(bytes);\n mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, normalizedPath);\n\n const relsPath = relsPathForDocument(normalizedPath);\n const relsBytes = sourceAssets.archive[relsPath];\n if (!relsBytes) {\n return;\n }\n archive[relsPath] = cloneBytes(relsBytes);\n mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, relsPath);\n for (const relationship of parseRelationships(sourceAssets.archive, relsPath, normalizedPath).values()) {\n if (relationship.targetMode !== \"External\" && !relationship.type.endsWith(\"/hyperlink\")) {\n copyDrawingPart(relationship.target);\n }\n }\n };\n\n sheetOrigins.forEach((origin, workbookSheetIndex) => {\n if (!origin?.attachments.length) {\n return;\n }\n\n const currentSheet = workbookSheets[workbookSheetIndex];\n if (!currentSheet) {\n return;\n }\n\n const sheetXml = readArchiveText(archive, currentSheet.path);\n if (!sheetXml) {\n return;\n }\n\n const sheetDocument = parseXml(sheetXml);\n const relsPath = relsPathForDocument(currentSheet.path);\n const relDocument = ensureRelationshipsDocument(archive, relsPath);\n if (!sheetDocument || !relDocument) {\n return;\n }\n\n removeDrawingReferences(sheetDocument, relDocument);\n\n origin.attachments.forEach((attachment) => {\n const drawingBytes = sourceAssets.archive[attachment.drawingPath];\n if (!drawingBytes) {\n return;\n }\n\n copyDrawingPart(attachment.drawingPath);\n\n const relationshipId = nextRelationshipId(relDocument);\n const relationshipNode = relDocument.createElementNS(PKG_REL_NS, \"Relationship\");\n relationshipNode.setAttribute(\"Id\", relationshipId);\n relationshipNode.setAttribute(\"Type\", DRAWING_REL_TYPE);\n relationshipNode.setAttribute(\"Target\", relativeArchivePath(currentSheet.path, attachment.drawingPath));\n relDocument.documentElement?.appendChild(relationshipNode);\n appendSheetDrawingReference(sheetDocument, relationshipId);\n });\n\n archive[currentSheet.path] = strToU8(serializeXml(sheetDocument));\n archive[relsPath] = strToU8(serializeXml(relDocument));\n mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, relsPath);\n });\n\n for (const dirtyPath of sourceAssets.dirtyArchivePaths) {\n const bytes = sourceAssets.archive[dirtyPath];\n if (!bytes) {\n continue;\n }\n archive[dirtyPath] = cloneBytes(bytes);\n mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, dirtyPath);\n }\n\n const hasDrawingOverride = getLocalElements(targetContentTypesDocument, \"Override\").some(\n (node) => node.getAttribute(\"ContentType\") === DRAWING_CONTENT_TYPE\n );\n if (!hasDrawingOverride) {\n for (const path of Object.keys(archive)) {\n if (path.startsWith(\"xl/drawings/\") && path.endsWith(\".xml\")) {\n mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, path);\n }\n }\n }\n\n archive[\"[Content_Types].xml\"] = strToU8(serializeXml(targetContentTypesDocument));\n return zipSync(archive, { level: 6 });\n } catch {\n return cloneBytes(savedBytes);\n }\n}\n\nexport function emuToPixels(value: number) {\n return value / EMU_PER_PIXEL;\n}\n\nexport function pixelsToEmu(value: number) {\n return value * EMU_PER_PIXEL;\n}\n\nexport function pxToSheetColumnWidth(widthPx: number) {\n return (Math.max(widthPx, MIN_COL_WIDTH_PX) - 5) / 7;\n}\n\nexport function resolveSheetColumnWidthPixels(width: number, columnWidthCharacterWidthPx?: number) {\n return sheetColumnWidthToPixels(width, columnWidthCharacterWidthPx);\n}\n\nexport function resolveSheetRowHeightPixels(height: number) {\n return Math.max(Math.round(height * 1.33), MIN_ROW_HEIGHT_PX);\n}\n\nexport function resolveRenderedSheetAxisPixels(sizePx: number, showGridLines = true) {\n return Math.max(0, sizePx) + (showGridLines ? resolveDeviceGridlineThicknessPx() : 0);\n}\n\nexport function resolveContentSheetAxisPixels(sizePx: number, showGridLines = true) {\n return Math.max(0, sizePx - (showGridLines ? resolveDeviceGridlineThicknessPx() : 0));\n}\n\nfunction markerFromOffset(offsetPx: number, getSizePx: (index: number) => number) {\n let remaining = Math.max(0, offsetPx);\n let index = 0;\n while (remaining > 0) {\n const size = Math.max(0, getSizePx(index));\n if (remaining < size) {\n break;\n }\n remaining -= size;\n index += 1;\n }\n\n return {\n index,\n offsetPx: remaining\n };\n}\n\nexport function rectToImageAnchor(\n rect: XlsxImageRect,\n currentAnchor: XlsxImage[\"anchor\"],\n options: {\n contentOffsetLeft: number;\n contentOffsetTop: number;\n getColumnWidthPx: (col: number) => number;\n getRowHeightPx: (row: number) => number;\n }\n): XlsxImage[\"anchor\"] {\n const contentLeft = Math.max(0, rect.left - options.contentOffsetLeft);\n const contentTop = Math.max(0, rect.top - options.contentOffsetTop);\n const contentRight = Math.max(contentLeft + 1, rect.left + rect.width - options.contentOffsetLeft);\n const contentBottom = Math.max(contentTop + 1, rect.top + rect.height - options.contentOffsetTop);\n\n if (currentAnchor.kind === \"absolute\") {\n return {\n kind: \"absolute\",\n positionEmu: {\n x: pixelsToEmu(contentLeft),\n y: pixelsToEmu(contentTop)\n },\n sizeEmu: {\n cx: pixelsToEmu(rect.width),\n cy: pixelsToEmu(rect.height)\n }\n };\n }\n\n const fromCol = markerFromOffset(contentLeft, options.getColumnWidthPx);\n const fromRow = markerFromOffset(contentTop, options.getRowHeightPx);\n if (currentAnchor.kind === \"one-cell\") {\n return {\n from: {\n col: fromCol.index,\n colOffsetEmu: pixelsToEmu(fromCol.offsetPx),\n row: fromRow.index,\n rowOffsetEmu: pixelsToEmu(fromRow.offsetPx)\n },\n kind: \"one-cell\",\n sizeEmu: {\n cx: pixelsToEmu(rect.width),\n cy: pixelsToEmu(rect.height)\n }\n };\n }\n\n const toCol = markerFromOffset(contentRight, options.getColumnWidthPx);\n const toRow = markerFromOffset(contentBottom, options.getRowHeightPx);\n return {\n from: {\n col: fromCol.index,\n colOffsetEmu: pixelsToEmu(fromCol.offsetPx),\n row: fromRow.index,\n rowOffsetEmu: pixelsToEmu(fromRow.offsetPx)\n },\n kind: \"two-cell\",\n to: {\n col: toCol.index,\n colOffsetEmu: pixelsToEmu(toCol.offsetPx),\n row: toRow.index,\n rowOffsetEmu: pixelsToEmu(toRow.offsetPx)\n }\n };\n}\n\nexport function resizeImageRect(\n rect: XlsxImageRect,\n handle: XlsxImageResizeHandlePosition,\n deltaX: number,\n deltaY: number,\n minimumSize = 16\n) {\n let left = rect.left;\n let top = rect.top;\n let width = rect.width;\n let height = rect.height;\n\n if (handle.includes(\"w\")) {\n left += deltaX;\n width -= deltaX;\n }\n if (handle.includes(\"e\")) {\n width += deltaX;\n }\n if (handle.includes(\"n\")) {\n top += deltaY;\n height -= deltaY;\n }\n if (handle.includes(\"s\")) {\n height += deltaY;\n }\n\n if (width < minimumSize) {\n if (handle.includes(\"w\")) {\n left -= minimumSize - width;\n }\n width = minimumSize;\n }\n if (height < minimumSize) {\n if (handle.includes(\"n\")) {\n top -= minimumSize - height;\n }\n height = minimumSize;\n }\n\n return { height, left, top, width };\n}\n","import type { Workbook } from \"@dukelib/sheets-wasm\";\n\nconst SHEET_REF_REGEX = /'((?:[^']|'')+)'!|([A-Za-z_\\u0080-\\uFFFF][\\w.\\u0080-\\uFFFF]*)!/g;\n\ntype FormulaCell = { formula?: string | null; row?: number; col?: number };\n\ntype CellPosition = { sheetIdx: number; row: number; col: number };\n\nfunction collectReferencedSheetNames(workbook: Workbook): Set<string> {\n const referenced = new Set<string>();\n for (let sheetIdx = 0; sheetIdx < workbook.sheetCount; sheetIdx += 1) {\n let sheet;\n try {\n sheet = workbook.getSheet(sheetIdx);\n } catch {\n continue;\n }\n const cells = sheet.formulaCells as FormulaCell[] | null | undefined;\n if (!Array.isArray(cells)) {\n continue;\n }\n for (const cell of cells) {\n const formula = cell?.formula;\n if (!formula) {\n continue;\n }\n SHEET_REF_REGEX.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = SHEET_REF_REGEX.exec(formula)) !== null) {\n const raw = match[1] ?? match[2];\n if (!raw) {\n continue;\n }\n referenced.add(raw.replace(/''/g, \"'\"));\n }\n }\n }\n return referenced;\n}\n\nfunction hasUnresolvedSheetReferences(workbook: Workbook): boolean {\n let names: string[];\n try {\n names = workbook.sheetNames;\n } catch {\n return false;\n }\n const known = new Set(names);\n const referenced = collectReferencedSheetNames(workbook);\n for (const name of referenced) {\n if (!known.has(name)) {\n return true;\n }\n }\n return false;\n}\n\nfunction collectFormulaCellsWithSavedValues(workbook: Workbook): CellPosition[] {\n const positions: CellPosition[] = [];\n for (let sheetIdx = 0; sheetIdx < workbook.sheetCount; sheetIdx += 1) {\n let sheet;\n try {\n sheet = workbook.getSheet(sheetIdx);\n } catch {\n continue;\n }\n const cells = sheet.formulaCells as FormulaCell[] | null | undefined;\n if (!Array.isArray(cells)) {\n continue;\n }\n for (const cell of cells) {\n if (typeof cell?.row !== \"number\" || typeof cell?.col !== \"number\") {\n continue;\n }\n let saved;\n try {\n saved = sheet.getCalculatedValueAt(cell.row, cell.col);\n } catch {\n continue;\n }\n if (!saved.is_empty && !saved.is_error) {\n positions.push({ sheetIdx, row: cell.row, col: cell.col });\n }\n }\n }\n return positions;\n}\n\nfunction anyBecameError(workbook: Workbook, positions: CellPosition[]): boolean {\n return positions.some(({ sheetIdx, row, col }) => {\n try {\n return workbook.getSheet(sheetIdx).getCalculatedValueAt(row, col).is_error;\n } catch {\n return false;\n }\n });\n}\n\nexport type SafeCalculateSkipReason = \"unresolved-sheet-refs\" | \"calculate-trapped\" | \"calculate-regressed\";\n\nexport type SafeCalculateResult = {\n workbook: Workbook;\n calculated: boolean;\n skipReason: SafeCalculateSkipReason | null;\n};\n\nexport type SafeCalculateOptions = {\n reparse?: () => Workbook;\n};\n\n// Pre-scans for formulas referencing missing sheets (which cause the Rust\n// engine to panic into a wasm `unreachable` trap that poisons the Workbook\n// instance). On trap, `reparse` is used to return a fresh usable instance.\n//\n// Also falls back to the saved values when calculating turns a formula that\n// had a saved value into an error — e.g. a reference to a name defined as a\n// formula (`Total = SUM(A1:A3)`), which the engine evaluates to #VALUE! —\n// instead of showing errors the file never had.\nexport function safeCalculate(workbook: Workbook, options: SafeCalculateOptions = {}): SafeCalculateResult {\n if (hasUnresolvedSheetReferences(workbook)) {\n return { workbook, calculated: false, skipReason: \"unresolved-sheet-refs\" };\n }\n const withSavedValues = options.reparse ? collectFormulaCellsWithSavedValues(workbook) : [];\n try {\n workbook.calculate();\n } catch (err) {\n console.warn(\"[react-xlsx] workbook.calculate() trapped; falling back to cached formula values\", err);\n if (options.reparse) {\n try {\n return { workbook: options.reparse(), calculated: false, skipReason: \"calculate-trapped\" };\n } catch (reparseErr) {\n console.warn(\"[react-xlsx] workbook reparse after calculate trap failed\", reparseErr);\n }\n }\n return { workbook, calculated: false, skipReason: \"calculate-trapped\" };\n }\n if (options.reparse && anyBecameError(workbook, withSavedValues)) {\n try {\n return { workbook: options.reparse(), calculated: false, skipReason: \"calculate-regressed\" };\n } catch (reparseErr) {\n console.warn(\"[react-xlsx] workbook reparse after calculate regression failed\", reparseErr);\n }\n }\n return { workbook, calculated: true, skipReason: null };\n}\n\nexport function tryRecalculate(workbook: Workbook): { calculated: boolean; error: unknown } {\n try {\n workbook.calculate();\n return { calculated: true, error: null };\n } catch (err) {\n console.warn(\"[react-xlsx] workbook.calculate() trapped during recalculation\", err);\n return { calculated: false, error: err };\n }\n}\n","import type { XlsxCellRange } from \"./types\";\n\nexport function expandHighlightedRange(range: XlsxCellRange, mergedRanges: XlsxCellRange[]): XlsxCellRange {\n const expanded = { start: { ...range.start }, end: { ...range.end } };\n let changed: boolean;\n do {\n changed = false;\n for (const merged of mergedRanges) {\n if (merged.end.row < expanded.start.row || merged.start.row > expanded.end.row\n || merged.end.col < expanded.start.col || merged.start.col > expanded.end.col) {\n continue;\n }\n const startRow = Math.min(expanded.start.row, merged.start.row);\n const startCol = Math.min(expanded.start.col, merged.start.col);\n const endRow = Math.max(expanded.end.row, merged.end.row);\n const endCol = Math.max(expanded.end.col, merged.end.col);\n if (startRow !== expanded.start.row || startCol !== expanded.start.col\n || endRow !== expanded.end.row || endCol !== expanded.end.col) {\n expanded.start = { row: startRow, col: startCol };\n expanded.end = { row: endRow, col: endCol };\n changed = true;\n }\n }\n } while (changed);\n return expanded;\n}\n\nfunction lowerBound(indices: number[], value: number): number {\n let low = 0;\n let high = indices.length;\n while (low < high) {\n const middle = (low + high) >>> 1;\n if (indices[middle] < value) {\n low = middle + 1;\n } else {\n high = middle;\n }\n }\n return low;\n}\n\nexport function getHighlightAxisExtent(indices: number[], prefixSums: number[], start: number, end: number) {\n const first = lowerBound(indices, start);\n const afterLast = lowerBound(indices, end + 1);\n if (first >= afterLast) {\n return null;\n }\n return { start: prefixSums[first] ?? 0, end: prefixSums[afterLast] ?? 0 };\n}\n\nexport function getHighlightScrollOffset(\n start: number,\n end: number,\n frozenExtent: number,\n viewportExtent: number,\n currentOffset: number\n): number {\n if (end <= frozenExtent || viewportExtent <= frozenExtent) {\n return currentOffset;\n }\n const scrollableStart = Math.max(start, frozenExtent);\n const availableExtent = viewportExtent - frozenExtent;\n if (scrollableStart < currentOffset + frozenExtent || end - scrollableStart > availableExtent) {\n return Math.max(0, scrollableStart - frozenExtent);\n }\n return end > currentOffset + viewportExtent ? end - viewportExtent : currentOffset;\n}\n","import type { XlsxChart, XlsxChartsheet, XlsxFormControl, XlsxSheetData, XlsxTable, XlsxWorkbookTab } from \"./types\";\nimport { getConfiguredWorkerWasmSource, type WorkerWasmSource } from \"./wasm\";\n\ntype WorkerMessage =\n | {\n id: number;\n type: \"load\";\n payload: {\n buffer: ArrayBuffer;\n showHiddenSheets?: boolean;\n skipXmlParsing?: boolean;\n wasmSource?: WorkerWasmSource;\n };\n }\n | {\n id: number;\n type: \"parseCharts\";\n payload: {\n buffer: ArrayBuffer;\n showHiddenSheets?: boolean;\n skipXmlParsing?: boolean;\n wasmSource?: WorkerWasmSource;\n };\n }\n | {\n id: number;\n type: \"getCellSnapshot\";\n payload: {\n workbookSheetIndex: number;\n row: number;\n col: number;\n locale?: string;\n };\n }\n | {\n id: number;\n type: \"getRowsBatch\";\n payload: {\n workbookSheetIndex: number;\n startRow: number;\n rowCount: number;\n locale?: string;\n };\n };\n\ntype WorkerSuccessMessage =\n | {\n id: number;\n success: true;\n result: {\n chartsByWorkbookSheetIndex: XlsxChart[][];\n chartsheets: XlsxChartsheet[];\n tabs: XlsxWorkbookTab[];\n };\n }\n | {\n id: number;\n success: true;\n result: {\n chartsByWorkbookSheetIndex: XlsxChart[][];\n chartsheets: XlsxChartsheet[];\n formControlsByWorkbookSheetIndex: XlsxFormControl[][];\n sheets: XlsxSheetData[];\n tablesByWorkbookSheetIndex: XlsxTable[][];\n tabs: XlsxWorkbookTab[];\n };\n }\n | {\n id: number;\n success: true;\n result: {\n displayValue: string;\n formula: string;\n };\n }\n | {\n id: number;\n success: true;\n result: unknown[] | null;\n };\n\ntype WorkerErrorMessage = {\n id: number;\n success: false;\n error: string;\n};\n\ntype WorkerResponse = WorkerSuccessMessage | WorkerErrorMessage | { type: \"ready\" };\n\ntype PendingRequest = {\n reject: (error: Error) => void;\n resolve: (value: unknown) => void;\n};\n\nfunction createAbortError() {\n if (typeof DOMException !== \"undefined\") {\n return new DOMException(\"Aborted\", \"AbortError\");\n }\n\n const error = new Error(\"Aborted\");\n error.name = \"AbortError\";\n return error;\n}\n\nexport class XlsxWorkerUnavailableError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"XlsxWorkerUnavailableError\";\n }\n}\n\nexport class XlsxWorkerClient {\n private readonly worker: Worker;\n\n private nextRequestId = 1;\n\n private readonly pendingRequests = new Map<number, PendingRequest>();\n\n private disposed = false;\n\n private failure: XlsxWorkerUnavailableError | null = null;\n\n private startupTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n try {\n this.worker = new Worker(new URL(\"./xlsx-worker.js\", import.meta.url), { type: \"module\" });\n } catch {\n throw new XlsxWorkerUnavailableError(\"The workbook worker could not be started.\");\n }\n this.worker.addEventListener(\"message\", this.handleMessage);\n this.worker.addEventListener(\"error\", this.handleError);\n this.worker.addEventListener(\"messageerror\", this.handleError);\n this.startupTimer = setTimeout(() => {\n this.fail(new XlsxWorkerUnavailableError(\"The workbook worker did not start in time.\"));\n }, 15000);\n }\n\n dispose() {\n if (this.disposed) {\n return;\n }\n\n this.disposed = true;\n this.stopWorker();\n const abortError = createAbortError();\n for (const request of this.pendingRequests.values()) {\n request.reject(abortError);\n }\n this.pendingRequests.clear();\n }\n\n private stopWorker() {\n if (this.startupTimer !== null) clearTimeout(this.startupTimer);\n this.startupTimer = null;\n this.worker.removeEventListener(\"message\", this.handleMessage);\n this.worker.removeEventListener(\"error\", this.handleError);\n this.worker.removeEventListener(\"messageerror\", this.handleError);\n this.worker.terminate();\n }\n\n loadWorkbook(buffer: ArrayBuffer, skipXmlParsing = false, showHiddenSheets = false) {\n const workerBuffer = cloneArrayBufferForTransfer(buffer);\n return this.request<{\n chartsByWorkbookSheetIndex: XlsxChart[][];\n chartsheets: XlsxChartsheet[];\n formControlsByWorkbookSheetIndex: XlsxFormControl[][];\n sheets: XlsxSheetData[];\n tablesByWorkbookSheetIndex: XlsxTable[][];\n tabs: XlsxWorkbookTab[];\n }>({\n id: 0,\n payload: {\n buffer: workerBuffer,\n showHiddenSheets,\n skipXmlParsing,\n wasmSource: getConfiguredWorkerWasmSource()\n },\n type: \"load\"\n }, [workerBuffer]);\n }\n\n getCellSnapshot(workbookSheetIndex: number, row: number, col: number, locale?: string) {\n return this.request<{\n displayValue: string;\n formula: string;\n }>({\n id: 0,\n payload: { col, row, workbookSheetIndex, locale },\n type: \"getCellSnapshot\"\n });\n }\n\n parseCharts(buffer: ArrayBuffer, skipXmlParsing = false, showHiddenSheets = false) {\n const workerBuffer = cloneArrayBufferForTransfer(buffer);\n return this.request<{\n chartsByWorkbookSheetIndex: XlsxChart[][];\n chartsheets: XlsxChartsheet[];\n tabs: XlsxWorkbookTab[];\n }>({\n id: 0,\n payload: {\n buffer: workerBuffer,\n showHiddenSheets,\n skipXmlParsing,\n wasmSource: getConfiguredWorkerWasmSource()\n },\n type: \"parseCharts\"\n }, [workerBuffer]);\n }\n\n getRowsBatch(workbookSheetIndex: number, startRow: number, rowCount: number, locale?: string) {\n return this.request<unknown[] | null>({\n id: 0,\n payload: { rowCount, startRow, workbookSheetIndex, locale },\n type: \"getRowsBatch\"\n });\n }\n\n private request<TResult>(message: WorkerMessage, transfer: Transferable[] = []) {\n return new Promise<TResult>((resolve, reject) => {\n if (this.disposed) {\n reject(createAbortError());\n return;\n }\n if (this.failure) {\n reject(this.failure);\n return;\n }\n\n const id = this.nextRequestId;\n this.nextRequestId += 1;\n this.pendingRequests.set(id, { reject, resolve: resolve as (value: unknown) => void });\n try {\n this.worker.postMessage({ ...message, id }, transfer);\n } catch {\n this.fail(new XlsxWorkerUnavailableError(\"The workbook worker could not receive a request.\"));\n }\n });\n }\n\n private readonly handleError = () => {\n this.fail(new XlsxWorkerUnavailableError(\"The workbook worker failed.\"));\n };\n\n private fail(error: XlsxWorkerUnavailableError) {\n if (this.disposed || this.failure) return;\n this.failure = error;\n this.stopWorker();\n for (const request of this.pendingRequests.values()) {\n request.reject(error);\n }\n this.pendingRequests.clear();\n }\n\n private readonly handleMessage = (event: MessageEvent<WorkerResponse>) => {\n const message = event.data;\n if (\"type\" in message) {\n if (this.startupTimer !== null) clearTimeout(this.startupTimer);\n this.startupTimer = null;\n return;\n }\n const request = this.pendingRequests.get(message.id);\n if (!request) {\n return;\n }\n\n this.pendingRequests.delete(message.id);\n if (!message.success) {\n request.reject(new Error(message.error));\n return;\n }\n\n request.resolve(message.result);\n };\n}\n\nfunction cloneArrayBufferForTransfer(buffer: ArrayBuffer) {\n return buffer.slice(0);\n}\n","/**\n * Some XLSX writers emit ZIP entry names using the Windows path separator\n * (`xl\\worksheets\\sheet1.xml`) instead of the forward slash required by the ZIP\n * specification (APPNOTE 4.4.17.1) and OPC. Readers that look parts up by their\n * canonical name cannot find those parts and report the workbook as corrupt.\n *\n * ZIP entry names are stored in both the central directory and each local file\n * header. Since `\\` and `/` have the same byte length, both copies can be\n * patched without decompressing or recompressing any workbook content.\n */\n\nconst BACKSLASH_BYTE = 0x5c;\nconst FORWARD_SLASH_BYTE = 0x2f;\nconst END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;\nconst CENTRAL_FILE_HEADER_SIGNATURE = 0x02014b50;\nconst LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;\nconst END_OF_CENTRAL_DIRECTORY_LENGTH = 22;\nconst CENTRAL_FILE_HEADER_LENGTH = 46;\nconst LOCAL_FILE_HEADER_LENGTH = 30;\nconst MAX_ZIP_COMMENT_LENGTH = 0xffff;\nconst ZIP16_SENTINEL = 0xffff;\nconst ZIP32_SENTINEL = 0xffffffff;\n\ntype CentralDirectoryInfo = {\n endOffset: number;\n entryCount: number;\n offset: number;\n};\n\ntype CentralDirectoryEntry = {\n diskNumberStart: number;\n localHeaderOffset: number;\n nameEnd: number;\n nameStart: number;\n nextOffset: number;\n};\n\nfunction findEndOfCentralDirectoryOffset(view: DataView) {\n const firstPossibleOffset = Math.max(\n 0,\n view.byteLength - END_OF_CENTRAL_DIRECTORY_LENGTH - MAX_ZIP_COMMENT_LENGTH\n );\n\n for (let offset = view.byteLength - END_OF_CENTRAL_DIRECTORY_LENGTH; offset >= firstPossibleOffset; offset -= 1) {\n if (view.getUint32(offset, true) !== END_OF_CENTRAL_DIRECTORY_SIGNATURE) {\n continue;\n }\n\n const commentLength = view.getUint16(offset + 20, true);\n if (offset + END_OF_CENTRAL_DIRECTORY_LENGTH + commentLength === view.byteLength) {\n return offset;\n }\n }\n\n return null;\n}\n\nfunction readCentralDirectoryInfo(view: DataView): CentralDirectoryInfo | null {\n if (view.byteLength < END_OF_CENTRAL_DIRECTORY_LENGTH) {\n return null;\n }\n\n const endOfCentralDirectoryOffset = findEndOfCentralDirectoryOffset(view);\n if (endOfCentralDirectoryOffset === null) {\n return null;\n }\n\n const diskNumber = view.getUint16(endOfCentralDirectoryOffset + 4, true);\n const centralDirectoryDiskNumber = view.getUint16(endOfCentralDirectoryOffset + 6, true);\n const entriesOnDisk = view.getUint16(endOfCentralDirectoryOffset + 8, true);\n const entryCount = view.getUint16(endOfCentralDirectoryOffset + 10, true);\n const centralDirectorySize = view.getUint32(endOfCentralDirectoryOffset + 12, true);\n const centralDirectoryOffset = view.getUint32(endOfCentralDirectoryOffset + 16, true);\n\n if (\n diskNumber !== 0 ||\n centralDirectoryDiskNumber !== 0 ||\n entriesOnDisk !== entryCount ||\n entryCount === ZIP16_SENTINEL ||\n centralDirectorySize === ZIP32_SENTINEL ||\n centralDirectoryOffset === ZIP32_SENTINEL\n ) {\n return null;\n }\n\n const centralDirectoryEndOffset = centralDirectoryOffset + centralDirectorySize;\n if (\n centralDirectoryOffset > endOfCentralDirectoryOffset ||\n centralDirectoryEndOffset > endOfCentralDirectoryOffset ||\n centralDirectoryEndOffset > view.byteLength\n ) {\n return null;\n }\n\n return {\n endOffset: centralDirectoryEndOffset,\n entryCount,\n offset: centralDirectoryOffset\n };\n}\n\nfunction readCentralDirectoryEntry(\n view: DataView,\n centralDirectoryEndOffset: number,\n offset: number\n): CentralDirectoryEntry | null {\n if (\n offset < 0 ||\n offset + CENTRAL_FILE_HEADER_LENGTH > centralDirectoryEndOffset ||\n offset + CENTRAL_FILE_HEADER_LENGTH > view.byteLength ||\n view.getUint32(offset, true) !== CENTRAL_FILE_HEADER_SIGNATURE\n ) {\n return null;\n }\n\n const nameLength = view.getUint16(offset + 28, true);\n const extraLength = view.getUint16(offset + 30, true);\n const commentLength = view.getUint16(offset + 32, true);\n const nameStart = offset + CENTRAL_FILE_HEADER_LENGTH;\n const nameEnd = nameStart + nameLength;\n const nextOffset = nameEnd + extraLength + commentLength;\n\n if (nameEnd > centralDirectoryEndOffset || nextOffset > centralDirectoryEndOffset) {\n return null;\n }\n\n return {\n diskNumberStart: view.getUint16(offset + 34, true),\n localHeaderOffset: view.getUint32(offset + 42, true),\n nameEnd,\n nameStart,\n nextOffset\n };\n}\n\nfunction entryNameUsesBackslash(bytes: Uint8Array, entry: CentralDirectoryEntry) {\n for (let cursor = entry.nameStart; cursor < entry.nameEnd; cursor += 1) {\n if (bytes[cursor] === BACKSLASH_BYTE) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction centralDirectoryUsesBackslashSeparators(\n bytes: Uint8Array,\n view: DataView,\n centralDirectory: CentralDirectoryInfo\n): boolean | null {\n let offset = centralDirectory.offset;\n\n for (let index = 0; index < centralDirectory.entryCount; index += 1) {\n const entry = readCentralDirectoryEntry(view, centralDirectory.endOffset, offset);\n if (!entry) {\n return null;\n }\n if (entryNameUsesBackslash(bytes, entry)) {\n return true;\n }\n offset = entry.nextOffset;\n }\n\n return false;\n}\n\nfunction normalizedEntryNameKey(bytes: Uint8Array, entry: CentralDirectoryEntry) {\n let key = \"\";\n for (let cursor = entry.nameStart; cursor < entry.nameEnd; cursor += 1) {\n const byte = bytes[cursor] === BACKSLASH_BYTE ? FORWARD_SLASH_BYTE : bytes[cursor];\n key += String.fromCharCode(byte);\n }\n return key;\n}\n\nfunction validateEntryNamePatches(\n bytes: Uint8Array,\n view: DataView,\n centralDirectory: CentralDirectoryInfo\n): boolean {\n const normalizedNames = new Set<string>();\n const localHeaderOffsets = new Set<number>();\n let offset = centralDirectory.offset;\n\n for (let index = 0; index < centralDirectory.entryCount; index += 1) {\n const entry = readCentralDirectoryEntry(view, centralDirectory.endOffset, offset);\n if (\n !entry ||\n entry.diskNumberStart !== 0 ||\n entry.localHeaderOffset === ZIP32_SENTINEL ||\n entry.localHeaderOffset + LOCAL_FILE_HEADER_LENGTH > centralDirectory.offset ||\n localHeaderOffsets.has(entry.localHeaderOffset)\n ) {\n return false;\n }\n\n localHeaderOffsets.add(entry.localHeaderOffset);\n\n const normalizedName = normalizedEntryNameKey(bytes, entry);\n if (normalizedNames.has(normalizedName)) {\n return false;\n }\n normalizedNames.add(normalizedName);\n\n if (!entryNameUsesBackslash(bytes, entry)) {\n offset = entry.nextOffset;\n continue;\n }\n\n if (\n view.getUint32(entry.localHeaderOffset, true) !== LOCAL_FILE_HEADER_SIGNATURE\n ) {\n return false;\n }\n\n const localNameLength = view.getUint16(entry.localHeaderOffset + 26, true);\n const localNameStart = entry.localHeaderOffset + LOCAL_FILE_HEADER_LENGTH;\n const localNameEnd = localNameStart + localNameLength;\n if (\n localNameLength !== entry.nameEnd - entry.nameStart ||\n localNameEnd > centralDirectory.offset\n ) {\n return false;\n }\n\n for (let nameIndex = 0; nameIndex < localNameLength; nameIndex += 1) {\n const centralNameOffset = entry.nameStart + nameIndex;\n const localNameOffset = localNameStart + nameIndex;\n if (bytes[centralNameOffset] !== bytes[localNameOffset]) {\n return false;\n }\n }\n\n offset = entry.nextOffset;\n }\n\n return true;\n}\n\nfunction patchEntryNameSeparators(\n bytes: Uint8Array,\n view: DataView,\n centralDirectory: CentralDirectoryInfo\n) {\n let offset = centralDirectory.offset;\n\n for (let index = 0; index < centralDirectory.entryCount; index += 1) {\n const entry = readCentralDirectoryEntry(view, centralDirectory.endOffset, offset);\n if (!entry) {\n return;\n }\n\n if (entryNameUsesBackslash(bytes, entry)) {\n const localNameStart = entry.localHeaderOffset + LOCAL_FILE_HEADER_LENGTH;\n for (let nameIndex = 0; nameIndex < entry.nameEnd - entry.nameStart; nameIndex += 1) {\n const centralNameOffset = entry.nameStart + nameIndex;\n if (bytes[centralNameOffset] === BACKSLASH_BYTE) {\n bytes[centralNameOffset] = FORWARD_SLASH_BYTE;\n bytes[localNameStart + nameIndex] = FORWARD_SLASH_BYTE;\n }\n }\n }\n\n offset = entry.nextOffset;\n }\n}\n\n/**\n * Detects backslash-separated entry names by walking only the central\n * directory. Invalid, multi-disk, and ZIP64 archives return `false`.\n */\nexport function zipEntryNamesUseBackslashSeparators(bytes: Uint8Array) {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const centralDirectory = readCentralDirectoryInfo(view);\n if (!centralDirectory) {\n return false;\n }\n\n return centralDirectoryUsesBackslashSeparators(bytes, view, centralDirectory) === true;\n}\n\n/**\n * Returns a copy with `/`-separated entry names, or the original bytes when no\n * rewrite is needed or the archive cannot be patched without ambiguity.\n */\nexport function normalizeZipEntryNames(bytes: Uint8Array) {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const centralDirectory = readCentralDirectoryInfo(view);\n if (\n !centralDirectory ||\n centralDirectoryUsesBackslashSeparators(bytes, view, centralDirectory) !== true\n ) {\n return bytes;\n }\n\n if (!validateEntryNamePatches(bytes, view, centralDirectory)) {\n return bytes;\n }\n\n const normalizedBytes = new Uint8Array(bytes.byteLength);\n normalizedBytes.set(bytes);\n const normalizedView = new DataView(\n normalizedBytes.buffer,\n normalizedBytes.byteOffset,\n normalizedBytes.byteLength\n );\n patchEntryNameSeparators(normalizedBytes, normalizedView, centralDirectory);\n return normalizedBytes;\n}\n\nexport function normalizeWorkbookArrayBuffer(buffer: ArrayBuffer) {\n const bytes = new Uint8Array(buffer);\n const normalizedBytes = normalizeZipEntryNames(bytes);\n if (normalizedBytes === bytes) {\n return buffer;\n }\n\n if (\n normalizedBytes.byteOffset === 0 &&\n normalizedBytes.byteLength === normalizedBytes.buffer.byteLength &&\n normalizedBytes.buffer instanceof ArrayBuffer\n ) {\n return normalizedBytes.buffer;\n }\n\n const normalizedBuffer = new ArrayBuffer(normalizedBytes.byteLength);\n new Uint8Array(normalizedBuffer).set(normalizedBytes);\n return normalizedBuffer;\n}\n","import { render, hydrate, unmountComponentAtNode } from 'preact/compat';\n\nexport function createRoot(container) {\n\treturn {\n\t\t// eslint-disable-next-line\n\t\trender: function (children) {\n\t\t\trender(children, container);\n\t\t},\n\t\t// eslint-disable-next-line\n\t\tunmount: function () {\n\t\t\tunmountComponentAtNode(container);\n\t\t}\n\t};\n}\n\nexport function hydrateRoot(container, children) {\n\thydrate(children, container);\n\treturn createRoot(container);\n}\n\nexport default {\n\tcreateRoot,\n\thydrateRoot\n};\n","export type NoInfer<A extends any> = [A][A extends any ? 0 : never]\n\nexport type PartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\n\nexport function memo<TDeps extends ReadonlyArray<any>, TResult>(\n getDeps: () => [...TDeps],\n fn: (...args: NoInfer<[...TDeps]>) => TResult,\n opts: {\n key: false | string\n debug?: () => boolean\n onChange?: (result: TResult) => void\n initialDeps?: TDeps\n skipInitialOnChange?: boolean\n },\n) {\n let deps = opts.initialDeps ?? []\n let result: TResult | undefined\n let isInitial = true\n\n function memoizedFunction(): TResult {\n let depTime: number\n if (opts.key && opts.debug?.()) depTime = Date.now()\n\n const newDeps = getDeps()\n\n const depsChanged =\n newDeps.length !== deps.length ||\n newDeps.some((dep: any, index: number) => deps[index] !== dep)\n\n if (!depsChanged) {\n return result!\n }\n\n deps = newDeps\n\n let resultTime: number\n if (opts.key && opts.debug?.()) resultTime = Date.now()\n\n result = fn(...newDeps)\n\n if (opts.key && opts.debug?.()) {\n const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100\n const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100\n const resultFpsPercentage = resultEndTime / 16\n\n const pad = (str: number | string, num: number) => {\n str = String(str)\n while (str.length < num) {\n str = ' ' + str\n }\n return str\n }\n\n console.info(\n `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,\n `\n font-size: .6rem;\n font-weight: bold;\n color: hsl(${Math.max(\n 0,\n Math.min(120 - 120 * resultFpsPercentage, 120),\n )}deg 100% 31%);`,\n opts?.key,\n )\n }\n\n if (opts?.onChange && !(isInitial && opts.skipInitialOnChange)) {\n opts.onChange(result)\n }\n\n isInitial = false\n\n return result\n }\n\n // Attach updateDeps to the function itself\n memoizedFunction.updateDeps = (newDeps: [...TDeps]) => {\n deps = newDeps\n }\n\n return memoizedFunction\n}\n\nexport function notUndefined<T>(value: T | undefined, msg?: string): T {\n if (value === undefined) {\n throw new Error(`Unexpected undefined${msg ? `: ${msg}` : ''}`)\n } else {\n return value\n }\n}\n\nexport const approxEqual = (a: number, b: number) => Math.abs(a - b) < 1.01\n\nexport const debounce = (\n targetWindow: Window & typeof globalThis,\n fn: Function,\n ms: number,\n) => {\n let timeoutId: number\n return function (this: any, ...args: Array<any>) {\n targetWindow.clearTimeout(timeoutId)\n timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms)\n }\n}\n","import { approxEqual, debounce, memo, notUndefined } from './utils'\n\nexport * from './utils'\n\n//\n\ntype ScrollDirection = 'forward' | 'backward'\n\ntype ScrollAlignment = 'start' | 'center' | 'end' | 'auto'\n\ntype ScrollBehavior = 'auto' | 'smooth' | 'instant'\n\nexport interface ScrollToOptions {\n align?: ScrollAlignment\n behavior?: ScrollBehavior\n}\n\ntype ScrollToOffsetOptions = ScrollToOptions\n\ntype ScrollToIndexOptions = ScrollToOptions\n\nexport interface Range {\n startIndex: number\n endIndex: number\n overscan: number\n count: number\n}\n\ntype Key = number | string | bigint\n\nexport interface VirtualItem {\n key: Key\n index: number\n start: number\n end: number\n size: number\n lane: number\n}\n\nexport interface Rect {\n width: number\n height: number\n}\n\n//\n\nconst getRect = (element: HTMLElement): Rect => {\n const { offsetWidth, offsetHeight } = element\n return { width: offsetWidth, height: offsetHeight }\n}\n\nexport const defaultKeyExtractor = (index: number) => index\n\nexport const defaultRangeExtractor = (range: Range) => {\n const start = Math.max(range.startIndex - range.overscan, 0)\n const end = Math.min(range.endIndex + range.overscan, range.count - 1)\n\n const arr = []\n\n for (let i = start; i <= end; i++) {\n arr.push(i)\n }\n\n return arr\n}\n\nexport const observeElementRect = <T extends Element>(\n instance: Virtualizer<T, any>,\n cb: (rect: Rect) => void,\n) => {\n const element = instance.scrollElement\n if (!element) {\n return\n }\n const targetWindow = instance.targetWindow\n if (!targetWindow) {\n return\n }\n\n const handler = (rect: Rect) => {\n const { width, height } = rect\n cb({ width: Math.round(width), height: Math.round(height) })\n }\n\n handler(getRect(element as unknown as HTMLElement))\n\n if (!targetWindow.ResizeObserver) {\n return () => {}\n }\n\n const observer = new targetWindow.ResizeObserver((entries) => {\n const run = () => {\n const entry = entries[0]\n if (entry?.borderBoxSize) {\n const box = entry.borderBoxSize[0]\n if (box) {\n handler({ width: box.inlineSize, height: box.blockSize })\n return\n }\n }\n handler(getRect(element as unknown as HTMLElement))\n }\n\n instance.options.useAnimationFrameWithResizeObserver\n ? requestAnimationFrame(run)\n : run()\n })\n\n observer.observe(element, { box: 'border-box' })\n\n return () => {\n observer.unobserve(element)\n }\n}\n\nconst addEventListenerOptions = {\n passive: true,\n}\n\nexport const observeWindowRect = (\n instance: Virtualizer<Window, any>,\n cb: (rect: Rect) => void,\n) => {\n const element = instance.scrollElement\n if (!element) {\n return\n }\n\n const handler = () => {\n cb({ width: element.innerWidth, height: element.innerHeight })\n }\n handler()\n\n element.addEventListener('resize', handler, addEventListenerOptions)\n\n return () => {\n element.removeEventListener('resize', handler)\n }\n}\n\nconst supportsScrollend =\n typeof window == 'undefined' ? true : 'onscrollend' in window\n\ntype ObserveOffsetCallBack = (offset: number, isScrolling: boolean) => void\n\nexport const observeElementOffset = <T extends Element>(\n instance: Virtualizer<T, any>,\n cb: ObserveOffsetCallBack,\n) => {\n const element = instance.scrollElement\n if (!element) {\n return\n }\n const targetWindow = instance.targetWindow\n if (!targetWindow) {\n return\n }\n\n let offset = 0\n const fallback =\n instance.options.useScrollendEvent && supportsScrollend\n ? () => undefined\n : debounce(\n targetWindow,\n () => {\n cb(offset, false)\n },\n instance.options.isScrollingResetDelay,\n )\n\n const createHandler = (isScrolling: boolean) => () => {\n const { horizontal, isRtl } = instance.options\n offset = horizontal\n ? element['scrollLeft'] * ((isRtl && -1) || 1)\n : element['scrollTop']\n fallback()\n cb(offset, isScrolling)\n }\n const handler = createHandler(true)\n const endHandler = createHandler(false)\n\n element.addEventListener('scroll', handler, addEventListenerOptions)\n const registerScrollendEvent =\n instance.options.useScrollendEvent && supportsScrollend\n if (registerScrollendEvent) {\n element.addEventListener('scrollend', endHandler, addEventListenerOptions)\n }\n return () => {\n element.removeEventListener('scroll', handler)\n if (registerScrollendEvent) {\n element.removeEventListener('scrollend', endHandler)\n }\n }\n}\n\nexport const observeWindowOffset = (\n instance: Virtualizer<Window, any>,\n cb: ObserveOffsetCallBack,\n) => {\n const element = instance.scrollElement\n if (!element) {\n return\n }\n const targetWindow = instance.targetWindow\n if (!targetWindow) {\n return\n }\n\n let offset = 0\n const fallback =\n instance.options.useScrollendEvent && supportsScrollend\n ? () => undefined\n : debounce(\n targetWindow,\n () => {\n cb(offset, false)\n },\n instance.options.isScrollingResetDelay,\n )\n\n const createHandler = (isScrolling: boolean) => () => {\n offset = element[instance.options.horizontal ? 'scrollX' : 'scrollY']\n fallback()\n cb(offset, isScrolling)\n }\n const handler = createHandler(true)\n const endHandler = createHandler(false)\n\n element.addEventListener('scroll', handler, addEventListenerOptions)\n const registerScrollendEvent =\n instance.options.useScrollendEvent && supportsScrollend\n if (registerScrollendEvent) {\n element.addEventListener('scrollend', endHandler, addEventListenerOptions)\n }\n return () => {\n element.removeEventListener('scroll', handler)\n if (registerScrollendEvent) {\n element.removeEventListener('scrollend', endHandler)\n }\n }\n}\n\nexport const measureElement = <TItemElement extends Element>(\n element: TItemElement,\n entry: ResizeObserverEntry | undefined,\n instance: Virtualizer<any, TItemElement>,\n) => {\n if (entry?.borderBoxSize) {\n const box = entry.borderBoxSize[0]\n if (box) {\n const size = Math.round(\n box[instance.options.horizontal ? 'inlineSize' : 'blockSize'],\n )\n return size\n }\n }\n\n return (element as unknown as HTMLElement)[\n instance.options.horizontal ? 'offsetWidth' : 'offsetHeight'\n ]\n}\n\nexport const windowScroll = <T extends Window>(\n offset: number,\n {\n adjustments = 0,\n behavior,\n }: { adjustments?: number; behavior?: ScrollBehavior },\n instance: Virtualizer<T, any>,\n) => {\n const toOffset = offset + adjustments\n\n instance.scrollElement?.scrollTo?.({\n [instance.options.horizontal ? 'left' : 'top']: toOffset,\n behavior,\n })\n}\n\nexport const elementScroll = <T extends Element>(\n offset: number,\n {\n adjustments = 0,\n behavior,\n }: { adjustments?: number; behavior?: ScrollBehavior },\n instance: Virtualizer<T, any>,\n) => {\n const toOffset = offset + adjustments\n\n instance.scrollElement?.scrollTo?.({\n [instance.options.horizontal ? 'left' : 'top']: toOffset,\n behavior,\n })\n}\n\nexport interface VirtualizerOptions<\n TScrollElement extends Element | Window,\n TItemElement extends Element,\n> {\n // Required from the user\n count: number\n getScrollElement: () => TScrollElement | null\n estimateSize: (index: number) => number\n\n // Required from the framework adapter (but can be overridden)\n scrollToFn: (\n offset: number,\n options: { adjustments?: number; behavior?: ScrollBehavior },\n instance: Virtualizer<TScrollElement, TItemElement>,\n ) => void\n observeElementRect: (\n instance: Virtualizer<TScrollElement, TItemElement>,\n cb: (rect: Rect) => void,\n ) => void | (() => void)\n observeElementOffset: (\n instance: Virtualizer<TScrollElement, TItemElement>,\n cb: ObserveOffsetCallBack,\n ) => void | (() => void)\n // Optional\n debug?: boolean\n initialRect?: Rect\n onChange?: (\n instance: Virtualizer<TScrollElement, TItemElement>,\n sync: boolean,\n ) => void\n measureElement?: (\n element: TItemElement,\n entry: ResizeObserverEntry | undefined,\n instance: Virtualizer<TScrollElement, TItemElement>,\n ) => number\n overscan?: number\n horizontal?: boolean\n paddingStart?: number\n paddingEnd?: number\n scrollPaddingStart?: number\n scrollPaddingEnd?: number\n initialOffset?: number | (() => number)\n getItemKey?: (index: number) => Key\n rangeExtractor?: (range: Range) => Array<number>\n scrollMargin?: number\n gap?: number\n indexAttribute?: string\n initialMeasurementsCache?: Array<VirtualItem>\n lanes?: number\n isScrollingResetDelay?: number\n useScrollendEvent?: boolean\n enabled?: boolean\n isRtl?: boolean\n useAnimationFrameWithResizeObserver?: boolean\n}\n\ntype ScrollState = {\n // what we want\n index: number | null\n align: ScrollAlignment\n behavior: ScrollBehavior\n\n // lifecycle\n startedAt: number\n\n // target tracking\n lastTargetOffset: number\n\n // settling\n stableFrames: number\n}\n\nexport class Virtualizer<\n TScrollElement extends Element | Window,\n TItemElement extends Element,\n> {\n private unsubs: Array<void | (() => void)> = []\n options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>\n scrollElement: TScrollElement | null = null\n targetWindow: (Window & typeof globalThis) | null = null\n isScrolling = false\n private scrollState: ScrollState | null = null\n measurementsCache: Array<VirtualItem> = []\n private itemSizeCache = new Map<Key, number>()\n private laneAssignments = new Map<number, number>() // index → lane cache\n private pendingMeasuredCacheIndexes: Array<number> = []\n private prevLanes: number | undefined = undefined\n private lanesChangedFlag = false\n private lanesSettling = false\n scrollRect: Rect | null = null\n scrollOffset: number | null = null\n scrollDirection: ScrollDirection | null = null\n private scrollAdjustments = 0\n shouldAdjustScrollPositionOnItemSizeChange:\n | undefined\n | ((\n item: VirtualItem,\n delta: number,\n instance: Virtualizer<TScrollElement, TItemElement>,\n ) => boolean)\n elementsCache = new Map<Key, TItemElement>()\n private now = () => this.targetWindow?.performance?.now?.() ?? Date.now()\n private observer = (() => {\n let _ro: ResizeObserver | null = null\n\n const get = () => {\n if (_ro) {\n return _ro\n }\n\n if (!this.targetWindow || !this.targetWindow.ResizeObserver) {\n return null\n }\n\n return (_ro = new this.targetWindow.ResizeObserver((entries) => {\n entries.forEach((entry) => {\n const run = () => {\n const node = entry.target as TItemElement\n const index = this.indexFromElement(node)\n\n if (!node.isConnected) {\n this.observer.unobserve(node)\n return\n }\n\n if (this.shouldMeasureDuringScroll(index)) {\n this.resizeItem(\n index,\n this.options.measureElement(node, entry, this),\n )\n }\n }\n this.options.useAnimationFrameWithResizeObserver\n ? requestAnimationFrame(run)\n : run()\n })\n }))\n }\n\n return {\n disconnect: () => {\n get()?.disconnect()\n _ro = null\n },\n observe: (target: Element) =>\n get()?.observe(target, { box: 'border-box' }),\n unobserve: (target: Element) => get()?.unobserve(target),\n }\n })()\n range: { startIndex: number; endIndex: number } | null = null\n\n constructor(opts: VirtualizerOptions<TScrollElement, TItemElement>) {\n this.setOptions(opts)\n }\n\n setOptions = (opts: VirtualizerOptions<TScrollElement, TItemElement>) => {\n Object.entries(opts).forEach(([key, value]) => {\n if (typeof value === 'undefined') delete (opts as any)[key]\n })\n\n this.options = {\n debug: false,\n initialOffset: 0,\n overscan: 1,\n paddingStart: 0,\n paddingEnd: 0,\n scrollPaddingStart: 0,\n scrollPaddingEnd: 0,\n horizontal: false,\n getItemKey: defaultKeyExtractor,\n rangeExtractor: defaultRangeExtractor,\n onChange: () => {},\n measureElement,\n initialRect: { width: 0, height: 0 },\n scrollMargin: 0,\n gap: 0,\n indexAttribute: 'data-index',\n initialMeasurementsCache: [],\n lanes: 1,\n isScrollingResetDelay: 150,\n enabled: true,\n isRtl: false,\n useScrollendEvent: false,\n useAnimationFrameWithResizeObserver: false,\n ...opts,\n }\n }\n\n private notify = (sync: boolean) => {\n this.options.onChange?.(this, sync)\n }\n\n private maybeNotify = memo(\n () => {\n this.calculateRange()\n\n return [\n this.isScrolling,\n this.range ? this.range.startIndex : null,\n this.range ? this.range.endIndex : null,\n ]\n },\n (isScrolling) => {\n this.notify(isScrolling)\n },\n {\n key: process.env.NODE_ENV !== 'production' && 'maybeNotify',\n debug: () => this.options.debug,\n initialDeps: [\n this.isScrolling,\n this.range ? this.range.startIndex : null,\n this.range ? this.range.endIndex : null,\n ] as [boolean, number | null, number | null],\n },\n )\n\n private cleanup = () => {\n this.unsubs.filter(Boolean).forEach((d) => d!())\n this.unsubs = []\n this.observer.disconnect()\n if (this.rafId != null && this.targetWindow) {\n this.targetWindow.cancelAnimationFrame(this.rafId)\n this.rafId = null\n }\n this.scrollState = null\n this.scrollElement = null\n this.targetWindow = null\n }\n\n _didMount = () => {\n return () => {\n this.cleanup()\n }\n }\n\n _willUpdate = () => {\n const scrollElement = this.options.enabled\n ? this.options.getScrollElement()\n : null\n\n if (this.scrollElement !== scrollElement) {\n this.cleanup()\n\n if (!scrollElement) {\n this.maybeNotify()\n return\n }\n\n this.scrollElement = scrollElement\n\n if (this.scrollElement && 'ownerDocument' in this.scrollElement) {\n this.targetWindow = this.scrollElement.ownerDocument.defaultView\n } else {\n this.targetWindow = this.scrollElement?.window ?? null\n }\n\n this.elementsCache.forEach((cached) => {\n this.observer.observe(cached)\n })\n\n this.unsubs.push(\n this.options.observeElementRect(this, (rect) => {\n this.scrollRect = rect\n this.maybeNotify()\n }),\n )\n\n this.unsubs.push(\n this.options.observeElementOffset(this, (offset, isScrolling) => {\n this.scrollAdjustments = 0\n this.scrollDirection = isScrolling\n ? this.getScrollOffset() < offset\n ? 'forward'\n : 'backward'\n : null\n this.scrollOffset = offset\n this.isScrolling = isScrolling\n\n if (this.scrollState) {\n this.scheduleScrollReconcile()\n }\n this.maybeNotify()\n }),\n )\n\n this._scrollToOffset(this.getScrollOffset(), {\n adjustments: undefined,\n behavior: undefined,\n })\n }\n }\n\n private rafId: number | null = null\n private scheduleScrollReconcile() {\n if (!this.targetWindow) {\n this.scrollState = null\n return\n }\n if (this.rafId != null) return\n this.rafId = this.targetWindow.requestAnimationFrame(() => {\n this.rafId = null\n this.reconcileScroll()\n })\n }\n private reconcileScroll() {\n if (!this.scrollState) return\n\n const el = this.scrollElement\n if (!el) return\n\n // Safety valve: bail out if reconciliation has been running too long\n const MAX_RECONCILE_MS = 5000\n if (this.now() - this.scrollState.startedAt > MAX_RECONCILE_MS) {\n this.scrollState = null\n return\n }\n\n const offsetInfo =\n this.scrollState.index != null\n ? this.getOffsetForIndex(this.scrollState.index, this.scrollState.align)\n : undefined\n const targetOffset = offsetInfo\n ? offsetInfo[0]\n : this.scrollState.lastTargetOffset\n\n // Require one stable frame where target matches scroll offset.\n // approxEqual() already tolerates minor fluctuations, so one frame is sufficient\n // to confirm scroll has reached its target without premature cleanup.\n const STABLE_FRAMES = 1\n\n const targetChanged = targetOffset !== this.scrollState.lastTargetOffset\n\n if (!targetChanged && approxEqual(targetOffset, this.getScrollOffset())) {\n this.scrollState.stableFrames++\n if (this.scrollState.stableFrames >= STABLE_FRAMES) {\n this.scrollState = null\n return\n }\n } else {\n this.scrollState.stableFrames = 0\n\n if (targetChanged) {\n this.scrollState.lastTargetOffset = targetOffset\n // Switch to 'auto' behavior once measurements cause target to change\n // We want to jump directly to the correct position, not smoothly animate to it\n this.scrollState.behavior = 'auto'\n\n this._scrollToOffset(targetOffset, {\n adjustments: undefined,\n behavior: 'auto',\n })\n }\n }\n\n // Always reschedule while scrollState is active to guarantee\n // the safety valve timeout runs even if no scroll events fire\n // (e.g. no-op scrollToFn, detached element)\n this.scheduleScrollReconcile()\n }\n\n private getSize = () => {\n if (!this.options.enabled) {\n this.scrollRect = null\n return 0\n }\n\n this.scrollRect = this.scrollRect ?? this.options.initialRect\n\n return this.scrollRect[this.options.horizontal ? 'width' : 'height']\n }\n\n private getScrollOffset = () => {\n if (!this.options.enabled) {\n this.scrollOffset = null\n return 0\n }\n\n this.scrollOffset =\n this.scrollOffset ??\n (typeof this.options.initialOffset === 'function'\n ? this.options.initialOffset()\n : this.options.initialOffset)\n\n return this.scrollOffset\n }\n\n private getFurthestMeasurement = (\n measurements: Array<VirtualItem>,\n index: number,\n ) => {\n const furthestMeasurementsFound = new Map<number, true>()\n const furthestMeasurements = new Map<number, VirtualItem>()\n for (let m = index - 1; m >= 0; m--) {\n const measurement = measurements[m]!\n\n if (furthestMeasurementsFound.has(measurement.lane)) {\n continue\n }\n\n const previousFurthestMeasurement = furthestMeasurements.get(\n measurement.lane,\n )\n if (\n previousFurthestMeasurement == null ||\n measurement.end > previousFurthestMeasurement.end\n ) {\n furthestMeasurements.set(measurement.lane, measurement)\n } else if (measurement.end < previousFurthestMeasurement.end) {\n furthestMeasurementsFound.set(measurement.lane, true)\n }\n\n if (furthestMeasurementsFound.size === this.options.lanes) {\n break\n }\n }\n\n return furthestMeasurements.size === this.options.lanes\n ? Array.from(furthestMeasurements.values()).sort((a, b) => {\n if (a.end === b.end) {\n return a.index - b.index\n }\n\n return a.end - b.end\n })[0]\n : undefined\n }\n\n private getMeasurementOptions = memo(\n () => [\n this.options.count,\n this.options.paddingStart,\n this.options.scrollMargin,\n this.options.getItemKey,\n this.options.enabled,\n this.options.lanes,\n ],\n (count, paddingStart, scrollMargin, getItemKey, enabled, lanes) => {\n const lanesChanged =\n this.prevLanes !== undefined && this.prevLanes !== lanes\n\n if (lanesChanged) {\n // Set flag for getMeasurements to handle\n this.lanesChangedFlag = true\n }\n\n this.prevLanes = lanes\n this.pendingMeasuredCacheIndexes = []\n\n return {\n count,\n paddingStart,\n scrollMargin,\n getItemKey,\n enabled,\n lanes,\n }\n },\n {\n key: false,\n },\n )\n\n private getMeasurements = memo(\n () => [this.getMeasurementOptions(), this.itemSizeCache],\n (\n { count, paddingStart, scrollMargin, getItemKey, enabled, lanes },\n itemSizeCache,\n ) => {\n if (!enabled) {\n this.measurementsCache = []\n this.itemSizeCache.clear()\n this.laneAssignments.clear()\n return []\n }\n\n // Clean up stale lane cache entries when count decreases\n if (this.laneAssignments.size > count) {\n for (const index of this.laneAssignments.keys()) {\n if (index >= count) {\n this.laneAssignments.delete(index)\n }\n }\n }\n\n // ✅ Force complete recalculation when lanes change\n if (this.lanesChangedFlag) {\n this.lanesChangedFlag = false // Reset immediately\n this.lanesSettling = true // Start settling period\n this.measurementsCache = []\n this.itemSizeCache.clear()\n this.laneAssignments.clear() // Clear lane cache for new lane count\n // Clear pending indexes to force min = 0\n this.pendingMeasuredCacheIndexes = []\n }\n\n // Don't restore from initialMeasurementsCache during lane changes\n // as it contains stale lane assignments from the previous lane count\n if (this.measurementsCache.length === 0 && !this.lanesSettling) {\n this.measurementsCache = this.options.initialMeasurementsCache\n this.measurementsCache.forEach((item) => {\n this.itemSizeCache.set(item.key, item.size)\n })\n }\n\n // ✅ During lanes settling, ignore pendingMeasuredCacheIndexes to prevent repositioning\n const min = this.lanesSettling\n ? 0\n : this.pendingMeasuredCacheIndexes.length > 0\n ? Math.min(...this.pendingMeasuredCacheIndexes)\n : 0\n this.pendingMeasuredCacheIndexes = []\n\n // ✅ End settling period when cache is fully built\n if (this.lanesSettling && this.measurementsCache.length === count) {\n this.lanesSettling = false\n }\n\n const measurements = this.measurementsCache.slice(0, min)\n\n // ✅ Performance: Track last item index per lane for O(1) lookup\n const laneLastIndex: Array<number | undefined> = new Array(lanes).fill(\n undefined,\n )\n\n // Initialize from existing measurements (before min)\n for (let m = 0; m < min; m++) {\n const item = measurements[m]\n if (item) {\n laneLastIndex[item.lane] = m\n }\n }\n\n for (let i = min; i < count; i++) {\n const key = getItemKey(i)\n\n // Check for cached lane assignment\n const cachedLane = this.laneAssignments.get(i)\n let lane: number\n let start: number\n\n if (cachedLane !== undefined && this.options.lanes > 1) {\n // Use cached lane - O(1) lookup for previous item in same lane\n lane = cachedLane\n const prevIndex = laneLastIndex[lane]\n const prevInLane =\n prevIndex !== undefined ? measurements[prevIndex] : undefined\n start = prevInLane\n ? prevInLane.end + this.options.gap\n : paddingStart + scrollMargin\n } else {\n // No cache - use original logic (find shortest lane)\n const furthestMeasurement =\n this.options.lanes === 1\n ? measurements[i - 1]\n : this.getFurthestMeasurement(measurements, i)\n\n start = furthestMeasurement\n ? furthestMeasurement.end + this.options.gap\n : paddingStart + scrollMargin\n\n lane = furthestMeasurement\n ? furthestMeasurement.lane\n : i % this.options.lanes\n\n // Cache the lane assignment\n if (this.options.lanes > 1) {\n this.laneAssignments.set(i, lane)\n }\n }\n\n const measuredSize = itemSizeCache.get(key)\n const size =\n typeof measuredSize === 'number'\n ? measuredSize\n : this.options.estimateSize(i)\n\n const end = start + size\n\n measurements[i] = {\n index: i,\n start,\n size,\n end,\n key,\n lane,\n }\n\n // ✅ Performance: Update lane's last item index\n laneLastIndex[lane] = i\n }\n\n this.measurementsCache = measurements\n\n return measurements\n },\n {\n key: process.env.NODE_ENV !== 'production' && 'getMeasurements',\n debug: () => this.options.debug,\n },\n )\n\n calculateRange = memo(\n () => [\n this.getMeasurements(),\n this.getSize(),\n this.getScrollOffset(),\n this.options.lanes,\n ],\n (measurements, outerSize, scrollOffset, lanes) => {\n return (this.range =\n measurements.length > 0 && outerSize > 0\n ? calculateRange({\n measurements,\n outerSize,\n scrollOffset,\n lanes,\n })\n : null)\n },\n {\n key: process.env.NODE_ENV !== 'production' && 'calculateRange',\n debug: () => this.options.debug,\n },\n )\n\n getVirtualIndexes = memo(\n () => {\n let startIndex: number | null = null\n let endIndex: number | null = null\n const range = this.calculateRange()\n if (range) {\n startIndex = range.startIndex\n endIndex = range.endIndex\n }\n this.maybeNotify.updateDeps([this.isScrolling, startIndex, endIndex])\n return [\n this.options.rangeExtractor,\n this.options.overscan,\n this.options.count,\n startIndex,\n endIndex,\n ]\n },\n (rangeExtractor, overscan, count, startIndex, endIndex) => {\n return startIndex === null || endIndex === null\n ? []\n : rangeExtractor({\n startIndex,\n endIndex,\n overscan,\n count,\n })\n },\n {\n key: process.env.NODE_ENV !== 'production' && 'getVirtualIndexes',\n debug: () => this.options.debug,\n },\n )\n\n indexFromElement = (node: TItemElement) => {\n const attributeName = this.options.indexAttribute\n const indexStr = node.getAttribute(attributeName)\n\n if (!indexStr) {\n console.warn(\n `Missing attribute name '${attributeName}={index}' on measured element.`,\n )\n return -1\n }\n\n return parseInt(indexStr, 10)\n }\n\n /**\n * Determines if an item at the given index should be measured during smooth scroll.\n * During smooth scroll, only items within a buffer range around the target are measured\n * to prevent items far from the target from pushing it away.\n */\n private shouldMeasureDuringScroll = (index: number): boolean => {\n // No scroll state or not smooth scroll - always allow measurements\n if (!this.scrollState || this.scrollState.behavior !== 'smooth') {\n return true\n }\n\n const scrollIndex =\n this.scrollState.index ??\n this.getVirtualItemForOffset(this.scrollState.lastTargetOffset)?.index\n\n if (scrollIndex !== undefined && this.range) {\n // Allow measurements within a buffer range around the scroll target\n const bufferSize = Math.max(\n this.options.overscan,\n Math.ceil((this.range.endIndex - this.range.startIndex) / 2),\n )\n const minIndex = Math.max(0, scrollIndex - bufferSize)\n const maxIndex = Math.min(\n this.options.count - 1,\n scrollIndex + bufferSize,\n )\n return index >= minIndex && index <= maxIndex\n }\n\n return true\n }\n\n measureElement = (node: TItemElement | null) => {\n if (!node) {\n this.elementsCache.forEach((cached, key) => {\n if (!cached.isConnected) {\n this.observer.unobserve(cached)\n this.elementsCache.delete(key)\n }\n })\n return\n }\n\n const index = this.indexFromElement(node)\n const key = this.options.getItemKey(index)\n const prevNode = this.elementsCache.get(key)\n\n if (prevNode !== node) {\n if (prevNode) {\n this.observer.unobserve(prevNode)\n }\n this.observer.observe(node)\n this.elementsCache.set(key, node)\n }\n\n // Sync-measure when idle (initial render) or during programmatic scrolling\n // (scrollToIndex/scrollToOffset) where reconcileScroll needs sizes in the same frame.\n // During normal user scrolling, skip sync measurement — the RO callback handles it async.\n if (\n (!this.isScrolling || this.scrollState) &&\n this.shouldMeasureDuringScroll(index)\n ) {\n this.resizeItem(index, this.options.measureElement(node, undefined, this))\n }\n }\n\n resizeItem = (index: number, size: number) => {\n const item = this.measurementsCache[index]\n if (!item) return\n\n const itemSize = this.itemSizeCache.get(item.key) ?? item.size\n const delta = size - itemSize\n\n if (delta !== 0) {\n if (\n this.scrollState?.behavior !== 'smooth' &&\n (this.shouldAdjustScrollPositionOnItemSizeChange !== undefined\n ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this)\n : item.start < this.getScrollOffset() + this.scrollAdjustments)\n ) {\n if (process.env.NODE_ENV !== 'production' && this.options.debug) {\n console.info('correction', delta)\n }\n this._scrollToOffset(this.getScrollOffset(), {\n adjustments: (this.scrollAdjustments += delta),\n behavior: undefined,\n })\n }\n\n this.pendingMeasuredCacheIndexes.push(item.index)\n this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size))\n\n this.notify(false)\n }\n }\n\n getVirtualItems = memo(\n () => [this.getVirtualIndexes(), this.getMeasurements()],\n (indexes, measurements) => {\n const virtualItems: Array<VirtualItem> = []\n\n for (let k = 0, len = indexes.length; k < len; k++) {\n const i = indexes[k]!\n const measurement = measurements[i]!\n\n virtualItems.push(measurement)\n }\n\n return virtualItems\n },\n {\n key: process.env.NODE_ENV !== 'production' && 'getVirtualItems',\n debug: () => this.options.debug,\n },\n )\n\n getVirtualItemForOffset = (offset: number) => {\n const measurements = this.getMeasurements()\n if (measurements.length === 0) {\n return undefined\n }\n return notUndefined(\n measurements[\n findNearestBinarySearch(\n 0,\n measurements.length - 1,\n (index: number) => notUndefined(measurements[index]).start,\n offset,\n )\n ],\n )\n }\n\n private getMaxScrollOffset = () => {\n if (!this.scrollElement) return 0\n\n if ('scrollHeight' in this.scrollElement) {\n // Element\n return this.options.horizontal\n ? this.scrollElement.scrollWidth - this.scrollElement.clientWidth\n : this.scrollElement.scrollHeight - this.scrollElement.clientHeight\n } else {\n // Window\n const doc = this.scrollElement.document.documentElement\n return this.options.horizontal\n ? doc.scrollWidth - this.scrollElement.innerWidth\n : doc.scrollHeight - this.scrollElement.innerHeight\n }\n }\n\n getOffsetForAlignment = (\n toOffset: number,\n align: ScrollAlignment,\n itemSize = 0,\n ) => {\n if (!this.scrollElement) return 0\n\n const size = this.getSize()\n const scrollOffset = this.getScrollOffset()\n\n if (align === 'auto') {\n align = toOffset >= scrollOffset + size ? 'end' : 'start'\n }\n\n if (align === 'center') {\n // When aligning to a particular item (e.g. with scrollToIndex),\n // adjust offset by the size of the item to center on the item\n toOffset += (itemSize - size) / 2\n } else if (align === 'end') {\n toOffset -= size\n }\n\n const maxOffset = this.getMaxScrollOffset()\n\n return Math.max(Math.min(maxOffset, toOffset), 0)\n }\n\n getOffsetForIndex = (index: number, align: ScrollAlignment = 'auto') => {\n index = Math.max(0, Math.min(index, this.options.count - 1))\n\n const size = this.getSize()\n const scrollOffset = this.getScrollOffset()\n\n const item = this.measurementsCache[index]\n if (!item) return\n\n if (align === 'auto') {\n if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) {\n align = 'end'\n } else if (item.start <= scrollOffset + this.options.scrollPaddingStart) {\n align = 'start'\n } else {\n return [scrollOffset, align] as const\n }\n }\n\n // For the last item with 'end' alignment, use browser's actual max scroll\n // to account for borders/padding that aren't in our measurements\n if (align === 'end' && index === this.options.count - 1) {\n return [this.getMaxScrollOffset(), align] as const\n }\n\n const toOffset =\n align === 'end'\n ? item.end + this.options.scrollPaddingEnd\n : item.start - this.options.scrollPaddingStart\n\n return [\n this.getOffsetForAlignment(toOffset, align, item.size),\n align,\n ] as const\n }\n\n scrollToOffset = (\n toOffset: number,\n { align = 'start', behavior = 'auto' }: ScrollToOffsetOptions = {},\n ) => {\n const offset = this.getOffsetForAlignment(toOffset, align)\n\n const now = this.now()\n this.scrollState = {\n index: null,\n align,\n behavior,\n startedAt: now,\n lastTargetOffset: offset,\n stableFrames: 0,\n }\n\n this._scrollToOffset(offset, { adjustments: undefined, behavior })\n\n this.scheduleScrollReconcile()\n }\n\n scrollToIndex = (\n index: number,\n {\n align: initialAlign = 'auto',\n behavior = 'auto',\n }: ScrollToIndexOptions = {},\n ) => {\n index = Math.max(0, Math.min(index, this.options.count - 1))\n\n const offsetInfo = this.getOffsetForIndex(index, initialAlign)\n if (!offsetInfo) {\n return\n }\n const [offset, align] = offsetInfo\n\n const now = this.now()\n this.scrollState = {\n index,\n align,\n behavior,\n startedAt: now,\n lastTargetOffset: offset,\n stableFrames: 0,\n }\n\n this._scrollToOffset(offset, { adjustments: undefined, behavior })\n\n this.scheduleScrollReconcile()\n }\n\n scrollBy = (\n delta: number,\n { behavior = 'auto' }: ScrollToOffsetOptions = {},\n ) => {\n const offset = this.getScrollOffset() + delta\n const now = this.now()\n\n this.scrollState = {\n index: null,\n align: 'start',\n behavior,\n startedAt: now,\n lastTargetOffset: offset,\n stableFrames: 0,\n }\n\n this._scrollToOffset(offset, { adjustments: undefined, behavior })\n\n this.scheduleScrollReconcile()\n }\n\n getTotalSize = () => {\n const measurements = this.getMeasurements()\n\n let end: number\n // If there are no measurements, set the end to paddingStart\n // If there is only one lane, use the last measurement's end\n // Otherwise find the maximum end value among all measurements\n if (measurements.length === 0) {\n end = this.options.paddingStart\n } else if (this.options.lanes === 1) {\n end = measurements[measurements.length - 1]?.end ?? 0\n } else {\n const endByLane = Array<number | null>(this.options.lanes).fill(null)\n let endIndex = measurements.length - 1\n while (endIndex >= 0 && endByLane.some((val) => val === null)) {\n const item = measurements[endIndex]!\n if (endByLane[item.lane] === null) {\n endByLane[item.lane] = item.end\n }\n\n endIndex--\n }\n\n end = Math.max(...endByLane.filter((val): val is number => val !== null))\n }\n\n return Math.max(\n end - this.options.scrollMargin + this.options.paddingEnd,\n 0,\n )\n }\n\n private _scrollToOffset = (\n offset: number,\n {\n adjustments,\n behavior,\n }: {\n adjustments: number | undefined\n behavior: ScrollBehavior | undefined\n },\n ) => {\n this.options.scrollToFn(offset, { behavior, adjustments }, this)\n }\n\n measure = () => {\n this.itemSizeCache = new Map()\n this.laneAssignments = new Map() // Clear lane cache for full re-layout\n this.notify(false)\n }\n}\n\nconst findNearestBinarySearch = (\n low: number,\n high: number,\n getCurrentValue: (i: number) => number,\n value: number,\n) => {\n while (low <= high) {\n const middle = ((low + high) / 2) | 0\n const currentValue = getCurrentValue(middle)\n\n if (currentValue < value) {\n low = middle + 1\n } else if (currentValue > value) {\n high = middle - 1\n } else {\n return middle\n }\n }\n\n if (low > 0) {\n return low - 1\n } else {\n return 0\n }\n}\n\nfunction calculateRange({\n measurements,\n outerSize,\n scrollOffset,\n lanes,\n}: {\n measurements: Array<VirtualItem>\n outerSize: number\n scrollOffset: number\n lanes: number\n}) {\n const lastIndex = measurements.length - 1\n const getOffset = (index: number) => measurements[index]!.start\n\n // handle case when item count is less than or equal to lanes\n if (measurements.length <= lanes) {\n return {\n startIndex: 0,\n endIndex: lastIndex,\n }\n }\n\n let startIndex = findNearestBinarySearch(\n 0,\n lastIndex,\n getOffset,\n scrollOffset,\n )\n let endIndex = startIndex\n\n if (lanes === 1) {\n while (\n endIndex < lastIndex &&\n measurements[endIndex]!.end < scrollOffset + outerSize\n ) {\n endIndex++\n }\n } else if (lanes > 1) {\n // Expand forward until we include the visible items from all lanes\n // which are closer to the end of the virtualizer window\n const endPerLane = Array(lanes).fill(0)\n while (\n endIndex < lastIndex &&\n endPerLane.some((pos) => pos < scrollOffset + outerSize)\n ) {\n const item = measurements[endIndex]!\n endPerLane[item.lane] = item.end\n endIndex++\n }\n\n // Expand backward until we include all lanes' visible items\n // closer to the top\n const startPerLane = Array(lanes).fill(scrollOffset + outerSize)\n while (startIndex >= 0 && startPerLane.some((pos) => pos >= scrollOffset)) {\n const item = measurements[startIndex]!\n startPerLane[item.lane] = item.start\n startIndex--\n }\n\n // Align startIndex to the beginning of its lane\n startIndex = Math.max(0, startIndex - (startIndex % lanes))\n // Align endIndex to the end of its lane\n endIndex = Math.min(lastIndex, endIndex + (lanes - 1 - (endIndex % lanes)))\n }\n\n return { startIndex, endIndex }\n}\n","import * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport {\n Virtualizer,\n elementScroll,\n observeElementOffset,\n observeElementRect,\n observeWindowOffset,\n observeWindowRect,\n windowScroll,\n} from '@tanstack/virtual-core'\nimport type { PartialKeys, VirtualizerOptions } from '@tanstack/virtual-core'\n\nexport * from '@tanstack/virtual-core'\n\nconst useIsomorphicLayoutEffect =\n typeof document !== 'undefined' ? React.useLayoutEffect : React.useEffect\n\nexport type ReactVirtualizerOptions<\n TScrollElement extends Element | Window,\n TItemElement extends Element,\n> = VirtualizerOptions<TScrollElement, TItemElement> & {\n useFlushSync?: boolean\n}\n\nfunction useVirtualizerBase<\n TScrollElement extends Element | Window,\n TItemElement extends Element,\n>({\n useFlushSync = true,\n ...options\n}: ReactVirtualizerOptions<TScrollElement, TItemElement>): Virtualizer<\n TScrollElement,\n TItemElement\n> {\n const rerender = React.useReducer(() => ({}), {})[1]\n\n const resolvedOptions: VirtualizerOptions<TScrollElement, TItemElement> = {\n ...options,\n onChange: (instance, sync) => {\n if (useFlushSync && sync) {\n flushSync(rerender)\n } else {\n rerender()\n }\n options.onChange?.(instance, sync)\n },\n }\n\n const [instance] = React.useState(\n () => new Virtualizer<TScrollElement, TItemElement>(resolvedOptions),\n )\n\n instance.setOptions(resolvedOptions)\n\n useIsomorphicLayoutEffect(() => {\n return instance._didMount()\n }, [])\n\n useIsomorphicLayoutEffect(() => {\n return instance._willUpdate()\n })\n\n return instance\n}\n\nexport function useVirtualizer<\n TScrollElement extends Element,\n TItemElement extends Element,\n>(\n options: PartialKeys<\n ReactVirtualizerOptions<TScrollElement, TItemElement>,\n 'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n >,\n): Virtualizer<TScrollElement, TItemElement> {\n return useVirtualizerBase<TScrollElement, TItemElement>({\n observeElementRect: observeElementRect,\n observeElementOffset: observeElementOffset,\n scrollToFn: elementScroll,\n ...options,\n })\n}\n\nexport function useWindowVirtualizer<TItemElement extends Element>(\n options: PartialKeys<\n ReactVirtualizerOptions<Window, TItemElement>,\n | 'getScrollElement'\n | 'observeElementRect'\n | 'observeElementOffset'\n | 'scrollToFn'\n >,\n): Virtualizer<Window, TItemElement> {\n return useVirtualizerBase<Window, TItemElement>({\n getScrollElement: () => (typeof document !== 'undefined' ? window : null),\n observeElementRect: observeWindowRect,\n observeElementOffset: observeWindowOffset,\n scrollToFn: windowScroll,\n initialOffset: () => (typeof document !== 'undefined' ? window.scrollY : 0),\n ...options,\n })\n}\n","// Generated by scripts/generate-builtin-table-styles.mjs. Do not edit by hand.\n//\n// Excel's built-in table styles (TableStyleLight1-21, TableStyleMedium1-28 and\n// TableStyleDark1-11) as defined in ECMA-376 Part 1, taken from Apache POI's\n// presetTableStyles.xml. Workbooks only reference these styles by name, so the\n// viewer ships the definitions. Element values use the same shape that\n// parseDifferentialStyle produces for custom table styles in styles.xml.\n//\n// Regenerate with: node scripts/generate-builtin-table-styles.mjs\nimport type { XlsxTableStyleDefinition } from \"./types\";\n\nexport const BUILTIN_TABLE_STYLES: Record<string, XlsxTableStyleDefinition> = {\n TableStyleLight1: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleLight2: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 4, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 4, \"tint\": -0.25}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 4, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 4, \"tint\": -0.25}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 4, \"tint\": -0.25}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight3: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 5, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 5, \"tint\": -0.25}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 5, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 5, \"tint\": -0.25}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 5, \"tint\": -0.25}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight4: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 6, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 6, \"tint\": -0.25}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 6, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 6, \"tint\": -0.25}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 6, \"tint\": -0.25}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight5: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 7, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 7, \"tint\": -0.25}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 7, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 7, \"tint\": -0.25}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 7, \"tint\": -0.25}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight6: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 8, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 8, \"tint\": -0.25}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 8, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 8, \"tint\": -0.25}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 8, \"tint\": -0.25}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight7: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 9, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 9, \"tint\": -0.25}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 9, \"tint\": -0.25}}, \"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 9, \"tint\": -0.25}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 9, \"tint\": -0.25}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight8: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n secondRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n firstColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n secondColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}}\n },\n TableStyleLight9: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}}},\n secondRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}}},\n firstColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}}},\n secondColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}}}\n },\n TableStyleLight10: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}}},\n secondRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}}},\n firstColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}}},\n secondColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}}}\n },\n TableStyleLight11: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}}},\n secondRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}}},\n firstColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}}},\n secondColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}}}\n },\n TableStyleLight12: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}}},\n secondRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}}},\n firstColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}}},\n secondColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}}}\n },\n TableStyleLight13: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}}},\n secondRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}}},\n firstColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}}},\n secondColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}}}\n },\n TableStyleLight14: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}}},\n secondRowStripe: {\"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}}},\n firstColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}}},\n secondColumnStripe: {\"border\": {\"left\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}}}\n },\n TableStyleLight15: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleLight16: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 4}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight17: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 5}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 5}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight18: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 6}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 6}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight19: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 7}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 7}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight20: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 8}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 8}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleLight21: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 9}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"bottom\": {\"color\": {\"theme\": 9}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium1: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium2: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium3: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium4: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium5: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium6: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium7: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.8}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium8: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}, \"border\": {\"horizontal\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.35}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.35}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium9: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"horizontal\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium10: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"horizontal\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium11: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"horizontal\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium12: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"horizontal\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium13: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"horizontal\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium14: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"horizontal\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 0}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"thick\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium15: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}, \"right\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}, \"left\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n totalRow: {\"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium16: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n totalRow: {\"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium17: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n totalRow: {\"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium18: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n totalRow: {\"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium19: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n totalRow: {\"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium20: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n totalRow: {\"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium21: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n totalRow: {\"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium22: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 1}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.35}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.35}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium23: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 4, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 4}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium24: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 5, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 5}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium25: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 6, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 6}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium26: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 7, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 7}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium27: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 8, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 8}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleMedium28: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 1}}, \"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.8}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}, \"right\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}, \"bottom\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}, \"left\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}, \"horizontal\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}, \"vertical\": {\"color\": {\"theme\": 9, \"tint\": 0.4}, \"style\": \"thin\"}}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 9}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleDark1: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1, \"tint\": 0.45}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1, \"tint\": 0.15}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1, \"tint\": 0.25}, \"fillType\": \"solid\"}, \"border\": {\"right\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1, \"tint\": 0.25}, \"fillType\": \"solid\"}, \"border\": {\"left\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 1, \"tint\": 0.25}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 1, \"tint\": 0.25}, \"fillType\": \"solid\"}}\n },\n TableStyleDark2: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4, \"tint\": -0.5}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"right\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 4, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"left\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": -0.25}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": -0.25}, \"fillType\": \"solid\"}}\n },\n TableStyleDark3: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5, \"tint\": -0.5}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"right\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"left\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": -0.25}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 5, \"tint\": -0.25}, \"fillType\": \"solid\"}}\n },\n TableStyleDark4: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6, \"tint\": -0.5}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"right\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 6, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"left\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": -0.25}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": -0.25}, \"fillType\": \"solid\"}}\n },\n TableStyleDark5: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7, \"tint\": -0.5}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"right\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"left\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": -0.25}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 7, \"tint\": -0.25}, \"fillType\": \"solid\"}}\n },\n TableStyleDark6: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8, \"tint\": -0.5}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"right\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 8, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"left\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": -0.25}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": -0.25}, \"fillType\": \"solid\"}}\n },\n TableStyleDark7: {\n wholeTable: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}, \"border\": {\"bottom\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9, \"tint\": -0.5}, \"fillType\": \"solid\"}, \"border\": {\"top\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"right\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9, \"tint\": -0.25}, \"fillType\": \"solid\"}, \"border\": {\"left\": {\"color\": {\"theme\": 0}, \"style\": \"medium\"}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": -0.25}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 9, \"tint\": -0.25}, \"fillType\": \"solid\"}}\n },\n TableStyleDark8: {\n wholeTable: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.15}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 1}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.35}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 0, \"tint\": -0.35}, \"fillType\": \"solid\"}}\n },\n TableStyleDark9: {\n wholeTable: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 5}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 4, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleDark10: {\n wholeTable: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 7}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 6, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n },\n TableStyleDark11: {\n wholeTable: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.8}, \"fillType\": \"solid\"}},\n headerRow: {\"font\": {\"color\": {\"theme\": 0}}, \"fill\": {\"color\": {\"theme\": 9}, \"fillType\": \"solid\"}},\n totalRow: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}, \"border\": {\"top\": {\"color\": {\"theme\": 1}, \"style\": \"double\"}}},\n firstColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n lastColumn: {\"font\": {\"bold\": true, \"color\": {\"theme\": 1}}},\n firstRowStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.6}, \"fillType\": \"solid\"}},\n firstColumnStripe: {\"fill\": {\"color\": {\"theme\": 8, \"tint\": 0.6}, \"fillType\": \"solid\"}}\n }\n};\n\n/**\n * Resolves one of Excel's built-in table styles by name. Workbooks reference\n * these by name only, so the definitions ship with the viewer. Returns null for\n * any other name, including custom styles defined in the workbook's styles.xml.\n */\nexport function resolveBuiltinTableStyle(name: string | undefined): XlsxTableStyleDefinition | null {\n if (!name || !Object.prototype.hasOwnProperty.call(BUILTIN_TABLE_STYLES, name)) {\n return null;\n }\n\n return BUILTIN_TABLE_STYLES[name] ?? null;\n}\n","export interface CellTextClipOverscan {\n horizontal: number;\n vertical: number;\n}\n\nexport function resolveCellTextClipOverscan(\n overscan: number,\n usesWrappedText: boolean\n): CellTextClipOverscan {\n return {\n horizontal: overscan,\n vertical: usesWrappedText ? 0 : overscan\n };\n}\n","export function growScrollDisplayLimit(\n current: number,\n growth: number,\n maximum: number,\n readOnly: boolean\n): number {\n const nextLimit = current + growth;\n return readOnly ? Math.max(current, Math.min(maximum, nextLimit)) : nextLimit;\n}\n","import type { XlsxCellAddress, XlsxCellRange } from \"./types\";\n\nexport type MergedRangeLookup = (cell: XlsxCellAddress) => XlsxCellRange | null;\n\ntype RangeNode = {\n range: XlsxCellRange;\n bounds: XlsxCellRange;\n left: RangeNode | null;\n right: RangeNode | null;\n};\n\n/**\n * Builds a lookup that returns the merged range containing a cell, or null.\n * Subtree bounds prune the search without expanding ranges into rows or cells.\n * Storage grows with the number of merges, independently of their dimensions.\n */\nexport function createMergedRangeLookup(ranges: readonly XlsxCellRange[]): MergedRangeLookup {\n const normalizedRanges: XlsxCellRange[] = [];\n\n for (const range of ranges) {\n const startRow = Math.min(range.start.row, range.end.row);\n const endRow = Math.max(range.start.row, range.end.row);\n const startCol = Math.min(range.start.col, range.end.col);\n const endCol = Math.max(range.start.col, range.end.col);\n if (![startRow, endRow, startCol, endCol].every((value) => Number.isInteger(value) && value >= 0)\n || (startRow === endRow && startCol === endCol)) {\n continue;\n }\n\n normalizedRanges.push({\n end: { col: endCol, row: endRow },\n start: { col: startCol, row: startRow }\n });\n }\n\n normalizedRanges.sort((a, b) => a.start.row - b.start.row || a.start.col - b.start.col);\n\n const buildNode = (start: number, end: number): RangeNode | null => {\n if (start >= end) return null;\n const middle = (start + end) >>> 1;\n const range = normalizedRanges[middle];\n const left = buildNode(start, middle);\n const right = buildNode(middle + 1, end);\n return {\n range,\n left,\n right,\n bounds: {\n start: {\n row: normalizedRanges[start].start.row,\n col: Math.min(range.start.col, left?.bounds.start.col ?? Infinity, right?.bounds.start.col ?? Infinity)\n },\n end: {\n row: Math.max(range.end.row, left?.bounds.end.row ?? -1, right?.bounds.end.row ?? -1),\n col: Math.max(range.end.col, left?.bounds.end.col ?? -1, right?.bounds.end.col ?? -1)\n }\n }\n };\n };\n const root = buildNode(0, normalizedRanges.length);\n const contains = (range: XlsxCellRange, cell: XlsxCellAddress) => (\n cell.row >= range.start.row && cell.row <= range.end.row\n && cell.col >= range.start.col && cell.col <= range.end.col\n );\n const find = (node: RangeNode | null, cell: XlsxCellAddress): XlsxCellRange | null => {\n if (!node || !contains(node.bounds, cell)) return null;\n if (contains(node.range, cell)) {\n return node.range;\n }\n return find(node.left, cell) ?? find(node.right, cell);\n };\n return (cell) => find(root, cell);\n}\n","import type { Worksheet } from \"@dukelib/sheets-wasm\";\n\nexport type CellGeneralAlignment = \"left\" | \"center\" | \"right\";\n\nexport function getCellGeneralAlignment(worksheet: Worksheet, row: number, col: number): CellGeneralAlignment {\n const value = worksheet.getCalculatedValueAt(row, col);\n try {\n if (value.is_number) return \"right\";\n if (value.is_boolean || value.is_error) return \"center\";\n return \"left\";\n } finally {\n value.free();\n }\n}\n","export type ViewerPalette = {\n border: string;\n buttonSurface: string;\n buttonText: string;\n canvas: string;\n danger: string;\n headerSurface: string;\n headerText: string;\n mutedSurface: string;\n mutedText: string;\n rowHeaderSurface: string;\n shadow: string;\n sheetActiveSurface: string;\n sheetActiveText: string;\n sheetInactiveSurface: string;\n sheetInactiveText: string;\n strongBorder: string;\n subtleSurface: string;\n surface: string;\n text: string;\n toolbarSurface: string;\n};\n\nconst LIGHT_PALETTE: ViewerPalette = {\n border: \"#e4e4e7\",\n buttonSurface: \"#ffffff\",\n buttonText: \"#18181b\",\n canvas: \"#fafafa\",\n danger: \"#dc2626\",\n headerSurface: \"#f4f4f5\",\n headerText: \"#71717a\",\n mutedSurface: \"#f5f5f5\",\n mutedText: \"#71717a\",\n rowHeaderSurface: \"#f4f4f5\",\n shadow: \"0 1px 2px rgba(0, 0, 0, 0.04)\",\n sheetActiveSurface: \"#ffffff\",\n sheetActiveText: \"#18181b\",\n sheetInactiveSurface: \"#e4e4e7\",\n sheetInactiveText: \"#52525b\",\n strongBorder: \"#d4d4d8\",\n subtleSurface: \"#fafafa\",\n surface: \"#ffffff\",\n text: \"#18181b\",\n toolbarSurface: \"#f5f5f5\"\n};\n\nconst DARK_PALETTE: ViewerPalette = {\n border: \"rgba(255, 255, 255, 0.10)\",\n buttonSurface: \"rgba(255, 255, 255, 0.06)\",\n buttonText: \"#f4f4f5\",\n canvas: \"#09090b\",\n danger: \"#f87171\",\n headerSurface: \"#18181b\",\n headerText: \"#a1a1aa\",\n mutedSurface: \"#111113\",\n mutedText: \"#a1a1aa\",\n rowHeaderSurface: \"#18181b\",\n shadow: \"0 1px 2px rgba(0, 0, 0, 0.28)\",\n sheetActiveSurface: \"#27272a\",\n sheetActiveText: \"#fafafa\",\n sheetInactiveSurface: \"#18181b\",\n sheetInactiveText: \"#a1a1aa\",\n strongBorder: \"rgba(255, 255, 255, 0.16)\",\n subtleSurface: \"#101012\",\n surface: \"#111113\",\n text: \"#f4f4f5\",\n toolbarSurface: \"#101012\"\n};\n\nexport function isDarkViewerPalette(palette: ViewerPalette) {\n return palette.surface === DARK_PALETTE.surface;\n}\n\nexport function resolveViewerPalette(\n isDark = false,\n headerBackgroundColor?: string,\n headerTextColor?: string\n): ViewerPalette {\n const basePalette = isDark ? DARK_PALETTE : LIGHT_PALETTE;\n if (headerBackgroundColor === undefined && headerTextColor === undefined) {\n return basePalette;\n }\n return {\n ...basePalette,\n headerSurface: headerBackgroundColor ?? basePalette.headerSurface,\n headerText: headerTextColor ?? basePalette.headerText,\n rowHeaderSurface: headerBackgroundColor ?? basePalette.rowHeaderSurface\n };\n}\n","import * as React from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type {\n XlsxContextMenuAction,\n XlsxContextMenuActionId,\n XlsxContextMenuContext,\n XlsxViewerController,\n XlsxViewerProps\n} from \"./types\";\nimport { isDarkViewerPalette, type ViewerPalette } from \"./viewer-palette\";\nimport { expandHighlightedRange } from \"./range-highlight\";\n\nconst menuStyles = `\n:where(.react-xlsx-context-menu) {\n background: var(--xlsx-context-menu-background);\n color: var(--xlsx-context-menu-color);\n border: 1px solid var(--xlsx-context-menu-border);\n border-radius: 8px;\n padding: 5px;\n min-width: min(220px, calc(100vw - 16px));\n font: 13px/1.4 system-ui, sans-serif;\n box-shadow: 0 8px 28px rgb(0 0 0 / 18%);\n}\n:where(.react-xlsx-context-menu-item) {\n display: flex; align-items: center; justify-content: space-between; gap: 24px;\n width: 100%; border: 0; border-radius: 4px; padding: 7px 10px;\n background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer;\n}\n:where(.react-xlsx-context-menu-item:focus-visible),\n:where(.react-xlsx-context-menu-item:hover:not(:disabled)) {\n background: var(--xlsx-context-menu-hover); outline: none;\n}\n:where(.react-xlsx-context-menu-item:disabled) { opacity: .45; cursor: default; }\n:where(.react-xlsx-context-menu-item[data-destructive=\"true\"]) { color: var(--xlsx-context-menu-danger); }\n:where(.react-xlsx-context-menu-shortcut) { font-size: 11px; color: var(--xlsx-context-menu-muted); }\n:where(.react-xlsx-context-menu-separator) { height: 1px; margin: 4px; background: var(--xlsx-context-menu-border); }\n`;\n\nexport function XlsxContextMenu({\n context, position, controller, palette, closeMenu, editCell, selectExtent,\n renderContextMenu, contextMenuClassName, contextMenuStyle\n}: Pick<XlsxViewerProps, \"renderContextMenu\" | \"contextMenuClassName\" | \"contextMenuStyle\"> & {\n context: XlsxContextMenuContext;\n position: { x: number; y: number };\n controller: XlsxViewerController;\n palette: ViewerPalette;\n closeMenu: (restoreFocus?: boolean) => void;\n editCell: () => void;\n selectExtent: (kind: \"row\" | \"column\" | \"sheet\") => void;\n}) {\n const rootRef = React.useRef<HTMLDivElement>(null);\n const controllerRef = React.useRef(controller);\n controllerRef.current = controller;\n const aliveRef = React.useRef(true);\n const runningRef = React.useRef(false);\n const [busy, setBusy] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n const [location, setLocation] = React.useState(position);\n const modifier = typeof navigator !== \"undefined\" && /Mac|iPhone|iPad/.test(navigator.platform) ? \"⌘\" : \"Ctrl+\";\n const table = controller.tables.find(({ start, end }) =>\n context.cell.row >= start.row && context.cell.row <= end.row &&\n context.cell.col >= start.col && context.cell.col <= end.col);\n const range = expandHighlightedRange(context.selection, controller.activeSheet?.mergedRanges ?? []);\n const multiple = range.start.row !== range.end.row || range.start.col !== range.end.col;\n const actions: XlsxContextMenuAction[] = [\n ...(!controller.readOnly ? [{ id: \"cut\" as const, label: \"Cut\" }] : []),\n { id: \"copy\", label: \"Copy\", shortcut: `${modifier}C` },\n ...(!controller.readOnly ? [\n { id: \"paste\" as const, label: \"Paste\", shortcut: `${modifier}V` },\n { id: \"editCell\" as const, label: \"Edit cell\", shortcut: \"F2\", separatorBefore: true },\n { id: \"clearContents\" as const, label: \"Clear contents\", destructive: true },\n { id: \"undo\" as const, label: \"Undo\", shortcut: `${modifier}Z`, separatorBefore: true, disabled: !controller.canUndo },\n { id: \"redo\" as const, label: \"Redo\", shortcut: `${modifier}⇧Z`, disabled: !controller.canRedo },\n { id: \"mergeAndCenter\" as const, label: \"Merge & Center\", separatorBefore: true, disabled: !multiple },\n { id: \"mergeAcross\" as const, label: \"Merge Across\", disabled: range.start.col === range.end.col },\n { id: \"mergeCells\" as const, label: \"Merge Cells\", disabled: !multiple },\n { id: \"unmergeCells\" as const, label: \"Unmerge Cells\" },\n ...(table ? [\n { id: \"sortAscending\" as const, label: \"Sort A to Z\", separatorBefore: true },\n { id: \"sortDescending\" as const, label: \"Sort Z to A\" }\n ] : [])\n ] : []),\n { id: \"selectRow\", label: \"Select row\", separatorBefore: true },\n { id: \"selectColumn\", label: \"Select column\" },\n { id: \"selectAll\", label: \"Select all\", shortcut: `${modifier}A` }\n ];\n\n React.useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const update = () => {\n const rect = root.getBoundingClientRect();\n setLocation({\n x: Math.max(8, Math.min(position.x, window.innerWidth - rect.width - 8)),\n y: Math.max(8, Math.min(position.y, window.innerHeight - rect.height - 8))\n });\n };\n update();\n const observer = new ResizeObserver(update);\n observer.observe(root);\n (root.querySelector<HTMLElement>('[role=\"menuitem\"]:not(:disabled):not([aria-disabled=\"true\"])') ?? root).focus();\n return () => observer.disconnect();\n }, [position]);\n\n React.useEffect(() => {\n aliveRef.current = true;\n const outside = (event: Event) => {\n if (!rootRef.current?.contains(event.target as Node)) closeMenu(false);\n };\n const dismiss = () => closeMenu(false);\n document.addEventListener(\"pointerdown\", outside, true);\n document.addEventListener(\"focusin\", outside);\n document.addEventListener(\"scroll\", outside, true);\n window.addEventListener(\"resize\", dismiss);\n window.addEventListener(\"blur\", dismiss);\n return () => {\n aliveRef.current = false;\n document.removeEventListener(\"pointerdown\", outside, true);\n document.removeEventListener(\"focusin\", outside);\n document.removeEventListener(\"scroll\", outside, true);\n window.removeEventListener(\"resize\", dismiss);\n window.removeEventListener(\"blur\", dismiss);\n };\n }, [closeMenu]);\n\n const runAction = async (id: XlsxContextMenuActionId): Promise<boolean> => {\n const action = actions.find((item) => item.id === id);\n if (!aliveRef.current || runningRef.current || !action || action.disabled) return false;\n const snapshot = controller;\n const isCurrent = () => aliveRef.current &&\n controllerRef.current.activeSheetIndex === snapshot.activeSheetIndex &&\n controllerRef.current.workbook === snapshot.workbook &&\n controllerRef.current.revision === snapshot.revision &&\n controllerRef.current.readOnly === snapshot.readOnly &&\n controllerRef.current.selection === snapshot.selection;\n if (!isCurrent()) return false;\n runningRef.current = true;\n setBusy(true);\n setError(null);\n try {\n switch (id) {\n case \"copy\":\n case \"cut\": {\n const copied = await controller.copySelectionToClipboard();\n if (!copied) throw new Error(\"Clipboard access is unavailable. Use the keyboard shortcut to copy.\");\n if (!isCurrent()) return false;\n if (id === \"cut\") controller.clearSelectedCells();\n break;\n }\n case \"paste\": {\n const clipboard = navigator.clipboard;\n if (!clipboard) throw new Error(\"Clipboard access is unavailable. Use the keyboard shortcut to paste.\");\n let structured: string | undefined;\n let text: string | undefined;\n if (clipboard.read) {\n const items = await clipboard.read();\n for (const item of items) {\n const type = [\"web application/x-react-xlsx-range+json\", \"application/x-react-xlsx-range+json\"]\n .find((type) => item.types.includes(type));\n if (type) {\n structured = await (await item.getType(type)).text();\n break;\n }\n if (text === undefined && item.types.includes(\"text/plain\")) text = await (await item.getType(\"text/plain\")).text();\n }\n } else {\n text = await clipboard.readText();\n }\n if (!isCurrent()) return false;\n const pasted = structured !== undefined\n ? controller.pasteStructuredClipboardData(structured)\n : text !== undefined && controller.pasteText(text);\n if (!pasted) throw new Error(\"No spreadsheet cells or text are available to paste.\");\n break;\n }\n case \"clearContents\": controller.clearSelectedCells(); break;\n case \"editCell\": closeMenu(); editCell(); return true;\n case \"undo\": controller.undo(); break;\n case \"redo\": controller.redo(); break;\n case \"mergeCells\": controller.mergeSelection(); break;\n case \"mergeAndCenter\": controller.mergeSelection(\"center\"); break;\n case \"mergeAcross\": controller.mergeSelection(\"across\"); break;\n case \"unmergeCells\": controller.unmergeSelection(); break;\n case \"sortAscending\":\n case \"sortDescending\":\n if (table) controller.sortTable(table.name, context.cell.col - table.start.col, id === \"sortAscending\" ? \"ascending\" : \"descending\");\n break;\n case \"selectRow\": selectExtent(\"row\"); break;\n case \"selectColumn\": selectExtent(\"column\"); break;\n case \"selectAll\": selectExtent(\"sheet\"); break;\n }\n closeMenu();\n return true;\n } catch (cause) {\n if (aliveRef.current) setError(cause instanceof Error ? cause.message : \"The action could not be completed.\");\n return false;\n } finally {\n runningRef.current = false;\n if (aliveRef.current) setBusy(false);\n }\n };\n\n const defaultNode = actions.map((action) => (\n <React.Fragment key={action.id}>\n {action.separatorBefore && <div className=\"react-xlsx-context-menu-separator\" role=\"separator\" />}\n <button\n className=\"react-xlsx-context-menu-item\"\n data-action={action.id}\n data-destructive={action.destructive || undefined}\n disabled={busy || action.disabled}\n onClick={() => { void runAction(action.id); }}\n role=\"menuitem\"\n tabIndex={-1}\n type=\"button\"\n >\n <span>{action.label}</span>\n {action.shortcut && <span className=\"react-xlsx-context-menu-shortcut\">{action.shortcut}</span>}\n </button>\n </React.Fragment>\n ));\n const content = renderContextMenu\n ? renderContextMenu({ context, actions, runAction, closeMenu, position: location, isDark: isDarkViewerPalette(palette), busy, error, defaultNode })\n : defaultNode;\n\n return createPortal(\n <div\n aria-label=\"Spreadsheet context menu\"\n aria-busy={busy}\n className={[\"react-xlsx-context-menu\", contextMenuClassName].filter(Boolean).join(\" \")}\n data-dark={isDarkViewerPalette(palette) ? \"true\" : \"false\"}\n onContextMenu={(event) => event.preventDefault()}\n onPointerDown={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation();\n if (event.key === \"Escape\" || event.key === \"Tab\") {\n if (event.key === \"Escape\") event.preventDefault();\n closeMenu();\n return;\n }\n const items = Array.from(rootRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not(:disabled):not([aria-disabled=\"true\"])') ?? []);\n const current = items.indexOf(document.activeElement as HTMLElement);\n let next: number | undefined;\n if (event.key === \"ArrowDown\") next = (current + 1) % items.length;\n if (event.key === \"ArrowUp\") next = (current - 1 + items.length) % items.length;\n if (event.key === \"Home\") next = 0;\n if (event.key === \"End\") next = items.length - 1;\n if (next !== undefined) {\n event.preventDefault();\n items[next]?.focus();\n }\n }}\n ref={rootRef}\n role=\"menu\"\n style={{\n \"--xlsx-context-menu-background\": palette.surface,\n \"--xlsx-context-menu-color\": palette.text,\n \"--xlsx-context-menu-border\": palette.border,\n \"--xlsx-context-menu-hover\": palette.mutedSurface,\n \"--xlsx-context-menu-muted\": palette.mutedText,\n \"--xlsx-context-menu-danger\": palette.danger,\n ...contextMenuStyle,\n position: \"fixed\", left: location.x, top: location.y,\n maxHeight: \"calc(100vh - 16px)\", maxWidth: \"calc(100vw - 16px)\",\n overflow: \"auto\", zIndex: 10000, boxSizing: \"border-box\"\n } as React.CSSProperties}\n tabIndex={-1}\n >\n <style>{menuStyles}</style>\n {content}\n {error && <div role=\"alert\" style={{ padding: \"8px 10px\", maxWidth: 280, color: palette.danger }}>{error}</div>}\n </div>,\n document.body\n );\n}\n","import type { OfficeCharts } from \"@extend-ai/rust-office-charts\";\nimport * as React from \"react\";\nimport { chartRenderer, officeChartRenderer, suppliedOfficeChartRenderer, officeExtendedChartRenderer } from \"./chart-loader\";\nimport { useChartModule } from \"./use-chart-module\";\nimport type { ChartSvgProps } from \"./chart-renderer\";\n\nclass ChartLoadBoundary extends React.Component<{\n children: React.ReactNode;\n}, { failed: boolean }> {\n state = { failed: false };\n\n static getDerivedStateFromError() {\n return { failed: true };\n }\n\n render() {\n if (this.state.failed) {\n return <div role=\"status\">Unable to load chart.</div>;\n }\n return this.props.children;\n }\n}\n\ntype LazyChartProps = ChartSvgProps & { experimentalOfficeCharts?: OfficeCharts; loadingFallback: (stage: \"renderer\" | \"map\") => React.ReactNode };\n\nfunction LegacyChartSvg({ loadingFallback, ...props }: LazyChartProps) {\n const renderer = useChartModule(chartRenderer);\n return renderer ? <renderer.MemoChartSvg {...props} /> : <>{loadingFallback(\"renderer\")}</>;\n}\n\nfunction SharedChart(props: LazyChartProps & { source: NonNullable<ChartSvgProps[\"chart\"][\"renderSource\"]> }) {\n const module = useChartModule(props.experimentalOfficeCharts ? suppliedOfficeChartRenderer : props.source.experimental ? officeExtendedChartRenderer : officeChartRenderer);\n if (!module) return <>{props.loadingFallback(props.chart.chartType === \"RegionMap\" ? \"map\" : \"renderer\")}</>;\n const fallback = props.chart.chartType === \"RegionMap\" ? <div role=\"status\">Map data is unavailable.</div> : <LegacyChartSvg {...props} />;\n const renderer = props.experimentalOfficeCharts ?? module.renderer;\n return renderer && module.SharedChartSvg\n ? <module.SharedChartSvg {...props} renderer={renderer} fallback={fallback} />\n : fallback;\n}\n\nexport function MemoChartSvg(props: LazyChartProps) {\n return (\n <ChartLoadBoundary key={props.chart.id}>\n {props.chart.renderSource\n ? <SharedChart {...props} source={props.chart.renderSource} />\n : <LegacyChartSvg {...props} />}\n </ChartLoadBoundary>\n );\n}\n","import { localizeCellNumber, localizeRows } from \"./number-locale\";\nimport * as React from \"react\";\nimport type { Workbook, Worksheet } from \"@dukelib/sheets-wasm\";\nimport {\n type VirtualItem,\n useVirtualizer\n} from \"@tanstack/react-virtual\";\nimport { resolveBuiltinTableStyle } from \"./builtin-table-styles\";\nimport { resolveCellTextClipOverscan } from \"./cell-text-clip\";\nimport { growScrollDisplayLimit } from \"./scroll-display-limit\";\nimport { expandHighlightedRange, getHighlightAxisExtent, getHighlightScrollOffset } from \"./range-highlight\";\nimport { createMergedRangeLookup } from \"./merged-range-lookup\";\nimport { getCellGeneralAlignment, type CellGeneralAlignment } from \"./cell-alignment\";\nimport { XLSX_MAX_COLUMNS, XLSX_MAX_ROWS } from \"./range-reference\";\nimport { resolveWorkbookColor, resolveWorkbookFillStyle } from \"./colors\";\nimport { useXlsxViewerController, XlsxFileSizeLimitExceededError } from \"./controller\";\nimport { XlsxContextMenu } from \"./context-menu\";\nimport { MemoChartSvg } from \"./lazy-chart\";\nimport { preloadChartRenderers } from \"./chart-loader\";\nimport {\n emuToPixels,\n resizeImageRect,\n resolveRenderedSheetAxisPixels,\n resolveSheetColumnWidthPixels,\n resolveSheetRowHeightPixels\n} from \"./images\";\nimport {\n isDarkViewerPalette,\n resolveViewerPalette,\n type ViewerPalette\n} from \"./viewer-palette\";\nimport type {\n XlsxChart,\n XlsxChartElementSelection,\n XlsxChartsheet,\n XlsxCellAddress,\n XlsxCellRange,\n XlsxContextMenuContext,\n XlsxConditionalFormatValueObject,\n XlsxFormControl,\n XlsxFormControlKindInput,\n XlsxImage,\n XlsxImageRect,\n XlsxImageRenderProps,\n XlsxImageResizeHandlePosition,\n XlsxImageSelectionRenderProps,\n XlsxScrollerRenderProps,\n XlsxShape,\n XlsxSheetData,\n XlsxSheetThumbnail,\n XlsxThemePalette,\n XlsxTable,\n XlsxTableColumn,\n XlsxTableHeaderMenuRenderProps,\n UseXlsxViewerThumbnailsOptions,\n XlsxViewerCharts,\n XlsxViewerTables,\n XlsxViewerController,\n XlsxViewerEditing,\n XlsxViewerImages,\n XlsxViewerProps,\n XlsxViewerProviderProps,\n XlsxViewerSelection,\n XlsxViewerThumbnails,\n XlsxViewerZoom\n} from \"./types\";\n\nconst RANGE_HIGHLIGHT_STYLES = `\n:where(.react-xlsx-range-highlight) {\n background: var(--xlsx-highlight-fill, rgb(59 130 246 / 8%));\n border: var(--xlsx-highlight-border, var(--xlsx-highlight-default-border-width, 1px) solid #3b82f6);\n border-radius: var(--xlsx-highlight-radius, 2px);\n}\n:where(.react-xlsx-range-highlight[data-dark=\"true\"]) {\n background: var(--xlsx-highlight-fill, rgb(147 197 253 / 12%));\n border: var(--xlsx-highlight-border, var(--xlsx-highlight-default-border-width, 1px) solid #93c5fd);\n}\n`;\n\nconst DEFAULT_ROW_HEIGHT = 24;\nconst DEFAULT_COL_WIDTH = 80;\nconst HEADER_HEIGHT = 24;\nconst ROW_HEADER_WIDTH = 40;\nconst INTERNAL_CLIPBOARD_MIME = \"application/x-react-xlsx-range+json\";\nconst MIN_OPEN_GRID_ROWS = 200;\nconst MIN_OPEN_GRID_COLS = 50;\nconst OPEN_GRID_ROW_PADDING = 120;\nconst OPEN_GRID_COL_PADDING = 24;\nconst INITIAL_WORKER_GRID_ROWS = 600;\nconst INITIAL_WORKER_GRID_COLS = 120;\nconst WORKER_GRID_GROW_ROWS = 2000;\nconst WORKER_GRID_GROW_COLS = 120;\nconst WORKER_GRID_GROW_THRESHOLD_ROWS = 160;\nconst WORKER_GRID_GROW_THRESHOLD_COLS = 24;\nconst OPEN_GRID_ROW_GROWTH = 200;\nconst OPEN_GRID_COL_GROWTH = 24;\nconst OPEN_GRID_VERTICAL_EDGE_PX = 600;\nconst OPEN_GRID_HORIZONTAL_EDGE_PX = 480;\nconst SELECTION_DRAG_THRESHOLD_PX = 4;\nconst IMAGE_MIN_SIZE_PX = 16;\nconst IMAGE_HANDLE_SIZE_PX = 10;\nconst CANVAS_RESIZE_HIT_SLOP_PX = 8;\nconst CANVAS_VIEWPORT_OVERSCAN_PX = 480;\nconst CANVAS_SCROLL_BUFFER_PX = 480;\nconst CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX - 96;\nconst CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX * 1.5;\nconst CANVAS_DIRTY_CELL_CULL_MARGIN_PX = CANVAS_VIEWPORT_OVERSCAN_PX * 2;\nconst THUMBNAIL_DEFAULT_MAX_DIMENSION = 192;\nconst THUMBNAIL_FALLBACK_ROWS = 12;\nconst THUMBNAIL_FALLBACK_COLS = 8;\nconst THUMBNAIL_MAX_ROWS = 200;\nconst THUMBNAIL_MAX_COLS = 80;\nconst THUMBNAIL_MAX_SOURCE_HEIGHT_PX = 900;\nconst THUMBNAIL_MAX_SOURCE_WIDTH_PX = 1440;\nconst LIVE_ZOOM_COMMIT_IDLE_MS = 48;\nconst WHEEL_ZOOM_SENSITIVITY = 0.0025;\nconst WHEEL_LINE_DELTA_PX = 16;\nconst CHART_SOURCE_HIGHLIGHT_COLORS = [\"#2563eb\", \"#dc2626\", \"#7c3aed\", \"#059669\", \"#ea580c\", \"#db2777\"];\nconst CHART_SOURCE_HIGHLIGHT_COLOR_BY_ROLE = {\n bubble: CHART_SOURCE_HIGHLIGHT_COLORS[3] ?? \"#059669\",\n category: CHART_SOURCE_HIGHLIGHT_COLORS[2] ?? \"#7c3aed\",\n name: CHART_SOURCE_HIGHLIGHT_COLORS[0] ?? \"#2563eb\",\n point: CHART_SOURCE_HIGHLIGHT_COLORS[5] ?? \"#db2777\",\n value: CHART_SOURCE_HIGHLIGHT_COLORS[4] ?? \"#ea580c\"\n};\nconst SHEET_SURFACE = \"#ffffff\";\nconst SHEET_GRIDLINE = \"#d9d9d9\";\nconst DRAWING_SELECTION_STROKE = \"#64748b\";\nconst DRAWING_SELECTION_HANDLE_FILL = \"#ffffff\";\nconst DRAWING_SELECTION_HANDLE_SHADOW = \"rgba(15, 23, 42, 0.18)\";\nconst DEFAULT_CELL_PADDING = \"0 4px\";\nconst IMAGE_HANDLE_POSITIONS: XlsxImageResizeHandlePosition[] = [\"nw\", \"n\", \"ne\", \"e\", \"se\", \"s\", \"sw\", \"w\"];\nconst CANVAS_CELL_STYLE_CACHE_LIMIT = 4096;\nconst CANVAS_TEXT_MEASURE_CACHE_LIMIT = 20000;\nconst CANVAS_TEXT_TRUNCATE_CACHE_LIMIT = 4096;\nconst CANVAS_TEXT_WRAP_CACHE_LIMIT = 2048;\nconst CANVAS_PATH2D_CACHE_LIMIT = 512;\nconst EMPTY_VIRTUAL_ITEMS: VirtualItem[] = [];\n\ntype SelectionDragState = {\n anchor: XlsxCellAddress;\n axis: \"cell\" | \"column\" | \"row\";\n contentScaleX: number;\n contentScaleY: number;\n committedOnPointerDown: boolean;\n didDrag: boolean;\n originCell: XlsxCellAddress;\n originOverlayRect: { height: number; left: number; top: number; width: number } | null;\n originContentX: number;\n originContentY: number;\n previewRange: XlsxCellRange;\n pointerId: number;\n startClientX: number;\n startClientY: number;\n};\n\nconst IMAGE_HANDLE_CURSOR: Record<XlsxImageResizeHandlePosition, React.CSSProperties[\"cursor\"]> = {\n e: \"ew-resize\",\n n: \"ns-resize\",\n ne: \"nesw-resize\",\n nw: \"nwse-resize\",\n s: \"ns-resize\",\n se: \"nwse-resize\",\n sw: \"nesw-resize\",\n w: \"ew-resize\"\n};\n\nconst NUMERIC_LENGTH_STYLE_KEYS = new Set([\n \"borderRadius\",\n \"borderTopLeftRadius\",\n \"borderTopRightRadius\",\n \"borderBottomLeftRadius\",\n \"borderBottomRightRadius\",\n \"bottom\",\n \"fontSize\",\n \"gap\",\n \"height\",\n \"left\",\n \"letterSpacing\",\n \"margin\",\n \"marginBottom\",\n \"marginLeft\",\n \"marginRight\",\n \"marginTop\",\n \"maxHeight\",\n \"maxWidth\",\n \"minHeight\",\n \"minWidth\",\n \"outlineOffset\",\n \"outlineWidth\",\n \"padding\",\n \"paddingBottom\",\n \"paddingLeft\",\n \"paddingRight\",\n \"paddingTop\",\n \"right\",\n \"textIndent\",\n \"top\",\n \"width\"\n]);\n\nconst canvasCellStyleCache = new Map<string, CanvasCellStyleCache>();\nconst canvasTextMeasureCache = new Map<string, number>();\nconst canvasTextTruncateCache = new Map<string, string>();\nconst canvasTextWrapCache = new Map<string, string[]>();\nconst canvasPath2DCache = new Map<string, Path2D>();\n\nfunction rememberBoundedCacheValue<K, V>(cache: Map<K, V>, key: K, value: V, limit: number) {\n if (cache.size >= limit) {\n const oldestKey = cache.keys().next().value as K | undefined;\n if (oldestKey !== undefined) {\n cache.delete(oldestKey);\n }\n }\n cache.set(key, value);\n return value;\n}\n\nfunction roundCanvasCacheWidth(width: number) {\n return Math.round(width * 4) / 4;\n}\n\nfunction measureCanvasTextWidth(context: CanvasRenderingContext2D, text: string) {\n if (text.length === 0) {\n return 0;\n }\n\n const cacheKey = `${context.font}\\u0000${text}`;\n const cached = canvasTextMeasureCache.get(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n return rememberBoundedCacheValue(\n canvasTextMeasureCache,\n cacheKey,\n context.measureText(text).width,\n CANVAS_TEXT_MEASURE_CACHE_LIMIT\n );\n}\n\nfunction drawCanvasTextDecorations(\n context: CanvasRenderingContext2D,\n {\n align,\n color,\n decoration,\n ellipsize = false,\n lineThroughY,\n maxWidth,\n text,\n textX,\n underlineY,\n zoomFactor\n }: {\n align: CanvasTextAlign;\n color: string;\n decoration: string | undefined;\n ellipsize?: boolean;\n lineThroughY: number;\n maxWidth?: number;\n text: string;\n textX: number;\n underlineY: number;\n zoomFactor: number;\n }\n) {\n if (!decoration || text.length === 0) {\n return;\n }\n\n const measured = ellipsize && maxWidth !== undefined\n ? Math.min(maxWidth, measureCanvasTextWidth(context, text))\n : measureCanvasTextWidth(context, text);\n const startX = align === \"right\"\n ? textX - measured\n : align === \"center\"\n ? textX - (measured / 2)\n : textX;\n\n context.strokeStyle = color;\n context.lineWidth = Math.max(1, zoomFactor * 0.75);\n\n if (decoration.includes(\"underline\")) {\n context.beginPath();\n context.moveTo(startX, underlineY);\n context.lineTo(startX + measured, underlineY);\n context.stroke();\n }\n\n if (decoration.includes(\"line-through\")) {\n context.beginPath();\n context.moveTo(startX, lineThroughY);\n context.lineTo(startX + measured, lineThroughY);\n context.stroke();\n }\n}\n\nfunction getCachedCanvasPath2D(path: string) {\n if (typeof Path2D === \"undefined\") {\n return null;\n }\n\n const cached = canvasPath2DCache.get(path);\n if (cached) {\n return cached;\n }\n\n return rememberBoundedCacheValue(canvasPath2DCache, path, new Path2D(path), CANVAS_PATH2D_CACHE_LIMIT);\n}\n\nfunction scaleCssLengthExpression(value: string, scale: number) {\n if (scale === 1) {\n return value;\n }\n\n return value.replace(/(-?\\d*\\.?\\d+)(px|pt)\\b/g, (_, rawNumber: string, unit: string) => {\n const nextValue = Number.parseFloat(rawNumber);\n if (!Number.isFinite(nextValue)) {\n return `${rawNumber}${unit}`;\n }\n return `${nextValue * scale}${unit}`;\n });\n}\n\nfunction scaleCssProperties(style: React.CSSProperties, scale: number): React.CSSProperties {\n if (scale === 1) {\n return style;\n }\n\n const nextStyle: React.CSSProperties = {};\n Object.entries(style).forEach(([key, value]) => {\n if (typeof value === \"string\") {\n nextStyle[key as keyof React.CSSProperties] = scaleCssLengthExpression(value, scale) as never;\n return;\n }\n\n if (typeof value === \"number\" && NUMERIC_LENGTH_STYLE_KEYS.has(key)) {\n nextStyle[key as keyof React.CSSProperties] = (value * scale) as never;\n return;\n }\n\n nextStyle[key as keyof React.CSSProperties] = value as never;\n });\n\n return nextStyle;\n}\n\nfunction parseCanvasLength(value: React.CSSProperties[keyof React.CSSProperties] | undefined, fallback = 0) {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n\n if (typeof value === \"string\") {\n const match = value.match(/-?\\d*\\.?\\d+/);\n if (match) {\n const parsed = Number.parseFloat(match[0]);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n }\n\n return fallback;\n}\n\nfunction resolveCanvasPadding(padding: React.CSSProperties[\"padding\"]) {\n if (typeof padding === \"number\") {\n return {\n bottom: padding,\n left: padding,\n right: padding,\n top: padding\n };\n }\n\n if (typeof padding === \"string\") {\n const parts = padding\n .trim()\n .split(/\\s+/)\n .map((part) => parseCanvasLength(part, 0));\n\n if (parts.length === 1) {\n const [value = 0] = parts;\n return {\n bottom: value,\n left: value,\n right: value,\n top: value\n };\n }\n\n if (parts.length === 2) {\n const [vertical = 0, horizontal = 0] = parts;\n return {\n bottom: vertical,\n left: horizontal,\n right: horizontal,\n top: vertical\n };\n }\n\n if (parts.length === 3) {\n const [top = 0, horizontal = 0, bottom = 0] = parts;\n return {\n bottom,\n left: horizontal,\n right: horizontal,\n top\n };\n }\n\n const [top = 0, right = 0, bottom = 0, left = 0] = parts;\n return {\n bottom,\n left,\n right,\n top\n };\n }\n\n return {\n bottom: 0,\n left: 0,\n right: 0,\n top: 0\n };\n}\n\nfunction resolveSpreadsheetTextRotation(textRotation: unknown) {\n if (typeof textRotation !== \"number\" || !Number.isFinite(textRotation)) {\n return null;\n }\n\n if (textRotation < 0 || textRotation > 180) {\n return null;\n }\n\n const excelDegrees = textRotation > 90 ? 90 - textRotation : textRotation;\n return -excelDegrees;\n}\n\nfunction resolveRotatedTextBounds(width: number, height: number, degrees: number) {\n const radians = degrees * Math.PI / 180;\n return {\n width: Math.abs(width * Math.cos(radians)) + Math.abs(height * Math.sin(radians)),\n height: Math.abs(width * Math.sin(radians)) + Math.abs(height * Math.cos(radians))\n };\n}\n\nfunction drawRotatedCellText(\n context: CanvasRenderingContext2D,\n text: string,\n degrees: number,\n rect: { left: number; top: number; width: number; height: number },\n align: CanvasTextAlign,\n verticalAlign: React.CSSProperties[\"verticalAlign\"],\n lineHeight: number\n) {\n const bounds = resolveRotatedTextBounds(context.measureText(text).width, lineHeight, degrees);\n const centerX = align === \"right\"\n ? rect.left + rect.width - bounds.width / 2\n : align === \"center\"\n ? rect.left + rect.width / 2\n : rect.left + bounds.width / 2;\n const centerY = verticalAlign === \"top\"\n ? rect.top + bounds.height / 2\n : verticalAlign === \"middle\"\n ? rect.top + rect.height / 2\n : rect.top + rect.height - bounds.height / 2;\n context.save();\n context.translate(centerX, centerY);\n context.rotate(degrees * Math.PI / 180);\n context.textAlign = \"center\";\n context.fillText(text, 0, 0);\n context.restore();\n}\n\nfunction resolveCanvasFont(style: React.CSSProperties, fallbackSize = 12) {\n if (typeof style.font === \"string\" && style.font.trim().length > 0) {\n return style.font;\n }\n\n const fontStyle = typeof style.fontStyle === \"string\" && style.fontStyle.trim().length > 0\n ? style.fontStyle\n : \"normal\";\n const fontSize = typeof style.fontSize === \"string\" && style.fontSize.trim().length > 0\n ? style.fontSize\n : typeof style.fontSize === \"number\" && Number.isFinite(style.fontSize)\n ? `${style.fontSize}px`\n : `${fallbackSize}px`;\n const fontWeight = typeof style.fontWeight === \"number\" || typeof style.fontWeight === \"string\"\n ? String(style.fontWeight)\n : \"400\";\n const fontFamily = typeof style.fontFamily === \"string\" && style.fontFamily.trim().length > 0\n ? style.fontFamily\n : \"ui-sans-serif, system-ui, sans-serif\";\n\n return `${fontStyle} ${fontWeight} ${fontSize} ${fontFamily}`;\n}\n\nfunction resolveCanvasFontWithPxSize(style: React.CSSProperties, fontSizePx: number) {\n const fontStyle = typeof style.fontStyle === \"string\" && style.fontStyle.trim().length > 0\n ? style.fontStyle\n : \"normal\";\n const fontWeight = typeof style.fontWeight === \"number\" || typeof style.fontWeight === \"string\"\n ? String(style.fontWeight)\n : \"400\";\n const fontFamily = typeof style.fontFamily === \"string\" && style.fontFamily.trim().length > 0\n ? style.fontFamily\n : \"ui-sans-serif, system-ui, sans-serif\";\n\n return `${fontStyle} ${fontWeight} ${Math.max(1, fontSizePx)}px ${fontFamily}`;\n}\n\ntype CanvasBorderDeclaration = {\n color: string;\n style: string;\n width: number;\n};\n\nfunction parseCanvasBorderDeclaration(borderValue: React.CSSProperties[\"borderTop\"]) {\n if (typeof borderValue !== \"string\") {\n return null;\n }\n\n const trimmed = borderValue.trim();\n if (!trimmed || trimmed === \"none\") {\n return null;\n }\n\n const parts = trimmed.split(/\\s+/);\n if (parts.length < 3) {\n return null;\n }\n\n const width = parseCanvasLength(parts[0], 1);\n const style = parts[1] ?? \"solid\";\n const color = parts.slice(2).join(\" \");\n if (!Number.isFinite(width) || width <= 0 || !color) {\n return null;\n }\n\n return { color, style, width } satisfies CanvasBorderDeclaration;\n}\n\nfunction getCanvasBorderPriority(border: CanvasBorderDeclaration | null) {\n if (!border) {\n return -1;\n }\n\n const stylePriority: Record<string, number> = {\n dashed: 1,\n dotted: 0,\n double: 3,\n solid: 2\n };\n return (border.width * 10) + (stylePriority[border.style] ?? 0);\n}\n\nfunction resolveCanvasBoundaryBorder(\n primary: CanvasBorderDeclaration | null,\n secondary: CanvasBorderDeclaration | null\n) {\n const primaryPriority = getCanvasBorderPriority(primary);\n const secondaryPriority = getCanvasBorderPriority(secondary);\n return secondaryPriority > primaryPriority ? secondary : primary;\n}\n\nfunction applyCanvasBorderDash(\n context: CanvasRenderingContext2D,\n style: string,\n width: number\n) {\n context.lineCap = \"butt\";\n if (style === \"dashed\") {\n context.setLineDash([Math.max(3, width * 3), Math.max(2, width * 2)]);\n return;\n }\n if (style === \"dotted\") {\n context.lineCap = \"round\";\n context.setLineDash([Math.max(0.01, width * 0.01), Math.max(2, width * 2.2)]);\n return;\n }\n context.setLineDash([]);\n}\n\nfunction strokeCanvasBorderSide(\n context: CanvasRenderingContext2D,\n side: \"top\" | \"right\" | \"bottom\" | \"left\",\n rect: { left: number; top: number; width: number; height: number },\n border: CanvasBorderDeclaration\n) {\n const strokeWidth = border.style === \"double\" ? border.width / 3 : border.width;\n const halfWidth = strokeWidth / 2;\n const left = rect.left;\n const right = rect.left + rect.width;\n const top = rect.top;\n const bottom = rect.top + rect.height;\n\n context.strokeStyle = border.color;\n context.lineWidth = border.width;\n applyCanvasBorderDash(context, border.style, border.width);\n\n const strokeLine = (offset = 0) => {\n context.beginPath();\n if (side === \"top\") {\n context.moveTo(left, top + halfWidth + offset);\n context.lineTo(right, top + halfWidth + offset);\n } else if (side === \"bottom\") {\n context.moveTo(left, bottom - halfWidth - offset);\n context.lineTo(right, bottom - halfWidth - offset);\n } else if (side === \"left\") {\n context.moveTo(left + halfWidth + offset, top);\n context.lineTo(left + halfWidth + offset, bottom);\n } else {\n context.moveTo(right - halfWidth - offset, top);\n context.lineTo(right - halfWidth - offset, bottom);\n }\n context.stroke();\n };\n\n if (border.style === \"double\") {\n const inset = border.width - strokeWidth;\n context.lineWidth = strokeWidth;\n context.setLineDash([]);\n strokeLine(0);\n strokeLine(inset);\n } else {\n strokeLine(0);\n }\n\n context.setLineDash([]);\n context.lineCap = \"butt\";\n context.lineWidth = 1;\n}\n\nfunction truncateCanvasText(\n context: CanvasRenderingContext2D,\n text: string,\n maxWidth: number\n) {\n if (maxWidth <= 0 || text.length === 0) {\n return \"\";\n }\n\n const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);\n const cacheKey = `${context.font}\\u0000${roundedMaxWidth}\\u0000${text}`;\n const cached = canvasTextTruncateCache.get(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n if (measureCanvasTextWidth(context, text) <= maxWidth) {\n rememberBoundedCacheValue(canvasTextTruncateCache, cacheKey, text, CANVAS_TEXT_TRUNCATE_CACHE_LIMIT);\n return text;\n }\n\n const ellipsis = \"…\";\n let low = 0;\n let high = text.length;\n while (low < high) {\n const mid = Math.ceil((low + high) / 2);\n const candidate = `${text.slice(0, mid)}${ellipsis}`;\n if (measureCanvasTextWidth(context, candidate) <= maxWidth) {\n low = mid;\n } else {\n high = mid - 1;\n }\n }\n\n return rememberBoundedCacheValue(\n canvasTextTruncateCache,\n cacheKey,\n low <= 0 ? ellipsis : `${text.slice(0, low)}${ellipsis}`,\n CANVAS_TEXT_TRUNCATE_CACHE_LIMIT\n );\n}\n\nfunction resolveCanvasFontSizePx(style: React.CSSProperties, fallbackSize = 12) {\n const rawFontSize = style.fontSize;\n if (typeof rawFontSize === \"number\" && Number.isFinite(rawFontSize)) {\n return rawFontSize;\n }\n\n if (typeof rawFontSize === \"string\") {\n const trimmed = rawFontSize.trim();\n const parsed = Number.parseFloat(trimmed);\n if (Number.isFinite(parsed)) {\n if (trimmed.endsWith(\"pt\")) {\n return parsed * (96 / 72);\n }\n if (trimmed.endsWith(\"em\") || trimmed.endsWith(\"rem\")) {\n return parsed * 16;\n }\n return parsed;\n }\n }\n\n return fallbackSize;\n}\n\nfunction resolveCanvasLineHeight(style: React.CSSProperties, fallbackFontSize = 12) {\n const fontSizePx = resolveCanvasFontSizePx(style, fallbackFontSize);\n const rawLineHeight = style.lineHeight;\n\n if (typeof rawLineHeight === \"number\" && Number.isFinite(rawLineHeight)) {\n return rawLineHeight > 4 ? rawLineHeight : rawLineHeight * fontSizePx;\n }\n\n if (typeof rawLineHeight === \"string\") {\n const trimmed = rawLineHeight.trim();\n if (!trimmed || trimmed === \"normal\") {\n return fontSizePx * 1.2;\n }\n\n const parsed = Number.parseFloat(trimmed);\n if (Number.isFinite(parsed)) {\n if (/^-?\\d*\\.?\\d+$/.test(trimmed)) {\n return parsed > 4 ? parsed : parsed * fontSizePx;\n }\n if (trimmed.endsWith(\"pt\")) {\n return parsed * (96 / 72);\n }\n if (trimmed.endsWith(\"em\") || trimmed.endsWith(\"rem\")) {\n return parsed * 16;\n }\n return parsed;\n }\n }\n\n return fontSizePx * 1.2;\n}\n\nfunction resolveCanvasTextMiddleY(\n verticalAlign: React.CSSProperties[\"verticalAlign\"],\n contentTop: number,\n contentHeight: number,\n lineHeight: number\n) {\n if (verticalAlign === \"top\") {\n return contentTop + (lineHeight / 2);\n }\n if (verticalAlign === \"middle\") {\n return contentTop + (contentHeight / 2);\n }\n return contentTop + contentHeight - (lineHeight / 2);\n}\n\nfunction resolveCanvasWrapIndex(\n context: CanvasRenderingContext2D,\n text: string,\n maxWidth: number\n) {\n if (text.length <= 1) {\n return text.length;\n }\n\n let low = 1;\n let high = text.length;\n let best = 1;\n while (low <= high) {\n const mid = Math.floor((low + high) / 2);\n const candidate = text.slice(0, mid);\n if (measureCanvasTextWidth(context, candidate) <= maxWidth) {\n best = mid;\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n return best;\n}\n\nfunction wrapCanvasText(\n context: CanvasRenderingContext2D,\n text: string,\n maxWidth: number\n) {\n if (text.length === 0) {\n return [\"\"];\n }\n\n if (maxWidth <= 0) {\n return text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n }\n\n const normalized = text.replace(/\\r\\n?/g, \"\\n\");\n const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);\n const cacheKey = `${context.font}\\u0000${roundedMaxWidth}\\u0000${normalized}`;\n const cached = canvasTextWrapCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const paragraphs = normalized.split(\"\\n\");\n const lines: string[] = [];\n\n for (const paragraph of paragraphs) {\n if (paragraph.length === 0) {\n lines.push(\"\");\n continue;\n }\n\n let remaining = paragraph;\n while (remaining.length > 0) {\n if (measureCanvasTextWidth(context, remaining) <= maxWidth) {\n lines.push(remaining);\n break;\n }\n\n const fit = Math.max(1, resolveCanvasWrapIndex(context, remaining, maxWidth));\n const candidate = remaining.slice(0, fit);\n const whitespaceIndex = fit < remaining.length\n ? Math.max(candidate.lastIndexOf(\" \"), candidate.lastIndexOf(\"\\t\"))\n : -1;\n const breakIndex = whitespaceIndex > 0 ? whitespaceIndex : fit;\n const nextLine = remaining.slice(0, breakIndex);\n\n if (nextLine.length === 0) {\n lines.push(remaining.slice(0, fit));\n remaining = remaining.slice(fit);\n continue;\n }\n\n lines.push(nextLine.replace(/\\s+$/g, \"\"));\n remaining = remaining.slice(breakIndex).replace(/^\\s+/g, \"\");\n }\n }\n\n return rememberBoundedCacheValue(canvasTextWrapCache, cacheKey, lines, CANVAS_TEXT_WRAP_CACHE_LIMIT);\n}\n\ntype CanvasCellStyleCache = {\n baseFont: string;\n leftBorder: CanvasBorderDeclaration | null;\n padding: ReturnType<typeof resolveCanvasPadding>;\n rightBorder: CanvasBorderDeclaration | null;\n textAlign: CanvasTextAlign;\n textColor: string;\n textDecoration?: string;\n textOverflowEllipsis: boolean;\n topBorder: CanvasBorderDeclaration | null;\n usesWrappedText: boolean;\n bottomBorder: CanvasBorderDeclaration | null;\n};\n\nfunction buildCanvasCellStyleCache(style: React.CSSProperties): CanvasCellStyleCache {\n const cacheKey = [\n style.font,\n style.fontStyle,\n style.fontWeight,\n style.fontSize,\n style.fontFamily,\n style.borderTop,\n style.borderRight,\n style.borderBottom,\n style.borderLeft,\n style.padding,\n style.textAlign,\n style.color,\n style.textDecoration,\n style.textOverflow,\n style.whiteSpace\n ].map((value) => value == null ? \"\" : String(value)).join(\"\\u0001\");\n const cached = canvasCellStyleCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n return rememberBoundedCacheValue(canvasCellStyleCache, cacheKey, {\n baseFont: resolveCanvasFont(style, 12),\n bottomBorder: parseCanvasBorderDeclaration(style.borderBottom),\n leftBorder: parseCanvasBorderDeclaration(style.borderLeft),\n padding: resolveCanvasPadding(style.padding),\n rightBorder: parseCanvasBorderDeclaration(style.borderRight),\n textAlign: style.textAlign === \"right\" || style.textAlign === \"center\" ? style.textAlign : \"left\",\n textColor: typeof style.color === \"string\" ? style.color : \"#000000\",\n textDecoration: typeof style.textDecoration === \"string\" ? style.textDecoration : undefined,\n textOverflowEllipsis: style.textOverflow === \"ellipsis\",\n topBorder: parseCanvasBorderDeclaration(style.borderTop),\n usesWrappedText: style.whiteSpace === \"pre-wrap\"\n }, CANVAS_CELL_STYLE_CACHE_LIMIT);\n}\n\nfunction splitCssGradientArgs(value: string) {\n const parts: string[] = [];\n let current = \"\";\n let depth = 0;\n for (const character of value) {\n if (character === \"(\") {\n depth += 1;\n current += character;\n continue;\n }\n if (character === \")\") {\n depth = Math.max(0, depth - 1);\n current += character;\n continue;\n }\n if (character === \",\" && depth === 0) {\n parts.push(current.trim());\n current = \"\";\n continue;\n }\n current += character;\n }\n if (current.trim().length > 0) {\n parts.push(current.trim());\n }\n return parts;\n}\n\nfunction parseCanvasGradientStops(value: string) {\n return splitCssGradientArgs(value)\n .map((part) => {\n const match = part.match(/^(.*?)(?:\\s+(-?\\d*\\.?\\d+)%)?$/);\n if (!match) {\n return null;\n }\n const color = match[1]?.trim();\n const percent = match[2] ? Number.parseFloat(match[2]) : Number.NaN;\n if (!color) {\n return null;\n }\n return {\n color,\n offset: Number.isFinite(percent) ? Math.max(0, Math.min(1, percent / 100)) : null\n };\n })\n .filter((stop): stop is { color: string; offset: number | null } => Boolean(stop));\n}\n\nfunction resolveCanvasGradientFill(\n context: CanvasRenderingContext2D,\n rect: { height: number; left: number; top: number; width: number },\n backgroundImage: string\n) {\n const linearMatch = backgroundImage.match(/^linear-gradient\\((.*)\\)$/i);\n if (linearMatch) {\n const parts = splitCssGradientArgs(linearMatch[1] ?? \"\");\n if (parts.length >= 2) {\n const rawAngle = parts[0]?.trim() ?? \"180deg\";\n const angleMatch = rawAngle.match(/(-?\\d*\\.?\\d+)deg/i);\n const cssAngle = angleMatch ? Number.parseFloat(angleMatch[1] ?? \"180\") : 180;\n const radians = ((90 - cssAngle) * Math.PI) / 180;\n const halfWidth = rect.width / 2;\n const halfHeight = rect.height / 2;\n const centerX = rect.left + halfWidth;\n const centerY = rect.top + halfHeight;\n const projectedHalfLength = Math.abs(Math.cos(radians) * halfWidth) + Math.abs(Math.sin(radians) * halfHeight);\n const startX = centerX - (Math.cos(radians) * projectedHalfLength);\n const startY = centerY + (Math.sin(radians) * projectedHalfLength);\n const endX = centerX + (Math.cos(radians) * projectedHalfLength);\n const endY = centerY - (Math.sin(radians) * projectedHalfLength);\n const gradient = context.createLinearGradient(startX, startY, endX, endY);\n const stops = parseCanvasGradientStops(parts.slice(1).join(\",\"));\n if (stops.length > 0) {\n const normalizedStops = stops.map((stop, index) => ({\n color: stop.color,\n offset: stop.offset ?? (stops.length === 1 ? 0 : index / (stops.length - 1))\n }));\n normalizedStops.forEach((stop) => {\n gradient.addColorStop(stop.offset, stop.color);\n });\n return gradient;\n }\n }\n }\n\n const radialMatch = backgroundImage.match(/^radial-gradient\\((.*)\\)$/i);\n if (radialMatch) {\n const parts = splitCssGradientArgs(radialMatch[1] ?? \"\");\n const stopParts = parts[0]?.startsWith(\"circle\") ? parts.slice(1) : parts;\n const gradient = context.createRadialGradient(\n rect.left + (rect.width / 2),\n rect.top + (rect.height / 2),\n 0,\n rect.left + (rect.width / 2),\n rect.top + (rect.height / 2),\n Math.max(rect.width, rect.height) / 2\n );\n const stops = parseCanvasGradientStops(stopParts.join(\",\"));\n if (stops.length > 0) {\n const normalizedStops = stops.map((stop, index) => ({\n color: stop.color,\n offset: stop.offset ?? (stops.length === 1 ? 0 : index / (stops.length - 1))\n }));\n normalizedStops.forEach((stop) => {\n gradient.addColorStop(stop.offset, stop.color);\n });\n return gradient;\n }\n }\n\n return null;\n}\n\nfunction resolveCanvasDataBarFill(\n context: CanvasRenderingContext2D,\n left: number,\n top: number,\n width: number,\n _height: number,\n dataBar: NonNullable<CellRenderData[\"conditionalDataBar\"]>\n) {\n if (dataBar.gradient === false) {\n return dataBar.color;\n }\n\n const gradient = context.createLinearGradient(left, top, left + width, top);\n gradient.addColorStop(0, lightenColor(dataBar.color, 0.28));\n gradient.addColorStop(1, dataBar.color);\n return gradient;\n}\n\nfunction drawCanvasConditionalIcon(\n context: CanvasRenderingContext2D,\n icon: NonNullable<CellRenderData[\"conditionalIcon\"]>,\n centerX: number,\n centerY: number,\n size: number\n) {\n context.save();\n if (icon.glyph) {\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n context.fillStyle = icon.color ?? icon.backgroundColor ?? \"#6b7280\";\n context.fillText(icon.glyph, centerX, centerY);\n context.restore();\n return;\n }\n\n if (icon.shape === \"arrow\") {\n const fill = icon.color ?? \"#111827\";\n const stroke = icon.borderColor ?? darkenColor(fill, 0.32);\n const scale = size / 16;\n context.translate(centerX, centerY);\n context.rotate(((icon.rotationDeg ?? 0) * Math.PI) / 180);\n context.translate(-8 * scale, -8 * scale);\n context.beginPath();\n context.moveTo(2.5 * scale, 8 * scale);\n context.lineTo(8.4 * scale, 2.4 * scale);\n context.lineTo(8.4 * scale, 5.2 * scale);\n context.lineTo(13.5 * scale, 5.2 * scale);\n context.lineTo(13.5 * scale, 10.8 * scale);\n context.lineTo(8.4 * scale, 10.8 * scale);\n context.lineTo(8.4 * scale, 13.6 * scale);\n context.closePath();\n context.fillStyle = fill;\n context.fill();\n context.strokeStyle = stroke;\n context.lineWidth = Math.max(1, 1.25 * scale);\n context.lineJoin = \"round\";\n context.stroke();\n context.restore();\n return;\n }\n\n context.beginPath();\n context.arc(centerX, centerY, size / 2, 0, Math.PI * 2);\n context.fillStyle = icon.color ?? icon.backgroundColor ?? \"#6b7280\";\n context.fill();\n context.restore();\n}\n\nfunction formatZoomScale(zoomScale: number) {\n return `${Math.round(zoomScale)}%`;\n}\n\nfunction clampValue(value: number, min: number, max: number) {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction normalizeWheelDelta(event: WheelEvent) {\n const dominantDelta = Math.abs(event.deltaY) >= Math.abs(event.deltaX) ? event.deltaY : event.deltaX;\n switch (event.deltaMode) {\n case WheelEvent.DOM_DELTA_LINE:\n return dominantDelta * WHEEL_LINE_DELTA_PX;\n case WheelEvent.DOM_DELTA_PAGE:\n return dominantDelta * window.innerHeight;\n default:\n return dominantDelta;\n }\n}\n\nfunction resolveEventAnchor(clientX: number, clientY: number, rect: DOMRect): ZoomAnchor {\n return {\n x: clampValue(clientX - rect.left, 0, rect.width),\n y: clampValue(clientY - rect.top, 0, rect.height)\n };\n}\n\nfunction resolveTouchDistance(firstTouch: Touch, secondTouch: Touch) {\n return Math.hypot(firstTouch.clientX - secondTouch.clientX, firstTouch.clientY - secondTouch.clientY);\n}\n\nfunction parseRgbColor(color: string) {\n const match = /^#?([0-9a-f]{6})$/i.exec(color);\n if (!match) {\n return null;\n }\n return {\n blue: Number.parseInt(match[1].slice(4, 6), 16),\n green: Number.parseInt(match[1].slice(2, 4), 16),\n red: Number.parseInt(match[1].slice(0, 2), 16)\n };\n}\n\nfunction mixRgbColor(color: string, mixWith: string, ratio: number) {\n const base = parseRgbColor(color);\n const target = parseRgbColor(mixWith);\n if (!base || !target) {\n return color;\n }\n const clamped = Math.max(0, Math.min(1, ratio));\n const mixChannel = (left: number, right: number) => Math.round(left + (right - left) * clamped);\n return `#${[\n mixChannel(base.red, target.red),\n mixChannel(base.green, target.green),\n mixChannel(base.blue, target.blue)\n ].map((value) => value.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nfunction lightenColor(color: string, ratio: number) {\n return mixRgbColor(color, \"#ffffff\", ratio);\n}\n\nfunction darkenColor(color: string, ratio: number) {\n return mixRgbColor(color, \"#000000\", ratio);\n}\n\nexport const ViewerContext = React.createContext<XlsxViewerController | null>(null);\nexport const ViewerAppearanceContext = React.createContext<{\n headerBackgroundColor?: string;\n headerTextColor?: string;\n isDark: boolean;\n}>({ isDark: false });\n\ntype ZoomAnchor = {\n x: number;\n y: number;\n};\n\ntype WebKitGestureEvent = Event & {\n clientX: number;\n clientY: number;\n scale: number;\n};\n\ntype LiveGestureZoomState = {\n anchor: ZoomAnchor;\n baseZoomScale: number;\n targetZoomScale: number;\n};\n\ntype ChartRangeHighlight = {\n fillColor: string;\n range: XlsxCellRange;\n strokeColor: string;\n workbookSheetIndex: number;\n};\n\ntype CellChartHighlight = {\n borderBottom: boolean;\n borderLeft: boolean;\n borderRight: boolean;\n borderTop: boolean;\n fillColor: string;\n strokeColor: string;\n};\n\ntype WorksheetWithRowsBatch = Worksheet & {\n getRowsBatch?: (startRow: number, maxRows: number, options?: unknown) => unknown;\n};\n\ntype WorksheetBatchCellEntry = {\n col?: unknown;\n formula?: unknown;\n generalAlignment?: unknown;\n numericValue?: unknown;\n hyperlink?: unknown;\n isMergedSecondary?: unknown;\n mergeSpan?: unknown;\n style?: unknown;\n value?: unknown;\n};\n\ntype WorksheetBatchRowEntry = {\n cells?: unknown;\n index?: unknown;\n};\n\ntype BatchedCellData = {\n formula: string | null;\n generalAlignment?: CellGeneralAlignment;\n numericValue?: number | null;\n hyperlink: {\n location?: string;\n target?: string;\n tooltip?: string;\n } | null;\n isMergedSecondary: boolean;\n mergeSpan: { colSpan?: number; rowSpan?: number } | null;\n style: Record<string, unknown> | null;\n value: string;\n};\n\ntype WorksheetBatchWindow = {\n cells: Map<string, BatchedCellData>;\n endRow: number;\n startRow: number;\n};\n\ntype ThumbnailWorkerRowBatchRequest = {\n endRow: number;\n startRow: number;\n};\n\nfunction classNames(...values: Array<string | false | null | undefined>) {\n return values.filter(Boolean).join(\" \");\n}\n\nfunction resolveDarkModeSurface(themePalette: XlsxSheetData[\"themePalette\"] | undefined, palette: ViewerPalette) {\n return \"hsl(225 4% 6%)\";\n}\n\nfunction resolveSheetSurface(sheet: XlsxSheetData | null, palette: ViewerPalette) {\n return paletteIsDark(palette)\n ? resolveDarkModeSurface(sheet?.themePalette, palette)\n : sheet?.themePalette.colorsByIndex[0] ?? SHEET_SURFACE;\n}\n\nfunction normalizeRange(range: XlsxCellRange): XlsxCellRange {\n return {\n start: {\n col: Math.min(range.start.col, range.end.col),\n row: Math.min(range.start.row, range.end.row)\n },\n end: {\n col: Math.max(range.start.col, range.end.col),\n row: Math.max(range.start.row, range.end.row)\n }\n };\n}\n\nfunction parseCellAddressAttribute(value: string | null): XlsxCellAddress | null {\n if (!value) {\n return null;\n }\n\n const [rowValue, colValue] = value.split(\":\");\n const row = Number(rowValue);\n const col = Number(colValue);\n if (!Number.isInteger(row) || !Number.isInteger(col) || row < 0 || col < 0) {\n return null;\n }\n\n return { row, col };\n}\n\nfunction isSameCell(left: XlsxCellAddress | null, right: XlsxCellAddress | null) {\n return Boolean(left && right && left.row === right.row && left.col === right.col);\n}\n\nfunction isCellInRange(cell: XlsxCellAddress, range: XlsxCellRange | null) {\n if (!range) {\n return false;\n }\n\n const normalized = normalizeRange(range);\n return (\n cell.row >= normalized.start.row &&\n cell.row <= normalized.end.row &&\n cell.col >= normalized.start.col &&\n cell.col <= normalized.end.col\n );\n}\n\nfunction resolveCellChartHighlight(\n cell: XlsxCellAddress,\n highlights: ChartRangeHighlight[]\n): CellChartHighlight | null {\n let match: CellChartHighlight | null = null;\n\n for (const highlight of highlights) {\n if (!isCellInRange(cell, highlight.range)) {\n continue;\n }\n\n const normalized = normalizeRange(highlight.range);\n match = {\n borderBottom: cell.row === normalized.end.row,\n borderLeft: cell.col === normalized.start.col,\n borderRight: cell.col === normalized.end.col,\n borderTop: cell.row === normalized.start.row,\n fillColor: highlight.fillColor,\n strokeColor: highlight.strokeColor\n };\n }\n\n return match;\n}\n\nfunction rangesEqual(left: XlsxCellRange | null, right: XlsxCellRange | null) {\n if (!left || !right) {\n return left === right;\n }\n\n const normalizedLeft = normalizeRange(left);\n const normalizedRight = normalizeRange(right);\n return (\n normalizedLeft.start.row === normalizedRight.start.row &&\n normalizedLeft.start.col === normalizedRight.start.col &&\n normalizedLeft.end.row === normalizedRight.end.row &&\n normalizedLeft.end.col === normalizedRight.end.col\n );\n}\n\nfunction isPrintableKey(event: Pick<KeyboardEvent, \"altKey\" | \"ctrlKey\" | \"key\" | \"metaKey\">) {\n return event.key.length === 1 && !event.altKey && !event.ctrlKey && !event.metaKey;\n}\n\nfunction getInteractiveFocusTarget(target: EventTarget | null) {\n if (!(target instanceof Element)) {\n return null;\n }\n\n return target.closest(\n \"a[href], button, input, select, textarea, [contenteditable=''], [contenteditable='true'], [role='button'], [role='menu'], [role='menuitem'], [role='textbox'], [tabindex]:not([tabindex='-1'])\"\n );\n}\n\nfunction isInteractiveFocusTarget(target: EventTarget | null, container: HTMLElement) {\n const interactiveElement = getInteractiveFocusTarget(target);\n return Boolean(interactiveElement && interactiveElement !== container && container.contains(interactiveElement));\n}\n\nfunction sumSegment(values: number[], startIndex: number, endIndex: number) {\n let total = 0;\n for (let index = startIndex; index <= endIndex; index += 1) {\n total += values[index] ?? 0;\n }\n return total;\n}\n\nfunction findIndexForOffset(values: number[], offset: number) {\n if (offset <= 0) {\n return 0;\n }\n\n let currentOffset = 0;\n for (let index = 0; index < values.length; index += 1) {\n currentOffset += values[index] ?? 0;\n if (offset < currentOffset) {\n return index;\n }\n }\n\n return values.length > 0 ? values.length - 1 : -1;\n}\n\nfunction buildPrefixSums(values: number[]) {\n const prefix = new Array(values.length + 1).fill(0);\n for (let index = 0; index < values.length; index += 1) {\n prefix[index + 1] = prefix[index] + (values[index] ?? 0);\n }\n return prefix;\n}\n\ntype IndexedMergedRegion = {\n endColIndex: number;\n endRowIndex: number;\n startColIndex: number;\n startRowIndex: number;\n};\n\nfunction setIntersectsIndexRange(indices: Set<number>, startIndex: number, endIndex: number) {\n for (let index = startIndex; index <= endIndex; index += 1) {\n if (indices.has(index)) {\n return true;\n }\n }\n return false;\n}\n\nfunction expandMergeAwareWindow(\n rowIndices: number[],\n colIndices: number[],\n mergedRegions: IndexedMergedRegion[]\n) {\n const nextRowIndices = new Set(rowIndices);\n const nextColIndices = new Set(colIndices);\n\n if (nextRowIndices.size === 0 || nextColIndices.size === 0 || mergedRegions.length === 0) {\n return {\n colIndices: Array.from(nextColIndices).sort((left, right) => left - right),\n rowIndices: Array.from(nextRowIndices).sort((left, right) => left - right)\n };\n }\n\n let changed = true;\n while (changed) {\n changed = false;\n for (const region of mergedRegions) {\n const intersectsRows = setIntersectsIndexRange(nextRowIndices, region.startRowIndex, region.endRowIndex);\n const intersectsCols = setIntersectsIndexRange(nextColIndices, region.startColIndex, region.endColIndex);\n if (!intersectsRows || !intersectsCols) {\n continue;\n }\n\n for (let rowIndex = region.startRowIndex; rowIndex <= region.endRowIndex; rowIndex += 1) {\n if (!nextRowIndices.has(rowIndex)) {\n nextRowIndices.add(rowIndex);\n changed = true;\n }\n }\n for (let colIndex = region.startColIndex; colIndex <= region.endColIndex; colIndex += 1) {\n if (!nextColIndices.has(colIndex)) {\n nextColIndices.add(colIndex);\n changed = true;\n }\n }\n }\n }\n\n return {\n colIndices: Array.from(nextColIndices).sort((left, right) => left - right),\n rowIndices: Array.from(nextRowIndices).sort((left, right) => left - right)\n };\n}\n\nfunction buildRenderedAxisSignature(\n items: Array<{ index: number; size: number; start: number }>,\n resolveActualKey: (item: { index: number; size: number; start: number }) => number\n) {\n return items\n .map((item) => `${resolveActualKey(item)}:${item.start}:${item.size}`)\n .join(\"|\");\n}\n\nfunction buildRangeSignature(range: XlsxCellRange | null) {\n if (!range) {\n return \"\";\n }\n\n const normalized = normalizeRange(range);\n return [\n normalized.start.row,\n normalized.start.col,\n normalized.end.row,\n normalized.end.col\n ].join(\":\");\n}\n\nfunction sumPrefixRange(prefixSums: number[], startIndex: number, endIndex: number) {\n if (startIndex > endIndex) {\n return 0;\n }\n\n return (prefixSums[endIndex + 1] ?? 0) - (prefixSums[startIndex] ?? 0);\n}\n\nfunction buildStickyOffsets(actualIndices: number[], sizesByActualIndex: number[], leadingOffset: number) {\n const offsets = new Map<number, number>();\n let nextOffset = leadingOffset;\n\n for (const actualIndex of actualIndices) {\n offsets.set(actualIndex, nextOffset);\n nextOffset += sizesByActualIndex[actualIndex] ?? 0;\n }\n\n return offsets;\n}\n\nfunction findIndexForOffsetPrefix(prefixSums: number[], offset: number) {\n const count = prefixSums.length - 1;\n if (count <= 0) {\n return -1;\n }\n if (offset <= 0) {\n return 0;\n }\n if (offset >= prefixSums[count]) {\n return count - 1;\n }\n\n let low = 0;\n let high = count - 1;\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n if ((prefixSums[mid + 1] ?? 0) <= offset) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n return low;\n}\n\nfunction clampContentOffset(offset: number, maxOffset: number) {\n if (!Number.isFinite(offset) || maxOffset <= 0) {\n return 0;\n }\n\n return Math.max(0, Math.min(offset, Math.max(0, maxOffset - 0.001)));\n}\n\nfunction resolveOpenGridExtent(maxUsedIndex: number, minimum: number, padding: number) {\n return Math.max(maxUsedIndex + 1 + padding, minimum);\n}\n\nfunction buildVisibleAxisIndices(\n precomputed: number[],\n displayLimit: number,\n maxUsedIndex: number,\n hiddenIndices?: Set<number>\n) {\n if (displayLimit <= 0) {\n return [];\n }\n\n if (precomputed.length === 0) {\n const visible: number[] = [];\n const usedLimit = Math.min(displayLimit, Math.max(0, maxUsedIndex + 1));\n for (let index = 0; index < usedLimit; index += 1) {\n if (!hiddenIndices?.has(index)) {\n visible.push(index);\n }\n }\n for (let index = usedLimit; index < displayLimit; index += 1) {\n visible.push(index);\n }\n return visible;\n }\n\n const visible = precomputed.filter((value) => value < displayLimit);\n const appendStart = Math.max(0, maxUsedIndex + 1);\n for (let index = appendStart; index < displayLimit; index += 1) {\n visible.push(index);\n }\n return visible;\n}\n\nfunction resolveInitialDisplayExtent(\n maxUsedIndex: number,\n minimum: number,\n padding: number,\n isWorkerBacked: boolean,\n initialCap: number\n) {\n const fullExtent = resolveOpenGridExtent(maxUsedIndex, minimum, padding);\n return isWorkerBacked ? Math.min(fullExtent, Math.max(minimum, initialCap)) : fullExtent;\n}\n\nfunction sumBeforeActualIndex(actualIndices: number[], sizes: number[], actualIndex: number) {\n let total = 0;\n for (let index = 0; index < actualIndices.length; index += 1) {\n if ((actualIndices[index] ?? 0) >= actualIndex) {\n break;\n }\n total += sizes[index] ?? 0;\n }\n return total;\n}\n\nfunction resolveAxisStartOffset(\n actualIndex: number,\n actualIndices: number[],\n sizes: number[],\n indexByActual?: Map<number, number>,\n prefixSums?: number[],\n actualPrefixSums?: number[]\n) {\n if (actualPrefixSums && actualIndex >= 0 && actualIndex < actualPrefixSums.length) {\n return actualPrefixSums[actualIndex] ?? 0;\n }\n\n const visibleIndex = indexByActual?.get(actualIndex);\n if (visibleIndex !== undefined && prefixSums) {\n return prefixSums[visibleIndex] ?? 0;\n }\n\n return sumBeforeActualIndex(actualIndices, sizes, actualIndex);\n}\n\nfunction resolveAnchoredRect(\n anchor: XlsxImage[\"anchor\"] | XlsxShape[\"anchor\"],\n visibleRows: number[],\n visibleCols: number[],\n rowHeights: number[],\n colWidths: number[],\n options?: {\n actualColPrefixSums?: number[];\n actualRowPrefixSums?: number[];\n colIndexByActual?: Map<number, number>;\n colPrefixSums?: number[];\n headerHeight?: number;\n rowIndexByActual?: Map<number, number>;\n rowHeaderWidth?: number;\n rowPrefixSums?: number[];\n zoomFactor?: number;\n }\n): XlsxImageRect {\n const headerHeight = options?.headerHeight ?? HEADER_HEIGHT;\n const rowHeaderWidth = options?.rowHeaderWidth ?? ROW_HEADER_WIDTH;\n const anchorZoomFactor = options?.zoomFactor ?? 1;\n const resolveMarkerLeft = (col: number, colOffsetEmu: number) =>\n rowHeaderWidth +\n resolveAxisStartOffset(\n col,\n visibleCols,\n colWidths,\n options?.colIndexByActual,\n options?.colPrefixSums,\n options?.actualColPrefixSums\n ) +\n emuToPixels(colOffsetEmu) * anchorZoomFactor;\n const resolveMarkerTop = (row: number, rowOffsetEmu: number) =>\n headerHeight +\n resolveAxisStartOffset(\n row,\n visibleRows,\n rowHeights,\n options?.rowIndexByActual,\n options?.rowPrefixSums,\n options?.actualRowPrefixSums\n ) +\n emuToPixels(rowOffsetEmu) * anchorZoomFactor;\n\n if (anchor.kind === \"absolute\") {\n return {\n height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),\n left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x) * anchorZoomFactor,\n top: headerHeight + emuToPixels(anchor.positionEmu.y) * anchorZoomFactor,\n width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)\n };\n }\n\n if (anchor.kind === \"one-cell\") {\n return {\n height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),\n left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),\n top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),\n width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)\n };\n }\n\n const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);\n const top = resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu);\n const right = resolveMarkerLeft(anchor.to.col, anchor.to.colOffsetEmu);\n const bottom = resolveMarkerTop(anchor.to.row, anchor.to.rowOffsetEmu);\n\n return {\n height: Math.max(1, bottom - top),\n left,\n top,\n width: Math.max(1, right - left)\n };\n}\n\nfunction resolveImageRect(\n image: XlsxImage,\n visibleRows: number[],\n visibleCols: number[],\n rowHeights: number[],\n colWidths: number[],\n options?: {\n actualColPrefixSums?: number[];\n actualRowPrefixSums?: number[];\n colIndexByActual?: Map<number, number>;\n colPrefixSums?: number[];\n headerHeight?: number;\n rowIndexByActual?: Map<number, number>;\n rowHeaderWidth?: number;\n rowPrefixSums?: number[];\n zoomFactor?: number;\n }\n): XlsxImageRect {\n return resolveAnchoredRect(image.anchor, visibleRows, visibleCols, rowHeights, colWidths, options);\n}\n\nfunction resolveImageAnchorExtents(image: XlsxImage) {\n if (image.anchor.kind === \"absolute\") {\n return { maxCol: 0, maxRow: 0 };\n }\n\n if (image.anchor.kind === \"one-cell\") {\n return {\n maxCol: image.anchor.from.col,\n maxRow: image.anchor.from.row\n };\n }\n\n return {\n maxCol: Math.max(image.anchor.from.col, image.anchor.to.col),\n maxRow: Math.max(image.anchor.from.row, image.anchor.to.row)\n };\n}\n\nfunction resolveAnchoredBounds(anchor: XlsxImage[\"anchor\"] | XlsxShape[\"anchor\"]) {\n if (anchor.kind === \"absolute\") {\n return null;\n }\n\n if (anchor.kind === \"one-cell\") {\n return {\n maxCol: anchor.from.col,\n maxRow: anchor.from.row,\n minCol: anchor.from.col,\n minRow: anchor.from.row\n };\n }\n\n return {\n maxCol: Math.max(anchor.from.col, anchor.to.col),\n maxRow: Math.max(anchor.from.row, anchor.to.row),\n minCol: Math.min(anchor.from.col, anchor.to.col),\n minRow: Math.min(anchor.from.row, anchor.to.row)\n };\n}\n\nfunction resolveShapeAnchorExtents(shape: XlsxShape) {\n if (shape.anchor.kind === \"absolute\") {\n return { maxCol: 0, maxRow: 0 };\n }\n\n if (shape.anchor.kind === \"one-cell\") {\n return {\n maxCol: shape.anchor.from.col,\n maxRow: shape.anchor.from.row\n };\n }\n\n return {\n maxCol: Math.max(shape.anchor.from.col, shape.anchor.to.col),\n maxRow: Math.max(shape.anchor.from.row, shape.anchor.to.row)\n };\n}\n\nfunction resolveChartAnchorExtents(chart: XlsxChart) {\n if (chart.anchor.kind === \"absolute\") {\n return { maxCol: 0, maxRow: 0 };\n }\n\n if (chart.anchor.kind === \"one-cell\") {\n return {\n maxCol: chart.anchor.from.col,\n maxRow: chart.anchor.from.row\n };\n }\n\n return {\n maxCol: Math.max(chart.anchor.from.col, chart.anchor.to.col),\n maxRow: Math.max(chart.anchor.from.row, chart.anchor.to.row)\n };\n}\n\nfunction rectIntersectsViewport(\n rect: XlsxImageRect,\n viewport: { height: number; left: number; top: number; width: number },\n overscan = 240\n) {\n const viewportLeft = viewport.left - overscan;\n const viewportTop = viewport.top - overscan;\n const viewportRight = viewport.left + viewport.width + overscan;\n const viewportBottom = viewport.top + viewport.height + overscan;\n const rectRight = rect.left + rect.width;\n const rectBottom = rect.top + rect.height;\n return rectRight >= viewportLeft\n && rect.left <= viewportRight\n && rectBottom >= viewportTop\n && rect.top <= viewportBottom;\n}\n\ntype FrozenDrawingPane = \"corner\" | \"left\" | \"scroll\" | \"top\";\ntype DrawingViewport = {\n height: number;\n left: number;\n top: number;\n width: number;\n};\n\nfunction resolveFrozenDrawingPane(\n rect: XlsxImageRect,\n frozenRows: number[],\n frozenCols: number[],\n actualRowHeights: number[],\n actualColWidths: number[],\n freezePanes: XlsxSheetData[\"freezePanes\"] | null,\n stickyTopByRow: Map<number, number>,\n stickyLeftByCol: Map<number, number>,\n options?: {\n defaultColWidth?: number;\n defaultRowHeight?: number;\n headerHeight?: number;\n rowHeaderWidth?: number;\n }\n): FrozenDrawingPane {\n const headerHeight = options?.headerHeight ?? HEADER_HEIGHT;\n const rowHeaderWidth = options?.rowHeaderWidth ?? ROW_HEADER_WIDTH;\n const defaultRowHeight = options?.defaultRowHeight ?? DEFAULT_ROW_HEIGHT;\n const defaultColWidth = options?.defaultColWidth ?? DEFAULT_COL_WIDTH;\n const frozenPaneBottom =\n freezePanes?.row && freezePanes.row > 0 && frozenRows.length > 0\n ? frozenRows.reduce(\n (max, row) => Math.max(max, (stickyTopByRow.get(row) ?? headerHeight) + (actualRowHeights[row] ?? defaultRowHeight)),\n headerHeight\n )\n : null;\n const frozenPaneRight =\n freezePanes?.col && freezePanes.col > 0 && frozenCols.length > 0\n ? frozenCols.reduce(\n (max, col) => Math.max(max, (stickyLeftByCol.get(col) ?? rowHeaderWidth) + (actualColWidths[col] ?? defaultColWidth)),\n rowHeaderWidth\n )\n : null;\n\n const freezeTop = frozenPaneBottom !== null && rect.top + rect.height <= frozenPaneBottom + 0.5;\n const freezeLeft = frozenPaneRight !== null && rect.left + rect.width <= frozenPaneRight + 0.5;\n\n if (freezeTop && freezeLeft) {\n return \"corner\";\n }\n if (freezeTop) {\n return \"top\";\n }\n if (freezeLeft) {\n return \"left\";\n }\n return \"scroll\";\n}\n\nfunction buildShapeContainerStyle(shape: XlsxShape, rect: XlsxImageRect, viewerScale = 1): React.CSSProperties {\n const borderWidth = shape.stroke?.none ? 0 : Math.max(0, shape.stroke?.widthPx ?? 0) * viewerScale;\n const strokeColor = shape.stroke?.color ?? \"transparent\";\n const fillColor = shape.fill?.none ? \"transparent\" : (shape.fill?.color ?? \"transparent\");\n const hasVisibleText = shape.paragraphs.some((paragraph) => paragraph.runs.some((run) => run.text.trim().length > 0));\n const transformParts = [\n shape.rotationDeg ? `rotate(${shape.rotationDeg}deg)` : \"\",\n shape.flipH ? \"scaleX(-1)\" : \"\",\n shape.flipV ? \"scaleY(-1)\" : \"\"\n ].filter(Boolean);\n\n let borderRadius: React.CSSProperties[\"borderRadius\"] = 0;\n if (shape.geometry === \"ellipse\") {\n borderRadius = \"9999px\";\n } else if (shape.geometry === \"roundRect\") {\n borderRadius = 12 * viewerScale;\n }\n\n return {\n alignItems:\n shape.textBox?.verticalAlign === \"middle\"\n ? \"center\"\n : shape.textBox?.verticalAlign === \"bottom\"\n ? \"flex-end\"\n : \"flex-start\",\n backgroundColor: fillColor,\n border: borderWidth > 0 ? `${borderWidth}px solid ${strokeColor}` : \"none\",\n borderRadius,\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n height: rect.height,\n justifyContent: \"flex-start\",\n left: rect.left,\n opacity: Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1),\n overflow: hasVisibleText ? \"hidden\" : \"visible\",\n position: \"absolute\",\n top: rect.top,\n transform: transformParts.join(\" \") || undefined,\n transformOrigin: \"center center\",\n width: rect.width,\n zIndex: shape.zIndex\n };\n}\n\nfunction clampPercent(value: number, fallback: number) {\n if (!Number.isFinite(value)) {\n return fallback;\n }\n return Math.max(0, Math.min(100, value));\n}\n\nfunction resolveShapeAdjustment(shape: XlsxShape, name: string, fallback: number) {\n const rawValue = shape.geometryAdjustments?.[name];\n if (typeof rawValue !== \"number\") {\n return fallback;\n }\n return clampPercent(rawValue / 1000, fallback);\n}\n\nfunction buildClosedPath(points: Array<[number, number]>) {\n if (points.length === 0) {\n return \"\";\n }\n\n return points\n .map(([x, y], index) => `${index === 0 ? \"M\" : \"L\"} ${x} ${y}`)\n .join(\" \") + \" Z\";\n}\n\nfunction pointOnCircle(radius: number, angleDeg: number, center = 50): [number, number] {\n const radians = angleDeg * (Math.PI / 180);\n return [\n center + Math.cos(radians) * radius,\n center + Math.sin(radians) * radius\n ];\n}\n\nfunction buildRegularPolygonPath(sides: number, rotationDeg = -90, radius = 50) {\n return buildClosedPath(\n Array.from({ length: sides }, (_, index) => pointOnCircle(radius, rotationDeg + (360 / sides) * index))\n );\n}\n\nfunction buildStarPath(points: number, innerRadius: number, rotationDeg = -90, outerRadius = 50) {\n return buildClosedPath(\n Array.from({ length: points * 2 }, (_, index) => {\n const radius = index % 2 === 0 ? outerRadius : innerRadius;\n return pointOnCircle(radius, rotationDeg + (360 / (points * 2)) * index);\n })\n );\n}\n\nfunction buildRoundRectPath(radius: number) {\n const cornerRadius = Math.max(0, Math.min(50, radius));\n const edge = 100 - cornerRadius;\n return [\n `M ${cornerRadius} 0`,\n `L ${edge} 0`,\n `Q 100 0 100 ${cornerRadius}`,\n `L 100 ${edge}`,\n `Q 100 100 ${edge} 100`,\n `L ${cornerRadius} 100`,\n `Q 0 100 0 ${edge}`,\n `L 0 ${cornerRadius}`,\n `Q 0 0 ${cornerRadius} 0`,\n \"Z\"\n ].join(\" \");\n}\n\nfunction buildEllipsePath() {\n return \"M 50 0 A 50 50 0 1 1 49.999 0 Z\";\n}\n\nfunction buildPiePath(startAngleDeg: number, endAngleDeg: number) {\n const [startX, startY] = pointOnCircle(50, startAngleDeg);\n const [endX, endY] = pointOnCircle(50, endAngleDeg);\n const sweep = endAngleDeg >= startAngleDeg ? endAngleDeg - startAngleDeg : 360 - (startAngleDeg - endAngleDeg);\n const largeArcFlag = sweep > 180 ? 1 : 0;\n return [\n \"M 50 50\",\n `L ${startX} ${startY}`,\n `A 50 50 0 ${largeArcFlag} 1 ${endX} ${endY}`,\n \"Z\"\n ].join(\" \");\n}\n\nfunction buildChordPath(startAngleDeg: number, endAngleDeg: number) {\n const [startX, startY] = pointOnCircle(50, startAngleDeg);\n const [endX, endY] = pointOnCircle(50, endAngleDeg);\n const sweep = endAngleDeg >= startAngleDeg ? endAngleDeg - startAngleDeg : 360 - (startAngleDeg - endAngleDeg);\n const largeArcFlag = sweep > 180 ? 1 : 0;\n return [\n `M ${startX} ${startY}`,\n `A 50 50 0 ${largeArcFlag} 1 ${endX} ${endY}`,\n \"Z\"\n ].join(\" \");\n}\n\nfunction buildPlusPath(thickness: number) {\n const arm = Math.max(10, Math.min(40, thickness));\n const inner = 50 - arm / 2;\n const outer = 50 + arm / 2;\n return buildClosedPath([\n [inner, 0],\n [outer, 0],\n [outer, inner],\n [100, inner],\n [100, outer],\n [outer, outer],\n [outer, 100],\n [inner, 100],\n [inner, outer],\n [0, outer],\n [0, inner],\n [inner, inner]\n ]);\n}\n\nfunction buildPentagonArrowPath() {\n return buildClosedPath([\n [0, 20],\n [70, 20],\n [70, 0],\n [100, 50],\n [70, 100],\n [70, 80],\n [0, 80]\n ]);\n}\n\nfunction buildChevronArrowPath() {\n return buildClosedPath([\n [0, 20],\n [54, 20],\n [54, 0],\n [100, 50],\n [54, 100],\n [54, 80],\n [0, 80],\n [32, 50]\n ]);\n}\n\nfunction buildNotchedRightArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 36);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 20);\n const notchDepth = Math.min(24, headLength * 0.7);\n const bodyTop = 50 - shaftHalf;\n const bodyBottom = 50 + shaftHalf;\n const headStart = 100 - headLength;\n return buildClosedPath([\n [0, bodyTop],\n [headStart, bodyTop],\n [headStart, 0],\n [100, 50],\n [headStart, 100],\n [headStart, bodyBottom],\n [notchDepth, bodyBottom],\n [0, 50],\n [notchDepth, bodyTop]\n ]);\n}\n\nfunction buildStripedRightArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 34);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 20);\n const stripeWidth = 12;\n const bodyTop = 50 - shaftHalf;\n const bodyBottom = 50 + shaftHalf;\n const headStart = 100 - headLength;\n return [\n `M 0 ${bodyTop} L ${stripeWidth} ${bodyTop} L ${stripeWidth} ${bodyBottom} L 0 ${bodyBottom} Z`,\n buildClosedPath([\n [stripeWidth * 1.6, bodyTop],\n [headStart, bodyTop],\n [headStart, 0],\n [100, 50],\n [headStart, 100],\n [headStart, bodyBottom],\n [stripeWidth * 1.6, bodyBottom]\n ])\n ].join(\" \");\n}\n\nfunction buildLeftUpArrowPath(shape: XlsxShape) {\n const stemWidth = resolveShapeAdjustment(shape, \"adj1\", 28);\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 34);\n return buildClosedPath([\n [100, 100 - stemWidth],\n [stemWidth, 100 - stemWidth],\n [stemWidth, 100],\n [0, 50],\n [stemWidth, 0],\n [stemWidth, headLength],\n [100, headLength]\n ]);\n}\n\nfunction buildBentUpArrowPath(shape: XlsxShape) {\n const stemWidth = resolveShapeAdjustment(shape, \"adj1\", 26);\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 28);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj3\", 12);\n const verticalCenter = 50;\n const bodyTop = verticalCenter - shaftHalf;\n const bodyBottom = verticalCenter + shaftHalf;\n return buildClosedPath([\n [0, bodyTop],\n [100 - stemWidth, bodyTop],\n [100 - stemWidth, headLength],\n [100 - stemWidth * 0.5, headLength],\n [100, 0],\n [100 - stemWidth * 1.5, headLength],\n [100 - stemWidth, headLength],\n [100 - stemWidth, 100],\n [100 - stemWidth * 2, 100],\n [100 - stemWidth * 2, bodyBottom],\n [0, bodyBottom]\n ]);\n}\n\nfunction buildUturnArrowPath(shape: XlsxShape) {\n const stemWidth = resolveShapeAdjustment(shape, \"adj1\", 22);\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 28);\n return buildClosedPath([\n [100, 100],\n [100, 55],\n [45, 55],\n [45, 100],\n [0, 50],\n [45, 0],\n [45, 45],\n [100 - stemWidth, 45],\n [100 - stemWidth, 100]\n ]);\n}\n\nfunction buildLeftRightUpArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 22);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 16);\n const bodyTop = 50 - shaftHalf;\n const bodyBottom = 50 + shaftHalf;\n const bodyLeft = 50 - shaftHalf;\n const bodyRight = 50 + shaftHalf;\n return buildClosedPath([\n [headLength, bodyTop],\n [bodyLeft, bodyTop],\n [bodyLeft, headLength],\n [0, headLength],\n [50, 0],\n [100, headLength],\n [bodyRight, headLength],\n [bodyRight, bodyTop],\n [100 - headLength, bodyTop],\n [100 - headLength, 0],\n [100, 50],\n [100 - headLength, 100],\n [100 - headLength, bodyBottom],\n [headLength, bodyBottom],\n [headLength, 100],\n [0, 50],\n [headLength, 0]\n ]);\n}\n\nfunction buildQuadArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 22);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 14);\n const bodyLeft = 50 - shaftHalf;\n const bodyRight = 50 + shaftHalf;\n const bodyTop = 50 - shaftHalf;\n const bodyBottom = 50 + shaftHalf;\n return buildClosedPath([\n [bodyLeft, headLength],\n [headLength, headLength],\n [headLength, bodyTop],\n [0, 50],\n [headLength, 100 - bodyTop],\n [headLength, bodyBottom],\n [bodyLeft, bodyBottom],\n [bodyLeft, 100 - headLength],\n [bodyTop, 100 - headLength],\n [50, 100],\n [100 - bodyTop, 100 - headLength],\n [bodyRight, 100 - headLength],\n [bodyRight, bodyBottom],\n [100 - headLength, bodyBottom],\n [100 - headLength, 100 - bodyTop],\n [100, 50],\n [100 - headLength, bodyTop],\n [100 - headLength, headLength],\n [bodyRight, headLength],\n [bodyRight, bodyTop],\n [100 - bodyTop, headLength],\n [50, 0],\n [bodyTop, headLength],\n [bodyLeft, headLength]\n ]);\n}\n\nfunction buildHeartPath() {\n return [\n \"M 50 92\",\n \"C 18 72 0 52 0 28\",\n \"C 0 10 14 0 28 0\",\n \"C 38 0 46 6 50 16\",\n \"C 54 6 62 0 72 0\",\n \"C 86 0 100 10 100 28\",\n \"C 100 52 82 72 50 92\",\n \"Z\"\n ].join(\" \");\n}\n\nfunction buildLightningBoltPath() {\n return buildClosedPath([\n [42, 0],\n [10, 58],\n [38, 58],\n [24, 100],\n [90, 36],\n [58, 36],\n [72, 0]\n ]);\n}\n\nfunction buildTeardropPath() {\n return [\n \"M 50 0\",\n \"C 20 24 0 44 0 68\",\n \"C 0 86 14 100 32 100\",\n \"C 60 100 82 78 82 50\",\n \"C 82 26 68 10 50 0\",\n \"Z\"\n ].join(\" \");\n}\n\nfunction buildCloudPath() {\n return [\n \"M 22 70\",\n \"C 8 70 0 62 0 50\",\n \"C 0 40 6 32 18 30\",\n \"C 20 16 30 8 44 8\",\n \"C 54 8 62 12 68 20\",\n \"C 72 14 80 12 88 14\",\n \"C 98 18 100 26 100 34\",\n \"C 100 44 94 50 88 52\",\n \"C 92 66 82 76 68 76\",\n \"C 62 84 52 88 42 88\",\n \"C 32 88 24 82 22 70\",\n \"Z\"\n ].join(\" \");\n}\n\nfunction buildRightArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 38);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 20);\n const bodyTop = 50 - shaftHalf;\n const bodyBottom = 50 + shaftHalf;\n const headStart = 100 - headLength;\n return buildClosedPath([\n [0, bodyTop],\n [headStart, bodyTop],\n [headStart, 0],\n [100, 50],\n [headStart, 100],\n [headStart, bodyBottom],\n [0, bodyBottom]\n ]);\n}\n\nfunction buildLeftArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 38);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 20);\n const bodyTop = 50 - shaftHalf;\n const bodyBottom = 50 + shaftHalf;\n const headEnd = headLength;\n return buildClosedPath([\n [100, bodyTop],\n [headEnd, bodyTop],\n [headEnd, 0],\n [0, 50],\n [headEnd, 100],\n [headEnd, bodyBottom],\n [100, bodyBottom]\n ]);\n}\n\nfunction buildUpArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 38);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 20);\n const bodyLeft = 50 - shaftHalf;\n const bodyRight = 50 + shaftHalf;\n const headEnd = headLength;\n return buildClosedPath([\n [bodyLeft, 100],\n [bodyLeft, headEnd],\n [0, headEnd],\n [50, 0],\n [100, headEnd],\n [bodyRight, headEnd],\n [bodyRight, 100]\n ]);\n}\n\nfunction buildDownArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 38);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 20);\n const bodyLeft = 50 - shaftHalf;\n const bodyRight = 50 + shaftHalf;\n const headStart = 100 - headLength;\n return buildClosedPath([\n [bodyLeft, 0],\n [bodyLeft, headStart],\n [0, headStart],\n [50, 100],\n [100, headStart],\n [bodyRight, headStart],\n [bodyRight, 0]\n ]);\n}\n\nfunction buildLeftRightArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 24);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 18);\n const bodyTop = 50 - shaftHalf;\n const bodyBottom = 50 + shaftHalf;\n return buildClosedPath([\n [headLength, bodyTop],\n [100 - headLength, bodyTop],\n [100 - headLength, 0],\n [100, 50],\n [100 - headLength, 100],\n [100 - headLength, bodyBottom],\n [headLength, bodyBottom],\n [headLength, 100],\n [0, 50],\n [headLength, 0]\n ]);\n}\n\nfunction buildUpDownArrowPath(shape: XlsxShape) {\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 24);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj1\", 18);\n const bodyLeft = 50 - shaftHalf;\n const bodyRight = 50 + shaftHalf;\n return buildClosedPath([\n [bodyLeft, headLength],\n [bodyLeft, 100 - headLength],\n [0, 100 - headLength],\n [50, 100],\n [100, 100 - headLength],\n [bodyRight, 100 - headLength],\n [bodyRight, headLength],\n [100, headLength],\n [50, 0],\n [0, headLength]\n ]);\n}\n\nfunction buildBentArrowPath(shape: XlsxShape) {\n const stemWidth = resolveShapeAdjustment(shape, \"adj1\", 25);\n const headLength = resolveShapeAdjustment(shape, \"adj2\", 27);\n const shaftHalf = resolveShapeAdjustment(shape, \"adj3\", 12.5);\n const bendY = resolveShapeAdjustment(shape, \"adj4\", 43.75);\n const bodyTop = bendY - shaftHalf;\n const bodyBottom = bendY + shaftHalf;\n const headStart = 100 - headLength;\n return buildClosedPath([\n [0, bodyTop],\n [headStart, bodyTop],\n [headStart, 0],\n [100, bendY],\n [headStart, 100],\n [headStart, bodyBottom],\n [stemWidth, bodyBottom],\n [stemWidth, 100],\n [0, 100]\n ]);\n}\n\nfunction buildPresetShapePath(shape: XlsxShape) {\n switch (shape.geometry) {\n case \"arc\":\n return {\n path: \"M 8 74 C 18 24 82 24 92 74\",\n viewBox: { width: 100, height: 100 }\n };\n case \"bentArrow\":\n return {\n path: buildBentArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"bentUpArrow\":\n return {\n path: buildBentUpArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"blockArc\":\n return {\n path: \"M 12 78 A 38 38 0 1 1 78 12 L 62 28 A 16 16 0 1 0 28 62 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"can\":\n return {\n path: \"M 18 14 C 18 6 82 6 82 14 L 82 86 C 82 94 18 94 18 86 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"chevron\":\n return {\n path: \"M 0 0 L 62 0 L 100 50 L 62 100 L 0 100 L 38 50 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"chevronArrow\":\n return {\n path: buildChevronArrowPath(),\n viewBox: { width: 100, height: 100 }\n };\n case \"chord\":\n return {\n path: buildChordPath(-30, 210),\n viewBox: { width: 100, height: 100 }\n };\n case \"cloud\":\n case \"cloudCallout\":\n return {\n path: buildCloudPath(),\n viewBox: { width: 100, height: 100 }\n };\n case \"diamond\":\n case \"flowChartDecision\":\n return {\n path: \"M 50 0 L 100 50 L 50 100 L 0 50 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"downArrow\":\n return {\n path: buildDownArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"line\":\n return {\n path: \"M 0 0 L 100 100\",\n viewBox: { width: 100, height: 100 }\n };\n case \"ellipse\":\n case \"flowChartTerminator\":\n return {\n path: buildEllipsePath(),\n viewBox: { width: 100, height: 100 }\n };\n case \"flowChartAlternateProcess\":\n case \"flowChartDelay\":\n case \"roundRect\":\n return {\n path: buildRoundRectPath(resolveShapeAdjustment(shape, \"adj\", 18)),\n viewBox: { width: 100, height: 100 }\n };\n case \"flowChartCollate\":\n return {\n path: \"M 0 0 L 100 0 L 0 100 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"flowChartData\":\n case \"flowChartInputOutput\":\n return {\n path: \"M 24 0 L 100 0 L 76 100 L 0 100 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"flowChartDocument\":\n return {\n path: \"M 0 0 L 100 0 L 100 82 C 78 70 60 94 40 82 C 24 72 12 88 0 82 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"flowChartMagneticDisk\":\n return {\n path: \"M 18 14 C 18 6 82 6 82 14 L 82 86 C 82 94 18 94 18 86 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"flowChartManualInput\":\n return {\n path: \"M 16 0 L 100 0 L 84 100 L 0 100 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"flowChartPreparation\":\n case \"hexagon\":\n return {\n path: \"M 25 0 L 75 0 L 100 50 L 75 100 L 25 100 L 0 50 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"flowChartProcess\":\n case \"rect\":\n return {\n path: \"M 0 0 L 100 0 L 100 100 L 0 100 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"heart\":\n return {\n path: buildHeartPath(),\n viewBox: { width: 100, height: 100 }\n };\n case \"heptagon\":\n return {\n path: buildRegularPolygonPath(7),\n viewBox: { width: 100, height: 100 }\n };\n case \"homePlate\":\n case \"flowChartOffpageConnector\":\n return {\n path: \"M 0 0 L 70 0 L 100 50 L 70 100 L 0 100 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"leftArrow\":\n return {\n path: buildLeftArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"leftBracket\":\n return {\n path: \"M 72 0 L 28 0 L 28 100 L 72 100\",\n viewBox: { width: 100, height: 100 }\n };\n case \"leftBrace\":\n return {\n path: \"M 82 0 C 46 0 52 24 52 38 C 52 46 46 50 24 50 C 46 50 52 54 52 62 C 52 76 46 100 82 100\",\n viewBox: { width: 100, height: 100 }\n };\n case \"leftRightArrowCallout\":\n return {\n path: buildLeftRightArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"leftRightArrow\":\n return {\n path: buildLeftRightArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"leftRightUpArrow\":\n return {\n path: buildLeftRightUpArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"leftUpArrow\":\n return {\n path: buildLeftUpArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"lightningBolt\":\n return {\n path: buildLightningBoltPath(),\n viewBox: { width: 100, height: 100 }\n };\n case \"mathPlus\":\n case \"plus\":\n return {\n path: buildPlusPath(resolveShapeAdjustment(shape, \"adj\", 26)),\n viewBox: { width: 100, height: 100 }\n };\n case \"cross\":\n return {\n path: buildPlusPath(resolveShapeAdjustment(shape, \"adj\", 22)),\n viewBox: { width: 100, height: 100 }\n };\n case \"mathMinus\":\n case \"minus\":\n return {\n path: \"M 0 40 L 100 40 L 100 60 L 0 60 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"mathMultiply\":\n case \"multiply\":\n return {\n path: \"M 18 0 L 50 32 L 82 0 L 100 18 L 68 50 L 100 82 L 82 100 L 50 68 L 18 100 L 0 82 L 32 50 L 0 18 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"mathDivide\":\n return {\n path: \"M 0 42 L 100 42 L 100 58 L 0 58 Z M 50 12 A 8 8 0 1 1 49.999 12 Z M 50 88 A 8 8 0 1 1 49.999 88 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"moon\":\n return {\n path: \"M 70 6 C 42 10 22 34 22 62 C 22 80 32 94 48 100 C 20 98 0 76 0 48 C 0 20 20 0 48 0 C 56 0 64 2 70 6 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"notchedRightArrow\":\n return {\n path: buildNotchedRightArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"octagon\":\n return {\n path: \"M 30 0 L 70 0 L 100 30 L 100 70 L 70 100 L 30 100 L 0 70 L 0 30 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"parallelogram\":\n return {\n path: \"M 24 0 L 100 0 L 76 100 L 0 100 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"pentagon\":\n return {\n path: \"M 50 0 L 100 38 L 81 100 L 19 100 L 0 38 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"pie\":\n case \"pieWedge\":\n return {\n path: buildPiePath(-35, 225),\n viewBox: { width: 100, height: 100 }\n };\n case \"pentagonArrow\":\n return {\n path: buildPentagonArrowPath(),\n viewBox: { width: 100, height: 100 }\n };\n case \"plaque\":\n return {\n path: \"M 12 0 L 88 0 L 100 12 L 100 88 L 88 100 L 12 100 L 0 88 L 0 12 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"quadArrow\":\n return {\n path: buildQuadArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"rightArrow\":\n return {\n path: buildRightArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"rightArrowCallout\":\n return {\n path: \"M 0 18 L 62 18 L 62 0 L 100 50 L 62 100 L 62 82 L 0 82 L 0 18 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"rightBracket\":\n return {\n path: \"M 28 0 L 72 0 L 72 100 L 28 100\",\n viewBox: { width: 100, height: 100 }\n };\n case \"downArrowCallout\":\n return {\n path: \"M 18 0 L 82 0 L 82 58 L 100 58 L 50 100 L 0 58 L 18 58 L 18 0 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"rightBrace\":\n return {\n path: \"M 18 0 C 54 0 48 24 48 38 C 48 46 54 50 76 50 C 54 50 48 54 48 62 C 48 76 54 100 18 100\",\n viewBox: { width: 100, height: 100 }\n };\n case \"round1Rect\":\n return {\n path: \"M 18 0 L 100 0 L 100 100 L 18 100 Q 0 100 0 82 L 0 18 Q 0 0 18 0 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"round2DiagRect\":\n return {\n path: \"M 18 0 L 100 0 L 100 82 Q 100 100 82 100 L 0 100 L 0 18 Q 0 0 18 0 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"round2SameRect\":\n return {\n path: \"M 18 0 L 82 0 Q 100 0 100 18 L 100 82 Q 100 100 82 100 L 0 100 L 0 0 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"decagon\":\n return {\n path: buildRegularPolygonPath(10),\n viewBox: { width: 100, height: 100 }\n };\n case \"dodecagon\":\n return {\n path: buildRegularPolygonPath(12),\n viewBox: { width: 100, height: 100 }\n };\n case \"rtTriangle\":\n return {\n path: \"M 0 0 L 100 100 L 0 100 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"star4\":\n return {\n path: buildStarPath(4, 20),\n viewBox: { width: 100, height: 100 }\n };\n case \"star5\":\n return {\n path: buildStarPath(5, 21),\n viewBox: { width: 100, height: 100 }\n };\n case \"star6\":\n return {\n path: buildStarPath(6, 23),\n viewBox: { width: 100, height: 100 }\n };\n case \"star7\":\n return {\n path: buildStarPath(7, 24),\n viewBox: { width: 100, height: 100 }\n };\n case \"star8\":\n return {\n path: buildStarPath(8, 25),\n viewBox: { width: 100, height: 100 }\n };\n case \"star10\":\n return {\n path: buildStarPath(10, 26),\n viewBox: { width: 100, height: 100 }\n };\n case \"star12\":\n return {\n path: buildStarPath(12, 26),\n viewBox: { width: 100, height: 100 }\n };\n case \"star16\":\n return {\n path: buildStarPath(16, 27),\n viewBox: { width: 100, height: 100 }\n };\n case \"star24\":\n return {\n path: buildStarPath(24, 28),\n viewBox: { width: 100, height: 100 }\n };\n case \"star32\":\n return {\n path: buildStarPath(32, 29),\n viewBox: { width: 100, height: 100 }\n };\n case \"stripedRightArrow\":\n return {\n path: buildStripedRightArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"sun\":\n return {\n path: buildStarPath(12, 36),\n viewBox: { width: 100, height: 100 }\n };\n case \"teardrop\":\n return {\n path: buildTeardropPath(),\n viewBox: { width: 100, height: 100 }\n };\n case \"triangle\":\n return {\n path: \"M 50 0 L 100 100 L 0 100 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"trapezoid\":\n return {\n path: \"M 20 0 L 80 0 L 100 100 L 0 100 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"upArrow\":\n return {\n path: buildUpArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"upArrowCallout\":\n return {\n path: \"M 50 0 L 100 42 L 82 42 L 82 100 L 18 100 L 18 42 L 0 42 L 50 0 Z\",\n viewBox: { width: 100, height: 100 }\n };\n case \"upDownArrow\":\n return {\n path: buildUpDownArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"upDownArrowCallout\":\n return {\n path: buildUpDownArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n case \"uturnArrow\":\n return {\n path: buildUturnArrowPath(shape),\n viewBox: { width: 100, height: 100 }\n };\n default:\n return null;\n }\n}\n\nfunction resolveShapeVector(shape: XlsxShape) {\n if (shape.svgPath && shape.svgViewBox) {\n return {\n path: shape.svgPath,\n viewBox: shape.svgViewBox\n };\n }\n\n return buildPresetShapePath(shape);\n}\n\nfunction drawCanvasRoundedRect(\n context: CanvasRenderingContext2D,\n left: number,\n top: number,\n width: number,\n height: number,\n radius: number\n) {\n const safeRadius = Math.max(0, Math.min(radius, width / 2, height / 2));\n context.beginPath();\n context.moveTo(left + safeRadius, top);\n context.lineTo(left + width - safeRadius, top);\n context.quadraticCurveTo(left + width, top, left + width, top + safeRadius);\n context.lineTo(left + width, top + height - safeRadius);\n context.quadraticCurveTo(left + width, top + height, left + width - safeRadius, top + height);\n context.lineTo(left + safeRadius, top + height);\n context.quadraticCurveTo(left, top + height, left, top + height - safeRadius);\n context.lineTo(left, top + safeRadius);\n context.quadraticCurveTo(left, top, left + safeRadius, top);\n context.closePath();\n}\n\nfunction applyCanvasShapeDash(context: CanvasRenderingContext2D, dash: string | undefined, lineWidth: number) {\n if (!dash) {\n context.setLineDash([]);\n return;\n }\n\n const unit = Math.max(1, lineWidth);\n switch (dash) {\n case \"dash\":\n context.setLineDash([4 * unit, 3 * unit]);\n break;\n case \"dashDot\":\n context.setLineDash([4 * unit, 2 * unit, unit, 2 * unit]);\n break;\n case \"dot\":\n context.setLineDash([unit, 2 * unit]);\n break;\n case \"lgDash\":\n context.setLineDash([8 * unit, 3 * unit]);\n break;\n default:\n context.setLineDash([]);\n break;\n }\n}\n\nfunction resolveShapeLineEndMarker(\n type: string | undefined,\n markerId: string,\n color: string,\n strokeWidth: number,\n rect: XlsxImageRect,\n viewBox: { height: number; width: number }\n) {\n if (type !== \"triangle\") {\n return null;\n }\n\n const pxToUserSpace = ((viewBox.width / Math.max(1, rect.width)) + (viewBox.height / Math.max(1, rect.height))) / 2;\n const markerSize = Math.max(pxToUserSpace * 8, pxToUserSpace * strokeWidth * 4);\n return (\n <marker\n id={markerId}\n key={markerId}\n markerHeight={markerSize}\n markerUnits=\"userSpaceOnUse\"\n markerWidth={markerSize}\n orient=\"auto-start-reverse\"\n overflow=\"visible\"\n refX={markerSize}\n refY={markerSize / 2}\n viewBox={`0 0 ${markerSize} ${markerSize}`}\n >\n <path d={`M 0 0 L ${markerSize} ${markerSize / 2} L 0 ${markerSize} z`} fill={color} />\n </marker>\n );\n}\n\nfunction renderShapeParagraph(\n paragraph: XlsxShape[\"paragraphs\"][number],\n index: number,\n fallbackAlign: React.CSSProperties[\"textAlign\"] = \"left\",\n textScale = 1\n) {\n return (\n <p\n key={index}\n style={{\n margin: 0,\n textAlign: paragraph.align ?? fallbackAlign,\n whiteSpace: \"pre-wrap\"\n }}\n >\n {paragraph.runs.map((run, runIndex) => (\n <span\n key={runIndex}\n style={{\n color: run.color,\n fontFamily: run.fontFamily,\n fontSize: run.fontSizePt ? `${run.fontSizePt * textScale}pt` : undefined,\n fontStyle: run.italic ? \"italic\" : undefined,\n fontWeight: run.bold ? 700 : undefined,\n textDecoration: run.underline ? \"underline\" : undefined\n }}\n >\n {run.text}\n </span>\n ))}\n </p>\n );\n}\n\nfunction clampImageRect(\n rect: XlsxImageRect,\n options?: {\n contentOffsetLeft?: number;\n contentOffsetTop?: number;\n minSizePx?: number;\n }\n): XlsxImageRect {\n const contentOffsetLeft = options?.contentOffsetLeft ?? ROW_HEADER_WIDTH;\n const contentOffsetTop = options?.contentOffsetTop ?? HEADER_HEIGHT;\n const minSizePx = options?.minSizePx ?? IMAGE_MIN_SIZE_PX;\n return {\n height: Math.max(minSizePx, rect.height),\n left: Math.max(contentOffsetLeft, rect.left),\n top: Math.max(contentOffsetTop, rect.top),\n width: Math.max(minSizePx, rect.width)\n };\n}\n\nfunction resolveImageHandleStyle(\n position: XlsxImageResizeHandlePosition,\n stroke: string,\n surface: string,\n scale = 1\n): React.CSSProperties {\n const handleSize = IMAGE_HANDLE_SIZE_PX * scale;\n const offset = handleSize / 2;\n const style: React.CSSProperties = {\n backgroundColor: surface,\n border: `${Math.max(1, scale)}px solid ${stroke}`,\n borderRadius: 3 * scale,\n boxShadow: `0 1px ${3 * scale}px ${DRAWING_SELECTION_HANDLE_SHADOW}`,\n boxSizing: \"border-box\",\n cursor: IMAGE_HANDLE_CURSOR[position],\n height: handleSize,\n pointerEvents: \"auto\",\n position: \"absolute\",\n width: handleSize\n };\n\n if (position.includes(\"n\")) {\n style.top = -offset;\n }\n if (position.includes(\"s\")) {\n style.bottom = -offset;\n }\n if (position.includes(\"w\")) {\n style.left = -offset;\n }\n if (position.includes(\"e\")) {\n style.right = -offset;\n }\n if (position === \"n\" || position === \"s\") {\n style.left = `calc(50% - ${offset}px)`;\n }\n if (position === \"e\" || position === \"w\") {\n style.top = `calc(50% - ${offset}px)`;\n }\n\n return style;\n}\n\nfunction useViewerPalette(\n isDark = false,\n headerBackgroundColor?: string,\n headerTextColor?: string\n) {\n return React.useMemo(\n () => resolveViewerPalette(isDark, headerBackgroundColor, headerTextColor),\n [headerBackgroundColor, headerTextColor, isDark]\n );\n}\n\ntype SheetThumbnailAxisItem = {\n actualIndex: number;\n size: number;\n start: number;\n};\n\ntype SheetThumbnailRenderPlan = {\n aspectRatio: number;\n contentHeight: number;\n contentWidth: number;\n height: number;\n paint: (canvas: HTMLCanvasElement | null) => boolean;\n sourceRange: XlsxCellRange;\n width: number;\n};\n\nfunction normalizeThumbnailResolution(\n resolution?: UseXlsxViewerThumbnailsOptions[\"resolution\"]\n) {\n if (typeof resolution === \"number\" && Number.isFinite(resolution) && resolution > 0) {\n return {\n maxHeight: resolution,\n maxWidth: resolution\n };\n }\n\n const resolvedObject = typeof resolution === \"object\" && resolution ? resolution : undefined;\n return {\n maxHeight: resolvedObject?.maxHeight && Number.isFinite(resolvedObject.maxHeight) && resolvedObject.maxHeight > 0\n ? resolvedObject.maxHeight\n : THUMBNAIL_DEFAULT_MAX_DIMENSION,\n maxWidth: resolvedObject?.maxWidth && Number.isFinite(resolvedObject.maxWidth) && resolvedObject.maxWidth > 0\n ? resolvedObject.maxWidth\n : THUMBNAIL_DEFAULT_MAX_DIMENSION\n };\n}\n\nfunction resolveThumbnailOutputSize(\n sourceWidth: number,\n sourceHeight: number,\n resolution?: UseXlsxViewerThumbnailsOptions[\"resolution\"]\n) {\n const normalized = normalizeThumbnailResolution(resolution);\n const safeSourceWidth = Math.max(1, sourceWidth);\n const safeSourceHeight = Math.max(1, sourceHeight);\n const scale = Math.min(\n normalized.maxWidth / safeSourceWidth,\n normalized.maxHeight / safeSourceHeight,\n 1\n );\n\n return {\n height: Math.max(1, Math.round(safeSourceHeight * scale)),\n maxHeight: normalized.maxHeight,\n maxWidth: normalized.maxWidth,\n scale,\n width: Math.max(1, Math.round(safeSourceWidth * scale))\n };\n}\n\nfunction resolveThumbnailAxisIndices({\n fallbackCount,\n getSizePx,\n maxCount,\n maxLogicalPixels,\n maxUsedIndex,\n minUsedIndex,\n precomputed\n}: {\n fallbackCount: number;\n getSizePx: (actualIndex: number) => number;\n maxCount: number;\n maxLogicalPixels: number;\n maxUsedIndex: number;\n minUsedIndex: number;\n precomputed: number[];\n}) {\n const sourceIndices = precomputed.length > 0\n ? precomputed\n : Array.from({\n length: Math.max(fallbackCount, Math.max(0, maxUsedIndex + 1))\n }, (_, index) => index);\n const hasUsedRange = maxUsedIndex >= minUsedIndex && maxUsedIndex >= 0;\n const preferredStart = hasUsedRange ? Math.max(0, minUsedIndex) : 0;\n const preferredEnd = hasUsedRange ? maxUsedIndex : Math.max(0, preferredStart + fallbackCount - 1);\n const picked: number[] = [];\n let totalSize = 0;\n\n for (const actualIndex of sourceIndices) {\n if (actualIndex < preferredStart) {\n continue;\n }\n if (hasUsedRange && actualIndex > preferredEnd) {\n break;\n }\n\n const size = Math.max(1, getSizePx(actualIndex));\n picked.push(actualIndex);\n totalSize += size;\n if (picked.length >= maxCount || totalSize >= maxLogicalPixels) {\n break;\n }\n }\n\n if (picked.length > 0) {\n return picked;\n }\n\n for (const actualIndex of sourceIndices) {\n const size = Math.max(1, getSizePx(actualIndex));\n picked.push(actualIndex);\n totalSize += size;\n if (picked.length >= fallbackCount || picked.length >= maxCount || totalSize >= maxLogicalPixels) {\n break;\n }\n }\n\n return picked.length > 0 ? picked : [0];\n}\n\nfunction buildThumbnailAxisItems(actualIndices: number[], getSizePx: (actualIndex: number) => number) {\n const items: SheetThumbnailAxisItem[] = [];\n let offset = 0;\n\n for (const actualIndex of actualIndices) {\n const size = Math.max(1, getSizePx(actualIndex));\n items.push({\n actualIndex,\n size,\n start: offset\n });\n offset += size;\n }\n\n return {\n items,\n totalSize: offset\n };\n}\n\nfunction resolveThumbnailAxisAbsoluteOffset(\n actualIndices: number[],\n getSizePx: (actualIndex: number) => number,\n actualIndex: number\n) {\n let total = 0;\n\n for (const candidate of actualIndices) {\n if (candidate >= actualIndex) {\n break;\n }\n total += Math.max(1, getSizePx(candidate));\n }\n\n return total;\n}\n\nfunction resolveThumbnailAxisMarkerOffset({\n actualIndex,\n actualIndices,\n getSizePx,\n offsetEmu,\n previewStartActualIndex\n}: {\n actualIndex: number;\n actualIndices: number[];\n getSizePx: (actualIndex: number) => number;\n offsetEmu: number;\n previewStartActualIndex: number;\n}) {\n return (\n resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, actualIndex)\n - resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, previewStartActualIndex)\n + emuToPixels(offsetEmu)\n );\n}\n\nfunction resolveThumbnailAnchoredRect(\n anchor: XlsxImage[\"anchor\"],\n visibleRows: number[],\n visibleCols: number[],\n getRowHeightPx: (actualIndex: number) => number,\n getColWidthPx: (actualIndex: number) => number,\n previewRows: number[],\n previewCols: number[],\n options: {\n headerHeight: number;\n rowHeaderWidth: number;\n }\n): XlsxImageRect {\n const previewStartRow = previewRows[0] ?? 0;\n const previewStartCol = previewCols[0] ?? 0;\n const previewOriginX = resolveThumbnailAxisAbsoluteOffset(visibleCols, getColWidthPx, previewStartCol);\n const previewOriginY = resolveThumbnailAxisAbsoluteOffset(visibleRows, getRowHeightPx, previewStartRow);\n const resolveMarkerLeft = (col: number, colOffsetEmu: number) =>\n options.rowHeaderWidth + resolveThumbnailAxisMarkerOffset({\n actualIndex: col,\n actualIndices: visibleCols,\n getSizePx: getColWidthPx,\n offsetEmu: colOffsetEmu,\n previewStartActualIndex: previewStartCol\n });\n const resolveMarkerTop = (row: number, rowOffsetEmu: number) =>\n options.headerHeight + resolveThumbnailAxisMarkerOffset({\n actualIndex: row,\n actualIndices: visibleRows,\n getSizePx: getRowHeightPx,\n offsetEmu: rowOffsetEmu,\n previewStartActualIndex: previewStartRow\n });\n\n if (anchor.kind === \"absolute\") {\n return {\n height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),\n left: options.rowHeaderWidth + emuToPixels(anchor.positionEmu.x) - previewOriginX,\n top: options.headerHeight + emuToPixels(anchor.positionEmu.y) - previewOriginY,\n width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))\n };\n }\n\n if (anchor.kind === \"one-cell\") {\n return {\n height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),\n left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),\n top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),\n width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))\n };\n }\n\n const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);\n const top = resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu);\n const right = resolveMarkerLeft(anchor.to.col, anchor.to.colOffsetEmu);\n const bottom = resolveMarkerTop(anchor.to.row, anchor.to.rowOffsetEmu);\n\n return {\n height: Math.max(1, bottom - top),\n left,\n top,\n width: Math.max(1, right - left)\n };\n}\n\nfunction columnLabel(col: number): string {\n let label = \"\";\n let nextValue = col;\n\n while (nextValue >= 0) {\n label = String.fromCharCode(65 + (nextValue % 26)) + label;\n nextValue = Math.floor(nextValue / 26) - 1;\n }\n\n return label;\n}\n\nfunction cellAddressToA1(cell: XlsxCellAddress): string {\n return `${columnLabel(cell.col)}${cell.row + 1}`;\n}\n\nfunction parseA1CellReference(reference: string): XlsxCellAddress | null {\n const match = /^([A-Z]+)(\\d+)$/i.exec(reference.trim());\n if (!match) {\n return null;\n }\n\n const [, columnPart, rowPart] = match;\n let col = 0;\n for (const char of columnPart.toUpperCase()) {\n col = col * 26 + (char.charCodeAt(0) - 64);\n }\n\n return {\n col: col - 1,\n row: Number(rowPart) - 1\n };\n}\n\nfunction resolveInheritedCellStyle(sheet: XlsxSheetData | null | undefined, row: number, col: number) {\n if (!sheet) {\n return null;\n }\n\n const colStyleId = sheet.colStyleIds[col];\n const rowStyleId = sheet.rowStyleIds[row];\n const colStyle = colStyleId !== undefined ? sheet.styleById[colStyleId] ?? null : null;\n const rowStyle = rowStyleId !== undefined ? sheet.styleById[rowStyleId] ?? null : null;\n return mergeResolvedCellStyle(colStyle, rowStyle, { replaceXfSubtrees: true });\n}\n\nfunction parseInternalSheetLink(target?: string | null) {\n if (!target) {\n return null;\n }\n\n const normalized = target.startsWith(\"#\") ? target.slice(1) : target;\n const separatorIndex = normalized.lastIndexOf(\"!\");\n if (separatorIndex < 0) {\n return null;\n }\n\n const rawSheetName = normalized.slice(0, separatorIndex).trim();\n const rawCellRef = normalized.slice(separatorIndex + 1).trim();\n const cell = parseA1CellReference(rawCellRef);\n if (!cell) {\n return null;\n }\n\n const sheetName = rawSheetName.startsWith(\"'\") && rawSheetName.endsWith(\"'\")\n ? rawSheetName.slice(1, -1).replace(/''/g, \"'\")\n : rawSheetName;\n\n return {\n cell,\n sheetName\n };\n}\n\nfunction decodeHtmlEntities(value: string): string {\n return value\n .replace(/"/g, \"\\\"\")\n .replace(/"/g, \"\\\"\")\n .replace(/'/g, \"'\")\n .replace(/'/g, \"'\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/&/g, \"&\");\n}\n\nfunction mapBorder(edge: { style: string; color?: Record<string, unknown> }, themePalette?: XlsxSheetData[\"themePalette\"]): string {\n const color = resolveWorkbookColor(edge.color as Record<string, unknown> | undefined, themePalette) ?? \"#000\";\n const widthMap: Record<string, string> = {\n dashed: \"1px\",\n dotted: \"1px\",\n double: \"3px\",\n hair: \"0.5px\",\n medium: \"2px\",\n thick: \"3px\",\n thin: \"1px\"\n };\n const styleMap: Record<string, string> = {\n dashDot: \"dashed\",\n dashDotDot: \"dotted\",\n dashed: \"dashed\",\n dotted: \"dotted\",\n double: \"double\",\n hair: \"solid\",\n medium: \"solid\",\n mediumDashDot: \"dashed\",\n mediumDashDotDot: \"dotted\",\n mediumDashed: \"dashed\",\n slantDashDot: \"dashed\",\n thick: \"solid\",\n thin: \"solid\"\n };\n\n return `${widthMap[edge.style] ?? \"1px\"} ${styleMap[edge.style] ?? \"solid\"} ${color}`;\n}\n\nfunction paletteIsDark(palette: ViewerPalette) {\n return isDarkViewerPalette(palette);\n}\n\nfunction parseHexColor(color: string): [number, number, number] | null {\n const normalized = color.trim().toLowerCase();\n const hexMatch = /^#([0-9a-f]{6})$/.exec(normalized);\n if (hexMatch) {\n const hex = hexMatch[1];\n return [\n Number.parseInt(hex.slice(0, 2), 16),\n Number.parseInt(hex.slice(2, 4), 16),\n Number.parseInt(hex.slice(4, 6), 16)\n ];\n }\n\n const rgbMatch = /^rgb\\(\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})\\s*\\)$/.exec(normalized);\n if (rgbMatch) {\n return [\n Math.max(0, Math.min(255, Number.parseInt(rgbMatch[1], 10))),\n Math.max(0, Math.min(255, Number.parseInt(rgbMatch[2], 10))),\n Math.max(0, Math.min(255, Number.parseInt(rgbMatch[3], 10)))\n ];\n }\n\n const hslMatch = /^hsl\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*(?:deg)?(?:\\s+|,\\s*)(\\d+(?:\\.\\d+)?)%(?:\\s+|,\\s*)(\\d+(?:\\.\\d+)?)%\\s*\\)$/.exec(normalized);\n if (!hslMatch) {\n return null;\n }\n\n const hue = ((Number.parseFloat(hslMatch[1]) % 360) + 360) % 360 / 360;\n const saturation = Math.max(0, Math.min(1, Number.parseFloat(hslMatch[2]) / 100));\n const lightness = Math.max(0, Math.min(1, Number.parseFloat(hslMatch[3]) / 100));\n return hslToRgb(hue, saturation, lightness);\n}\n\nfunction applyAlphaToColor(color: string, alpha: number) {\n const rgb = parseHexColor(color);\n if (!rgb) {\n return color;\n }\n\n const [red, green, blue] = rgb;\n return `rgba(${red}, ${green}, ${blue}, ${Math.max(0, Math.min(1, alpha))})`;\n}\n\nfunction relativeLuminance(color: string) {\n const rgb = parseHexColor(color);\n if (!rgb) {\n return null;\n }\n\n const [red, green, blue] = rgb.map((channel) => {\n const normalized = channel / 255;\n return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;\n });\n\n return 0.2126 * red + 0.7152 * green + 0.0722 * blue;\n}\n\nfunction contrastRatio(foreground: string, background: string) {\n const foregroundLuminance = relativeLuminance(foreground);\n const backgroundLuminance = relativeLuminance(background);\n if (foregroundLuminance === null || backgroundLuminance === null) {\n return null;\n }\n\n const lighter = Math.max(foregroundLuminance, backgroundLuminance);\n const darker = Math.min(foregroundLuminance, backgroundLuminance);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction resolveReadableTextColor(\n requestedColor: string | null,\n backgroundColor: string,\n palette: ViewerPalette\n) {\n if (!paletteIsDark(palette)) {\n return requestedColor ?? \"#000000\";\n }\n\n if (!requestedColor) {\n return palette.text;\n }\n\n const requestedContrast = contrastRatio(requestedColor, backgroundColor);\n const paletteContrast = contrastRatio(palette.text, backgroundColor);\n if (requestedContrast !== null && requestedContrast >= 4.5) {\n return requestedColor;\n }\n if (paletteContrast !== null && (requestedContrast === null || paletteContrast > requestedContrast)) {\n return palette.text;\n }\n\n return requestedColor;\n}\n\nfunction rgbToHsl(red: number, green: number, blue: number): [number, number, number] {\n const normalizedRed = red / 255;\n const normalizedGreen = green / 255;\n const normalizedBlue = blue / 255;\n const max = Math.max(normalizedRed, normalizedGreen, normalizedBlue);\n const min = Math.min(normalizedRed, normalizedGreen, normalizedBlue);\n const lightness = (max + min) / 2;\n\n if (max === min) {\n return [0, 0, lightness];\n }\n\n const delta = max - min;\n const saturation = lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min);\n let hue = 0;\n\n switch (max) {\n case normalizedRed:\n hue = (normalizedGreen - normalizedBlue) / delta + (normalizedGreen < normalizedBlue ? 6 : 0);\n break;\n case normalizedGreen:\n hue = (normalizedBlue - normalizedRed) / delta + 2;\n break;\n default:\n hue = (normalizedRed - normalizedGreen) / delta + 4;\n break;\n }\n\n return [hue / 6, saturation, lightness];\n}\n\nfunction hueToRgb(p: number, q: number, t: number) {\n let nextT = t;\n if (nextT < 0) {\n nextT += 1;\n }\n if (nextT > 1) {\n nextT -= 1;\n }\n if (nextT < 1 / 6) {\n return p + (q - p) * 6 * nextT;\n }\n if (nextT < 1 / 2) {\n return q;\n }\n if (nextT < 2 / 3) {\n return p + (q - p) * (2 / 3 - nextT) * 6;\n }\n return p;\n}\n\nfunction hslToRgb(hue: number, saturation: number, lightness: number): [number, number, number] {\n if (saturation === 0) {\n const gray = Math.round(lightness * 255);\n return [gray, gray, gray];\n }\n\n const q = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation;\n const p = 2 * lightness - q;\n\n return [\n Math.round(hueToRgb(p, q, hue + 1 / 3) * 255),\n Math.round(hueToRgb(p, q, hue) * 255),\n Math.round(hueToRgb(p, q, hue - 1 / 3) * 255)\n ];\n}\n\nfunction rgbToHex(red: number, green: number, blue: number) {\n return `#${[red, green, blue]\n .map((channel) => channel.toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\nfunction invertHexLightness(color: string): string | null {\n const rgb = parseHexColor(color);\n if (!rgb) {\n return null;\n }\n\n const [hue, saturation, lightness] = rgbToHsl(rgb[0], rgb[1], rgb[2]);\n const [nextRed, nextGreen, nextBlue] = hslToRgb(hue, saturation, 1 - lightness);\n return rgbToHex(nextRed, nextGreen, nextBlue);\n}\n\nfunction escapeCssFontFamily(name: string) {\n return /[\\s\"'(),]/.test(name) ? `\"${name.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"` : name;\n}\n\nfunction resolveSpreadsheetThemeFont(\n font: Record<string, unknown> | undefined,\n themePalette?: XlsxThemePalette\n) {\n const scheme = typeof font?.scheme === \"string\" ? font.scheme : null;\n if (scheme === \"major\") {\n return themePalette?.majorLatinFont;\n }\n if (scheme === \"minor\") {\n return themePalette?.minorLatinFont;\n }\n return undefined;\n}\n\nfunction buildSpreadsheetFontFamily(\n font: Record<string, unknown> | undefined,\n themePalette?: XlsxThemePalette\n) {\n const candidates = [\n typeof font?.name === \"string\" ? font.name.trim() : \"\",\n resolveSpreadsheetThemeFont(font, themePalette)?.trim() ?? \"\"\n ].filter((value, index, array) => value.length > 0 && array.indexOf(value) === index);\n\n if (candidates.length === 0) {\n return undefined;\n }\n\n const primaryFont = candidates[0]?.toLowerCase();\n if (primaryFont === \"calibri\") {\n return [\n \"Calibri\",\n \"Carlito\",\n \"\\\"React XLSX Carlito\\\"\",\n \"\\\"Aptos\\\"\",\n \"\\\"Segoe UI\\\"\",\n \"Tahoma\",\n \"Arial\",\n \"sans-serif\"\n ].join(\", \");\n }\n\n return [\n ...candidates.map(escapeCssFontFamily),\n \"\\\"Segoe UI\\\"\",\n \"Tahoma\",\n \"Arial\",\n \"sans-serif\"\n ].join(\", \");\n}\n\nfunction resolveSpreadsheetLineHeight(fontSizePt?: number) {\n const sizePt = typeof fontSizePt === \"number\" && Number.isFinite(fontSizePt) ? fontSizePt : 11;\n if (sizePt <= 11) {\n return 1.2;\n }\n if (sizePt <= 14) {\n return 1.25;\n }\n return 1.3;\n}\n\nfunction buildGridlineShadow(color: string, options?: { bottom?: boolean; right?: boolean }) {\n const parts: string[] = [];\n if (options?.right !== false) {\n parts.push(`inset -1px 0 0 ${color}`);\n }\n if (options?.bottom !== false) {\n parts.push(`inset 0 -1px 0 ${color}`);\n }\n return parts.join(\", \");\n}\n\nfunction buildCellStyle(\n style: Record<string, unknown> | null | undefined,\n palette: ViewerPalette,\n themePalette?: XlsxSheetData[\"themePalette\"],\n options?: { generalAlignment?: CellGeneralAlignment; showGridLines?: boolean }\n): React.CSSProperties {\n const showGridLines = options?.showGridLines ?? true;\n const baseSurface = paletteIsDark(palette)\n ? resolveDarkModeSurface(themePalette, palette)\n : themePalette?.colorsByIndex[0] ?? SHEET_SURFACE;\n const gridlineShadow = showGridLines ? buildGridlineShadow(palette.border) : undefined;\n const css: React.CSSProperties = {\n backgroundColor: baseSurface,\n borderBottom: \"none\",\n borderRight: \"none\",\n boxShadow: gridlineShadow,\n color: resolveReadableTextColor(null, baseSurface, palette),\n fontFamily: buildSpreadsheetFontFamily({ scheme: \"minor\" }, themePalette) ?? \"\\\"Segoe UI\\\", Tahoma, Arial, sans-serif\",\n fontSize: \"12px\",\n lineHeight: String(resolveSpreadsheetLineHeight(11)),\n overflow: \"hidden\",\n padding: DEFAULT_CELL_PADDING,\n textAlign: options?.generalAlignment ?? \"left\",\n textOverflow: \"clip\",\n verticalAlign: \"bottom\",\n whiteSpace: \"nowrap\"\n };\n\n if (!style) {\n return css;\n }\n\n const fill = style.fill as Record<string, unknown> | undefined;\n let resolvedFillColor: string | null = null;\n let hasExplicitFill = false;\n if (fill) {\n const fillStyle = resolveWorkbookFillStyle(fill, themePalette);\n if (fillStyle.backgroundColor || fillStyle.backgroundImage) {\n hasExplicitFill = true;\n resolvedFillColor = fillStyle.backgroundColor;\n if (fillStyle.backgroundColor) {\n css.backgroundColor = fillStyle.backgroundColor;\n }\n if (fillStyle.backgroundImage) {\n css.backgroundImage = fillStyle.backgroundImage;\n }\n }\n }\n\n const font = style.font as Record<string, unknown> | undefined;\n let resolvedFontColor: string | null = null;\n let resolvedFontSizePt = 11;\n if (font) {\n if (font.bold) {\n css.fontWeight = \"bold\";\n }\n if (font.italic) {\n css.fontStyle = \"italic\";\n }\n if (font.underline && font.underline !== \"none\") {\n css.textDecoration = \"underline\";\n }\n if (font.strikethrough) {\n css.textDecoration = `${css.textDecoration ?? \"\"} line-through`.trim();\n }\n const fontColor = resolveWorkbookColor(font.color as Record<string, unknown> | undefined, themePalette);\n if (fontColor) {\n resolvedFontColor = fontColor;\n css.color = fontColor;\n }\n if (typeof font.size === \"number\" && Number.isFinite(font.size) && font.size > 0) {\n resolvedFontSizePt = font.size;\n css.fontSize = `${font.size}pt`;\n }\n\n const fontFamily = buildSpreadsheetFontFamily(font, themePalette);\n if (fontFamily) {\n css.fontFamily = fontFamily;\n }\n }\n\n css.lineHeight = String(resolveSpreadsheetLineHeight(resolvedFontSizePt));\n\n if (paletteIsDark(palette) && !css.backgroundImage) {\n const effectiveBackgroundColor = typeof css.backgroundColor === \"string\" ? css.backgroundColor : baseSurface;\n css.color = resolveReadableTextColor(resolvedFontColor, effectiveBackgroundColor, palette);\n } else if (!hasExplicitFill) {\n css.color = resolveReadableTextColor(resolvedFontColor, baseSurface, palette);\n }\n\n const alignment = style.alignment as Record<string, unknown> | undefined;\n if (alignment) {\n if (alignment.horizontal && alignment.horizontal !== \"general\") {\n css.textAlign = alignment.horizontal as React.CSSProperties[\"textAlign\"];\n }\n if (alignment.vertical) {\n const verticalMap: Record<string, string> = {\n bottom: \"bottom\",\n center: \"middle\",\n top: \"top\"\n };\n const verticalValue = verticalMap[String(alignment.vertical)];\n if (verticalValue) {\n css.verticalAlign = verticalValue as React.CSSProperties[\"verticalAlign\"];\n }\n }\n if (alignment.wrapText) {\n css.whiteSpace = \"pre-wrap\";\n css.wordBreak = \"break-word\";\n }\n }\n\n const border = style.border as Record<string, Record<string, unknown>> | undefined;\n if (border) {\n const hasRightBorder = Boolean(border.right?.style && border.right.style !== \"none\");\n const hasBottomBorder = Boolean(border.bottom?.style && border.bottom.style !== \"none\");\n if (showGridLines && (hasRightBorder || hasBottomBorder)) {\n css.boxShadow = buildGridlineShadow(palette.border, {\n bottom: !hasBottomBorder,\n right: !hasRightBorder\n }) || undefined;\n }\n if (border.top?.style && border.top.style !== \"none\") {\n css.borderTop = mapBorder(border.top as { style: string; color?: Record<string, unknown> }, themePalette);\n }\n if (border.right?.style && border.right.style !== \"none\") {\n css.borderRight = mapBorder(border.right as { style: string; color?: Record<string, unknown> }, themePalette);\n }\n if (border.bottom?.style && border.bottom.style !== \"none\") {\n css.borderBottom = mapBorder(border.bottom as { style: string; color?: Record<string, unknown> }, themePalette);\n }\n if (border.left?.style && border.left.style !== \"none\") {\n css.borderLeft = mapBorder(border.left as { style: string; color?: Record<string, unknown> }, themePalette);\n }\n }\n\n return css;\n}\n\nlet textMeasureCanvas: HTMLCanvasElement | null = null;\n\nfunction getHorizontalPadding(padding: React.CSSProperties[\"padding\"]) {\n if (typeof padding !== \"string\") {\n return 8;\n }\n\n const values = padding\n .trim()\n .split(/\\s+/)\n .map((value) => Number.parseFloat(value))\n .filter((value) => Number.isFinite(value));\n\n if (values.length === 1) {\n return values[0] * 2;\n }\n if (values.length === 2) {\n return values[1] * 2;\n }\n if (values.length === 3) {\n return values[1] * 2;\n }\n if (values.length >= 4) {\n return values[1] + values[3];\n }\n\n return 8;\n}\n\nfunction buildCanvasFont(style: React.CSSProperties) {\n return resolveCanvasFont(style, 12);\n}\n\nfunction measureTextWidth(value: string, style: React.CSSProperties) {\n if (!value) {\n return 0;\n }\n\n if (typeof document === \"undefined\") {\n return value.length * 7;\n }\n\n textMeasureCanvas ??= document.createElement(\"canvas\");\n const context = textMeasureCanvas.getContext(\"2d\", { alpha: false });\n if (!context) {\n return value.length * 7;\n }\n\n context.font = buildCanvasFont(style);\n return measureCanvasTextWidth(context, value);\n}\n\nfunction measureWrappedTextHeight(value: string, style: React.CSSProperties, widthPx: number) {\n if (!value) {\n return 0;\n }\n\n const availableWidth = Math.max(1, widthPx - getHorizontalPadding(style.padding));\n const lineHeight = resolveCanvasLineHeight(style, 12);\n const padding = resolveCanvasPadding(style.padding);\n const fallbackLineCount = value.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\").length;\n\n if (typeof document === \"undefined\") {\n return (fallbackLineCount * lineHeight) + padding.top + padding.bottom;\n }\n\n textMeasureCanvas ??= document.createElement(\"canvas\");\n const context = textMeasureCanvas.getContext(\"2d\", { alpha: false });\n if (!context) {\n return (fallbackLineCount * lineHeight) + padding.top + padding.bottom;\n }\n\n context.font = buildCanvasFont(style);\n const wrappedLines = wrapCanvasText(context, value, availableWidth);\n return (wrappedLines.length * lineHeight) + padding.top + padding.bottom;\n}\n\nfunction canCellTextOverflow(data: CellRenderData) {\n if (!data.value || data.isMergedSecondary || data.shrinkToFit || data.textRotationDeg || data.conditionalIcon || data.style.whiteSpace === \"pre-wrap\") {\n return false;\n }\n\n const textAlign = data.style.textAlign;\n if (\n textAlign &&\n textAlign !== \"left\" &&\n textAlign !== \"start\" &&\n textAlign !== \"center\" &&\n textAlign !== \"right\" &&\n textAlign !== \"end\"\n ) {\n return false;\n }\n\n return true;\n}\n\nfunction canReceiveOverflowText(data: CellRenderData) {\n return !data.isMergedSecondary && !data.colSpan && data.value.length === 0;\n}\n\nfunction getCellDisplayValue(worksheet: Worksheet, row: number, col: number, activeSheet?: XlsxSheetData | null, locale = \"en-US\"): string {\n const formula = worksheet.getFormulaAt(row, col);\n const cachedFormulaValue = formula ? activeSheet?.cachedFormulaValues?.[cellAddressToA1({ row, col })] : undefined;\n const formatted = worksheet.getFormattedValueAt(row, col);\n if (formatted && !(formula && cachedFormulaValue !== undefined && formatted.startsWith(\"#\"))) {\n return localizeCellNumber(worksheet, row, col, decodeHtmlEntities(formatted), locale);\n }\n\n const cellValue = worksheet.getCalculatedValueAt(row, col);\n if (formula && cachedFormulaValue !== undefined && cellValue.is_error) {\n return cachedFormulaValue;\n }\n if (cellValue.is_error) {\n return cellValue.asError() ?? \"\";\n }\n if (cellValue.is_empty) {\n return \"\";\n }\n\n return cellValue.toString();\n}\n\nfunction getCellNumericValue(worksheet: Worksheet, row: number, col: number) {\n const cellValue = worksheet.getCalculatedValueAt(row, col);\n if (cellValue.is_number) {\n return cellValue.asNumber() ?? null;\n }\n if (cellValue.is_boolean) {\n return cellValue.asBoolean() ? 1 : 0;\n }\n if (cellValue.is_empty || cellValue.is_error) {\n return null;\n }\n\n const parsedValue = Number(cellValue.asText() ?? cellValue.toString());\n return Number.isFinite(parsedValue) ? parsedValue : null;\n}\n\nfunction getCellBooleanValue(worksheet: Worksheet, row: number, col: number) {\n const cellValue = worksheet.getCalculatedValueAt(row, col);\n if (cellValue.is_boolean) {\n return cellValue.asBoolean() ?? null;\n }\n if (cellValue.is_number) {\n const numeric = cellValue.asNumber();\n return numeric == null ? null : numeric !== 0;\n }\n const text = (cellValue.asText() ?? cellValue.toString()).trim().toLowerCase();\n if (text === \"true\") {\n return true;\n }\n if (text === \"false\") {\n return false;\n }\n return null;\n}\n\nfunction getBatchedCellNumericValue(cell: BatchedCellData | undefined) {\n if (!cell) {\n return null;\n }\n\n if (cell.numericValue !== undefined) return cell.numericValue;\n\n const text = cell.value.trim();\n if (text.length === 0) {\n return null;\n }\n if (text.toLowerCase() === \"true\") {\n return 1;\n }\n if (text.toLowerCase() === \"false\") {\n return 0;\n }\n\n const value = Number(text.replace(/,/g, \"\"));\n return Number.isFinite(value) ? value : null;\n}\n\nfunction getBatchedCellBooleanValue(cell: BatchedCellData | undefined) {\n if (!cell) {\n return null;\n }\n\n const text = cell.value.trim().toLowerCase();\n if (text.length === 0) {\n return null;\n }\n if (text === \"true\") {\n return true;\n }\n if (text === \"false\") {\n return false;\n }\n\n const value = Number(text);\n return Number.isFinite(value) ? value !== 0 : null;\n}\n\nfunction parseA1RangeReference(reference: string): XlsxCellRange | null {\n const [startRef, endRef = startRef] = reference.split(\":\");\n const start = parseA1CellReference(startRef ?? \"\");\n const end = parseA1CellReference(endRef ?? \"\");\n return start && end ? { end, start } : null;\n}\n\nfunction unescapeSheetNameReference(value: string) {\n const trimmed = value.trim();\n return trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\")\n ? trimmed.slice(1, -1).replace(/''/g, \"'\")\n : trimmed;\n}\n\nfunction splitSheetFormulaReference(reference: string) {\n let inQuotes = false;\n for (let index = 0; index < reference.length; index += 1) {\n const character = reference[index];\n if (character === \"'\") {\n if (inQuotes && reference[index + 1] === \"'\") {\n index += 1;\n continue;\n }\n inQuotes = !inQuotes;\n continue;\n }\n if (character === \"!\" && !inQuotes) {\n return {\n range: reference.slice(index + 1),\n sheetName: unescapeSheetNameReference(reference.slice(0, index))\n };\n }\n }\n return null;\n}\n\nfunction resolveChartFormulaRange(\n formula: string,\n fallbackSheetIndex: number,\n sheets: XlsxSheetData[],\n workbook: Workbook | null\n) {\n let reference = formula.trim().replace(/^=/, \"\");\n if (!reference) {\n return null;\n }\n\n if (!reference.includes(\"!\") && workbook) {\n try {\n const namedRange = workbook.getNamedRange(reference);\n if (typeof namedRange === \"string\" && namedRange.length > 0 && namedRange !== reference) {\n reference = namedRange;\n }\n } catch {\n // Ignore missing named ranges and fall back to direct parsing.\n }\n }\n\n const split = splitSheetFormulaReference(reference);\n const range = parseA1RangeReference((split?.range ?? reference).replace(/\\$/g, \"\"));\n if (!range) {\n return null;\n }\n\n let workbookSheetIndex = fallbackSheetIndex;\n if (split?.sheetName) {\n const workbookResolvedIndex = workbook?.sheetIndex(split.sheetName);\n if (typeof workbookResolvedIndex === \"number\" && Number.isInteger(workbookResolvedIndex) && workbookResolvedIndex >= 0) {\n workbookSheetIndex = workbookResolvedIndex;\n } else {\n const matchingSheet = sheets.find((sheet) => sheet.name === split.sheetName);\n if (matchingSheet) {\n workbookSheetIndex = matchingSheet.workbookSheetIndex;\n }\n }\n }\n\n return {\n range: normalizeRange(range),\n workbookSheetIndex\n };\n}\n\nfunction resolveRangePointCell(range: XlsxCellRange, pointIndex: number): XlsxCellRange | null {\n const normalized = normalizeRange(range);\n const rowCount = normalized.end.row - normalized.start.row + 1;\n const colCount = normalized.end.col - normalized.start.col + 1;\n const cellCount = rowCount * colCount;\n if (pointIndex < 0 || pointIndex >= cellCount) {\n return null;\n }\n\n const rowOffset = Math.floor(pointIndex / colCount);\n const colOffset = pointIndex % colCount;\n const cell = {\n col: normalized.start.col + colOffset,\n row: normalized.start.row + rowOffset\n };\n return { start: cell, end: cell };\n}\n\nfunction collectChartRangeHighlights(\n chart: XlsxChart | null,\n selectedElement: XlsxChartElementSelection | null,\n sheets: XlsxSheetData[],\n workbook: Workbook | null,\n isDark: boolean\n) {\n if (!chart) {\n return [] as ChartRangeHighlight[];\n }\n\n const highlights: ChartRangeHighlight[] = [];\n const seenRanges = new Set<string>();\n const selectedSeriesIndex = selectedElement?.chartId === chart.id && selectedElement.kind !== \"chart\"\n ? selectedElement.seriesIndex\n : null;\n const seriesEntries = chart.series\n .map((series, seriesIndex) => ({ series, seriesIndex }))\n .filter(({ series, seriesIndex }) => (\n series.hidden !== true\n && (selectedSeriesIndex == null || seriesIndex === selectedSeriesIndex)\n ));\n const references = seriesEntries.flatMap(({ series, seriesIndex }) => [\n {\n formula: typeof series.raw?.name === \"string\" ? series.raw.name : undefined,\n role: \"name\" as const,\n seriesIndex\n },\n {\n formula: series.categoriesRef?.formula,\n role: \"category\" as const,\n seriesIndex\n },\n {\n formula: series.valuesRef?.formula,\n role: \"value\" as const,\n seriesIndex\n },\n {\n formula: series.bubbleSizeRef?.formula,\n role: \"bubble\" as const,\n seriesIndex\n }\n ]);\n\n references.forEach((reference) => {\n if (!reference.formula) {\n return;\n }\n\n const resolved = resolveChartFormulaRange(reference.formula, chart.workbookSheetIndex, sheets, workbook);\n if (!resolved) {\n return;\n }\n\n const key = [\n resolved.workbookSheetIndex,\n resolved.range.start.row,\n resolved.range.start.col,\n resolved.range.end.row,\n resolved.range.end.col\n ].join(\":\");\n if (seenRanges.has(key)) {\n return;\n }\n\n seenRanges.add(key);\n const strokeColor = CHART_SOURCE_HIGHLIGHT_COLOR_BY_ROLE[reference.role];\n highlights.push({\n fillColor: applyAlphaToColor(strokeColor, isDark ? 0.24 : 0.14),\n range: resolved.range,\n strokeColor,\n workbookSheetIndex: resolved.workbookSheetIndex\n });\n });\n\n if (selectedElement?.chartId === chart.id && selectedElement.kind === \"point\") {\n references.forEach((reference) => {\n if (!reference.formula || reference.role === \"name\") {\n return;\n }\n\n const resolved = resolveChartFormulaRange(reference.formula, chart.workbookSheetIndex, sheets, workbook);\n if (!resolved) {\n return;\n }\n\n const range = resolveRangePointCell(resolved.range, selectedElement.pointIndex);\n if (!range) {\n return;\n }\n\n const strokeColor = CHART_SOURCE_HIGHLIGHT_COLOR_BY_ROLE.point;\n highlights.push({\n fillColor: applyAlphaToColor(strokeColor, isDark ? 0.34 : 0.22),\n range,\n strokeColor,\n workbookSheetIndex: resolved.workbookSheetIndex\n });\n });\n }\n\n return highlights;\n}\n\nfunction clampSparklineValue(value: number, min: number, max: number) {\n if (max <= min) {\n return 0.5;\n }\n return Math.max(0, Math.min(1, (value - min) / (max - min)));\n}\n\nfunction renderSparkline(\n sparkline: XlsxSheetData[\"sparklines\"][number],\n values: Array<number | null>,\n palette: ViewerPalette\n) {\n const width = 100;\n const height = 18;\n const innerWidth = width - 2;\n const innerHeight = height - 4;\n const points = values.map((value, index) => ({ index, value })).filter((entry): entry is { index: number; value: number } => typeof entry.value === \"number\" && Number.isFinite(entry.value));\n if (points.length === 0) {\n return null;\n }\n\n const negativeColor = sparkline.negativeColor ?? \"#c2410c\";\n const seriesColor = sparkline.color ?? \"#2563eb\";\n const markerColor = sparkline.markerColor ?? seriesColor;\n\n if (sparkline.type === \"winLoss\") {\n const normalizedValues = points.map((entry) => ({ ...entry, value: entry.value >= 0 ? 1 : -1 }));\n const segmentWidth = Math.max(4, innerWidth / Math.max(normalizedValues.length * 1.9, 1));\n const gap = normalizedValues.length > 1 ? (innerWidth - segmentWidth * normalizedValues.length) / (normalizedValues.length - 1) : 0;\n const positiveY = 4.5;\n const negativeY = height - 4.5;\n\n return (\n <svg aria-hidden=\"true\" height={height} style={{ display: \"block\", overflow: \"visible\", width: \"100%\" }} viewBox={`0 0 ${width} ${height}`} width=\"100%\">\n {normalizedValues.map((entry, index) => {\n const left = 1 + index * (segmentWidth + Math.max(0, gap));\n const y = entry.value >= 0 ? positiveY : negativeY;\n return (\n <line\n key={`spark-winloss-${index}`}\n stroke={seriesColor}\n strokeLinecap=\"round\"\n strokeWidth={1.8}\n x1={left}\n x2={left + segmentWidth}\n y1={y}\n y2={y}\n />\n );\n })}\n </svg>\n );\n }\n\n if (sparkline.type === \"column\") {\n const normalizedValues = points;\n const minValue = Math.min(0, ...normalizedValues.map((entry) => entry.value));\n const maxValue = Math.max(0, ...normalizedValues.map((entry) => entry.value));\n const zeroY = 2 + innerHeight - clampSparklineValue(0, minValue, maxValue) * innerHeight;\n const barWidth = Math.max(2, innerWidth / Math.max(normalizedValues.length * 1.8, 1));\n const gap = normalizedValues.length > 1 ? (innerWidth - barWidth * normalizedValues.length) / (normalizedValues.length - 1) : 0;\n\n return (\n <svg aria-hidden=\"true\" height={height} style={{ display: \"block\", overflow: \"visible\", width: \"100%\" }} viewBox={`0 0 ${width} ${height}`} width=\"100%\">\n <line stroke={palette.border} strokeWidth={1} x1={1} x2={width - 1} y1={zeroY} y2={zeroY} />\n {normalizedValues.map((entry, index) => {\n const left = 1 + index * (barWidth + Math.max(0, gap));\n const y = 2 + innerHeight - clampSparklineValue(entry.value, minValue, maxValue) * innerHeight;\n const top = Math.min(y, zeroY);\n const barHeight = Math.max(1, Math.abs(y - zeroY));\n const fill = entry.value < 0 ? negativeColor : seriesColor;\n return (\n <rect\n fill={fill}\n height={barHeight}\n key={`spark-bar-${index}`}\n rx={sparkline.type === \"column\" ? 0 : 0.8}\n ry={sparkline.type === \"column\" ? 0 : 0.8}\n width={barWidth}\n x={left}\n y={top}\n />\n );\n })}\n </svg>\n );\n }\n\n const minValue = Math.min(...points.map((entry) => entry.value));\n const maxValue = Math.max(...points.map((entry) => entry.value));\n const xStep = points.length > 1 ? innerWidth / (points.length - 1) : 0;\n const path = points.map((entry, index) => {\n const x = 1 + index * xStep;\n const y = 2 + innerHeight - clampSparklineValue(entry.value, minValue, maxValue) * innerHeight;\n return `${index === 0 ? \"M\" : \"L\"} ${x.toFixed(2)} ${y.toFixed(2)}`;\n }).join(\" \");\n\n const highValue = Math.max(...points.map((entry) => entry.value));\n const lowValue = Math.min(...points.map((entry) => entry.value));\n\n return (\n <svg aria-hidden=\"true\" height={height} style={{ display: \"block\", overflow: \"visible\", width: \"100%\" }} viewBox={`0 0 ${width} ${height}`} width=\"100%\">\n <path d={path} fill=\"none\" stroke={seriesColor} strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.6} />\n {sparkline.markers ? points.map((entry, index) => {\n const x = 1 + index * xStep;\n const y = 2 + innerHeight - clampSparklineValue(entry.value, minValue, maxValue) * innerHeight;\n let fill = markerColor;\n if (entry.value === highValue && sparkline.highColor) {\n fill = sparkline.highColor;\n } else if (entry.value === lowValue && sparkline.lowColor) {\n fill = sparkline.lowColor;\n } else if (index === 0 && sparkline.firstColor) {\n fill = sparkline.firstColor;\n } else if (index === points.length - 1 && sparkline.lastColor) {\n fill = sparkline.lastColor;\n } else if (entry.value < 0 && sparkline.negative && sparkline.negativeColor) {\n fill = sparkline.negativeColor;\n }\n return <circle cx={x} cy={y} fill={fill} key={`spark-point-${index}`} r={1.75} />;\n }) : null}\n </svg>\n );\n}\n\nfunction asNonNegativeInteger(value: unknown) {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0 ? value : null;\n}\n\nfunction asRecord(value: unknown) {\n return value && typeof value === \"object\" && !Array.isArray(value) ? (value as Record<string, unknown>) : null;\n}\n\nfunction normalizeBatchedHyperlink(value: unknown): BatchedCellData[\"hyperlink\"] {\n const hyperlink = asRecord(value);\n if (!hyperlink) {\n return null;\n }\n\n return {\n location: typeof hyperlink.location === \"string\" ? hyperlink.location : undefined,\n target: typeof hyperlink.target === \"string\" ? hyperlink.target : undefined,\n tooltip: typeof hyperlink.tooltip === \"string\" ? hyperlink.tooltip : undefined\n };\n}\n\nfunction normalizeBatchedMergeSpan(value: unknown): BatchedCellData[\"mergeSpan\"] {\n const mergeSpan = asRecord(value);\n if (!mergeSpan) {\n return null;\n }\n\n const rowSpan = asNonNegativeInteger(mergeSpan.rowSpan);\n const colSpan = asNonNegativeInteger(mergeSpan.colSpan);\n if (rowSpan === null && colSpan === null) {\n return null;\n }\n\n return {\n colSpan: colSpan ?? undefined,\n rowSpan: rowSpan ?? undefined\n };\n}\n\nfunction normalizeBatchedCellValue(\n rawValue: unknown,\n formula: string | null,\n row: number,\n col: number,\n activeSheet?: XlsxSheetData | null\n) {\n const value =\n typeof rawValue === \"string\"\n ? decodeHtmlEntities(rawValue)\n : rawValue === null || rawValue === undefined\n ? \"\"\n : String(rawValue);\n const cachedFormulaValue = formula ? activeSheet?.cachedFormulaValues?.[cellAddressToA1({ row, col })] : undefined;\n\n if (formula && cachedFormulaValue !== undefined && value.startsWith(\"#\")) {\n return cachedFormulaValue;\n }\n\n return value;\n}\n\nfunction buildWorksheetBatchWindow(\n rows: unknown[] | null,\n activeSheet: XlsxSheetData | null,\n startRow: number,\n endRow: number\n): WorksheetBatchWindow {\n const cells = new Map<string, BatchedCellData>();\n\n if (Array.isArray(rows)) {\n for (const rowEntry of rows as WorksheetBatchRowEntry[]) {\n const row = asNonNegativeInteger(rowEntry.index);\n if (row === null || !Array.isArray(rowEntry.cells)) {\n continue;\n }\n\n for (const cellEntry of rowEntry.cells as WorksheetBatchCellEntry[]) {\n const col = asNonNegativeInteger(cellEntry.col);\n if (col === null) {\n continue;\n }\n\n const formula = typeof cellEntry.formula === \"string\" ? cellEntry.formula : null;\n cells.set(`${row}:${col}`, {\n formula,\n numericValue: typeof cellEntry.numericValue === \"number\" ? cellEntry.numericValue : cellEntry.numericValue === null ? null : undefined,\n generalAlignment: cellEntry.generalAlignment === \"right\" || cellEntry.generalAlignment === \"center\"\n ? cellEntry.generalAlignment\n : \"left\",\n hyperlink: normalizeBatchedHyperlink(cellEntry.hyperlink),\n isMergedSecondary: cellEntry.isMergedSecondary === true,\n mergeSpan: normalizeBatchedMergeSpan(cellEntry.mergeSpan),\n style: asRecord(cellEntry.style),\n value: normalizeBatchedCellValue(cellEntry.value, formula, row, col, activeSheet)\n });\n }\n }\n }\n\n return {\n cells,\n endRow,\n startRow\n };\n}\n\nfunction getTableAtCell(tables: XlsxTable[], row: number, col: number) {\n return tables.find(\n (table) =>\n row >= table.start.row &&\n row <= table.end.row &&\n col >= table.start.col &&\n col <= table.end.col\n ) ?? null;\n}\n\nfunction mergeResolvedCellStyle(\n base: Record<string, unknown> | null | undefined,\n overlay: Record<string, unknown> | null | undefined,\n options?: { replaceXfSubtrees?: boolean }\n) {\n if (!base && !overlay) {\n return null;\n }\n\n const nextStyle: Record<string, unknown> = {\n ...(base ?? {}),\n ...(overlay ?? {})\n };\n\n const baseAlignment = base?.alignment as Record<string, unknown> | undefined;\n const overlayAlignment = overlay?.alignment as Record<string, unknown> | undefined;\n if (baseAlignment || overlayAlignment) {\n nextStyle.alignment = {\n ...(baseAlignment ?? {}),\n ...(overlayAlignment ?? {})\n };\n }\n\n const baseBorder = base?.border as Record<string, Record<string, unknown>> | undefined;\n const overlayBorder = overlay?.border as Record<string, Record<string, unknown>> | undefined;\n if (baseBorder || overlayBorder) {\n nextStyle.border = options?.replaceXfSubtrees && overlayBorder\n ? overlayBorder\n : {\n ...(baseBorder ?? {}),\n ...(overlayBorder ?? {})\n };\n }\n\n const baseFill = base?.fill as Record<string, unknown> | undefined;\n const overlayFill = overlay?.fill as Record<string, unknown> | undefined;\n if (baseFill || overlayFill) {\n nextStyle.fill = options?.replaceXfSubtrees && overlayFill\n ? overlayFill\n : (overlayFill ?? baseFill);\n }\n\n const baseFont = base?.font as Record<string, unknown> | undefined;\n const overlayFont = overlay?.font as Record<string, unknown> | undefined;\n if (baseFont || overlayFont) {\n nextStyle.font = options?.replaceXfSubtrees && overlayFont\n ? overlayFont\n : {\n ...(baseFont ?? {}),\n ...(overlayFont ?? {})\n };\n }\n\n return nextStyle;\n}\n\nfunction resolveCellTableOverrides(\n rawStyle: Record<string, unknown> | null,\n sheet: XlsxSheetData | null | undefined,\n row: number,\n col: number\n) {\n const styleId = sheet?.cellStyleIds?.[cellAddressToA1({ row, col })]\n ?? sheet?.rowStyleIds[row]\n ?? sheet?.colStyleIds[col];\n const declared = styleId === undefined ? undefined : sheet?.styleById[styleId]?.tableStyleOverrides;\n const overrides: Record<string, unknown> = {};\n const font = asRecord(rawStyle?.font);\n if (font) {\n const explicitFont: Record<string, unknown> = {};\n const fontDefaults: Record<string, unknown> = {\n name: \"Calibri\", size: 11, bold: false, italic: false, underline: \"none\",\n strikethrough: false, verticalAlign: \"baseline\"\n };\n for (const [key, value] of Object.entries(font)) {\n const isExplicit = declared\n ? Object.hasOwn(declared.font ?? {}, key)\n : key === \"color\"\n ? asRecord(value)?.colorType !== \"auto\"\n : value !== fontDefaults[key];\n if (isExplicit) {\n explicitFont[key] = value;\n }\n }\n overrides.font = explicitFont;\n }\n const fill = asRecord(rawStyle?.fill);\n if (fill && fill.fillType !== \"none\") {\n overrides.fill = fill;\n }\n const border = asRecord(rawStyle?.border);\n if (border) {\n overrides.border = Object.fromEntries(Object.entries(border).filter(([, edge]) => {\n const style = asRecord(edge)?.style;\n return typeof style === \"string\" && style !== \"none\";\n }));\n }\n return overrides;\n}\n\nfunction resolveBorderEdgePriority(edge: Record<string, unknown> | null | undefined) {\n const style = typeof edge?.style === \"string\" ? edge.style : \"none\";\n const stylePriority: Record<string, number> = {\n dashed: 2,\n dashDot: 3,\n dashDotDot: 3,\n dotted: 1,\n double: 8,\n hair: 0,\n medium: 6,\n mediumDashDot: 5,\n mediumDashDotDot: 5,\n mediumDashed: 5,\n none: -1,\n slantDashDot: 4,\n thick: 7,\n thin: 4\n };\n return stylePriority[style] ?? -1;\n}\n\nfunction resolvePreferredBorderEdge(\n current: Record<string, unknown> | null | undefined,\n candidate: Record<string, unknown> | null | undefined\n) {\n if (!candidate || typeof candidate.style !== \"string\" || candidate.style === \"none\") {\n return current ?? null;\n }\n if (!current) {\n return candidate;\n }\n\n const currentPriority = resolveBorderEdgePriority(current);\n const candidatePriority = resolveBorderEdgePriority(candidate);\n if (candidatePriority > currentPriority) {\n return candidate;\n }\n return current;\n}\n\nfunction resolveMergedRegionBorderOverlay(\n row: number,\n col: number,\n merge: { colSpan?: number; rowSpan?: number } | null | undefined,\n resolveBaseCellStyle: (targetRow: number, targetCol: number) => Record<string, unknown> | null\n) {\n const rowSpan = Math.max(1, merge?.rowSpan ?? 1);\n const colSpan = Math.max(1, merge?.colSpan ?? 1);\n if (rowSpan === 1 && colSpan === 1) {\n return null;\n }\n\n const endRow = row + rowSpan - 1;\n const endCol = col + colSpan - 1;\n let top: Record<string, unknown> | null = null;\n let right: Record<string, unknown> | null = null;\n let bottom: Record<string, unknown> | null = null;\n let left: Record<string, unknown> | null = null;\n\n for (let targetCol = col; targetCol <= endCol; targetCol += 1) {\n const topStyle = resolveBaseCellStyle(row, targetCol);\n const topBorder = topStyle?.border as Record<string, Record<string, unknown>> | undefined;\n top = resolvePreferredBorderEdge(top, topBorder?.top ?? null);\n\n const bottomStyle = resolveBaseCellStyle(endRow, targetCol);\n const bottomBorder = bottomStyle?.border as Record<string, Record<string, unknown>> | undefined;\n bottom = resolvePreferredBorderEdge(bottom, bottomBorder?.bottom ?? null);\n }\n\n for (let targetRow = row; targetRow <= endRow; targetRow += 1) {\n const leftStyle = resolveBaseCellStyle(targetRow, col);\n const leftBorder = leftStyle?.border as Record<string, Record<string, unknown>> | undefined;\n left = resolvePreferredBorderEdge(left, leftBorder?.left ?? null);\n\n const rightStyle = resolveBaseCellStyle(targetRow, endCol);\n const rightBorder = rightStyle?.border as Record<string, Record<string, unknown>> | undefined;\n right = resolvePreferredBorderEdge(right, rightBorder?.right ?? null);\n }\n\n const border: Record<string, Record<string, unknown>> = {};\n if (top) {\n border.top = top;\n }\n if (right) {\n border.right = right;\n }\n if (bottom) {\n border.bottom = bottom;\n }\n if (left) {\n border.left = left;\n }\n\n if (Object.keys(border).length === 0) {\n return null;\n }\n\n return { border } satisfies Record<string, unknown>;\n}\n\ntype TableStyleRegion = {\n endCol: number;\n endRow: number;\n startCol: number;\n startRow: number;\n};\n\nfunction normalizeTableStyleEdges(\n style: Record<string, unknown> | null | undefined,\n region: TableStyleRegion,\n row: number,\n col: number\n) {\n if (!style) {\n return null;\n }\n\n const border = style.border as Record<string, Record<string, unknown>> | undefined;\n if (!border) {\n return style;\n }\n\n // A table style element's border describes the region that element covers:\n // top/right/bottom/left are the region's outer edges and only apply to cells\n // on that boundary, while horizontal/vertical describe the inner grid lines.\n const nextBorder: Record<string, Record<string, unknown>> = {};\n if (border.top && row === region.startRow) {\n nextBorder.top = border.top;\n }\n if (border.bottom && row === region.endRow) {\n nextBorder.bottom = border.bottom;\n }\n if (border.left && col === region.startCol) {\n nextBorder.left = border.left;\n }\n if (border.right && col === region.endCol) {\n nextBorder.right = border.right;\n }\n if (border.horizontal && row < region.endRow) {\n nextBorder.bottom = nextBorder.bottom ?? border.horizontal;\n }\n if (border.vertical && col < region.endCol) {\n nextBorder.right = nextBorder.right ?? border.vertical;\n }\n\n const nextStyle: Record<string, unknown> = { ...style };\n if (Object.keys(nextBorder).length === 0) {\n delete nextStyle.border;\n } else {\n nextStyle.border = nextBorder;\n }\n return nextStyle;\n}\n\nfunction resolveTableCellStyle(\n table: XlsxTable | null,\n row: number,\n col: number,\n activeSheet: XlsxSheetData | null | undefined\n) {\n if (!table || !activeSheet) {\n return null;\n }\n\n const styleName = table.styleInfo?.name;\n if (!styleName) {\n return null;\n }\n\n // A workbook may define its own table style under any name, including a\n // built-in one, so the workbook's definition wins over the shipped preset.\n const tableStyle = activeSheet.tableStyleByName[styleName] ?? resolveBuiltinTableStyle(styleName);\n if (!tableStyle) {\n return null;\n }\n\n const headerRowCount = Math.max(table.headerRowCount, 1);\n const totalsRowCount = table.totalsRowCount;\n const headerEndRow = table.start.row + headerRowCount - 1;\n const totalsStartRow = table.end.row - totalsRowCount + 1;\n const bodyStartRow = headerEndRow + 1;\n const bodyEndRow = totalsStartRow - 1;\n const isHeaderRow = row >= table.start.row && row <= headerEndRow;\n const isTotalsRow = totalsRowCount > 0 && row >= totalsStartRow && row <= table.end.row;\n const isBodyRow = row >= bodyStartRow && row <= bodyEndRow;\n const isFirstColumn = col === table.start.col;\n const isLastColumn = col === table.end.col;\n\n const tableRegion: TableStyleRegion = {\n endCol: table.end.col,\n endRow: table.end.row,\n startCol: table.start.col,\n startRow: table.start.row\n };\n const headerRegion: TableStyleRegion = { ...tableRegion, endRow: headerEndRow };\n const totalsRegion: TableStyleRegion = { ...tableRegion, startRow: totalsStartRow };\n\n let resolved: Record<string, unknown> | null = null;\n const applyElement = (elementType: string, region: TableStyleRegion, enabled = true) => {\n if (!enabled) {\n return;\n }\n\n const nextStyle = normalizeTableStyleEdges(tableStyle[elementType] ?? null, region, row, col);\n if (!nextStyle) {\n return;\n }\n resolved = mergeResolvedCellStyle(resolved, nextStyle);\n };\n\n // Excel layers table style elements from lowest to highest precedence: whole\n // table, column stripes, row stripes, last column, first column, header row,\n // total row, and finally the corner header/total cells.\n applyElement(\"wholeTable\", tableRegion);\n\n if (isBodyRow && table.styleInfo?.showColumnStripes) {\n const isFirstStripe = (col - table.start.col) % 2 === 0;\n applyElement(isFirstStripe ? \"firstColumnStripe\" : \"secondColumnStripe\", {\n endCol: col,\n endRow: bodyEndRow,\n startCol: col,\n startRow: bodyStartRow\n });\n }\n\n if (isBodyRow && table.styleInfo?.showRowStripes) {\n const isFirstStripe = (row - bodyStartRow) % 2 === 0;\n applyElement(isFirstStripe ? \"firstRowStripe\" : \"secondRowStripe\", {\n ...tableRegion,\n endRow: row,\n startRow: row\n });\n }\n\n applyElement(\"lastColumn\", { ...tableRegion, startCol: table.end.col }, Boolean(table.styleInfo?.showLastColumn) && isLastColumn);\n applyElement(\"firstColumn\", { ...tableRegion, endCol: table.start.col }, Boolean(table.styleInfo?.showFirstColumn) && isFirstColumn);\n\n applyElement(\"headerRow\", headerRegion, isHeaderRow);\n if (isHeaderRow && table.headerRowCellStyle) {\n const namedHeaderStyle = activeSheet.namedCellStyleByName[table.headerRowCellStyle];\n if (namedHeaderStyle) {\n resolved = mergeResolvedCellStyle(resolved, namedHeaderStyle);\n }\n }\n\n applyElement(\"totalRow\", totalsRegion, isTotalsRow);\n\n applyElement(\"firstHeaderCell\", { ...headerRegion, endCol: table.start.col }, isHeaderRow && isFirstColumn);\n applyElement(\"lastHeaderCell\", { ...headerRegion, startCol: table.end.col }, isHeaderRow && isLastColumn);\n applyElement(\"firstTotalCell\", { ...totalsRegion, endCol: table.start.col }, isTotalsRow && isFirstColumn);\n applyElement(\"lastTotalCell\", { ...totalsRegion, startCol: table.end.col }, isTotalsRow && isLastColumn);\n\n return resolved;\n}\n\nfunction getTableHeaderColumn(table: XlsxTable | null, row: number, col: number): XlsxTableColumn | null {\n if (!table || row !== table.start.row) {\n return null;\n }\n\n const index = col - table.start.col;\n return table.columns[index] ?? null;\n}\n\nfunction DefaultTableHeaderMenu({\n direction,\n sortAscending,\n sortDescending\n}: XlsxTableHeaderMenuRenderProps) {\n return (\n <div\n style={{\n backgroundColor: \"var(--xlsx-menu-surface)\",\n border: \"1px solid var(--xlsx-menu-border)\",\n borderRadius: 10,\n boxShadow: \"0 10px 30px rgba(0, 0, 0, 0.16)\",\n display: \"grid\",\n gap: 4,\n minWidth: 160,\n padding: 6\n }}\n >\n <button\n onClick={sortAscending}\n style={{\n background: direction === \"ascending\" ? \"var(--xlsx-menu-active)\" : \"transparent\",\n border: \"none\",\n borderRadius: 8,\n color: \"inherit\",\n cursor: \"pointer\",\n fontSize: 12,\n padding: \"8px 10px\",\n textAlign: \"left\"\n }}\n type=\"button\"\n >\n Sort A to Z\n </button>\n <button\n onClick={sortDescending}\n style={{\n background: direction === \"descending\" ? \"var(--xlsx-menu-active)\" : \"transparent\",\n border: \"none\",\n borderRadius: 8,\n color: \"inherit\",\n cursor: \"pointer\",\n fontSize: 12,\n padding: \"8px 10px\",\n textAlign: \"left\"\n }}\n type=\"button\"\n >\n Sort Z to A\n </button>\n </div>\n );\n}\n\nfunction resolveTableHeaderTriggerStyle(palette: ViewerPalette, zoomFactor: number): React.CSSProperties {\n return {\n alignItems: \"center\",\n background: \"transparent\",\n border: \"none\",\n color: palette.mutedText,\n cursor: \"pointer\",\n display: \"inline-flex\",\n fontSize: 10 * zoomFactor,\n height: 16 * zoomFactor,\n justifyContent: \"center\",\n padding: 0,\n position: \"absolute\",\n right: 4 * zoomFactor,\n top: 3 * zoomFactor,\n width: 16 * zoomFactor,\n zIndex: 6\n };\n}\n\nfunction SegmentedControl({\n items,\n onValueChange,\n palette,\n value\n}: {\n items: Array<{ id: string; label: string; title?: string; visibility?: \"hidden\" | \"veryHidden\" | \"visible\" }>;\n onValueChange: (value: string) => void;\n palette: ViewerPalette;\n value: string;\n}) {\n return (\n <div\n aria-label=\"Workbook sheets\"\n role=\"tablist\"\n style={{\n alignItems: \"center\",\n backgroundColor: palette.sheetInactiveSurface,\n border: `1px solid ${palette.strongBorder}`,\n borderRadius: 10,\n display: \"inline-flex\",\n gap: 2,\n minHeight: 36,\n padding: 2\n }}\n >\n {items.map((item) => {\n const selected = item.id === value;\n const isHidden = item.visibility === \"hidden\" || item.visibility === \"veryHidden\";\n return (\n <button\n aria-selected={selected}\n key={item.id}\n onClick={() => onValueChange(item.id)}\n role=\"tab\"\n style={{\n backgroundColor: selected ? palette.sheetActiveSurface : \"transparent\",\n border: \"none\",\n borderRadius: 8,\n boxShadow: selected ? palette.shadow : \"none\",\n color: selected ? palette.sheetActiveText : palette.sheetInactiveText,\n cursor: \"pointer\",\n fontSize: 12,\n fontWeight: selected ? 600 : 500,\n opacity: isHidden && !selected ? 0.68 : 1,\n padding: \"7px 12px\",\n transition: \"background-color 120ms ease, color 120ms ease, box-shadow 120ms ease\",\n whiteSpace: \"nowrap\"\n }}\n title={item.title}\n type=\"button\"\n >\n {item.label}\n </button>\n );\n })}\n </div>\n );\n}\n\nfunction DefaultToolbar({ controller, palette }: { controller: XlsxViewerController; palette: ViewerPalette }) {\n const {\n activeTabIndex,\n canDownload,\n canZoomIn,\n canZoomOut,\n defaultZoomScale,\n displayFileName,\n download,\n resetZoom,\n setActiveTabIndex,\n tabs,\n zoomIn,\n zoomOut,\n zoomScale\n } = controller;\n\n return (\n <>\n <div\n style={{\n alignItems: \"center\",\n backgroundColor: palette.toolbarSurface,\n borderBottom: `1px solid ${palette.border}`,\n color: palette.text,\n display: \"flex\",\n gap: 12,\n justifyContent: \"space-between\",\n minHeight: 48,\n padding: \"0 16px\"\n }}\n >\n <div style={{ minWidth: 0 }}>\n <div\n style={{\n color: palette.text,\n fontSize: 14,\n fontWeight: 600,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\"\n }}\n >\n {displayFileName}\n </div>\n </div>\n <div style={{ alignItems: \"center\", display: \"flex\", gap: 8 }}>\n <div\n style={{\n alignItems: \"center\",\n background: palette.buttonSurface,\n border: `1px solid ${palette.strongBorder}`,\n borderRadius: 8,\n display: \"flex\",\n overflow: \"hidden\"\n }}\n >\n <button\n disabled={!canZoomOut}\n onClick={zoomOut}\n style={{\n background: \"transparent\",\n border: \"none\",\n color: canZoomOut ? palette.buttonText : palette.mutedText,\n cursor: canZoomOut ? \"pointer\" : \"default\",\n fontSize: 14,\n fontWeight: 600,\n padding: \"6px 10px\"\n }}\n type=\"button\"\n >\n -\n </button>\n <button\n onClick={resetZoom}\n style={{\n background: Math.round(zoomScale) === Math.round(defaultZoomScale) ? \"transparent\" : palette.subtleSurface,\n border: \"none\",\n borderLeft: `1px solid ${palette.strongBorder}`,\n borderRight: `1px solid ${palette.strongBorder}`,\n color: palette.buttonText,\n cursor: \"pointer\",\n fontSize: 12,\n fontWeight: 600,\n minWidth: 60,\n padding: \"6px 10px\"\n }}\n type=\"button\"\n >\n {formatZoomScale(zoomScale)}\n </button>\n <button\n disabled={!canZoomIn}\n onClick={zoomIn}\n style={{\n background: \"transparent\",\n border: \"none\",\n color: canZoomIn ? palette.buttonText : palette.mutedText,\n cursor: canZoomIn ? \"pointer\" : \"default\",\n fontSize: 14,\n fontWeight: 600,\n padding: \"6px 10px\"\n }}\n type=\"button\"\n >\n +\n </button>\n </div>\n {canDownload ? (\n <button\n aria-label=\"Download workbook\"\n onClick={download}\n style={{\n alignItems: \"center\",\n background: palette.buttonSurface,\n border: `1px solid ${palette.strongBorder}`,\n borderRadius: 8,\n color: palette.buttonText,\n cursor: \"pointer\",\n display: \"inline-flex\",\n fontSize: 16,\n fontWeight: 500,\n height: 32,\n justifyContent: \"center\",\n padding: 0,\n width: 32\n }}\n title=\"Download workbook\"\n type=\"button\"\n >\n ↓\n </button>\n ) : null}\n </div>\n </div>\n {tabs.length > 1 ? (\n <div\n style={{\n backgroundColor: palette.subtleSurface,\n borderBottom: `1px solid ${palette.border}`,\n overflowX: \"auto\",\n padding: \"8px 12px\"\n }}\n >\n <SegmentedControl\n items={tabs.map((tab, index) => ({\n id: String(index),\n label: tab.name,\n title:\n tab.visibility === \"hidden\"\n ? `${tab.name} (hidden)`\n : tab.visibility === \"veryHidden\"\n ? `${tab.name} (very hidden)`\n : tab.name,\n visibility: tab.visibility\n }))}\n onValueChange={(value) => setActiveTabIndex(Number(value))}\n palette={palette}\n value={String(activeTabIndex)}\n />\n </div>\n ) : null}\n </>\n );\n}\n\nfunction resolveToolbar(\n toolbar: XlsxViewerProps[\"toolbar\"],\n showDefaultToolbar: boolean,\n controller: XlsxViewerController,\n palette: ViewerPalette\n) {\n if (typeof toolbar === \"function\") {\n return toolbar(controller);\n }\n\n if (toolbar !== undefined) {\n return toolbar;\n }\n\n if (!showDefaultToolbar) {\n return null;\n }\n\n return <DefaultToolbar controller={controller} palette={palette} />;\n}\n\nfunction renderError(errorState: XlsxViewerProps[\"errorState\"], error: Error, palette: ViewerPalette) {\n if (typeof errorState === \"function\") {\n return errorState(error);\n }\n if (errorState !== undefined) {\n return errorState;\n }\n\n return (\n <div\n style={{\n alignItems: \"center\",\n color: palette.danger,\n display: \"flex\",\n fontSize: 14,\n height: \"100%\",\n justifyContent: \"center\",\n padding: 16,\n textAlign: \"center\"\n }}\n >\n {error.message}\n </div>\n );\n}\n\nfunction renderLoading(\n loadingComponent: XlsxViewerProps[\"loadingComponent\"],\n loadingState: XlsxViewerProps[\"loadingState\"],\n palette: ViewerPalette\n) {\n if (loadingComponent !== undefined) {\n return loadingComponent;\n }\n\n if (loadingState !== undefined) {\n return loadingState;\n }\n\n return (\n <div\n style={{\n alignItems: \"center\",\n color: palette.mutedText,\n display: \"flex\",\n fontSize: 14,\n height: \"100%\",\n justifyContent: \"center\"\n }}\n >\n Loading workbook...\n </div>\n );\n}\n\nfunction renderEmpty(emptyState: XlsxViewerProps[\"emptyState\"], palette: ViewerPalette) {\n if (emptyState !== undefined) {\n return emptyState;\n }\n\n return (\n <div\n style={{\n alignItems: \"center\",\n color: palette.mutedText,\n display: \"flex\",\n fontSize: 14,\n height: \"100%\",\n justifyContent: \"center\",\n padding: 16,\n textAlign: \"center\"\n }}\n >\n No workbook loaded.\n </div>\n );\n}\n\nfunction formatBinaryBytes(bytes: number) {\n if (!Number.isFinite(bytes) || bytes <= 0) {\n return \"0 B\";\n }\n\n const units = [\"B\", \"KB\", \"MB\", \"GB\"];\n let value = bytes;\n let unitIndex = 0;\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex += 1;\n }\n\n return `${value >= 10 || unitIndex === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[unitIndex]}`;\n}\n\nfunction renderFileTooLarge(\n fileTooLargeState: XlsxViewerProps[\"fileTooLargeState\"],\n renderProps: {\n displayFileName: string;\n fileSizeBytes: number;\n maxFileSizeBytes: number;\n },\n palette: ViewerPalette\n) {\n const defaultNode = (\n <div\n style={{\n alignItems: \"center\",\n color: palette.mutedText,\n display: \"flex\",\n flexDirection: \"column\",\n fontSize: 14,\n gap: 8,\n height: \"100%\",\n justifyContent: \"center\",\n padding: 24,\n textAlign: \"center\"\n }}\n >\n <div style={{ color: palette.text, fontWeight: 600 }}>\n {renderProps.displayFileName}\n </div>\n <div>\n File size {formatBinaryBytes(renderProps.fileSizeBytes)} exceeds the configured limit of{\" \"}\n {formatBinaryBytes(renderProps.maxFileSizeBytes)}.\n </div>\n </div>\n );\n\n if (typeof fileTooLargeState === \"function\") {\n return fileTooLargeState({\n defaultNode,\n displayFileName: renderProps.displayFileName,\n fileSizeBytes: renderProps.fileSizeBytes,\n maxFileSizeBytes: renderProps.maxFileSizeBytes\n });\n }\n\n if (fileTooLargeState !== undefined) {\n return fileTooLargeState;\n }\n\n return defaultNode;\n}\n\nfunction renderCustomFileTooLarge(\n fileTooLargeState: XlsxViewerProps[\"fileTooLargeState\"],\n renderProps: {\n displayFileName: string;\n fileSizeBytes: number;\n maxFileSizeBytes: number;\n },\n palette: ViewerPalette\n) {\n if (fileTooLargeState === undefined) {\n return undefined;\n }\n\n return renderFileTooLarge(fileTooLargeState, renderProps, palette);\n}\n\nfunction renderDefaultChartLoadingCard(rect: XlsxImageRect) {\n const bars = [18, 32, 24];\n const barWidth = Math.max(8, Math.min(12, Math.round(rect.width * 0.018)));\n const barGap = 8;\n\n return (\n <div\n style={{\n backgroundColor: \"#ffffff\",\n border: \"1px solid #e5e7eb\",\n borderRadius: 10,\n boxSizing: \"border-box\",\n display: \"flex\",\n height: \"100%\",\n justifyContent: \"center\",\n padding: 12,\n width: \"100%\"\n }}\n >\n <div\n style={{\n alignItems: \"flex-end\",\n display: \"flex\",\n gap: barGap,\n justifyContent: \"center\"\n }}\n >\n {bars.map((heightPx, index) => (\n <div\n key={index}\n style={{\n backgroundColor: \"#e5e7eb\",\n borderRadius: 999,\n height: heightPx,\n width: barWidth\n }}\n />\n ))}\n </div>\n </div>\n );\n}\n\nfunction renderChartLoadingNode(\n renderChartLoading: XlsxViewerProps[\"renderChartLoading\"],\n chart: XlsxChart,\n rect: XlsxImageRect,\n stage: \"metadata\" | \"renderer\" | \"map\" = \"metadata\"\n) {\n const defaultNode = renderDefaultChartLoadingCard(rect);\n if (!renderChartLoading) {\n return defaultNode;\n }\n\n return renderChartLoading({\n chart,\n defaultNode,\n rect,\n stage\n });\n}\n\nfunction formatBytes(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n return \"0 B\";\n }\n\n const units = [\"B\", \"KB\", \"MB\", \"GB\"];\n let size = value;\n let unitIndex = 0;\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex += 1;\n }\n\n return `${size >= 10 || unitIndex === 0 ? size.toFixed(0) : size.toFixed(1)} ${units[unitIndex]}`;\n}\n\nfunction renderDeferredLoad(\n controller: XlsxViewerController,\n palette: ViewerPalette\n) {\n return (\n <div\n style={{\n alignItems: \"center\",\n color: palette.text,\n display: \"flex\",\n height: \"100%\",\n justifyContent: \"center\",\n padding: 24\n }}\n >\n <div\n style={{\n backgroundColor: palette.surface,\n border: `1px solid ${palette.strongBorder}`,\n borderRadius: 12,\n boxShadow: palette.shadow,\n maxWidth: 420,\n padding: 20,\n width: \"100%\"\n }}\n >\n <div style={{ fontSize: 16, fontWeight: 600 }}>Large workbook detected</div>\n <div style={{ color: palette.mutedText, fontSize: 13, lineHeight: 1.5, marginTop: 8 }}>\n This workbook is {formatBytes(controller.deferredLoadFileSize ?? 0)}. Loading it immediately can block the main thread and freeze the page.\n </div>\n <div style={{ color: palette.mutedText, fontSize: 13, lineHeight: 1.5, marginTop: 8 }}>\n Best practice is to gate large files or move parsing into a worker. You can still load it manually below.\n </div>\n <div style={{ display: \"flex\", gap: 10, marginTop: 16 }}>\n <button\n onClick={controller.continueDeferredLoad}\n style={{\n background: palette.buttonSurface,\n border: `1px solid ${palette.strongBorder}`,\n borderRadius: 8,\n color: palette.buttonText,\n cursor: controller.canLoadDeferred ? \"pointer\" : \"default\",\n fontSize: 12,\n fontWeight: 600,\n opacity: controller.canLoadDeferred ? 1 : 0.6,\n padding: \"8px 12px\"\n }}\n type=\"button\"\n >\n Load Anyway\n </button>\n </div>\n </div>\n </div>\n );\n}\n\nfunction resolveSelectionColors({\n palette,\n selectionColor,\n selectionFillColor,\n selectionHeaderColor\n}: {\n palette: ViewerPalette;\n selectionColor?: string;\n selectionFillColor?: string;\n selectionHeaderColor?: string;\n}) {\n const stroke = selectionColor ?? (paletteIsDark(palette) ? \"#60a5fa\" : \"#2563eb\");\n const fill =\n selectionFillColor ??\n `color-mix(in srgb, ${stroke} ${paletteIsDark(palette) ? \"16%\" : \"10%\"}, transparent)`;\n const header =\n selectionHeaderColor ??\n `color-mix(in srgb, ${stroke} ${paletteIsDark(palette) ? \"24%\" : \"16%\"}, ${palette.headerSurface})`;\n\n return {\n fill,\n header,\n stroke\n };\n}\n\ntype CellRenderData = {\n canvas?: CanvasCellStyleCache;\n chartHighlight?: CellChartHighlight | null;\n checkboxState?: boolean | null;\n conditionalColorScale?: {\n color: string;\n } | null;\n colSpan?: number;\n conditionalDataBar?: {\n axisColor?: string;\n border?: boolean;\n borderColor?: string;\n color: string;\n gradient?: boolean;\n negativeBorderColor?: string;\n negativeFillColor?: string;\n widthPercent: number;\n } | null;\n conditionalIcon?: {\n backgroundColor?: string;\n borderColor?: string;\n color?: string;\n glyph?: string;\n rotationDeg?: number;\n shape?: \"arrow\";\n } | null;\n hyperlink?: {\n location?: string;\n target?: string;\n tooltip?: string;\n } | null;\n isMergedSecondary: boolean;\n shrinkToFit?: boolean;\n shrinkToFitFontSizePx?: number;\n isTableHeader?: boolean;\n rowSpan?: number;\n spillOffsetX?: number;\n spillSide?: \"left\" | \"right\";\n sparkline?: {\n config: XlsxSheetData[\"sparklines\"][number];\n values: Array<number | null>;\n } | null;\n spillWidth?: number;\n style: React.CSSProperties;\n validation?: {\n message?: string;\n showDropdown: boolean;\n validationType: string;\n } | null;\n value: string;\n textRotationDeg?: number | null;\n};\n\ntype PaintedBodyCanvasSignature = {\n activeSheet: XlsxSheetData | null;\n bakedDrawingSignature: string;\n bodyHeight: number;\n bodyWidth: number;\n colSignature: string;\n getCellData: (row: number, col: number) => CellRenderData;\n headerHeight: number;\n palette: ViewerPalette;\n rowHeaderWidth: number;\n rowSignature: string;\n viewportLeft: number;\n viewportTop: number;\n zoomFactor: number;\n};\n\ntype PaintedHeaderCanvasSignature = {\n activeSheet: XlsxSheetData | null;\n bodyHeight: number;\n bodyWidth: number;\n colSignature: string;\n headerHeight: number;\n palette: ViewerPalette;\n rangeSignature: string;\n rowHeaderWidth: number;\n rowSignature: string;\n viewportLeft: number;\n viewportTop: number;\n zoomFactor: number;\n};\n\ntype CanvasDirtyRect = {\n height: number;\n left: number;\n top: number;\n width: number;\n};\n\ntype CanvasStickyColumnHeaderCell = {\n actualCol: number;\n height: number;\n isFrozen: boolean;\n left: number;\n localLeft: number;\n width: number;\n};\n\ntype CanvasStickyRowHeaderCell = {\n actualRow: number;\n height: number;\n isFrozen: boolean;\n localTop: number;\n top: number;\n};\n\nfunction buildFullCanvasDirtyRect(width: number, height: number): CanvasDirtyRect[] {\n if (width <= 0 || height <= 0) {\n return [];\n }\n\n return [{\n height,\n left: 0,\n top: 0,\n width\n }];\n}\n\nfunction intersectsCanvasDirtyRects(\n left: number,\n top: number,\n width: number,\n height: number,\n dirtyRects: CanvasDirtyRect[]\n) {\n if (dirtyRects.length === 0 || width <= 0 || height <= 0) {\n return false;\n }\n\n const right = left + width;\n const bottom = top + height;\n return dirtyRects.some((dirtyRect) => (\n left < dirtyRect.left + dirtyRect.width\n && right > dirtyRect.left\n && top < dirtyRect.top + dirtyRect.height\n && bottom > dirtyRect.top\n ));\n}\n\nfunction blitCanvasWithScrollDelta(\n canvas: HTMLCanvasElement,\n context: CanvasRenderingContext2D,\n bufferCanvas: HTMLCanvasElement,\n dpr: number,\n width: number,\n height: number,\n deltaX: number,\n deltaY: number\n): CanvasDirtyRect[] | null {\n if (width <= 0 || height <= 0) {\n return [];\n }\n\n const clampedDeltaX = Math.max(-width, Math.min(width, deltaX));\n const clampedDeltaY = Math.max(-height, Math.min(height, deltaY));\n if (Math.abs(clampedDeltaX) < 0.01 && Math.abs(clampedDeltaY) < 0.01) {\n return [];\n }\n\n if (Math.abs(clampedDeltaX) >= width || Math.abs(clampedDeltaY) >= height) {\n return null;\n }\n\n const deviceWidth = Math.max(1, Math.round(width * dpr));\n const deviceHeight = Math.max(1, Math.round(height * dpr));\n if (bufferCanvas.width !== deviceWidth) {\n bufferCanvas.width = deviceWidth;\n }\n if (bufferCanvas.height !== deviceHeight) {\n bufferCanvas.height = deviceHeight;\n }\n\n const bufferContext = bufferCanvas.getContext(\"2d\", { alpha: false });\n if (!bufferContext) {\n return null;\n }\n\n bufferContext.setTransform(1, 0, 0, 1, 0, 0);\n bufferContext.clearRect(0, 0, deviceWidth, deviceHeight);\n bufferContext.drawImage(canvas, 0, 0);\n\n const deltaDeviceX = clampedDeltaX * dpr;\n const deltaDeviceY = clampedDeltaY * dpr;\n const overlapDeviceWidth = deviceWidth - Math.abs(deltaDeviceX);\n const overlapDeviceHeight = deviceHeight - Math.abs(deltaDeviceY);\n if (overlapDeviceWidth <= 0 || overlapDeviceHeight <= 0) {\n return null;\n }\n\n const sourceX = deltaDeviceX > 0 ? deltaDeviceX : 0;\n const sourceY = deltaDeviceY > 0 ? deltaDeviceY : 0;\n const destinationX = deltaDeviceX > 0 ? 0 : -deltaDeviceX;\n const destinationY = deltaDeviceY > 0 ? 0 : -deltaDeviceY;\n\n context.setTransform(1, 0, 0, 1, 0, 0);\n context.drawImage(\n bufferCanvas,\n sourceX,\n sourceY,\n overlapDeviceWidth,\n overlapDeviceHeight,\n destinationX,\n destinationY,\n overlapDeviceWidth,\n overlapDeviceHeight\n );\n context.setTransform(dpr, 0, 0, dpr, 0, 0);\n\n const dirtyRects: CanvasDirtyRect[] = [];\n if (clampedDeltaX > 0) {\n dirtyRects.push({\n height,\n left: Math.max(0, width - clampedDeltaX),\n top: 0,\n width: Math.min(width, clampedDeltaX)\n });\n } else if (clampedDeltaX < 0) {\n dirtyRects.push({\n height,\n left: 0,\n top: 0,\n width: Math.min(width, -clampedDeltaX)\n });\n }\n\n if (clampedDeltaY > 0) {\n dirtyRects.push({\n height: Math.min(height, clampedDeltaY),\n left: 0,\n top: Math.max(0, height - clampedDeltaY),\n width\n });\n } else if (clampedDeltaY < 0) {\n dirtyRects.push({\n height: Math.min(height, -clampedDeltaY),\n left: 0,\n top: 0,\n width\n });\n }\n\n return dirtyRects;\n}\n\nfunction buildConditionalFormatRuleKey(rule: XlsxSheetData[\"conditionalFormatRules\"][number]) {\n return `${rule.kind}:${rule.priority}:${rule.ranges\n .map((range) => `${range.start.row}:${range.start.col}-${range.end.row}:${range.end.col}`)\n .join(\"|\")}`;\n}\n\nfunction resolveConditionalRuleThreshold(\n threshold: XlsxConditionalFormatValueObject | undefined,\n numericValues: number[]\n) {\n if (!threshold) {\n return null;\n }\n\n const fallbackValue = typeof threshold.value === \"number\" ? threshold.value : null;\n const thresholdType = threshold.type.trim().toLowerCase();\n if (thresholdType === \"num\" || thresholdType === \"formula\") {\n return fallbackValue;\n }\n\n if (numericValues.length === 0) {\n return fallbackValue;\n }\n\n const minValue = Math.min(...numericValues);\n const maxValue = Math.max(...numericValues);\n if (thresholdType === \"min\") {\n return minValue;\n }\n if (thresholdType === \"max\") {\n return maxValue;\n }\n // Excel's automatic data bar axis anchors the scale at zero: the minimum is\n // zero unless the range holds negative values and the maximum is zero unless\n // it holds positive values, so all-positive data gets bars proportional to\n // the cell value rather than to its distance from the smallest value.\n if (thresholdType === \"automin\" || thresholdType === \"automaticmin\") {\n return Math.min(0, minValue);\n }\n if (thresholdType === \"automax\" || thresholdType === \"automaticmax\") {\n return Math.max(0, maxValue);\n }\n\n if ((thresholdType === \"percent\" || thresholdType === \"percentile\") && fallbackValue !== null) {\n return minValue + (maxValue - minValue) * (fallbackValue / 100);\n }\n\n return fallbackValue;\n}\n\nfunction parseConditionalFormulaLiteral(formula: string) {\n const trimmed = formula.trim();\n if (trimmed.length === 0) {\n return null;\n }\n\n const withoutEquals = trimmed.startsWith(\"=\") ? trimmed.slice(1).trim() : trimmed;\n if (withoutEquals.startsWith(\"\\\"\") && withoutEquals.endsWith(\"\\\"\") && withoutEquals.length >= 2) {\n return withoutEquals.slice(1, -1).replace(/\\\"\\\"/g, \"\\\"\");\n }\n\n const numeric = Number(withoutEquals.replace(/,/g, \"\"));\n if (Number.isFinite(numeric)) {\n return numeric;\n }\n\n return withoutEquals;\n}\n\nfunction resolveConditionalCellIsMatch(\n operator: string,\n left: string | number | null,\n right: string | number | null,\n numericValue: number | null,\n textValue: string\n) {\n const normalizedOperator = operator.trim();\n const normalizedText = textValue.trim();\n const parsedNumericText = Number(normalizedText.replace(/,/g, \"\"));\n const effectiveNumericValue = numericValue ?? (Number.isFinite(parsedNumericText) ? parsedNumericText : null);\n const cellIsTextComparable = effectiveNumericValue === null && normalizedText.length > 0;\n\n const compareComparable = (literal: string | number | null, predicate: (cell: number, value: number) => boolean) => {\n if (typeof literal !== \"number\") {\n return false;\n }\n\n if (effectiveNumericValue !== null) {\n return predicate(effectiveNumericValue, literal);\n }\n\n if (cellIsTextComparable) {\n // Excel orders text after every number, so text satisfies \"greater than\"\n // comparisons and fails \"less than\" ones.\n return predicate(1, 0);\n }\n\n return false;\n };\n\n switch (normalizedOperator) {\n case \"greaterThan\":\n return compareComparable(left, (cell, value) => cell > value);\n case \"greaterThanOrEqual\":\n return compareComparable(left, (cell, value) => cell >= value);\n case \"lessThan\":\n return compareComparable(left, (cell, value) => cell < value);\n case \"lessThanOrEqual\":\n return compareComparable(left, (cell, value) => cell <= value);\n case \"between\":\n if (typeof left !== \"number\" || typeof right !== \"number\") {\n return false;\n }\n if (effectiveNumericValue === null) {\n return false;\n }\n return effectiveNumericValue >= left && effectiveNumericValue <= right;\n case \"notBetween\":\n if (typeof left !== \"number\" || typeof right !== \"number\") {\n return false;\n }\n if (effectiveNumericValue === null) {\n return cellIsTextComparable;\n }\n return effectiveNumericValue < left || effectiveNumericValue > right;\n case \"notEqual\":\n if (typeof left === \"number\") {\n if (effectiveNumericValue !== null) {\n return effectiveNumericValue !== left;\n }\n return cellIsTextComparable;\n }\n // Excel compares text case-insensitively; a rule without an operand\n // matches nothing.\n return left !== null && normalizedText.toLowerCase() !== String(left).toLowerCase();\n case \"equal\":\n default:\n if (typeof left === \"number\") {\n if (effectiveNumericValue !== null) {\n return effectiveNumericValue === left;\n }\n return false;\n }\n return left !== null && normalizedText.toLowerCase() === String(left).toLowerCase();\n }\n}\n\nfunction resolveConditionalFormulaComparable(\n formula: string,\n worksheet: Worksheet | null | undefined,\n sheet: XlsxSheetData | null | undefined,\n batchedCells?: Map<string, BatchedCellData>\n) {\n const literal = parseConditionalFormulaLiteral(formula);\n if (typeof literal === \"number\") {\n return literal;\n }\n\n const normalized = formula.trim().replace(/^=/, \"\").trim();\n if (normalized.length === 0) {\n return literal;\n }\n\n const splitReference = splitSheetFormulaReference(normalized);\n const targetSheetName = splitReference?.sheetName ?? null;\n if (targetSheetName && sheet?.name && targetSheetName !== sheet.name) {\n return literal;\n }\n\n const cellRef = splitReference?.range ?? normalized;\n const cell = parseA1CellReference(cellRef.replace(/\\$/g, \"\"));\n if (!cell) {\n return literal;\n }\n\n if (worksheet) {\n const numeric = getCellNumericValue(worksheet, cell.row, cell.col);\n if (numeric !== null) {\n return numeric;\n }\n return getCellDisplayValue(worksheet, cell.row, cell.col, sheet);\n }\n\n if (batchedCells) {\n const batched = batchedCells.get(`${cell.row}:${cell.col}`);\n if (batched) {\n const numeric = getBatchedCellNumericValue(batched);\n if (numeric !== null) {\n return numeric;\n }\n return batched.value;\n }\n }\n\n return literal;\n}\n\nfunction normalizeConditionalTextOperand(value: string | number | null | undefined) {\n if (value === null || value === undefined) {\n return \"\";\n }\n\n return String(value).trim().toLowerCase();\n}\n\nfunction resolveConditionalStyledRuleMatch(\n rule: Extract<XlsxSheetData[\"conditionalFormatRules\"][number], { kind: \"styled\" }>,\n numericValue: number | null,\n textValue: string,\n worksheet: Worksheet | null | undefined,\n sheet: XlsxSheetData | null | undefined,\n batchedCells?: Map<string, BatchedCellData>\n) {\n const normalizedRuleType = rule.ruleType.trim().toLowerCase();\n const left = resolveConditionalFormulaComparable(rule.formulas?.[0] ?? \"\", worksheet, sheet, batchedCells);\n const right = resolveConditionalFormulaComparable(rule.formulas?.[1] ?? \"\", worksheet, sheet, batchedCells);\n\n if (normalizedRuleType === \"cellis\") {\n return resolveConditionalCellIsMatch(rule.operator ?? \"equal\", left, right, numericValue, textValue);\n }\n\n const normalizedCellText = textValue.trim().toLowerCase();\n const normalizedOperand = normalizeConditionalTextOperand(rule.text)\n || normalizeConditionalTextOperand(left);\n\n switch (normalizedRuleType) {\n case \"beginwith\":\n case \"beginswith\":\n return normalizedOperand.length > 0 && normalizedCellText.startsWith(normalizedOperand);\n case \"endwith\":\n case \"endswith\":\n return normalizedOperand.length > 0 && normalizedCellText.endsWith(normalizedOperand);\n case \"containstext\":\n return normalizedOperand.length > 0 && normalizedCellText.includes(normalizedOperand);\n case \"notcontainstext\":\n return normalizedOperand.length > 0 && !normalizedCellText.includes(normalizedOperand);\n default:\n return false;\n }\n}\n\ntype XlsxConditionalStyledRuleEntry = Extract<XlsxSheetData[\"conditionalFormatRules\"][number], { kind: \"styled\" }>;\n\n// Styled rules are usually a small subset of a sheet's conditional formats, or\n// absent entirely, and this lookup runs for every rendered cell, so the subset\n// is computed once per rule list instead of being filtered per cell.\nconst styledConditionalRulesByRuleList = new WeakMap<\n XlsxSheetData[\"conditionalFormatRules\"],\n XlsxConditionalStyledRuleEntry[]\n>();\n\nfunction getStyledConditionalRules(rules: XlsxSheetData[\"conditionalFormatRules\"]) {\n let styledRules = styledConditionalRulesByRuleList.get(rules);\n if (!styledRules) {\n styledRules = rules.filter((rule): rule is XlsxConditionalStyledRuleEntry => rule.kind === \"styled\");\n styledConditionalRulesByRuleList.set(rules, styledRules);\n }\n return styledRules;\n}\n\nfunction resolveConditionalCellStyleForCell(\n row: number,\n col: number,\n sheet: XlsxSheetData | null | undefined,\n readNumericValue: () => number | null,\n readTextValue: () => string,\n worksheet?: Worksheet | null,\n batchedCells?: Map<string, BatchedCellData>\n) {\n const rules = sheet?.conditionalFormatRules;\n if (!sheet || !rules || rules.length === 0) {\n return null;\n }\n\n const styledRules = getStyledConditionalRules(rules);\n if (styledRules.length === 0) {\n return null;\n }\n\n // Rules are sorted by ascending priority number, i.e. highest precedence\n // first. The cell's values are only read once a rule actually covers it.\n const matchedStyles: Array<Record<string, unknown>> = [];\n let numericValue: number | null | undefined;\n let textValue: string | undefined;\n for (const rule of styledRules) {\n if (!rule.ranges.some((range) => isCellInRange({ row, col }, range))) {\n continue;\n }\n\n if (numericValue === undefined) {\n numericValue = readNumericValue();\n }\n if (textValue === undefined) {\n textValue = readTextValue();\n }\n if (!resolveConditionalStyledRuleMatch(rule, numericValue, textValue, worksheet, sheet, batchedCells)) {\n continue;\n }\n\n matchedStyles.push(rule.style);\n if (rule.stopIfTrue) {\n break;\n }\n }\n\n if (matchedStyles.length === 0) {\n return null;\n }\n\n // Apply from lowest precedence to highest so that a higher-precedence rule\n // wins wherever two matching rules set the same property.\n let resolved: Record<string, unknown> | null = null;\n for (let index = matchedStyles.length - 1; index >= 0; index -= 1) {\n resolved = mergeResolvedCellStyle(resolved, matchedStyles[index]);\n }\n\n return resolved;\n}\n\nfunction buildConditionalIcon(iconSet: string, iconId: number): NonNullable<CellRenderData[\"conditionalIcon\"]> | null {\n switch (iconSet) {\n case \"NoIcons\":\n return null;\n case \"4TrafficLights\": {\n const definitions = [\n { backgroundColor: \"#111827\", borderColor: \"#6b7280\" },\n { backgroundColor: \"#22c55e\", borderColor: \"#166534\" },\n { backgroundColor: \"#ef4444\", borderColor: \"#991b1b\" },\n { backgroundColor: \"#facc15\", borderColor: \"#a16207\" }\n ];\n return definitions[iconId] ?? definitions[definitions.length - 1] ?? null;\n }\n case \"3TrafficLights1\":\n case \"3TrafficLights2\": {\n const definitions = [\n { backgroundColor: \"#22c55e\", borderColor: iconSet === \"3TrafficLights2\" ? \"#111827\" : \"#166534\" },\n { backgroundColor: \"#facc15\", borderColor: iconSet === \"3TrafficLights2\" ? \"#111827\" : \"#a16207\" },\n { backgroundColor: \"#ef4444\", borderColor: iconSet === \"3TrafficLights2\" ? \"#111827\" : \"#991b1b\" }\n ];\n return definitions[iconId] ?? definitions[definitions.length - 1] ?? null;\n }\n case \"4RedToBlack\": {\n const definitions = [\n { backgroundColor: \"#ef4444\", borderColor: \"#991b1b\" },\n { backgroundColor: \"#fca5a5\", borderColor: \"#b91c1c\" },\n { backgroundColor: \"#9ca3af\", borderColor: \"#4b5563\" },\n { backgroundColor: \"#111827\", borderColor: \"#6b7280\" }\n ];\n return definitions[iconId] ?? definitions[definitions.length - 1] ?? null;\n }\n case \"3Symbols\":\n return [\n { backgroundColor: \"#22c55e\", color: \"#ffffff\", glyph: \"✓\" },\n { backgroundColor: \"#facc15\", color: \"#111827\", glyph: \"!\" },\n { backgroundColor: \"#ef4444\", color: \"#ffffff\", glyph: \"×\" }\n ][iconId] ?? null;\n case \"3Symbols2\":\n return [\n { color: \"#16a34a\", glyph: \"✓\" },\n { color: \"#ca8a04\", glyph: \"!\" },\n { color: \"#dc2626\", glyph: \"×\" }\n ][iconId] ?? null;\n case \"3Signs\":\n return [\n { backgroundColor: \"#22c55e\", borderColor: \"#166534\" },\n { backgroundColor: \"#facc15\", borderColor: \"#a16207\", glyph: \"▲\", color: \"#111827\" },\n { backgroundColor: \"#ef4444\", borderColor: \"#991b1b\", glyph: \"◆\", color: \"#ffffff\" }\n ][iconId] ?? null;\n default:\n if (iconSet.includes(\"Arrows\")) {\n const arrowColors = iconSet.includes(\"Gray\")\n ? [\"#9ca3af\", \"#9ca3af\", \"#9ca3af\", \"#9ca3af\", \"#9ca3af\"]\n : [\"#16a34a\", \"#ca8a04\", \"#ca8a04\", \"#ca8a04\", \"#dc2626\"];\n const arrowRotations = iconSet.startsWith(\"3\")\n ? [-90, 0, 90]\n : iconSet.startsWith(\"4\")\n ? [-90, -35, 35, 90]\n : [-90, -35, 0, 35, 90];\n const resolvedIndex = Math.min(iconId, arrowColors.length - 1, arrowRotations.length - 1);\n return {\n borderColor: iconSet.includes(\"Gray\") ? \"#6b7280\" : darkenColor(arrowColors[resolvedIndex] ?? \"#16a34a\", 0.34),\n color: arrowColors[resolvedIndex],\n rotationDeg: arrowRotations[resolvedIndex] ?? 0,\n shape: \"arrow\"\n };\n }\n\n return {\n backgroundColor: \"#111827\",\n borderColor: \"#6b7280\"\n };\n }\n}\n\nfunction renderConditionalIcon(icon: NonNullable<CellRenderData[\"conditionalIcon\"]>, scale = 1) {\n const iconSize = 14 * scale;\n if (icon.shape === \"arrow\") {\n const fill = icon.color ?? \"#111827\";\n const stroke = icon.borderColor ?? darkenColor(fill, 0.32);\n return (\n <svg\n aria-hidden=\"true\"\n height={iconSize}\n style={{ display: \"block\" }}\n viewBox=\"0 0 16 16\"\n width={iconSize}\n >\n <g transform={`rotate(${icon.rotationDeg ?? 0} 8 8)`}>\n <path\n d=\"M2.5 8 L8.4 2.4 L8.4 5.2 L13.5 5.2 L13.5 10.8 L8.4 10.8 L8.4 13.6 Z\"\n fill={fill}\n stroke={stroke}\n strokeLinejoin=\"round\"\n strokeWidth={1.25}\n />\n </g>\n </svg>\n );\n }\n if (icon.glyph) {\n return (\n <span\n style={{\n alignItems: \"center\",\n color: icon.color ?? \"#111827\",\n display: \"inline-flex\",\n fontSize: 13 * scale,\n fontWeight: 700,\n height: iconSize,\n justifyContent: \"center\",\n lineHeight: 1,\n width: iconSize\n }}\n >\n {icon.glyph}\n </span>\n );\n }\n\n return (\n <span\n style={{\n backgroundColor: icon.backgroundColor ?? \"#111827\",\n border: icon.borderColor ? `1px solid ${icon.borderColor}` : \"none\",\n borderRadius: \"999px\",\n display: \"inline-block\",\n height: 12 * scale,\n width: 12 * scale\n }}\n />\n );\n}\n\nfunction isFormControlChecked(control: XlsxFormControl) {\n return control.state ? control.state === \"checked\" : Boolean(control.checked);\n}\n\nfunction isFormControlMixed(control: XlsxFormControl) {\n return control.state === \"mixed\";\n}\n\nfunction buildFormControlKindInput(control: XlsxFormControl): XlsxFormControlKindInput | null {\n switch (control.kind) {\n case \"button\":\n return { caption: control.caption ?? control.label ?? \"\", kind: \"button\" };\n case \"checkbox\":\n return {\n caption: control.caption ?? control.label ?? \"\",\n cellLink: control.linkedCell,\n kind: \"checkbox\",\n no3D: control.no3D ?? false,\n state: control.state ?? (control.checked ? \"checked\" : \"unchecked\")\n };\n case \"radio\":\n return {\n caption: control.caption ?? control.label ?? \"\",\n cellLink: control.linkedCell,\n kind: \"optionButton\",\n no3D: control.no3D ?? false,\n state: control.state === \"checked\" ? \"checked\" : \"unchecked\"\n };\n case \"label\":\n return { caption: control.caption ?? control.label ?? \"\", kind: \"label\" };\n case \"group-box\":\n return {\n caption: control.caption ?? control.label ?? \"\",\n kind: \"groupBox\",\n no3D: control.no3D ?? false\n };\n case \"listbox\":\n return {\n cellLink: control.linkedCell,\n inputRange: control.inputRange,\n kind: \"listBox\",\n no3D: control.no3D ?? false,\n selected: Array.isArray(control.selected) ? control.selected : control.selected === undefined ? [] : [control.selected],\n selection: control.selection ?? \"single\"\n };\n case \"dropdown\":\n return {\n cellLink: control.linkedCell,\n inputRange: control.inputRange,\n kind: \"dropdown\",\n lines: control.lines ?? 8,\n no3D: control.no3D ?? false,\n selected: typeof control.selected === \"number\" ? control.selected : undefined\n };\n case \"scrollbar\":\n return {\n cellLink: control.linkedCell,\n horizontal: control.horizontal ?? true,\n increment: control.increment ?? 1,\n kind: \"scrollbar\",\n max: control.max ?? 100,\n min: control.min ?? 0,\n page: control.page ?? 10,\n value: control.value ?? control.min ?? 0\n };\n case \"spinner\":\n return {\n cellLink: control.linkedCell,\n increment: control.increment ?? 1,\n kind: \"spinner\",\n max: control.max ?? 100,\n min: control.min ?? 0,\n value: control.value ?? control.min ?? 0\n };\n default:\n return null;\n }\n}\n\nfunction renderCheckboxControl(checked: boolean, palette: ViewerPalette, scale = 1, mixed = false) {\n const stroke = paletteIsDark(palette) ? \"#cbd5e1\" : \"#475569\";\n const fill = checked || mixed ? (paletteIsDark(palette) ? \"#60a5fa\" : \"#2563eb\") : \"transparent\";\n const check = paletteIsDark(palette) ? \"#020617\" : \"#ffffff\";\n return (\n <svg aria-hidden=\"true\" height={14 * scale} style={{ display: \"block\" }} viewBox=\"0 0 16 16\" width={14 * scale}>\n <rect fill={fill} height={11} rx={2} ry={2} stroke={stroke} strokeWidth={1.2} width={11} x={2.5} y={2.5} />\n {mixed ? (\n <path d=\"M5 8h6\" fill=\"none\" stroke={check} strokeLinecap=\"round\" strokeWidth={1.8} />\n ) : checked ? (\n <path\n d=\"M5 8.1 7.1 10.2 11.3 5.8\"\n fill=\"none\"\n stroke={check}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.8}\n />\n ) : null}\n </svg>\n );\n}\n\nfunction renderRadioControl(checked: boolean, palette: ViewerPalette, scale = 1) {\n const stroke = paletteIsDark(palette) ? \"#cbd5e1\" : \"#475569\";\n const dot = paletteIsDark(palette) ? \"#60a5fa\" : \"#2563eb\";\n return (\n <svg aria-hidden=\"true\" height={14 * scale} style={{ display: \"block\" }} viewBox=\"0 0 16 16\" width={14 * scale}>\n <circle cx={8} cy={8} fill=\"transparent\" r={5.5} stroke={stroke} strokeWidth={1.2} />\n {checked ? <circle cx={8} cy={8} fill={dot} r={2.75} /> : null}\n </svg>\n );\n}\n\nfunction resolveFormControlLabel(control: XlsxFormControl) {\n const label = control.label ?? control.name;\n if (!label) {\n return \"\";\n }\n\n const normalized = label.replace(/\\u00a0/g, \" \").replace(/\\s+/g, \" \").trim().toLowerCase();\n if (/^(option button|group box|check box|drop down|dropdown|list box|edit box|scroll bar|spinner|spin button|button)\\s+\\d+$/.test(normalized)) {\n return \"\";\n }\n\n return label.replace(/\\u00a0/g, \" \").replace(/^\\s+/, \"\");\n}\n\nfunction drawStaticShapeText(\n context: CanvasRenderingContext2D,\n shape: XlsxShape,\n left: number,\n top: number,\n width: number,\n height: number,\n zoomFactor: number\n) {\n if (shape.paragraphs.length === 0) {\n return;\n }\n\n const inset = shape.textBox?.insetPx;\n const paddingLeft = (inset?.left ?? 6) * zoomFactor;\n const paddingRight = (inset?.right ?? 6) * zoomFactor;\n const paddingTop = (inset?.top ?? 4) * zoomFactor;\n const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;\n const textLeft = left + paddingLeft;\n const textTop = top + paddingTop;\n const textWidth = Math.max(0, width - paddingLeft - paddingRight);\n const textHeight = Math.max(0, height - paddingTop - paddingBottom);\n if (textWidth <= 0 || textHeight <= 0) {\n return;\n }\n\n const lineMetrics = shape.paragraphs.map((paragraph) => {\n const lineHeight = Math.max(\n 12 * zoomFactor,\n ...paragraph.runs.map((run) => ((run.fontSizePt ?? 11) * 96 / 72) * zoomFactor * 1.2)\n );\n const widthPx = paragraph.runs.reduce((total, run) => {\n const fontSize = ((run.fontSizePt ?? 11) * 96 / 72) * zoomFactor;\n context.font = `${run.italic ? \"italic \" : \"\"}${run.bold ? \"700 \" : \"400 \"}${fontSize}px ${run.fontFamily ?? \"Calibri, sans-serif\"}`;\n return total + measureCanvasTextWidth(context, run.text);\n }, 0);\n return { lineHeight, widthPx };\n });\n const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);\n let y = textTop;\n if (shape.textBox?.verticalAlign === \"middle\") {\n y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);\n } else if (shape.textBox?.verticalAlign === \"bottom\") {\n y = textTop + Math.max(0, textHeight - totalTextHeight);\n }\n\n context.save();\n context.beginPath();\n context.rect(textLeft, textTop, textWidth, textHeight);\n context.clip();\n context.textBaseline = \"middle\";\n shape.paragraphs.forEach((paragraph, paragraphIndex) => {\n const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };\n let x = textLeft;\n const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? \"left\";\n if (align === \"center\") {\n x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);\n } else if (align === \"right\") {\n x = textLeft + Math.max(0, textWidth - metric.widthPx);\n }\n\n paragraph.runs.forEach((run) => {\n const fontSize = ((run.fontSizePt ?? 11) * 96 / 72) * zoomFactor;\n context.font = `${run.italic ? \"italic \" : \"\"}${run.bold ? \"700 \" : \"400 \"}${fontSize}px ${run.fontFamily ?? \"Calibri, sans-serif\"}`;\n context.fillStyle = run.color ?? \"#000000\";\n context.textAlign = \"left\";\n const textY = y + metric.lineHeight / 2;\n context.fillText(run.text, x, textY);\n if (run.underline && run.text.length > 0) {\n const textWidthPx = measureCanvasTextWidth(context, run.text);\n context.strokeStyle = run.color ?? \"#000000\";\n context.lineWidth = Math.max(1, zoomFactor * 0.75);\n context.beginPath();\n context.moveTo(x, textY + Math.max(2, fontSize * 0.28));\n context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));\n context.stroke();\n }\n x += measureCanvasTextWidth(context, run.text);\n });\n y += metric.lineHeight;\n });\n context.restore();\n}\n\nfunction drawStaticShape(\n context: CanvasRenderingContext2D,\n shape: XlsxShape,\n rect: XlsxImageRect,\n zoomFactor: number\n) {\n const fillColor = shape.fill?.none ? \"transparent\" : (shape.fill?.color ?? \"transparent\");\n const strokeColor = shape.stroke?.none ? \"transparent\" : (shape.stroke?.color ?? \"transparent\");\n const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === \"line\" ? 2 : 1)) * zoomFactor);\n const vectorShape = resolveShapeVector(shape);\n const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);\n\n context.save();\n context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);\n if (shape.rotationDeg) {\n context.rotate((shape.rotationDeg * Math.PI) / 180);\n }\n context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);\n context.globalAlpha *= opacity;\n context.lineWidth = lineWidth;\n context.strokeStyle = strokeColor;\n context.fillStyle = fillColor;\n applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);\n\n const localLeft = -rect.width / 2;\n const localTop = -rect.height / 2;\n if (vectorShape && typeof Path2D !== \"undefined\") {\n context.save();\n context.translate(localLeft, localTop);\n context.scale(\n rect.width / Math.max(1, vectorShape.viewBox.width),\n rect.height / Math.max(1, vectorShape.viewBox.height)\n );\n const path = getCachedCanvasPath2D(vectorShape.path);\n if (!path) {\n context.restore();\n context.restore();\n return;\n }\n if (fillColor !== \"transparent\") {\n context.fill(path);\n }\n if (strokeColor !== \"transparent\" && lineWidth > 0) {\n context.stroke(path);\n }\n context.restore();\n } else if (shape.geometry === \"ellipse\") {\n context.beginPath();\n context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);\n if (fillColor !== \"transparent\") {\n context.fill();\n }\n if (strokeColor !== \"transparent\" && lineWidth > 0) {\n context.stroke();\n }\n } else {\n const radius = shape.geometry === \"roundRect\" ? 12 * zoomFactor : 0;\n drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);\n if (fillColor !== \"transparent\") {\n context.fill();\n }\n if (strokeColor !== \"transparent\" && lineWidth > 0) {\n context.stroke();\n }\n }\n\n drawStaticShapeText(context, shape, localLeft, localTop, rect.width, rect.height, zoomFactor);\n context.restore();\n}\n\nfunction drawStaticFormControl(\n context: CanvasRenderingContext2D,\n control: XlsxFormControl,\n rect: XlsxImageRect,\n palette: ViewerPalette,\n zoomFactor: number,\n sheetSurface = SHEET_SURFACE\n) {\n const label = resolveFormControlLabel(control);\n const stroke = paletteIsDark(palette) ? \"#cbd5e1\" : \"#475569\";\n const textColor = control.textColor ?? \"#000000\";\n const fontSizePx = Math.max(9 * zoomFactor, ((control.fontSizePt ?? 9) * 96 / 72) * zoomFactor);\n const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));\n const controlChecked = isFormControlChecked(control);\n const controlMixed = isFormControlMixed(control);\n context.save();\n context.font = `400 ${fontSizePx}px ${control.fontFamily ?? \"Calibri, sans-serif\"}`;\n context.textBaseline = \"middle\";\n context.fillStyle = textColor;\n context.strokeStyle = stroke;\n context.lineWidth = Math.max(1, zoomFactor);\n\n if (control.kind === \"group-box\") {\n const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;\n drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);\n context.stroke();\n if (label) {\n context.fillStyle = sheetSurface;\n const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);\n context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);\n context.fillStyle = textColor;\n context.textAlign = \"left\";\n context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);\n }\n context.restore();\n return;\n }\n\n if (control.kind === \"button\" || control.kind === \"dropdown\" || control.kind === \"editbox\" || control.kind === \"listbox\" || control.kind === \"scrollbar\" || control.kind === \"spinner\" || control.kind === \"unknown\") {\n if (control.kind === \"button\") {\n const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);\n gradient.addColorStop(0, \"#f8fafc\");\n gradient.addColorStop(1, \"#e2e8f0\");\n context.fillStyle = gradient;\n } else {\n context.fillStyle = \"transparent\";\n }\n drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === \"button\" ? 4 * zoomFactor : 2 * zoomFactor);\n if (control.kind === \"button\") {\n context.fill();\n }\n context.stroke();\n }\n\n let textLeft = rect.left + 2 * zoomFactor;\n const textRightInset = control.kind === \"dropdown\" ? 18 * zoomFactor : 6 * zoomFactor;\n if (control.kind === \"checkbox\") {\n context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);\n if (controlChecked || controlMixed) {\n context.fillStyle = paletteIsDark(palette) ? \"#60a5fa\" : \"#2563eb\";\n context.fillRect(rect.left + 2 * zoomFactor + 1.5, rect.top + (rect.height - iconSize) / 2 + 1.5, Math.max(0, iconSize - 3), Math.max(0, iconSize - 3));\n context.strokeStyle = paletteIsDark(palette) ? \"#020617\" : \"#ffffff\";\n context.beginPath();\n if (controlMixed) {\n context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.25, rect.top + rect.height / 2);\n context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.75, rect.top + rect.height / 2);\n } else {\n context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);\n context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);\n context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);\n }\n context.stroke();\n }\n textLeft += iconSize + 4 * zoomFactor;\n } else if (control.kind === \"radio\") {\n context.beginPath();\n context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);\n context.stroke();\n if (controlChecked) {\n context.fillStyle = paletteIsDark(palette) ? \"#60a5fa\" : \"#2563eb\";\n context.beginPath();\n context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);\n context.fill();\n }\n textLeft += iconSize + 4 * zoomFactor;\n }\n\n if (label) {\n const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);\n const text = truncateCanvasText(context, label, maxTextWidth);\n context.fillStyle = textColor;\n context.textAlign = control.textAlign === \"right\" ? \"right\" : control.textAlign === \"center\" ? \"center\" : \"left\";\n const textX = context.textAlign === \"right\"\n ? rect.left + rect.width - textRightInset\n : context.textAlign === \"center\"\n ? textLeft + maxTextWidth / 2\n : textLeft;\n context.fillText(text, textX, rect.top + rect.height / 2);\n }\n if (control.kind === \"dropdown\") {\n context.fillStyle = textColor;\n context.textAlign = \"center\";\n context.fillText(\"▼\", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);\n }\n context.restore();\n}\n\nfunction resolveConditionalDataBarForCell(\n row: number,\n col: number,\n worksheet: Worksheet,\n sheet: XlsxSheetData | null | undefined,\n metricsCache: Map<string, number[]>\n): CellRenderData[\"conditionalDataBar\"] {\n const rules = sheet?.conditionalFormatRules ?? [];\n const matchingRule = rules.find(\n (rule): rule is Extract<XlsxSheetData[\"conditionalFormatRules\"][number], { kind: \"dataBar\" }> =>\n rule.kind === \"dataBar\" && rule.ranges.some((range) => isCellInRange({ row, col }, range))\n );\n if (!matchingRule) {\n return null;\n }\n\n const numericValue = getCellNumericValue(worksheet, row, col);\n if (numericValue === null) {\n return null;\n }\n\n const cacheKey = buildConditionalFormatRuleKey(matchingRule);\n let ruleValues = metricsCache.get(cacheKey);\n if (!ruleValues) {\n ruleValues = matchingRule.ranges.flatMap((range) => {\n const values: number[] = [];\n for (let targetRow = range.start.row; targetRow <= range.end.row; targetRow += 1) {\n for (let targetCol = range.start.col; targetCol <= range.end.col; targetCol += 1) {\n const value = getCellNumericValue(worksheet, targetRow, targetCol);\n if (value !== null) {\n values.push(value);\n }\n }\n }\n return values;\n });\n metricsCache.set(cacheKey, ruleValues);\n }\n\n const minValue = resolveConditionalRuleThreshold(matchingRule.cfvos[0], ruleValues);\n const maxValue = resolveConditionalRuleThreshold(matchingRule.cfvos[matchingRule.cfvos.length - 1], ruleValues);\n if (minValue === null || maxValue === null) {\n return null;\n }\n\n const minLength = Number.isFinite(matchingRule.minLength ?? Number.NaN) ? (matchingRule.minLength ?? 0) : 0;\n const maxLength = Number.isFinite(matchingRule.maxLength ?? Number.NaN) ? (matchingRule.maxLength ?? 100) : 100;\n const span = maxValue - minValue;\n const ratio = span === 0 ? (numericValue >= maxValue ? 1 : 0) : (numericValue - minValue) / span;\n const widthPercent = minLength + Math.max(0, Math.min(1, ratio)) * (maxLength - minLength);\n const color = resolveWorkbookColor(matchingRule.color, sheet?.themePalette);\n if (!color || widthPercent <= 0) {\n return null;\n }\n\n return {\n axisColor: resolveWorkbookColor(matchingRule.axisColor, sheet?.themePalette) ?? undefined,\n border: matchingRule.border ?? undefined,\n borderColor: resolveWorkbookColor(matchingRule.borderColor, sheet?.themePalette) ?? undefined,\n color,\n gradient: matchingRule.gradient ?? undefined,\n negativeBorderColor: resolveWorkbookColor(matchingRule.negativeBorderColor, sheet?.themePalette) ?? undefined,\n negativeFillColor: resolveWorkbookColor(matchingRule.negativeFillColor, sheet?.themePalette) ?? undefined,\n widthPercent\n };\n}\n\nfunction resolveConditionalColorScaleForCell(\n row: number,\n col: number,\n worksheet: Worksheet,\n sheet: XlsxSheetData | null | undefined,\n metricsCache: Map<string, number[]>\n): CellRenderData[\"conditionalColorScale\"] {\n const rules = sheet?.conditionalFormatRules ?? [];\n const matchingRule = rules.find(\n (rule): rule is Extract<XlsxSheetData[\"conditionalFormatRules\"][number], { kind: \"colorScale\" }> =>\n rule.kind === \"colorScale\" && rule.ranges.some((range) => isCellInRange({ row, col }, range))\n );\n if (!matchingRule) {\n return null;\n }\n\n const numericValue = getCellNumericValue(worksheet, row, col);\n if (numericValue === null) {\n return null;\n }\n\n const cacheKey = buildConditionalFormatRuleKey(matchingRule);\n let ruleValues = metricsCache.get(cacheKey);\n if (!ruleValues) {\n ruleValues = matchingRule.ranges.flatMap((range) => {\n const values: number[] = [];\n for (let targetRow = range.start.row; targetRow <= range.end.row; targetRow += 1) {\n for (let targetCol = range.start.col; targetCol <= range.end.col; targetCol += 1) {\n const value = getCellNumericValue(worksheet, targetRow, targetCol);\n if (value !== null) {\n values.push(value);\n }\n }\n }\n return values;\n });\n metricsCache.set(cacheKey, ruleValues);\n }\n\n const colors = matchingRule.colors\n .map((color) => resolveWorkbookColor(color, sheet?.themePalette))\n .filter((color): color is string => typeof color === \"string\");\n const thresholds = matchingRule.cfvos\n .map((threshold) => resolveConditionalRuleThreshold(threshold, ruleValues))\n .filter((value): value is number => typeof value === \"number\" && Number.isFinite(value));\n if (colors.length < 2 || thresholds.length < 2) {\n return null;\n }\n\n const segments = Math.min(colors.length, thresholds.length) - 1;\n let resolvedColor = colors[colors.length - 1];\n for (let index = 0; index < segments; index += 1) {\n const startValue = thresholds[index] ?? thresholds[0];\n const endValue = thresholds[index + 1] ?? thresholds[thresholds.length - 1];\n if (numericValue <= endValue || index === segments - 1) {\n const ratio = endValue === startValue ? 1 : (numericValue - startValue) / (endValue - startValue);\n resolvedColor = mixRgbColor(colors[index] ?? colors[0], colors[index + 1] ?? colors[colors.length - 1], Math.max(0, Math.min(1, ratio)));\n break;\n }\n }\n\n return { color: resolvedColor };\n}\n\nfunction resolveConditionalIconForCell(\n row: number,\n col: number,\n worksheet: Worksheet,\n sheet: XlsxSheetData | null | undefined,\n metricsCache: Map<string, number[]>\n): CellRenderData[\"conditionalIcon\"] {\n const rules = sheet?.conditionalFormatRules ?? [];\n const matchingRule = rules.find(\n (rule): rule is Extract<XlsxSheetData[\"conditionalFormatRules\"][number], { kind: \"iconSet\" }> =>\n rule.kind === \"iconSet\" && rule.ranges.some((range) => isCellInRange({ row, col }, range))\n );\n if (!matchingRule) {\n return null;\n }\n\n const numericValue = getCellNumericValue(worksheet, row, col);\n if (numericValue === null) {\n return null;\n }\n\n const cacheKey = buildConditionalFormatRuleKey(matchingRule);\n let ruleValues = metricsCache.get(cacheKey);\n if (!ruleValues) {\n ruleValues = matchingRule.ranges.flatMap((range) => {\n const values: number[] = [];\n for (let targetRow = range.start.row; targetRow <= range.end.row; targetRow += 1) {\n for (let targetCol = range.start.col; targetCol <= range.end.col; targetCol += 1) {\n const value = getCellNumericValue(worksheet, targetRow, targetCol);\n if (value !== null) {\n values.push(value);\n }\n }\n }\n return values;\n });\n metricsCache.set(cacheKey, ruleValues);\n }\n\n const thresholds = matchingRule.cfvos.map((threshold) => resolveConditionalRuleThreshold(threshold, ruleValues));\n let selectedIndex = 0;\n thresholds.forEach((threshold, index) => {\n if (threshold !== null && numericValue >= threshold) {\n selectedIndex = index;\n }\n });\n\n const iconIndex = matchingRule.reverse\n ? Math.max(0, matchingRule.icons.length - 1 - selectedIndex)\n : selectedIndex;\n const icon = matchingRule.icons[Math.min(iconIndex, matchingRule.icons.length - 1)];\n return icon ? buildConditionalIcon(icon.iconSet, icon.iconId) : null;\n}\n\nfunction resolveCellDataValidation(\n row: number,\n col: number,\n sheet: XlsxSheetData | null | undefined\n): CellRenderData[\"validation\"] {\n const validation = sheet?.dataValidations.find((entry) => entry.ranges.some((range) => isCellInRange({ row, col }, range)));\n if (!validation) {\n return null;\n }\n\n const messageParts = [validation.inputMessage, validation.errorMessage].filter(\n (value): value is string => typeof value === \"string\" && value.trim().length > 0\n );\n\n return {\n message: messageParts.join(\"\\n\"),\n showDropdown: validation.showDropdown !== false,\n validationType: validation.validationType\n };\n}\n\nfunction resolveCellContentJustify(verticalAlign: React.CSSProperties[\"verticalAlign\"]) {\n if (verticalAlign === \"middle\") {\n return \"center\";\n }\n if (verticalAlign === \"top\") {\n return \"flex-start\";\n }\n return \"flex-end\";\n}\n\nfunction resolveCellContentAlignItems(textAlign: React.CSSProperties[\"textAlign\"]) {\n if (textAlign === \"center\") {\n return \"center\";\n }\n if (textAlign === \"right\" || textAlign === \"end\") {\n return \"flex-end\";\n }\n return \"flex-start\";\n}\n\nfunction resolveFirstUsedVisibleIndex(visibleIndices: number[], minUsedIndex: number) {\n if (visibleIndices.length === 0) {\n return -1;\n }\n if (minUsedIndex <= 0) {\n return visibleIndices[0] ?? -1;\n }\n\n const firstVisibleUsed = visibleIndices.find((index) => index >= minUsedIndex);\n return firstVisibleUsed ?? visibleIndices[0] ?? -1;\n}\n\nfunction resolveLastUsedVisibleIndex(visibleIndices: number[], maxUsedIndex: number) {\n if (visibleIndices.length === 0) {\n return -1;\n }\n if (maxUsedIndex < 0) {\n return visibleIndices[visibleIndices.length - 1] ?? -1;\n }\n\n for (let index = visibleIndices.length - 1; index >= 0; index -= 1) {\n const visibleIndex = visibleIndices[index];\n if (visibleIndex !== undefined && visibleIndex <= maxUsedIndex) {\n return visibleIndex;\n }\n }\n\n return visibleIndices[visibleIndices.length - 1] ?? -1;\n}\n\ntype RenderedAxisItem = {\n end: number;\n index: number;\n key: React.Key;\n size: number;\n start: number;\n};\n\ntype RenderedColumn = {\n actualCol: number;\n key: React.Key;\n size: number;\n virtualIndex: number;\n};\n\ntype GridRowProps = {\n actualRow: number;\n editingCell: XlsxCellAddress | null;\n editingValue: string;\n frozenRowHeaderZIndex: number;\n getCellData: (row: number, col: number) => CellRenderData;\n headerLabelLiveScale: number;\n leadingSpacerWidth: number;\n onCellDoubleClick: (cell: XlsxCellAddress) => void;\n onCellClick: (cell: XlsxCellAddress, cellData: CellRenderData) => void;\n onCellPointerDown: (event: React.PointerEvent<HTMLTableCellElement>, cell: XlsxCellAddress) => void;\n onEditingBlur: () => void;\n onEditingCancel: () => void;\n onEditingCommit: () => void;\n onEditingNavigate: (key: string, value: string) => void;\n onEditingValueChange: (value: string) => void;\n onRowHeaderRef: (actualRow: number, element: HTMLTableCellElement | null) => void;\n onRowPointerDown: (event: React.PointerEvent<HTMLTableCellElement>, actualRow: number) => void;\n onRowResizePointerDown: (event: React.PointerEvent<HTMLDivElement>, actualRow: number, rowHeight: number) => void;\n palette: ViewerPalette;\n readOnly: boolean;\n renderCellAdornment?: (cell: XlsxCellAddress) => React.ReactNode;\n rowHeight: number;\n rowHeaderZIndex: number;\n rowHeaderWidth: number;\n stickyLeftByCol: Map<number, number>;\n stickyTop?: number;\n trailingSpacerWidth: number;\n visibleCols: RenderedColumn[];\n zoomFactor: number;\n};\n\nfunction GridRow({\n actualRow,\n editingCell,\n editingValue,\n frozenRowHeaderZIndex,\n getCellData,\n headerLabelLiveScale,\n leadingSpacerWidth,\n onCellClick,\n onCellDoubleClick,\n onCellPointerDown,\n onEditingBlur,\n onEditingCancel,\n onEditingCommit,\n onEditingNavigate,\n onEditingValueChange,\n onRowHeaderRef,\n onRowPointerDown,\n onRowResizePointerDown,\n palette,\n readOnly,\n renderCellAdornment,\n rowHeight,\n rowHeaderZIndex,\n rowHeaderWidth,\n stickyLeftByCol,\n stickyTop,\n trailingSpacerWidth,\n visibleCols,\n zoomFactor\n}: GridRowProps) {\n const gutterSeparatorShadow = `inset -1px 0 0 ${palette.border}, inset 0 -1px 0 ${palette.border}`;\n\n return (\n <tr data-xlsx-row={actualRow} style={{ height: rowHeight }}>\n <td\n ref={(element) => onRowHeaderRef(actualRow, element)}\n onPointerDown={(event) => onRowPointerDown(event, actualRow)}\n style={{\n backgroundColor: palette.rowHeaderSurface,\n borderBottom: \"none\",\n borderRight: \"none\",\n boxSizing: \"border-box\",\n boxShadow: gutterSeparatorShadow,\n color: palette.headerText,\n fontSize: scaleCssLengthExpression(\"11px\", zoomFactor),\n height: rowHeight,\n left: 0,\n maxHeight: rowHeight,\n minWidth: rowHeaderWidth,\n padding: scaleCssLengthExpression(\"2px 4px\", zoomFactor),\n position: \"sticky\",\n top: stickyTop,\n textAlign: \"center\",\n userSelect: \"none\",\n width: rowHeaderWidth,\n zIndex: stickyTop !== undefined ? frozenRowHeaderZIndex : rowHeaderZIndex\n }}\n >\n <div style={{ position: \"relative\" }}>\n <span\n style={{\n display: \"inline-block\",\n transform: headerLabelLiveScale !== 1 ? `scale(${1 / headerLabelLiveScale})` : undefined,\n transformOrigin: \"center center\"\n }}\n >\n {actualRow + 1}\n </span>\n <div\n onPointerDown={(event) => onRowResizePointerDown(event, actualRow, rowHeight)}\n style={{\n backgroundColor: \"transparent\",\n bottom: -8 * zoomFactor,\n cursor: \"row-resize\",\n height: 16 * zoomFactor,\n left: 0,\n position: \"absolute\",\n width: \"100%\",\n zIndex: 5\n }}\n />\n </div>\n </td>\n {leadingSpacerWidth > 0 ? (\n <td\n aria-hidden=\"true\"\n style={{\n backgroundColor: \"transparent\",\n border: \"none\",\n padding: 0,\n width: leadingSpacerWidth\n }}\n />\n ) : null}\n {visibleCols.map(({ actualCol, key }) => {\n const cellData = getCellData(actualRow, actualCol);\n if (cellData.isMergedSecondary) {\n return null;\n }\n\n const cell = { row: actualRow, col: actualCol };\n const isEditing = isSameCell(editingCell, cell);\n const isSpilling = Boolean(cellData.spillWidth && cellData.spillWidth > 0);\n const adornment = renderCellAdornment ? renderCellAdornment(cell) : null;\n const stickyLeft = stickyLeftByCol.get(actualCol);\n const validationRight = adornment ? 24 : 4;\n const cellStyle: React.CSSProperties = {\n ...cellData.style,\n boxSizing: \"border-box\",\n cursor: isEditing ? \"text\" : cellData.hyperlink ? \"pointer\" : \"cell\"\n };\n\n if (!cellData.rowSpan) {\n cellStyle.height = rowHeight;\n cellStyle.maxHeight = rowHeight;\n }\n\n if (isSpilling) {\n cellStyle.position = \"relative\";\n cellStyle.zIndex = 2;\n }\n if (cellData.conditionalColorScale) {\n cellStyle.backgroundColor = cellData.conditionalColorScale.color;\n }\n if (cellData.conditionalColorScale || cellData.conditionalDataBar || cellData.conditionalIcon) {\n cellStyle.position = \"relative\";\n }\n if (cellData.chartHighlight) {\n cellStyle.position = cellStyle.position ?? \"relative\";\n }\n if (cellData.isTableHeader) {\n cellStyle.position = \"relative\";\n cellStyle.zIndex = Math.max(Number(cellStyle.zIndex ?? 0), 4);\n }\n if (stickyTop !== undefined) {\n cellStyle.position = \"sticky\";\n cellStyle.top = stickyTop;\n cellStyle.zIndex = Math.max(Number(cellStyle.zIndex ?? 0), 28);\n }\n if (stickyLeft !== undefined) {\n cellStyle.left = stickyLeft;\n cellStyle.position = \"sticky\";\n cellStyle.zIndex = Math.max(Number(cellStyle.zIndex ?? 0), stickyTop !== undefined ? 30 : 24);\n }\n if (isSpilling) {\n cellStyle.overflow = \"visible\";\n cellStyle.textOverflow = \"clip\";\n cellStyle.position = cellStyle.position ?? \"relative\";\n cellStyle.zIndex = Math.max(Number(cellStyle.zIndex ?? 0), stickyTop !== undefined ? 32 : 6);\n }\n if (isEditing) {\n cellStyle.padding = 0;\n }\n\n const cellContentStyle: React.CSSProperties = {\n alignItems: \"stretch\",\n display: \"flex\",\n flexDirection: \"column\",\n font: \"inherit\",\n height: \"100%\",\n justifyContent: resolveCellContentJustify(cellStyle.verticalAlign),\n maxHeight: cellData.rowSpan ? undefined : rowHeight,\n minHeight: 0,\n overflow: \"hidden\",\n pointerEvents: \"none\",\n textAlign: \"inherit\",\n textDecoration: \"inherit\",\n textOverflow: \"inherit\",\n whiteSpace: \"inherit\",\n width: \"100%\",\n wordBreak: \"inherit\"\n };\n const usesWrappedText = cellData.style.whiteSpace === \"pre-wrap\" || cellData.value.includes(\"\\n\");\n const wrappedTextStyle = usesWrappedText\n ? {\n display: \"block\",\n flex: \"0 0 auto\",\n maxWidth: \"100%\",\n minWidth: 0\n } satisfies React.CSSProperties\n : null;\n if (usesWrappedText) {\n cellContentStyle.overflow = \"clip\";\n }\n const trailingInset = adornment ? 20 : 0;\n if (cellData.conditionalDataBar) {\n cellContentStyle.position = \"relative\";\n cellContentStyle.zIndex = 1;\n }\n if (cellData.chartHighlight) {\n cellContentStyle.position = cellContentStyle.position ?? \"relative\";\n cellContentStyle.zIndex = Math.max(Number(cellContentStyle.zIndex ?? 0), 1);\n }\n if (trailingInset > 0) {\n cellContentStyle.paddingRight = (trailingInset + 4) * zoomFactor;\n }\n if (cellData.conditionalIcon) {\n cellContentStyle.boxSizing = \"border-box\";\n cellContentStyle.paddingLeft = 18 * zoomFactor;\n cellContentStyle.position = \"relative\";\n cellContentStyle.zIndex = 1;\n }\n if (cellData.sparkline || cellData.checkboxState !== null) {\n cellContentStyle.alignItems = \"center\";\n cellContentStyle.justifyContent = \"center\";\n }\n if (cellData.textRotationDeg) {\n cellContentStyle.alignItems = resolveCellContentAlignItems(cellStyle.textAlign);\n cellContentStyle.overflow = \"visible\";\n }\n if (cellData.shrinkToFitFontSizePx) {\n cellContentStyle.fontSize = cellData.shrinkToFitFontSizePx;\n cellContentStyle.lineHeight = `${resolveCanvasLineHeight(cellData.style, cellData.shrinkToFitFontSizePx)}px`;\n }\n const rotatedTextWidth = cellData.textRotationDeg ? measureTextWidth(cellData.value, cellData.style) : 0;\n const rotatedTextHeight = resolveCanvasLineHeight(cellData.style, 12 * zoomFactor);\n const rotatedTextBounds = cellData.textRotationDeg\n ? resolveRotatedTextBounds(rotatedTextWidth, rotatedTextHeight, cellData.textRotationDeg)\n : null;\n const rotatedTextStyle = cellData.textRotationDeg\n ? {\n display: \"inline-block\",\n left: \"50%\",\n lineHeight: `${rotatedTextHeight}px`,\n maxWidth: \"none\",\n position: \"absolute\",\n top: \"50%\",\n transform: `translate(-50%, -50%) rotate(${cellData.textRotationDeg}deg)`,\n transformOrigin: \"center center\",\n whiteSpace: \"nowrap\",\n width: \"max-content\"\n } satisfies React.CSSProperties\n : null;\n const title = [cellData.hyperlink?.tooltip, cellData.validation?.message, cellData.value]\n .filter((value, index, values): value is string => typeof value === \"string\" && value.length > 0 && values.indexOf(value) === index)\n .join(\"\\n\");\n\n return (\n <td\n data-xlsx-cell={`${actualRow}:${actualCol}`}\n key={key}\n colSpan={cellData.colSpan}\n rowSpan={cellData.rowSpan}\n onDoubleClick={() => {\n if (readOnly) {\n return;\n }\n\n onCellDoubleClick(cell);\n }}\n onClick={() => onCellClick(cell, cellData)}\n onPointerDown={(event) => onCellPointerDown(event, cell)}\n style={cellStyle}\n title={title}\n >\n {cellData.chartHighlight ? (\n <div\n aria-hidden=\"true\"\n style={{\n backgroundColor: cellData.chartHighlight.fillColor,\n borderBottom: cellData.chartHighlight.borderBottom ? `${Math.max(1, zoomFactor)}px solid ${cellData.chartHighlight.strokeColor}` : undefined,\n borderLeft: cellData.chartHighlight.borderLeft ? `${Math.max(1, zoomFactor)}px solid ${cellData.chartHighlight.strokeColor}` : undefined,\n borderRight: cellData.chartHighlight.borderRight ? `${Math.max(1, zoomFactor)}px solid ${cellData.chartHighlight.strokeColor}` : undefined,\n borderTop: cellData.chartHighlight.borderTop ? `${Math.max(1, zoomFactor)}px solid ${cellData.chartHighlight.strokeColor}` : undefined,\n boxSizing: \"border-box\",\n inset: Math.max(1, zoomFactor * 0.5),\n pointerEvents: \"none\",\n position: \"absolute\"\n }}\n />\n ) : null}\n {cellData.conditionalDataBar ? (\n <div\n aria-hidden=\"true\"\n style={{\n alignItems: \"center\",\n bottom: 4 * zoomFactor,\n display: \"flex\",\n left: 4 * zoomFactor,\n pointerEvents: \"none\",\n position: \"absolute\",\n right: 4 * zoomFactor,\n top: 4 * zoomFactor,\n zIndex: 0\n }}\n >\n <div\n style={{\n background: cellData.conditionalDataBar.gradient === false\n ? cellData.conditionalDataBar.color\n : `linear-gradient(90deg, ${lightenColor(cellData.conditionalDataBar.color, 0.28)} 0%, ${cellData.conditionalDataBar.color} 100%)`,\n border: cellData.conditionalDataBar.border !== false && cellData.conditionalDataBar.borderColor\n ? `1px solid ${cellData.conditionalDataBar.borderColor}`\n : \"none\",\n borderRadius: 2,\n height: \"100%\",\n opacity: 1,\n width: `${cellData.conditionalDataBar.widthPercent}%`\n }}\n />\n </div>\n ) : null}\n {adornment}\n {cellData.conditionalIcon ? (\n <div\n aria-hidden=\"true\"\n style={{\n alignItems: \"center\",\n display: \"inline-flex\",\n justifyContent: \"center\",\n pointerEvents: \"none\",\n position: \"absolute\",\n left: 4 * zoomFactor,\n top: \"50%\",\n transform: \"translateY(-50%)\",\n zIndex: 2\n }}\n >\n {renderConditionalIcon(cellData.conditionalIcon, zoomFactor)}\n </div>\n ) : null}\n {isEditing ? (\n <input\n autoFocus\n onBlur={onEditingBlur}\n onChange={(event) => onEditingValueChange(event.target.value)}\n onKeyDown={(event) => {\n event.stopPropagation();\n if (event.key === \"Enter\") {\n event.preventDefault();\n onEditingCommit();\n return;\n }\n\n if (event.key === \"Escape\") {\n event.preventDefault();\n onEditingCancel();\n return;\n }\n\n if (\n event.key === \"ArrowDown\" ||\n event.key === \"ArrowLeft\" ||\n event.key === \"ArrowRight\" ||\n event.key === \"ArrowUp\"\n ) {\n event.preventDefault();\n onEditingNavigate(event.key, event.currentTarget.value);\n }\n }}\n style={{\n backgroundColor: \"transparent\",\n border: 0,\n color: \"inherit\",\n font: \"inherit\",\n height: \"100%\",\n margin: 0,\n outline: \"none\",\n padding: scaleCssLengthExpression(DEFAULT_CELL_PADDING, zoomFactor),\n width: \"100%\"\n }}\n value={editingValue}\n />\n ) : isSpilling ? (\n <div\n style={{\n left: cellData.spillOffsetX ?? 0,\n maxWidth: cellData.spillWidth,\n overflow: \"visible\",\n pointerEvents: \"none\",\n position: \"relative\",\n whiteSpace: \"inherit\",\n width: cellData.spillWidth\n }}\n >\n {cellData.value}\n </div>\n ) : cellData.sparkline ? (\n <div\n style={{\n alignItems: \"center\",\n display: \"flex\",\n height: \"100%\",\n justifyContent: \"center\",\n pointerEvents: \"none\",\n width: \"100%\"\n }}\n >\n {renderSparkline(cellData.sparkline.config, cellData.sparkline.values, palette)}\n </div>\n ) : cellData.checkboxState != null ? (\n <div\n style={{\n alignItems: \"center\",\n display: \"flex\",\n height: \"100%\",\n justifyContent: \"center\",\n pointerEvents: \"none\",\n width: \"100%\"\n }}\n >\n {renderCheckboxControl(cellData.checkboxState, palette, zoomFactor)}\n </div>\n ) : (\n <div style={cellContentStyle}>\n {rotatedTextStyle\n ? <span style={{ ...rotatedTextBounds, flex: \"0 0 auto\", position: \"relative\" }}><span style={rotatedTextStyle}>{cellData.value}</span></span>\n : wrappedTextStyle\n ? <span style={wrappedTextStyle}>{cellData.value}</span>\n : cellData.value}\n </div>\n )}\n </td>\n );\n })}\n {trailingSpacerWidth > 0 ? (\n <td\n aria-hidden=\"true\"\n style={{\n backgroundColor: \"transparent\",\n border: \"none\",\n padding: 0,\n width: trailingSpacerWidth\n }}\n />\n ) : null}\n </tr>\n );\n}\n\nconst MemoGridRow = React.memo(GridRow, (prev, next) => {\n if (\n prev.actualRow !== next.actualRow ||\n prev.rowHeight !== next.rowHeight ||\n prev.headerLabelLiveScale !== next.headerLabelLiveScale ||\n prev.palette !== next.palette ||\n prev.readOnly !== next.readOnly ||\n prev.visibleCols !== next.visibleCols ||\n prev.leadingSpacerWidth !== next.leadingSpacerWidth ||\n prev.rowHeaderWidth !== next.rowHeaderWidth ||\n prev.stickyLeftByCol !== next.stickyLeftByCol ||\n prev.stickyTop !== next.stickyTop ||\n prev.trailingSpacerWidth !== next.trailingSpacerWidth ||\n prev.zoomFactor !== next.zoomFactor ||\n prev.getCellData !== next.getCellData ||\n prev.onCellClick !== next.onCellClick ||\n prev.onCellDoubleClick !== next.onCellDoubleClick ||\n prev.onCellPointerDown !== next.onCellPointerDown ||\n prev.onEditingBlur !== next.onEditingBlur ||\n prev.onEditingCancel !== next.onEditingCancel ||\n prev.onEditingCommit !== next.onEditingCommit ||\n prev.onEditingNavigate !== next.onEditingNavigate ||\n prev.onEditingValueChange !== next.onEditingValueChange ||\n prev.onRowHeaderRef !== next.onRowHeaderRef ||\n prev.onRowPointerDown !== next.onRowPointerDown ||\n prev.onRowResizePointerDown !== next.onRowResizePointerDown ||\n prev.renderCellAdornment !== next.renderCellAdornment\n ) {\n return false;\n }\n\n const prevEditingCol = prev.editingCell?.row === prev.actualRow ? prev.editingCell.col : -1;\n const nextEditingCol = next.editingCell?.row === next.actualRow ? next.editingCell.col : -1;\n if (prevEditingCol !== nextEditingCol) {\n return false;\n }\n\n if (prevEditingCol !== -1 && prev.editingValue !== next.editingValue) {\n return false;\n }\n\n return true;\n});\n\nfunction XlsxGrid({\n allowResizeInReadOnly = false,\n controller,\n emptyState,\n enableCanvasSelectionAnimation = true,\n errorState,\n fileTooLargeState,\n getCellStyle,\n highlightClassName,\n loadingComponent,\n loadingState,\n onFormControlAction,\n onFormControlChange,\n renderChartLoading,\n renderContextMenu,\n enableContextMenu = true,\n contextMenuClassName,\n contextMenuStyle,\n renderFormControl,\n palette,\n renderImage,\n renderImageSelection,\n renderTableHeaderMenu,\n renderActiveCellOverlay,\n renderScroller,\n enableGestureZoom = true,\n experimentalCanvas = true,\n experimentalOfficeCharts,\n selectionColor,\n selectionFillColor,\n selectionHeaderColor,\n showImages = true\n}: Pick<\n XlsxViewerProps,\n \"renderContextMenu\" | \"enableContextMenu\" | \"contextMenuClassName\" | \"contextMenuStyle\" | \"allowResizeInReadOnly\" | \"emptyState\" | \"enableCanvasSelectionAnimation\" | \"enableGestureZoom\" | \"errorState\" | \"experimentalCanvas\" | \"experimentalOfficeCharts\" | \"fileTooLargeState\" | \"getCellStyle\" | \"highlightClassName\" | \"loadingComponent\" | \"loadingState\" | \"onFormControlAction\" | \"onFormControlChange\" | \"renderChartLoading\" | \"renderFormControl\" | \"renderImage\" | \"renderImageSelection\" | \"renderScroller\" | \"renderTableHeaderMenu\" | \"renderActiveCellOverlay\" | \"selectionColor\" | \"selectionFillColor\" | \"selectionHeaderColor\" | \"showImages\"\n> & {\n controller: XlsxViewerController;\n palette: ViewerPalette;\n}) {\n const xlsxCanvasProfileTarget = typeof window !== \"undefined\"\n ? (window as unknown as {\n __xlsxCanvasProfile?: Array<Record<string, number | boolean>>;\n }).__xlsxCanvasProfile\n : undefined;\n const xlsxGridRenderStart = xlsxCanvasProfileTarget ? performance.now() : 0;\n const {\n activeCell,\n activeSheet,\n activeSheetIndex,\n activeTab,\n activeTabIndex,\n canLoadDeferred,\n charts,\n clearSelectedChart,\n clearSelectedImage,\n clearSelectedCells,\n continueDeferredLoad,\n deferredLoadFileSize,\n displayFileName,\n error,\n fillSelection,\n formControls,\n getActiveWorksheet,\n getChartById,\n getRowsBatchAsync,\n getClipboardData,\n getCellDisplayValue: getControllerCellDisplayValue,\n locale,\n getFormControlItems,\n highlightedRanges,\n images,\n shapes,\n isLoadDeferred,\n isLoading,\n isChartsLoading,\n isWorkerBacked,\n maxZoomScale,\n minZoomScale,\n copySelectionToClipboard,\n pasteFromClipboard,\n pasteStructuredClipboardData,\n pasteText,\n readOnly,\n redo,\n revision,\n selectedChart,\n selectedChartElement,\n selectedChartId,\n selectedImage,\n selectedImageId,\n selectCell,\n selectChart,\n selectChartElement,\n selectImage,\n selectRange,\n selection,\n setActiveSheetIndex,\n setChartRect,\n setImageRect,\n setCellValue,\n setZoomScale,\n sheets,\n tabs,\n sortState,\n sortTable,\n tables,\n undo,\n updateFormControl,\n workbook,\n zoomScale\n } = controller;\n React.useEffect(() => {\n if (showImages && !isChartsLoading) preloadChartRenderers([charts], experimentalOfficeCharts);\n }, [charts, isChartsLoading, showImages, experimentalOfficeCharts]);\n const formControlItemsById = React.useMemo(() => new Map(\n formControls.flatMap((control) => (\n control.controlIndex !== undefined && (control.kind === \"dropdown\" || control.kind === \"listbox\")\n ? [[control.id, getFormControlItems(control.controlIndex, control.sheetIndex)] as const]\n : []\n ))\n ), [formControls, getFormControlItems]);\n const [contextMenu, setContextMenu] = React.useState<{\n context: XlsxContextMenuContext;\n position: { x: number; y: number };\n } | null>(null);\n React.useEffect(() => {\n setContextMenu(null);\n }, [activeTabIndex, workbook, readOnly, enableContextMenu]);\n React.useEffect(() => {\n if (contextMenu && (!selection || !rangesEqual(normalizeRange(selection), contextMenu.context.selection))) {\n setContextMenu(null);\n }\n }, [contextMenu, selection]);\n const canResizeHeaders = !readOnly || allowResizeInReadOnly;\n const scrollRef = React.useRef<HTMLDivElement>(null);\n const scrolledHighlightRef = React.useRef<XlsxViewerController[\"highlightedRanges\"][number] | null>(null);\n const wrapperRef = React.useRef<HTMLDivElement>(null);\n const tableRef = React.useRef<HTMLTableElement>(null);\n const gridKeyboardActiveRef = React.useRef(false);\n const gridKeyboardHandlerRef = React.useRef<((event: KeyboardEvent) => void) | null>(null);\n const axisSelectionRef = React.useRef<\n | { axis: \"column\"; endCol: number; startCol: number }\n | { axis: \"row\"; endRow: number; startRow: number }\n | null\n >(null);\n const scrollBodyCanvasRef = React.useRef<HTMLCanvasElement>(null);\n const topBodyCanvasRef = React.useRef<HTMLCanvasElement>(null);\n const leftBodyCanvasRef = React.useRef<HTMLCanvasElement>(null);\n const cornerBodyCanvasRef = React.useRef<HTMLCanvasElement>(null);\n const bodyBlitBufferCanvasRef = React.useRef<Record<FrozenDrawingPane, HTMLCanvasElement | null>>({\n corner: null,\n left: null,\n scroll: null,\n top: null\n });\n const headerBlitBufferCanvasRef = React.useRef<{ left: HTMLCanvasElement | null; top: HTMLCanvasElement | null }>({\n left: null,\n top: null\n });\n const topFrozenHeaderCanvasRef = React.useRef<HTMLCanvasElement>(null);\n const topScrollHeaderCanvasRef = React.useRef<HTMLCanvasElement>(null);\n const leftFrozenHeaderCanvasRef = React.useRef<HTMLCanvasElement>(null);\n const leftScrollHeaderCanvasRef = React.useRef<HTMLCanvasElement>(null);\n const cornerHeaderCanvasRef = React.useRef<HTMLCanvasElement>(null);\n const canvasScrollOverlayContentRef = React.useRef<HTMLDivElement>(null);\n const canvasTopOverlayContentRef = React.useRef<HTMLDivElement>(null);\n const canvasLeftOverlayContentRef = React.useRef<HTMLDivElement>(null);\n const canvasCornerOverlayContentRef = React.useRef<HTMLDivElement>(null);\n const canvasImageCacheRef = React.useRef(new Map<string, {\n failed: boolean;\n image: HTMLImageElement;\n loaded: boolean;\n src: string;\n }>());\n const selectionOverlayRef = React.useRef<HTMLDivElement>(null);\n const selectionPaneContentRefs = React.useRef(new Map<FrozenDrawingPane, HTMLDivElement>());\n const selectionPaneOverlayRefs = React.useRef(new Map<FrozenDrawingPane, HTMLDivElement>());\n const selectionPaneFillHandleRefs = React.useRef(new Map<FrozenDrawingPane, HTMLDivElement>());\n const canvasSelectionLayerRef = React.useRef<HTMLDivElement>(null);\n const activeValidationOverlayRef = React.useRef<HTMLDivElement>(null);\n const fillHandleRef = React.useRef<HTMLDivElement>(null);\n const tableMenuRef = React.useRef<HTMLDivElement>(null);\n const colElementRefs = React.useRef(new Map<number, HTMLTableColElement>());\n const colHeaderCellRefs = React.useRef(new Map<number, HTMLTableCellElement>());\n const rowElementRefs = React.useRef(new Map<number, HTMLTableRowElement>());\n const rowHeaderCellRefs = React.useRef(new Map<number, HTMLTableCellElement>());\n const columnPreviewRef = React.useRef<{ actualIndex: number; size: number } | null>(null);\n const rowPreviewRef = React.useRef<{ actualIndex: number; size: number } | null>(null);\n const activeCellRef = React.useRef<XlsxCellAddress | null>(activeCell);\n const selectedChartIdRef = React.useRef<string | null>(selectedChartId);\n const selectedImageIdRef = React.useRef<string | null>(selectedImageId);\n const pendingSelectionCommitRef = React.useRef<XlsxCellRange | null>(null);\n const selectionCommitFrameRef = React.useRef<number | null>(null);\n const selectionRef = React.useRef<XlsxCellRange | null>(null);\n const editingCellRef = React.useRef<XlsxCellAddress | null>(null);\n const commitEditingRef = React.useRef<() => void>(() => {});\n const editingInputRef = React.useRef<HTMLInputElement>(null);\n const readOnlyRef = React.useRef(readOnly);\n const committedZoomScaleRef = React.useRef(zoomScale);\n const gestureZoomScaleRef = React.useRef(zoomScale);\n const liveGestureZoomRef = React.useRef<LiveGestureZoomState | null>(null);\n const pendingLiveGestureZoomStateRef = React.useRef<LiveGestureZoomState | null>(null);\n const liveGestureZoomFrameRef = React.useRef<number | null>(null);\n const liveGestureZoomTransformFrameRef = React.useRef<number | null>(null);\n const pendingZoomAnchorRef = React.useRef<ZoomAnchor | null>(null);\n const liveZoomCommitTimerRef = React.useRef<number | null>(null);\n const pendingLiveZoomCommitRef = React.useRef<number | null>(null);\n const gestureScrollerRectRef = React.useRef<DOMRect | null>(null);\n const touchPinchStateRef = React.useRef<{ startDistance: number; startZoomScale: number } | null>(null);\n const safariPinchStartZoomRef = React.useRef<number | null>(null);\n const canvasTransformStyleCacheRef = React.useRef(new WeakMap<HTMLElement, {\n transform: string;\n transformOrigin: string | null;\n willChange: string;\n }>());\n const displayedSelectionRef = React.useRef<XlsxCellRange | null>(null);\n const firstVisibleColRef = React.useRef<number | undefined>(undefined);\n const lastVisibleColRef = React.useRef<number | undefined>(undefined);\n const firstVisibleRowRef = React.useRef<number | undefined>(undefined);\n const lastVisibleRowRef = React.useRef<number | undefined>(undefined);\n const cellRenderCacheRef = React.useRef(new Map<string, CellRenderData>());\n const cellBaseRawStyleCacheRef = React.useRef(new Map<string, Record<string, unknown> | null>());\n const mergedBorderOverlayCacheRef = React.useRef(new Map<string, Record<string, unknown> | null>());\n const conditionalFormatMetricsCacheRef = React.useRef(new Map<string, number[]>());\n const resizeStateRef = React.useRef<\n | {\n actualIndex: number;\n initialPx: number;\n pointerId: number;\n startPosition: number;\n type: \"column\" | \"row\";\n }\n | null\n >(null);\n const resizeFrameRef = React.useRef<number | null>(null);\n const pendingResizePreviewRef = React.useRef<\n | {\n actualIndex: number;\n size: number;\n type: \"column\" | \"row\";\n }\n | null\n >(null);\n const selectionDragRef = React.useRef<\n SelectionDragState | null\n >(null);\n const pendingSelectionDragRef = React.useRef<SelectionDragState | null>(null);\n const fillDragRef = React.useRef<\n | {\n pointerId: number;\n previewRange: XlsxCellRange;\n sourceRange: XlsxCellRange;\n }\n | null\n >(null);\n const selectionDragCleanupRef = React.useRef<(() => void) | null>(null);\n const pendingSelectionDragCleanupRef = React.useRef<(() => void) | null>(null);\n const fillDragCleanupRef = React.useRef<(() => void) | null>(null);\n const cachedScrollerRectRef = React.useRef<DOMRect | null>(null);\n const installImageInteractionListenersRef = React.useRef(installImageInteractionListeners);\n const installChartInteractionListenersRef = React.useRef(installChartInteractionListeners);\n const imageInteractionCleanupRef = React.useRef<(() => void) | null>(null);\n const imageInteractionRef = React.useRef<\n | {\n baseRect: XlsxImageRect;\n didMove: boolean;\n imageId: string;\n pointerId: number;\n startClientX: number;\n startClientY: number;\n type: \"move\";\n }\n | {\n baseRect: XlsxImageRect;\n didMove: boolean;\n handle: XlsxImageResizeHandlePosition;\n imageId: string;\n pointerId: number;\n startClientX: number;\n startClientY: number;\n type: \"resize\";\n }\n | null\n >(null);\n const chartInteractionCleanupRef = React.useRef<(() => void) | null>(null);\n const chartInteractionRef = React.useRef<\n | {\n baseRect: XlsxImageRect;\n didMove: boolean;\n chartId: string;\n pointerId: number;\n startClientX: number;\n startClientY: number;\n type: \"move\";\n }\n | {\n baseRect: XlsxImageRect;\n didMove: boolean;\n chartId: string;\n handle: XlsxImageResizeHandlePosition;\n pointerId: number;\n startClientX: number;\n startClientY: number;\n type: \"resize\";\n }\n | null\n >(null);\n const [editingCell, setEditingCell] = React.useState<XlsxCellAddress | null>(null);\n const [editingValue, setEditingValue] = React.useState(\"\");\n const [openTableMenu, setOpenTableMenu] = React.useState<{ col: number; row: number; tableName: string } | null>(null);\n const [fillPreviewRange, setFillPreviewRange] = React.useState<XlsxCellRange | null>(null);\n const [chartPreviewRect, setChartPreviewRect] = React.useState<{ id: string; rect: XlsxImageRect } | null>(null);\n const [liveGestureZoom, setLiveGestureZoom] = React.useState<LiveGestureZoomState | null>(null);\n const [canvasImageLoadVersion, setCanvasImageLoadVersion] = React.useState(0);\n const liveDrawingViewportRef = React.useRef<DrawingViewport>({\n height: 0,\n left: 0,\n top: 0,\n width: 0\n });\n const canvasLayoutMetricsRef = React.useRef({\n displayHeaderHeight: HEADER_HEIGHT,\n displayRowHeaderWidth: ROW_HEADER_WIDTH,\n frozenPaneBottom: HEADER_HEIGHT,\n frozenPaneRight: ROW_HEADER_WIDTH\n });\n const paintedDrawingViewportRef = React.useRef<DrawingViewport>({\n height: 0,\n left: 0,\n top: 0,\n width: 0\n });\n const paintedBodyCanvasSignatureRef = React.useRef<PaintedBodyCanvasSignature | null>(null);\n const paintedHeaderCanvasSignatureRef = React.useRef<PaintedHeaderCanvasSignature | null>(null);\n const pendingDrawingViewportRef = React.useRef<DrawingViewport | null>(null);\n const drawingViewportFrameRef = React.useRef<number | null>(null);\n const initialScrollKeyRef = React.useRef<string | null>(null);\n const chartPreviewRectRef = React.useRef<{ id: string; rect: XlsxImageRect } | null>(null);\n const skipNextChartClickRef = React.useRef<string | null>(null);\n const paneDrawingNodesCacheRef = React.useRef<{\n chartRects: Array<{ chart: XlsxChart; rect: XlsxImageRect }>;\n drawingViewportSignature: string;\n formControlItemsById: Map<string, string[]>;\n formControlRects: Array<{ control: XlsxFormControl; rect: XlsxImageRect }>;\n imageRects: Array<{ image: XlsxImage; rect: XlsxImageRect }>;\n isChartsLoading: boolean;\n onFormControlAction: XlsxViewerProps[\"onFormControlAction\"];\n onFormControlChange: XlsxViewerProps[\"onFormControlChange\"];\n palette: ViewerPalette;\n readOnly: boolean;\n renderChartLoading: XlsxViewerProps[\"renderChartLoading\"];\n experimentalOfficeCharts: XlsxViewerProps[\"experimentalOfficeCharts\"];\n renderFormControl: XlsxViewerProps[\"renderFormControl\"];\n renderImage: XlsxViewerProps[\"renderImage\"];\n renderImageSelection: XlsxViewerProps[\"renderImageSelection\"];\n selectedChartElement: XlsxChartElementSelection | null;\n selectedChartId: string | null;\n selectedImageId: string | null;\n selectionStroke: string;\n shapeRects: Array<{ rect: XlsxImageRect; shape: XlsxShape }>;\n showImages: boolean;\n updateFormControl: XlsxViewerController[\"updateFormControl\"];\n value: Record<FrozenDrawingPane, React.ReactNode>;\n } | null>(null);\n const [drawingViewport, setDrawingViewport] = React.useState<DrawingViewport>({\n height: 0,\n left: 0,\n top: 0,\n width: 0\n });\n const drawingViewportStateRef = React.useRef<DrawingViewport>({\n height: 0,\n left: 0,\n top: 0,\n width: 0\n });\n const [resizeGuide, setResizeGuide] = React.useState<{ position: number; type: \"column\" | \"row\" } | null>(null);\n const selectionPreviewRangeRef = React.useRef<XlsxCellRange | null>(null);\n const [imagePreviewRect, setImagePreviewRect] = React.useState<{ id: string; rect: XlsxImageRect } | null>(null);\n const imagePreviewRectRef = React.useRef<{ id: string; rect: XlsxImageRect } | null>(null);\n const imagePreviewFrameRef = React.useRef<number | null>(null);\n const pendingImagePreviewRef = React.useRef<{ id: string; rect: XlsxImageRect } | null>(null);\n const chartPreviewFrameRef = React.useRef<number | null>(null);\n const pendingChartPreviewRef = React.useRef<{ id: string; rect: XlsxImageRect } | null>(null);\n const skipNextImageClickRef = React.useRef<string | null>(null);\n const [pendingNavigation, setPendingNavigation] = React.useState<{ cell: XlsxCellAddress; sheetIndex: number } | null>(null);\n const interactionModeRef = React.useRef<\"idle\" | \"fill\" | \"select\">(\"idle\");\n const setInteractionMode = React.useCallback((mode: \"idle\" | \"fill\" | \"select\") => {\n interactionModeRef.current = mode;\n }, []);\n const worksheet = React.useMemo(\n () => getActiveWorksheet(),\n [activeSheetIndex, getActiveWorksheet, revision]\n );\n const mergedRegions = React.useMemo<XlsxCellRange[]>(() => {\n const regions: XlsxCellRange[] = [];\n const rawMergedRegions = Array.isArray(worksheet?.mergedRegions) ? worksheet.mergedRegions : [];\n\n for (const entry of rawMergedRegions) {\n const region = asRecord(entry);\n const startRow = asNonNegativeInteger(region?.startRow);\n const startCol = asNonNegativeInteger(region?.startCol);\n const endRow = asNonNegativeInteger(region?.endRow);\n const endCol = asNonNegativeInteger(region?.endCol);\n if (startRow === null || startCol === null || endRow === null || endCol === null) {\n continue;\n }\n\n regions.push({\n end: { col: endCol, row: endRow },\n start: { col: startCol, row: startRow }\n });\n }\n\n return regions;\n }, [worksheet]);\n // Worker-backed sheets have no main-thread worksheet, so selection resolves merges from the\n // sheet metadata, which is populated in both loading modes.\n const findMergedRange = React.useMemo(\n () => createMergedRangeLookup(activeSheet?.mergedRanges ?? mergedRegions),\n [activeSheet?.mergedRanges, mergedRegions]\n );\n const resolveMergeAnchorCell = React.useCallback((cell: XlsxCellAddress): XlsxCellAddress => {\n return findMergedRange(cell)?.start ?? cell;\n }, [findMergedRange]);\n const resolveCellSelectionRange = React.useCallback((range: XlsxCellRange): XlsxCellRange => {\n const normalized = normalizeRange(range);\n const start = resolveMergeAnchorCell(normalized.start);\n const end = resolveMergeAnchorCell(normalized.end);\n if (isSameCell(start, end)) {\n return { start, end };\n }\n return expandHighlightedRange(normalized, activeSheet?.mergedRanges ?? mergedRegions);\n }, [activeSheet?.mergedRanges, mergedRegions, resolveMergeAnchorCell]);\n const highlightedCellRanges = React.useMemo(\n () => highlightedRanges.flatMap((highlight, index) => activeSheet?.workbookSheetIndex === highlight.workbookSheetIndex\n ? [{ highlight, index, range: expandHighlightedRange(highlight.range, activeSheet?.mergedRanges ?? mergedRegions) }]\n : []),\n [activeSheet?.mergedRanges, activeSheet?.workbookSheetIndex, highlightedRanges, mergedRegions]\n );\n const normalizedSelection = React.useMemo(() => (selection ? normalizeRange(selection) : null), [selection]);\n const zoomFactor = React.useMemo(() => Math.max(0.1, zoomScale / 100), [zoomScale]);\n const previousZoomFactorRef = React.useRef(zoomFactor);\n const effectiveTables = tables;\n const [displayRowLimit, setDisplayRowLimit] = React.useState(() =>\n resolveInitialDisplayExtent(\n activeSheet?.maxUsedRow ?? -1,\n MIN_OPEN_GRID_ROWS,\n OPEN_GRID_ROW_PADDING,\n Boolean(isWorkerBacked),\n INITIAL_WORKER_GRID_ROWS\n )\n );\n const [displayColLimit, setDisplayColLimit] = React.useState(() =>\n resolveInitialDisplayExtent(\n activeSheet?.maxUsedCol ?? -1,\n MIN_OPEN_GRID_COLS,\n OPEN_GRID_COL_PADDING,\n Boolean(isWorkerBacked),\n INITIAL_WORKER_GRID_COLS\n )\n );\n const hiddenRowSet = React.useMemo(() => new Set(activeSheet?.hiddenRows ?? []), [activeSheet?.hiddenRows]);\n const hiddenColSet = React.useMemo(() => new Set(activeSheet?.hiddenCols ?? []), [activeSheet?.hiddenCols]);\n const displayDefaultRowHeight = DEFAULT_ROW_HEIGHT * zoomFactor;\n const displayDefaultColWidth = DEFAULT_COL_WIDTH * zoomFactor;\n const displayHeaderHeight = HEADER_HEIGHT * zoomFactor;\n const displayRowHeaderWidth = ROW_HEADER_WIDTH * zoomFactor;\n const displayImageMinSize = IMAGE_MIN_SIZE_PX * zoomFactor;\n const applyCanvasViewportCompensation = React.useCallback((liveViewport?: DrawingViewport) => {\n const nextLiveViewport = liveViewport ?? liveDrawingViewportRef.current;\n const paintedViewport = paintedDrawingViewportRef.current;\n const {\n displayHeaderHeight: liveDisplayHeaderHeight,\n displayRowHeaderWidth: liveDisplayRowHeaderWidth,\n frozenPaneBottom: liveFrozenPaneBottom,\n frozenPaneRight: liveFrozenPaneRight\n } = canvasLayoutMetricsRef.current;\n const currentLiveGestureZoom = liveGestureZoomRef.current;\n const isLiveZooming = experimentalCanvas && currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;\n const liveZoomScale = isLiveZooming\n ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale)\n : 1;\n const scrollDeltaX = paintedViewport.left - nextLiveViewport.left;\n const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;\n const resolveClampedLiveZoomAnchor = (\n scrollOffset: number,\n viewportSize: number,\n scrollExtent: number,\n gestureAnchor: number\n ) => {\n if (!isLiveZooming || liveZoomScale === 1) {\n return gestureAnchor;\n }\n\n const targetScrollExtent = Math.max(viewportSize, scrollExtent * liveZoomScale);\n const targetScrollOffset = clampValue(\n ((scrollOffset + gestureAnchor) * liveZoomScale) - gestureAnchor,\n 0,\n Math.max(0, targetScrollExtent - viewportSize)\n );\n return ((scrollOffset * liveZoomScale) - targetScrollOffset) / (1 - liveZoomScale);\n };\n const scroller = scrollRef.current;\n const liveZoomAnchorX = resolveClampedLiveZoomAnchor(\n nextLiveViewport.left,\n nextLiveViewport.width,\n scroller?.scrollWidth ?? nextLiveViewport.width,\n currentLiveGestureZoom?.anchor.x ?? 0\n );\n const liveZoomAnchorY = resolveClampedLiveZoomAnchor(\n nextLiveViewport.top,\n nextLiveViewport.height,\n scroller?.scrollHeight ?? nextLiveViewport.height,\n currentLiveGestureZoom?.anchor.y ?? 0\n );\n const applyElementTransform = (\n element: HTMLElement | null,\n transform: string,\n willChange: string,\n transformOrigin: string | null = null\n ) => {\n if (!element) {\n return;\n }\n\n const cached = canvasTransformStyleCacheRef.current.get(element);\n if (cached?.transform !== transform || element.style.transform !== transform) {\n element.style.transform = transform;\n }\n if (cached?.willChange !== willChange || element.style.willChange !== willChange) {\n element.style.willChange = willChange;\n }\n if (\n transformOrigin !== null\n && (cached?.transformOrigin !== transformOrigin || element.style.transformOrigin !== transformOrigin)\n ) {\n element.style.transformOrigin = transformOrigin;\n }\n canvasTransformStyleCacheRef.current.set(element, {\n transform,\n transformOrigin,\n willChange\n });\n };\n const resolveLiveTranslation = (\n baseTranslation: number,\n anchor: number,\n origin: number\n ) => (\n (baseTranslation * liveZoomScale)\n + ((anchor - origin) * (1 - liveZoomScale))\n );\n const applyCanvasTransform = (\n canvas: HTMLCanvasElement | null,\n baseTranslateX: number,\n baseTranslateY: number,\n originX: number,\n originY: number,\n anchorX: number,\n anchorY: number\n ) => {\n const translateX = resolveLiveTranslation(baseTranslateX, anchorX, originX);\n const translateY = resolveLiveTranslation(baseTranslateY, anchorY, originY);\n const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale !== 1;\n applyElementTransform(\n canvas,\n shouldTransform ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale})` : \"\",\n shouldTransform ? \"transform\" : \"\"\n );\n };\n const applyOverlayTransform = (\n pane: FrozenDrawingPane,\n element: HTMLDivElement | null,\n baseTranslateX: number,\n baseTranslateY: number,\n paneLeft: number,\n paneTop: number,\n anchorX: number,\n anchorY: number\n ) => {\n const translateX = resolveLiveTranslation(baseTranslateX, anchorX, paneLeft);\n const translateY = resolveLiveTranslation(baseTranslateY, anchorY, paneTop);\n for (const content of [element, selectionPaneContentRefs.current.get(pane) ?? null]) {\n applyElementTransform(\n content,\n liveZoomScale !== 1\n ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale})`\n : `translate3d(${baseTranslateX}px, ${baseTranslateY}px, 0)`,\n \"transform\",\n \"0 0\"\n );\n }\n };\n const liveScrollBodyViewportWidth = Math.max(0, nextLiveViewport.width - liveFrozenPaneRight);\n const liveScrollBodyViewportHeight = Math.max(0, nextLiveViewport.height - liveFrozenPaneBottom);\n const liveScrollBodyCanvasLeft = liveFrozenPaneRight\n - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportWidth);\n const liveScrollBodyCanvasTop = liveFrozenPaneBottom\n - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportHeight);\n const selectionLayerTranslateX = (nextLiveViewport.left + liveZoomAnchorX) * (1 - liveZoomScale);\n const selectionLayerTranslateY = (nextLiveViewport.top + liveZoomAnchorY) * (1 - liveZoomScale);\n const shouldTransformSelectionLayer = experimentalCanvas && isLiveZooming && liveZoomScale !== 1;\n\n applyElementTransform(\n canvasSelectionLayerRef.current,\n shouldTransformSelectionLayer\n ? `translate3d(${selectionLayerTranslateX}px, ${selectionLayerTranslateY}px, 0) scale(${liveZoomScale})`\n : \"\",\n shouldTransformSelectionLayer ? \"transform\" : \"\",\n \"0 0\"\n );\n\n applyCanvasTransform(\n scrollBodyCanvasRef.current,\n scrollDeltaX,\n scrollDeltaY,\n liveScrollBodyCanvasLeft,\n liveScrollBodyCanvasTop,\n liveZoomAnchorX,\n liveZoomAnchorY\n );\n applyCanvasTransform(\n topBodyCanvasRef.current,\n scrollDeltaX,\n 0,\n liveScrollBodyCanvasLeft,\n liveDisplayHeaderHeight,\n liveZoomAnchorX,\n 0\n );\n applyCanvasTransform(\n leftBodyCanvasRef.current,\n 0,\n scrollDeltaY,\n liveDisplayRowHeaderWidth,\n liveScrollBodyCanvasTop,\n 0,\n liveZoomAnchorY\n );\n applyCanvasTransform(\n cornerBodyCanvasRef.current,\n 0,\n 0,\n liveDisplayRowHeaderWidth,\n liveDisplayHeaderHeight,\n 0,\n 0\n );\n\n applyCanvasTransform(\n topScrollHeaderCanvasRef.current,\n scrollDeltaX,\n 0,\n liveScrollBodyCanvasLeft,\n 0,\n liveZoomAnchorX,\n 0\n );\n applyCanvasTransform(\n topFrozenHeaderCanvasRef.current,\n 0,\n 0,\n liveDisplayRowHeaderWidth,\n 0,\n 0,\n 0\n );\n applyCanvasTransform(\n leftScrollHeaderCanvasRef.current,\n 0,\n scrollDeltaY,\n 0,\n liveScrollBodyCanvasTop,\n 0,\n liveZoomAnchorY\n );\n applyCanvasTransform(\n leftFrozenHeaderCanvasRef.current,\n 0,\n 0,\n 0,\n liveDisplayHeaderHeight,\n 0,\n 0\n );\n applyCanvasTransform(\n cornerHeaderCanvasRef.current,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n );\n applyOverlayTransform(\n \"scroll\",\n canvasScrollOverlayContentRef.current,\n -nextLiveViewport.left - liveFrozenPaneRight,\n -nextLiveViewport.top - liveFrozenPaneBottom,\n liveFrozenPaneRight,\n liveFrozenPaneBottom,\n liveZoomAnchorX,\n liveZoomAnchorY\n );\n applyOverlayTransform(\n \"top\",\n canvasTopOverlayContentRef.current,\n -nextLiveViewport.left - liveFrozenPaneRight,\n -liveDisplayHeaderHeight,\n liveFrozenPaneRight,\n liveDisplayHeaderHeight,\n liveZoomAnchorX,\n 0\n );\n applyOverlayTransform(\n \"left\",\n canvasLeftOverlayContentRef.current,\n -liveDisplayRowHeaderWidth,\n -nextLiveViewport.top - liveFrozenPaneBottom,\n liveDisplayRowHeaderWidth,\n liveFrozenPaneBottom,\n 0,\n liveZoomAnchorY\n );\n applyOverlayTransform(\n \"corner\",\n canvasCornerOverlayContentRef.current,\n -liveDisplayRowHeaderWidth,\n -liveDisplayHeaderHeight,\n liveDisplayRowHeaderWidth,\n liveDisplayHeaderHeight,\n 0,\n 0\n );\n }, [experimentalCanvas, zoomScale]);\n const updateLiveGestureZoomState = React.useCallback((\n nextState:\n | LiveGestureZoomState\n | null\n | ((current: LiveGestureZoomState | null) => LiveGestureZoomState | null)\n ) => {\n const previousState = liveGestureZoomRef.current;\n const resolvedState = typeof nextState === \"function\"\n ? nextState(previousState)\n : nextState;\n liveGestureZoomRef.current = resolvedState;\n const shouldApplyTransformImmediately = !experimentalCanvas || previousState === null || resolvedState === null;\n if (shouldApplyTransformImmediately) {\n if (liveGestureZoomTransformFrameRef.current !== null) {\n window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);\n liveGestureZoomTransformFrameRef.current = null;\n }\n applyCanvasViewportCompensation();\n } else if (liveGestureZoomTransformFrameRef.current === null) {\n liveGestureZoomTransformFrameRef.current = window.requestAnimationFrame(() => {\n liveGestureZoomTransformFrameRef.current = null;\n applyCanvasViewportCompensation();\n });\n }\n // Canvas zoom is compositor-driven; React only needs gesture activity changes\n // so selection geometry transitions stay disabled through the commit frame.\n const shouldSyncReactState = !experimentalCanvas || (previousState === null) !== (resolvedState === null);\n if (!shouldSyncReactState) {\n return;\n }\n\n pendingLiveGestureZoomStateRef.current = resolvedState;\n if (liveGestureZoomFrameRef.current !== null) {\n return;\n }\n liveGestureZoomFrameRef.current = window.requestAnimationFrame(() => {\n liveGestureZoomFrameRef.current = null;\n const pendingState = pendingLiveGestureZoomStateRef.current;\n pendingLiveGestureZoomStateRef.current = null;\n setLiveGestureZoom(pendingState);\n });\n }, [applyCanvasViewportCompensation, experimentalCanvas]);\n const clearLiveZoomCommitTimer = React.useCallback(() => {\n if (liveZoomCommitTimerRef.current !== null) {\n window.clearTimeout(liveZoomCommitTimerRef.current);\n liveZoomCommitTimerRef.current = null;\n }\n }, []);\n const clearLiveGestureZoom = React.useCallback(() => {\n clearLiveZoomCommitTimer();\n pendingLiveZoomCommitRef.current = null;\n pendingZoomAnchorRef.current = null;\n touchPinchStateRef.current = null;\n safariPinchStartZoomRef.current = null;\n gestureScrollerRectRef.current = null;\n gestureZoomScaleRef.current = committedZoomScaleRef.current;\n updateLiveGestureZoomState(null);\n }, [clearLiveZoomCommitTimer, updateLiveGestureZoomState]);\n const updateLiveGestureZoomTarget = React.useCallback((nextZoomScale: number, anchor: ZoomAnchor) => {\n const clampedZoomScale = clampValue(nextZoomScale, minZoomScale, maxZoomScale);\n gestureZoomScaleRef.current = clampedZoomScale;\n pendingLiveZoomCommitRef.current = null;\n updateLiveGestureZoomState((current) => ({\n anchor,\n baseZoomScale: current?.baseZoomScale ?? committedZoomScaleRef.current,\n targetZoomScale: clampedZoomScale\n }));\n }, [maxZoomScale, minZoomScale, updateLiveGestureZoomState]);\n const commitLiveGestureZoom = React.useCallback(() => {\n clearLiveZoomCommitTimer();\n const currentLiveGestureZoom = liveGestureZoomRef.current;\n if (!currentLiveGestureZoom) {\n return;\n }\n\n const nextCommittedZoomScale = clampValue(\n Math.round(currentLiveGestureZoom.targetZoomScale),\n minZoomScale,\n maxZoomScale\n );\n if (nextCommittedZoomScale === committedZoomScaleRef.current) {\n clearLiveGestureZoom();\n return;\n }\n\n if (pendingLiveZoomCommitRef.current === nextCommittedZoomScale) {\n return;\n }\n\n pendingLiveZoomCommitRef.current = nextCommittedZoomScale;\n pendingZoomAnchorRef.current = currentLiveGestureZoom.anchor;\n gestureZoomScaleRef.current = nextCommittedZoomScale;\n setZoomScale(nextCommittedZoomScale);\n }, [clearLiveGestureZoom, clearLiveZoomCommitTimer, maxZoomScale, minZoomScale, setZoomScale]);\n const scheduleLiveGestureZoomCommit = React.useCallback(() => {\n clearLiveZoomCommitTimer();\n liveZoomCommitTimerRef.current = window.setTimeout(() => {\n liveZoomCommitTimerRef.current = null;\n commitLiveGestureZoom();\n }, LIVE_ZOOM_COMMIT_IDLE_MS);\n }, [clearLiveZoomCommitTimer, commitLiveGestureZoom]);\n const syncDrawingViewport = React.useCallback((scroller: HTMLDivElement | null, options?: { immediate?: boolean }) => {\n if (!scroller) {\n return;\n }\n\n const nextViewport = {\n height: scroller.clientHeight,\n left: scroller.scrollLeft,\n top: scroller.scrollTop,\n width: scroller.clientWidth\n };\n const stateViewport = drawingViewportStateRef.current;\n const matchesStateViewport =\n stateViewport.left === nextViewport.left\n && stateViewport.top === nextViewport.top\n && stateViewport.width === nextViewport.width\n && stateViewport.height === nextViewport.height;\n const liveViewport = liveDrawingViewportRef.current;\n const matchesLiveViewport =\n liveViewport.left === nextViewport.left\n && liveViewport.top === nextViewport.top\n && liveViewport.width === nextViewport.width\n && liveViewport.height === nextViewport.height;\n liveDrawingViewportRef.current = nextViewport;\n applyCanvasViewportCompensation(nextViewport);\n\n if (options?.immediate) {\n pendingDrawingViewportRef.current = null;\n if (drawingViewportFrameRef.current !== null) {\n window.cancelAnimationFrame(drawingViewportFrameRef.current);\n drawingViewportFrameRef.current = null;\n }\n if (matchesStateViewport) {\n return;\n }\n setDrawingViewport((current) => (\n current.left === nextViewport.left\n && current.top === nextViewport.top\n && current.width === nextViewport.width\n && current.height === nextViewport.height\n ? current\n : nextViewport\n ));\n return;\n }\n\n if (matchesLiveViewport && matchesStateViewport) {\n return;\n }\n\n const shouldCommitDeferredViewport =\n !matchesStateViewport\n && (\n stateViewport.width !== nextViewport.width\n || stateViewport.height !== nextViewport.height\n || Math.abs(nextViewport.left - stateViewport.left) >= CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX\n || Math.abs(nextViewport.top - stateViewport.top) >= CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX\n );\n if (!shouldCommitDeferredViewport) {\n pendingDrawingViewportRef.current = null;\n return;\n }\n\n pendingDrawingViewportRef.current = nextViewport;\n if (drawingViewportFrameRef.current !== null) {\n return;\n }\n drawingViewportFrameRef.current = window.requestAnimationFrame(() => {\n drawingViewportFrameRef.current = null;\n const pendingViewport = pendingDrawingViewportRef.current;\n pendingDrawingViewportRef.current = null;\n if (!pendingViewport) {\n return;\n }\n setDrawingViewport((current) => (\n current.left === pendingViewport.left\n && current.top === pendingViewport.top\n && current.width === pendingViewport.width\n && current.height === pendingViewport.height\n ? current\n : pendingViewport\n ));\n });\n }, [applyCanvasViewportCompensation]);\n const setGlobalCursor = React.useCallback((cursor: string) => {\n document.body.style.cursor = cursor;\n document.documentElement.style.cursor = cursor;\n if (scrollRef.current) {\n scrollRef.current.style.cursor = cursor;\n }\n }, []);\n const clearGlobalCursor = React.useCallback(() => {\n document.body.style.cursor = \"\";\n document.documentElement.style.cursor = \"\";\n if (scrollRef.current) {\n scrollRef.current.style.cursor = \"\";\n }\n }, []);\n const getBodyBlitBufferCanvas = React.useCallback((pane: FrozenDrawingPane) => {\n let bufferCanvas = bodyBlitBufferCanvasRef.current[pane];\n if (!bufferCanvas && typeof document !== \"undefined\") {\n bufferCanvas = document.createElement(\"canvas\");\n bodyBlitBufferCanvasRef.current[pane] = bufferCanvas;\n }\n return bufferCanvas;\n }, []);\n const getHeaderBlitBufferCanvas = React.useCallback((axis: \"left\" | \"top\") => {\n let bufferCanvas = headerBlitBufferCanvasRef.current[axis];\n if (!bufferCanvas && typeof document !== \"undefined\") {\n bufferCanvas = document.createElement(\"canvas\");\n headerBlitBufferCanvasRef.current[axis] = bufferCanvas;\n }\n return bufferCanvas;\n }, []);\n const visibleRows = React.useMemo(() => {\n return buildVisibleAxisIndices(\n activeSheet?.visibleRows ?? [],\n displayRowLimit,\n activeSheet?.maxUsedRow ?? -1,\n hiddenRowSet\n );\n }, [activeSheet?.maxUsedRow, activeSheet?.visibleRows, displayRowLimit, hiddenRowSet]);\n const visibleCols = React.useMemo(() => {\n return buildVisibleAxisIndices(\n activeSheet?.visibleCols ?? [],\n displayColLimit,\n activeSheet?.maxUsedCol ?? -1,\n hiddenColSet\n );\n }, [activeSheet?.maxUsedCol, activeSheet?.visibleCols, displayColLimit, hiddenColSet]);\n const frozenRows = React.useMemo(() => {\n const freezeRow = activeSheet?.freezePanes?.row ?? 0;\n if (freezeRow <= 0) {\n return [];\n }\n\n return visibleRows.filter((row) => row < freezeRow);\n }, [activeSheet?.freezePanes?.row, visibleRows]);\n const frozenCols = React.useMemo(() => {\n const freezeCol = activeSheet?.freezePanes?.col ?? 0;\n if (freezeCol <= 0) {\n return [];\n }\n\n return visibleCols.filter((col) => col < freezeCol);\n }, [activeSheet?.freezePanes?.col, visibleCols]);\n const actualColWidths = React.useMemo(\n () => {\n const office = experimentalOfficeCharts ? activeSheet?.officeLayout : undefined;\n if (office) {\n return Array.from({ length: displayColLimit }, (_, index) => {\n if (hiddenColSet.has(index)) return 0;\n const original = office.columns[index];\n const override = activeSheet?.colWidthOverridesPx[index];\n return override !== undefined && override !== original?.sourcePx\n ? override\n : original?.sizePx ?? office.defaultColumnPx;\n });\n }\n const widths = new Array(displayColLimit).fill(0);\n const showGridLines = activeSheet?.showGridLines ?? true;\n const fallbackWidth = Math.max(\n resolveRenderedSheetAxisPixels(activeSheet?.defaultColWidthPx ?? DEFAULT_COL_WIDTH, showGridLines),\n DEFAULT_COL_WIDTH / 2\n );\n\n if (worksheet) {\n for (let col = 0; col < displayColLimit; col += 1) {\n if (worksheet.isColumnHidden(col)) {\n continue;\n }\n\n const width = worksheet.getColumnWidth(col);\n if (width !== undefined && width !== null) {\n widths[col] = Math.max(\n resolveRenderedSheetAxisPixels(\n resolveSheetColumnWidthPixels(width, activeSheet?.columnWidthCharacterWidthPx),\n showGridLines\n ),\n DEFAULT_COL_WIDTH / 2\n );\n continue;\n }\n\n widths[col] = Math.max(\n resolveRenderedSheetAxisPixels(\n activeSheet?.colWidthOverridesPx[col] ?? activeSheet?.defaultColWidthPx ?? DEFAULT_COL_WIDTH,\n showGridLines\n ),\n DEFAULT_COL_WIDTH / 2\n );\n }\n\n return widths;\n }\n\n const precomputedCols = activeSheet?.visibleCols ?? [];\n const precomputedWidths = activeSheet?.colWidths ?? [];\n for (let index = 0; index < precomputedCols.length; index += 1) {\n const col = precomputedCols[index];\n if (col === undefined || col >= displayColLimit) {\n continue;\n }\n\n widths[col] = Math.max(\n resolveRenderedSheetAxisPixels(\n activeSheet?.colWidthOverridesPx[col]\n ?? precomputedWidths[index]\n ?? activeSheet?.defaultColWidthPx\n ?? DEFAULT_COL_WIDTH,\n showGridLines\n ),\n DEFAULT_COL_WIDTH / 2\n );\n }\n\n for (let col = Math.max(0, (activeSheet?.maxUsedCol ?? -1) + 1); col < displayColLimit; col += 1) {\n const overrideWidth = activeSheet?.colWidthOverridesPx[col];\n widths[col] = overrideWidth === undefined\n ? fallbackWidth\n : Math.max(resolveRenderedSheetAxisPixels(overrideWidth, showGridLines), DEFAULT_COL_WIDTH / 2);\n }\n\n return widths;\n },\n [\n experimentalOfficeCharts,\n activeSheet?.officeLayout,\n activeSheet?.colWidthOverridesPx,\n activeSheet?.colWidths,\n activeSheet?.defaultColWidthPx,\n activeSheet?.maxUsedCol,\n activeSheet?.showGridLines,\n activeSheet?.visibleCols,\n displayColLimit,\n worksheet,\n revision,\n locale\n ]\n );\n const actualRowHeights = React.useMemo(\n () => {\n const office = experimentalOfficeCharts ? activeSheet?.officeLayout : undefined;\n if (office) {\n return Array.from({ length: displayRowLimit }, (_, index) => {\n if (hiddenRowSet.has(index)) return 0;\n const original = office.rows[index];\n const override = activeSheet?.rowHeightOverridesPx[index];\n return override !== undefined && override !== original?.sourcePx\n ? override\n : original?.sizePx ?? office.defaultRowPx;\n });\n }\n const heights = new Array(displayRowLimit).fill(0);\n const showGridLines = activeSheet?.showGridLines ?? true;\n const fallbackHeight = Math.max(\n resolveRenderedSheetAxisPixels(activeSheet?.defaultRowHeightPx ?? DEFAULT_ROW_HEIGHT, showGridLines),\n DEFAULT_ROW_HEIGHT / 1.5\n );\n\n if (worksheet) {\n for (let row = 0; row < displayRowLimit; row += 1) {\n if (worksheet.isRowHidden(row)) {\n continue;\n }\n\n const height = worksheet.getRowHeight(row);\n if (height !== undefined && height !== null) {\n heights[row] = Math.max(\n resolveRenderedSheetAxisPixels(resolveSheetRowHeightPixels(height), showGridLines),\n DEFAULT_ROW_HEIGHT / 1.5\n );\n continue;\n }\n\n const maxMeasuredCol = Math.min(displayColLimit - 1, activeSheet?.maxUsedCol ?? -1);\n let estimatedAutoHeight = 0;\n if (maxMeasuredCol >= 0) {\n for (let col = 0; col <= maxMeasuredCol; col += 1) {\n if (worksheet.isColumnHidden(col) || worksheet.isMergedSecondary(row, col)) {\n continue;\n }\n\n const cellValue = localizeCellNumber(worksheet, row, col, worksheet.getFormattedValueAt(row, col), locale);\n if (!cellValue) {\n continue;\n }\n\n const rawStyle = (worksheet.getCellStyleAt(row, col) as Record<string, unknown> | null | undefined) ?? null;\n const cellStyle = buildCellStyle(rawStyle, palette, activeSheet?.themePalette, {\n showGridLines: activeSheet?.showGridLines\n });\n if (cellStyle.whiteSpace !== \"pre-wrap\" && !cellValue.includes(\"\\n\") && !cellValue.includes(\"\\r\")) {\n continue;\n }\n\n const merge = worksheet.getMergeSpan(row, col) as { colSpan?: number } | null | undefined;\n const colSpan = Math.max(1, merge?.colSpan ?? 1);\n let cellWidth = 0;\n for (let spanCol = col; spanCol < Math.min(displayColLimit, col + colSpan); spanCol += 1) {\n cellWidth += actualColWidths[spanCol] ?? (activeSheet?.defaultColWidthPx ?? DEFAULT_COL_WIDTH);\n }\n estimatedAutoHeight = Math.max(\n estimatedAutoHeight,\n measureWrappedTextHeight(cellValue, cellStyle, cellWidth)\n );\n }\n }\n if (estimatedAutoHeight > 0) {\n heights[row] = Math.max(\n resolveRenderedSheetAxisPixels(Math.ceil(estimatedAutoHeight), showGridLines),\n fallbackHeight\n );\n continue;\n }\n\n heights[row] = Math.max(\n resolveRenderedSheetAxisPixels(\n activeSheet?.rowHeightOverridesPx[row] ?? activeSheet?.defaultRowHeightPx ?? DEFAULT_ROW_HEIGHT,\n showGridLines\n ),\n DEFAULT_ROW_HEIGHT / 1.5\n );\n }\n\n return heights;\n }\n\n const precomputedRows = activeSheet?.visibleRows ?? [];\n const precomputedHeights = activeSheet?.rowHeights ?? [];\n for (let index = 0; index < precomputedRows.length; index += 1) {\n const row = precomputedRows[index];\n if (row === undefined || row >= displayRowLimit) {\n continue;\n }\n\n heights[row] = Math.max(\n resolveRenderedSheetAxisPixels(\n activeSheet?.rowHeightOverridesPx[row]\n ?? precomputedHeights[index]\n ?? activeSheet?.defaultRowHeightPx\n ?? DEFAULT_ROW_HEIGHT,\n showGridLines\n ),\n DEFAULT_ROW_HEIGHT / 1.5\n );\n }\n\n for (let row = Math.max(0, (activeSheet?.maxUsedRow ?? -1) + 1); row < displayRowLimit; row += 1) {\n const overrideHeight = activeSheet?.rowHeightOverridesPx[row];\n heights[row] = overrideHeight === undefined\n ? fallbackHeight\n : Math.max(resolveRenderedSheetAxisPixels(overrideHeight, showGridLines), DEFAULT_ROW_HEIGHT / 1.5);\n }\n\n return heights;\n },\n [\n experimentalOfficeCharts,\n activeSheet?.officeLayout,\n activeSheet?.defaultRowHeightPx,\n activeSheet?.maxUsedRow,\n activeSheet?.maxUsedCol,\n activeSheet?.rowHeightOverridesPx,\n activeSheet?.rowHeights,\n activeSheet?.showGridLines,\n activeSheet?.themePalette,\n activeSheet?.visibleRows,\n actualColWidths,\n displayRowLimit,\n displayColLimit,\n palette,\n worksheet,\n revision,\n locale\n ]\n );\n const displayActualColWidths = React.useMemo(\n () => actualColWidths.map((width) => width * zoomFactor),\n [actualColWidths, zoomFactor]\n );\n const displayActualRowHeights = React.useMemo(\n () => actualRowHeights.map((height) => height * zoomFactor),\n [actualRowHeights, zoomFactor]\n );\n const displayEffectiveColWidths = React.useMemo(\n () => visibleCols.map((col) => displayActualColWidths[col] ?? displayDefaultColWidth),\n [displayActualColWidths, displayDefaultColWidth, visibleCols]\n );\n const displayEffectiveRowHeights = React.useMemo(\n () => visibleRows.map((row) => displayActualRowHeights[row] ?? displayDefaultRowHeight),\n [displayActualRowHeights, displayDefaultRowHeight, visibleRows]\n );\n const rowIndexByActual = React.useMemo(() => new Map(visibleRows.map((row, index) => [row, index])), [visibleRows]);\n const colIndexByActual = React.useMemo(() => new Map(visibleCols.map((col, index) => [col, index])), [visibleCols]);\n const visibleRowsRef = React.useRef<number[]>(visibleRows);\n const visibleColsRef = React.useRef<number[]>(visibleCols);\n const effectiveRowHeightsRef = React.useRef<number[]>(displayEffectiveRowHeights);\n const effectiveColWidthsRef = React.useRef<number[]>(displayEffectiveColWidths);\n const rowPrefixSums = React.useMemo(() => buildPrefixSums(displayEffectiveRowHeights), [displayEffectiveRowHeights]);\n const colPrefixSums = React.useMemo(() => buildPrefixSums(displayEffectiveColWidths), [displayEffectiveColWidths]);\n const actualRowPrefixSums = React.useMemo(() => buildPrefixSums(displayActualRowHeights), [displayActualRowHeights]);\n const actualColPrefixSums = React.useMemo(() => buildPrefixSums(displayActualColWidths), [displayActualColWidths]);\n const stickyTopByRow = React.useMemo(\n () => buildStickyOffsets(frozenRows, displayActualRowHeights, displayHeaderHeight),\n [displayActualRowHeights, displayHeaderHeight, frozenRows]\n );\n const stickyLeftByCol = React.useMemo(\n () => buildStickyOffsets(frozenCols, displayActualColWidths, displayRowHeaderWidth),\n [displayActualColWidths, displayRowHeaderWidth, frozenCols]\n );\n const frozenPaneBottom = React.useMemo(\n () => (\n frozenRows.length > 0\n ? frozenRows.reduce(\n (max, row) => Math.max(max, (stickyTopByRow.get(row) ?? displayHeaderHeight) + (displayActualRowHeights[row] ?? displayDefaultRowHeight)),\n displayHeaderHeight\n )\n : displayHeaderHeight\n ),\n [displayActualRowHeights, displayDefaultRowHeight, displayHeaderHeight, frozenRows, stickyTopByRow]\n );\n const frozenPaneRight = React.useMemo(\n () => (\n frozenCols.length > 0\n ? frozenCols.reduce(\n (max, col) => Math.max(max, (stickyLeftByCol.get(col) ?? displayRowHeaderWidth) + (displayActualColWidths[col] ?? displayDefaultColWidth)),\n displayRowHeaderWidth\n )\n : displayRowHeaderWidth\n ),\n [displayActualColWidths, displayDefaultColWidth, displayRowHeaderWidth, frozenCols, stickyLeftByCol]\n );\n canvasLayoutMetricsRef.current = {\n displayHeaderHeight,\n displayRowHeaderWidth,\n frozenPaneBottom,\n frozenPaneRight\n };\n const rowPrefixSumsRef = React.useRef<number[]>(rowPrefixSums);\n const colPrefixSumsRef = React.useRef<number[]>(colPrefixSums);\n const firstVisibleRow = visibleRows[0];\n const lastVisibleRow = visibleRows[visibleRows.length - 1];\n const firstVisibleCol = visibleCols[0];\n const lastVisibleCol = visibleCols[visibleCols.length - 1];\n const displayedSelection = fillPreviewRange ?? normalizedSelection;\n const toLogicalRect = React.useCallback((rect: XlsxImageRect): XlsxImageRect => ({\n height: rect.height / zoomFactor,\n left: rect.left / zoomFactor,\n top: rect.top / zoomFactor,\n width: rect.width / zoomFactor\n }), [zoomFactor]);\n const drawingExtents = React.useMemo(() => {\n const imageExtents = images.reduce(\n (current, image) => {\n const extents = resolveImageAnchorExtents(image);\n return {\n maxCol: Math.max(current.maxCol, extents.maxCol),\n maxRow: Math.max(current.maxRow, extents.maxRow)\n };\n },\n { maxCol: -1, maxRow: -1 }\n );\n const shapeExtents = shapes.reduce(\n (current, shape) => {\n const extents = resolveShapeAnchorExtents(shape);\n return {\n maxCol: Math.max(current.maxCol, extents.maxCol),\n maxRow: Math.max(current.maxRow, extents.maxRow)\n };\n },\n { maxCol: -1, maxRow: -1 }\n );\n const chartExtents = charts.reduce(\n (current, chart) => {\n const extents = resolveChartAnchorExtents(chart);\n return {\n maxCol: Math.max(current.maxCol, extents.maxCol),\n maxRow: Math.max(current.maxRow, extents.maxRow)\n };\n },\n { maxCol: -1, maxRow: -1 }\n );\n\n return {\n maxCol: Math.max(imageExtents.maxCol, shapeExtents.maxCol, chartExtents.maxCol),\n maxRow: Math.max(imageExtents.maxRow, shapeExtents.maxRow, chartExtents.maxRow)\n };\n }, [charts, images, shapes]);\n const shouldVirtualizeRows = visibleRows.length > 0;\n const shouldVirtualizeCols = visibleCols.length > 0 && frozenCols.length === 0;\n const shouldUseDomVirtualizer = !experimentalCanvas;\n const getScrollElement = React.useCallback(() => scrollRef.current, []);\n const estimateRowSize = React.useCallback(\n (index: number) => displayEffectiveRowHeights[index] ?? displayDefaultRowHeight,\n [displayDefaultRowHeight, displayEffectiveRowHeights]\n );\n const getRowItemKey = React.useCallback((index: number) => visibleRows[index] ?? index, [visibleRows]);\n const estimateColSize = React.useCallback(\n (index: number) => displayEffectiveColWidths[index] ?? displayDefaultColWidth,\n [displayDefaultColWidth, displayEffectiveColWidths]\n );\n const getColItemKey = React.useCallback((index: number) => visibleCols[index] ?? index, [visibleCols]);\n\n const rowVirtualizer = useVirtualizer({\n count: visibleRows.length,\n enabled: shouldUseDomVirtualizer,\n estimateSize: estimateRowSize,\n getScrollElement,\n getItemKey: getRowItemKey,\n overscan: 10\n });\n const colVirtualizer = useVirtualizer({\n count: visibleCols.length,\n enabled: shouldUseDomVirtualizer,\n estimateSize: estimateColSize,\n getScrollElement,\n getItemKey: getColItemKey,\n horizontal: true,\n overscan: 6\n });\n const currentRowVirtualItems = shouldUseDomVirtualizer ? rowVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;\n const currentColVirtualItems = shouldUseDomVirtualizer ? colVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;\n const frozenRowVirtualIndices = React.useMemo(\n () => frozenRows\n .map((row) => rowIndexByActual.get(row))\n .filter((index): index is number => index !== undefined),\n [frozenRows, rowIndexByActual]\n );\n const frozenColVirtualIndices = React.useMemo(\n () => frozenCols\n .map((col) => colIndexByActual.get(col))\n .filter((index): index is number => index !== undefined),\n [colIndexByActual, frozenCols]\n );\n const indexedMergedRegions = React.useMemo(\n () =>\n mergedRegions.flatMap((region) => {\n const startRowIndex = rowIndexByActual.get(region.start.row);\n const endRowIndex = rowIndexByActual.get(region.end.row);\n const startColIndex = colIndexByActual.get(region.start.col);\n const endColIndex = colIndexByActual.get(region.end.col);\n if (\n startRowIndex === undefined\n || endRowIndex === undefined\n || startColIndex === undefined\n || endColIndex === undefined\n ) {\n return [];\n }\n\n return [{\n endColIndex,\n endRowIndex,\n startColIndex,\n startRowIndex\n } satisfies IndexedMergedRegion];\n }),\n [colIndexByActual, mergedRegions, rowIndexByActual]\n );\n const domBaseRowIndices = React.useMemo(\n () => Array.from(new Set([\n ...currentRowVirtualItems.map((virtualRow) => virtualRow.index),\n ...frozenRowVirtualIndices\n ])).sort((left, right) => left - right),\n [currentRowVirtualItems, frozenRowVirtualIndices]\n );\n const domBaseColIndices = React.useMemo(\n () => Array.from(new Set([\n ...(shouldVirtualizeCols\n ? currentColVirtualItems.map((virtualCol) => virtualCol.index)\n : visibleCols.map((_, index) => index)),\n ...frozenColVirtualIndices\n ])).sort((left, right) => left - right),\n [currentColVirtualItems, frozenColVirtualIndices, shouldVirtualizeCols, visibleCols]\n );\n const domExpandedWindow = React.useMemo(\n () => expandMergeAwareWindow(domBaseRowIndices, domBaseColIndices, indexedMergedRegions),\n [domBaseColIndices, domBaseRowIndices, indexedMergedRegions]\n );\n const rowVirtualItemByIndex = React.useMemo(\n () => new Map(currentRowVirtualItems.map((virtualRow) => [virtualRow.index, virtualRow])),\n [currentRowVirtualItems]\n );\n const colVirtualItemByIndex = React.useMemo(\n () => new Map(currentColVirtualItems.map((virtualCol) => [virtualCol.index, virtualCol])),\n [currentColVirtualItems]\n );\n const maxRowDisplayLimit = React.useMemo(\n () => resolveOpenGridExtent(activeSheet?.maxUsedRow ?? -1, MIN_OPEN_GRID_ROWS, OPEN_GRID_ROW_PADDING),\n [activeSheet?.maxUsedRow]\n );\n const maxColDisplayLimit = React.useMemo(\n () => resolveOpenGridExtent(activeSheet?.maxUsedCol ?? -1, MIN_OPEN_GRID_COLS, OPEN_GRID_COL_PADDING),\n [activeSheet?.maxUsedCol]\n );\n\n React.useEffect(() => {\n if (!isWorkerBacked) {\n return;\n }\n\n const lastVirtualRowIndex = currentRowVirtualItems[currentRowVirtualItems.length - 1]?.index ?? -1;\n const lastVirtualColIndex = currentColVirtualItems[currentColVirtualItems.length - 1]?.index ?? -1;\n\n if (\n lastVirtualRowIndex >= visibleRows.length - WORKER_GRID_GROW_THRESHOLD_ROWS\n && displayRowLimit < maxRowDisplayLimit\n ) {\n setDisplayRowLimit((current) => Math.min(maxRowDisplayLimit, current + WORKER_GRID_GROW_ROWS));\n }\n\n if (\n lastVirtualColIndex >= visibleCols.length - WORKER_GRID_GROW_THRESHOLD_COLS\n && displayColLimit < maxColDisplayLimit\n ) {\n setDisplayColLimit((current) => Math.min(maxColDisplayLimit, current + WORKER_GRID_GROW_COLS));\n }\n }, [\n currentColVirtualItems,\n currentRowVirtualItems,\n displayColLimit,\n displayRowLimit,\n isWorkerBacked,\n maxColDisplayLimit,\n maxRowDisplayLimit,\n visibleCols.length,\n visibleRows.length\n ]);\n\n React.useEffect(() => {\n activeCellRef.current = activeCell;\n }, [activeCell]);\n\n React.useEffect(() => {\n selectedChartIdRef.current = selectedChartId;\n }, [selectedChartId]);\n\n React.useEffect(() => {\n selectedImageIdRef.current = selectedImageId;\n }, [selectedImageId]);\n\n React.useEffect(() => {\n committedZoomScaleRef.current = zoomScale;\n if (pendingLiveZoomCommitRef.current !== null && zoomScale === pendingLiveZoomCommitRef.current) {\n pendingLiveZoomCommitRef.current = null;\n touchPinchStateRef.current = null;\n safariPinchStartZoomRef.current = null;\n clearLiveZoomCommitTimer();\n updateLiveGestureZoomState(null);\n gestureZoomScaleRef.current = zoomScale;\n return;\n }\n\n const currentLiveGestureZoom = liveGestureZoomRef.current;\n if (currentLiveGestureZoom && zoomScale !== currentLiveGestureZoom.baseZoomScale) {\n clearLiveGestureZoom();\n return;\n }\n\n if (currentLiveGestureZoom === null) {\n gestureZoomScaleRef.current = zoomScale;\n }\n }, [clearLiveGestureZoom, clearLiveZoomCommitTimer, updateLiveGestureZoomState, zoomScale]);\n\n React.useLayoutEffect(() => {\n drawingViewportStateRef.current = drawingViewport;\n applyCanvasViewportCompensation();\n }, [applyCanvasViewportCompensation, drawingViewport]);\n\n React.useEffect(() => {\n selectionRef.current = normalizedSelection;\n }, [normalizedSelection]);\n\n React.useEffect(() => {\n editingCellRef.current = editingCell;\n }, [editingCell]);\n\n React.useLayoutEffect(() => {\n if (!editingCell) {\n return;\n }\n\n const input = editingInputRef.current;\n if (!input) {\n return;\n }\n\n input.focus();\n const caret = input.value.length;\n input.setSelectionRange(caret, caret);\n }, [editingCell]);\n\n React.useEffect(() => {\n readOnlyRef.current = readOnly;\n }, [readOnly]);\n\n React.useLayoutEffect(() => {\n installImageInteractionListenersRef.current = installImageInteractionListeners;\n installChartInteractionListenersRef.current = installChartInteractionListeners;\n });\n\n React.useEffect(() => {\n chartPreviewRectRef.current = chartPreviewRect;\n }, [chartPreviewRect]);\n\n React.useEffect(() => {\n imagePreviewRectRef.current = imagePreviewRect;\n }, [imagePreviewRect]);\n\n React.useEffect(() => () => {\n if (selectionCommitFrameRef.current !== null) {\n window.cancelAnimationFrame(selectionCommitFrameRef.current);\n selectionCommitFrameRef.current = null;\n }\n if (liveGestureZoomFrameRef.current !== null) {\n window.cancelAnimationFrame(liveGestureZoomFrameRef.current);\n liveGestureZoomFrameRef.current = null;\n }\n if (liveGestureZoomTransformFrameRef.current !== null) {\n window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);\n liveGestureZoomTransformFrameRef.current = null;\n }\n if (drawingViewportFrameRef.current !== null) {\n window.cancelAnimationFrame(drawingViewportFrameRef.current);\n drawingViewportFrameRef.current = null;\n }\n clearLiveZoomCommitTimer();\n if (imagePreviewFrameRef.current !== null) {\n window.cancelAnimationFrame(imagePreviewFrameRef.current);\n imagePreviewFrameRef.current = null;\n }\n if (chartPreviewFrameRef.current !== null) {\n window.cancelAnimationFrame(chartPreviewFrameRef.current);\n chartPreviewFrameRef.current = null;\n }\n }, [clearLiveZoomCommitTimer]);\n\n const scheduleImagePreviewRect = React.useCallback((preview: { id: string; rect: XlsxImageRect }) => {\n pendingImagePreviewRef.current = preview;\n if (imagePreviewFrameRef.current !== null) {\n return;\n }\n imagePreviewFrameRef.current = window.requestAnimationFrame(() => {\n imagePreviewFrameRef.current = null;\n const nextPreview = pendingImagePreviewRef.current;\n pendingImagePreviewRef.current = null;\n if (!nextPreview) {\n return;\n }\n imagePreviewRectRef.current = nextPreview;\n setImagePreviewRect(nextPreview);\n });\n }, []);\n\n const scheduleChartPreviewRect = React.useCallback((preview: { id: string; rect: XlsxImageRect }) => {\n pendingChartPreviewRef.current = preview;\n if (chartPreviewFrameRef.current !== null) {\n return;\n }\n chartPreviewFrameRef.current = window.requestAnimationFrame(() => {\n chartPreviewFrameRef.current = null;\n const nextPreview = pendingChartPreviewRef.current;\n pendingChartPreviewRef.current = null;\n if (!nextPreview) {\n return;\n }\n chartPreviewRectRef.current = nextPreview;\n setChartPreviewRect(nextPreview);\n });\n }, []);\n\n React.useEffect(() => {\n displayedSelectionRef.current = selectionPreviewRangeRef.current ?? displayedSelection;\n }, [displayedSelection]);\n\n React.useEffect(() => {\n const previewRange = selectionPreviewRangeRef.current;\n if (!previewRange || pendingSelectionDragRef.current || selectionDragRef.current || fillDragRef.current) {\n return;\n }\n\n if (normalizedSelection && rangesEqual(previewRange, normalizedSelection)) {\n selectionPreviewRangeRef.current = null;\n displayedSelectionRef.current = displayedSelection;\n return;\n }\n\n if (!normalizedSelection && !displayedSelection) {\n selectionPreviewRangeRef.current = null;\n displayedSelectionRef.current = null;\n }\n }, [displayedSelection, normalizedSelection]);\n\n React.useEffect(() => {\n firstVisibleColRef.current = firstVisibleCol;\n lastVisibleColRef.current = lastVisibleCol;\n firstVisibleRowRef.current = firstVisibleRow;\n lastVisibleRowRef.current = lastVisibleRow;\n }, [firstVisibleCol, firstVisibleRow, lastVisibleCol, lastVisibleRow]);\n\n React.useEffect(() => {\n visibleRowsRef.current = visibleRows;\n visibleColsRef.current = visibleCols;\n effectiveRowHeightsRef.current = displayEffectiveRowHeights;\n effectiveColWidthsRef.current = displayEffectiveColWidths;\n rowPrefixSumsRef.current = rowPrefixSums;\n colPrefixSumsRef.current = colPrefixSums;\n }, [colPrefixSums, displayEffectiveColWidths, displayEffectiveRowHeights, rowPrefixSums, visibleCols, visibleRows]);\n\n React.useLayoutEffect(() => {\n const scroller = scrollRef.current;\n const previousZoomFactor = previousZoomFactorRef.current;\n if (!scroller || previousZoomFactor === zoomFactor) {\n previousZoomFactorRef.current = zoomFactor;\n return;\n }\n\n const zoomAnchor = pendingZoomAnchorRef.current;\n if (zoomAnchor) {\n scroller.scrollLeft = ((scroller.scrollLeft + zoomAnchor.x) / previousZoomFactor) * zoomFactor - zoomAnchor.x;\n scroller.scrollTop = ((scroller.scrollTop + zoomAnchor.y) / previousZoomFactor) * zoomFactor - zoomAnchor.y;\n pendingZoomAnchorRef.current = null;\n } else {\n scroller.scrollLeft = (scroller.scrollLeft / previousZoomFactor) * zoomFactor;\n scroller.scrollTop = (scroller.scrollTop / previousZoomFactor) * zoomFactor;\n }\n previousZoomFactorRef.current = zoomFactor;\n if (shouldUseDomVirtualizer) {\n rowVirtualizer.measure();\n colVirtualizer.measure();\n }\n syncDrawingViewport(scroller, { immediate: true });\n }, [shouldUseDomVirtualizer, syncDrawingViewport, zoomFactor]);\n\n React.useLayoutEffect(() => {\n syncDrawingViewport(scrollRef.current, { immediate: true });\n }, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, highlightedCellRanges, syncDrawingViewport, zoomFactor]);\n\n React.useLayoutEffect(() => {\n const scroller = scrollRef.current;\n if (!scroller) {\n return;\n }\n syncDrawingViewport(scroller, { immediate: true });\n if (typeof ResizeObserver === \"undefined\") {\n return;\n }\n const observer = new ResizeObserver(() => {\n syncDrawingViewport(scroller, { immediate: true });\n });\n observer.observe(scroller);\n return () => {\n observer.disconnect();\n };\n }, [activeSheet, syncDrawingViewport]);\n\n React.useEffect(() => {\n setDisplayRowLimit(\n resolveInitialDisplayExtent(\n activeSheet?.maxUsedRow ?? -1,\n MIN_OPEN_GRID_ROWS,\n OPEN_GRID_ROW_PADDING,\n Boolean(isWorkerBacked),\n INITIAL_WORKER_GRID_ROWS\n )\n );\n setDisplayColLimit(\n resolveInitialDisplayExtent(\n activeSheet?.maxUsedCol ?? -1,\n MIN_OPEN_GRID_COLS,\n OPEN_GRID_COL_PADDING,\n Boolean(isWorkerBacked),\n INITIAL_WORKER_GRID_COLS\n )\n );\n }, [activeSheet?.maxUsedCol, activeSheet?.maxUsedRow, activeSheetIndex, isWorkerBacked]);\n\n React.useEffect(() => {\n const selectionEndRow = normalizedSelection?.end.row ?? -1;\n const selectionEndCol = normalizedSelection?.end.col ?? -1;\n const nextRowLimit = Math.max(\n resolveInitialDisplayExtent(\n activeSheet?.maxUsedRow ?? -1,\n MIN_OPEN_GRID_ROWS,\n OPEN_GRID_ROW_PADDING,\n false,\n INITIAL_WORKER_GRID_ROWS\n ),\n (activeCell?.row ?? -1) + OPEN_GRID_ROW_PADDING + 1,\n selectionEndRow + OPEN_GRID_ROW_PADDING + 1,\n drawingExtents.maxRow + OPEN_GRID_ROW_PADDING + 1\n );\n const nextColLimit = Math.max(\n resolveInitialDisplayExtent(\n activeSheet?.maxUsedCol ?? -1,\n MIN_OPEN_GRID_COLS,\n OPEN_GRID_COL_PADDING,\n false,\n INITIAL_WORKER_GRID_COLS\n ),\n (activeCell?.col ?? -1) + OPEN_GRID_COL_PADDING + 1,\n selectionEndCol + OPEN_GRID_COL_PADDING + 1,\n drawingExtents.maxCol + OPEN_GRID_COL_PADDING + 1\n );\n\n setDisplayRowLimit((current) => (current < nextRowLimit ? nextRowLimit : current));\n setDisplayColLimit((current) => (current < nextColLimit ? nextColLimit : current));\n }, [\n activeCell,\n activeSheet?.maxUsedCol,\n activeSheet?.maxUsedRow,\n drawingExtents.maxCol,\n drawingExtents.maxRow,\n normalizedSelection?.end.col,\n normalizedSelection?.end.row\n ]);\n\n React.useEffect(() => {\n const initialScrollKey = [\n displayFileName,\n activeSheetIndex,\n activeSheet?.workbookSheetIndex ?? -1,\n activeSheet?.name ?? \"\",\n activeSheet?.minUsedRow ?? -1,\n activeSheet?.minUsedCol ?? -1,\n isWorkerBacked ? \"worker\" : \"main\"\n ].join(\"|\");\n if (initialScrollKeyRef.current === initialScrollKey) {\n return;\n }\n\n const initialUsedRow = resolveFirstUsedVisibleIndex(visibleRows, activeSheet?.minUsedRow ?? -1);\n const initialUsedCol = resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1);\n const initialScrollTop = initialUsedRow > 0\n ? sumPrefixRange(actualRowPrefixSums, 0, initialUsedRow - 1)\n : 0;\n const initialScrollLeft = initialUsedCol > 0\n ? sumPrefixRange(actualColPrefixSums, 0, initialUsedCol - 1)\n : 0;\n\n if (shouldUseDomVirtualizer && shouldVirtualizeRows) {\n rowVirtualizer.scrollToOffset(initialScrollTop);\n } else if (scrollRef.current) {\n scrollRef.current.scrollTop = initialScrollTop;\n }\n if (scrollRef.current) {\n scrollRef.current.scrollLeft = initialScrollLeft;\n }\n if (shouldUseDomVirtualizer && shouldVirtualizeCols && frozenCols.length === 0) {\n colVirtualizer.scrollToOffset(initialScrollLeft);\n }\n if (scrollRef.current) {\n syncDrawingViewport(scrollRef.current, { immediate: true });\n }\n initialScrollKeyRef.current = initialScrollKey;\n }, [\n activeSheet?.minUsedCol,\n activeSheet?.minUsedRow,\n activeSheet?.name,\n activeSheet?.workbookSheetIndex,\n activeSheetIndex,\n actualColPrefixSums,\n actualRowPrefixSums,\n colVirtualizer,\n displayFileName,\n frozenCols.length,\n isWorkerBacked,\n shouldVirtualizeCols,\n shouldVirtualizeRows,\n shouldUseDomVirtualizer,\n syncDrawingViewport,\n rowVirtualizer,\n visibleCols,\n visibleRows\n ]);\n\n React.useEffect(() => {\n clearLiveGestureZoom();\n }, [activeTabIndex, clearLiveGestureZoom]);\n\n React.useEffect(() => {\n setOpenTableMenu(null);\n }, [activeSheet, activeSheetIndex]);\n\n React.useEffect(() => {\n if (!pendingNavigation || pendingNavigation.sheetIndex !== activeSheetIndex) {\n return;\n }\n\n selectCell(pendingNavigation.cell);\n setPendingNavigation(null);\n }, [activeSheetIndex, pendingNavigation, selectCell]);\n\n React.useEffect(() => {\n if (shouldUseDomVirtualizer && shouldVirtualizeRows) {\n rowVirtualizer.measure();\n }\n if (shouldUseDomVirtualizer && shouldVirtualizeCols) {\n colVirtualizer.measure();\n }\n }, [\n activeSheetIndex,\n revision,\n shouldUseDomVirtualizer,\n shouldVirtualizeCols,\n shouldVirtualizeRows,\n visibleCols.length,\n visibleRows.length\n ]);\n\n const handleScrollerScroll = React.useCallback((event: React.UIEvent<HTMLDivElement>) => {\n const currentScroller = event.currentTarget;\n cachedScrollerRectRef.current = null;\n gestureScrollerRectRef.current = null;\n const paintedViewport = paintedDrawingViewportRef.current;\n const shouldSyncDrawingViewportImmediately =\n Math.abs(currentScroller.scrollLeft - paintedViewport.left) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX\n || Math.abs(currentScroller.scrollTop - paintedViewport.top) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX;\n syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });\n if (\n currentScroller.scrollHeight - (currentScroller.scrollTop + currentScroller.clientHeight) <\n OPEN_GRID_VERTICAL_EDGE_PX\n ) {\n setDisplayRowLimit((current) =>\n growScrollDisplayLimit(current, OPEN_GRID_ROW_GROWTH, maxRowDisplayLimit, readOnly)\n );\n }\n\n if (\n currentScroller.scrollWidth - (currentScroller.scrollLeft + currentScroller.clientWidth) <\n OPEN_GRID_HORIZONTAL_EDGE_PX\n ) {\n setDisplayColLimit((current) =>\n growScrollDisplayLimit(current, OPEN_GRID_COL_GROWTH, maxColDisplayLimit, readOnly)\n );\n }\n }, [maxColDisplayLimit, maxRowDisplayLimit, readOnly, syncDrawingViewport]);\n\n React.useEffect(() => {\n const scroller = scrollRef.current;\n if (!scroller || !enableGestureZoom) {\n clearLiveGestureZoom();\n return;\n }\n\n const handleWheel = (event: WheelEvent) => {\n if (!experimentalCanvas) {\n return;\n }\n if (!event.ctrlKey && !event.metaKey) {\n return;\n }\n\n const delta = normalizeWheelDelta(event);\n if (delta === 0) {\n return;\n }\n\n event.preventDefault();\n const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();\n gestureScrollerRectRef.current = scrollerRect;\n const anchor = resolveEventAnchor(event.clientX, event.clientY, scrollerRect);\n updateLiveGestureZoomTarget(\n gestureZoomScaleRef.current * Math.exp(-delta * WHEEL_ZOOM_SENSITIVITY),\n anchor\n );\n scheduleLiveGestureZoomCommit();\n };\n\n const handleTouchStart = (event: TouchEvent) => {\n if (event.touches.length !== 2) {\n return;\n }\n\n const [firstTouch, secondTouch] = [event.touches[0], event.touches[1]];\n if (!firstTouch || !secondTouch) {\n return;\n }\n\n const distance = resolveTouchDistance(firstTouch, secondTouch);\n if (distance <= 0) {\n return;\n }\n\n event.preventDefault();\n const scrollerRect = scroller.getBoundingClientRect();\n gestureScrollerRectRef.current = scrollerRect;\n const anchor = resolveEventAnchor(\n (firstTouch.clientX + secondTouch.clientX) / 2,\n (firstTouch.clientY + secondTouch.clientY) / 2,\n scrollerRect\n );\n clearLiveZoomCommitTimer();\n updateLiveGestureZoomState({\n anchor,\n baseZoomScale: committedZoomScaleRef.current,\n targetZoomScale: committedZoomScaleRef.current\n });\n gestureZoomScaleRef.current = committedZoomScaleRef.current;\n touchPinchStateRef.current = {\n startDistance: distance,\n startZoomScale: committedZoomScaleRef.current\n };\n };\n\n const handleTouchMove = (event: TouchEvent) => {\n const pinchState = touchPinchStateRef.current;\n if (!pinchState || event.touches.length !== 2) {\n return;\n }\n\n const [firstTouch, secondTouch] = [event.touches[0], event.touches[1]];\n if (!firstTouch || !secondTouch) {\n return;\n }\n\n const distance = resolveTouchDistance(firstTouch, secondTouch);\n if (distance <= 0) {\n return;\n }\n\n event.preventDefault();\n const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();\n gestureScrollerRectRef.current = scrollerRect;\n const anchor = resolveEventAnchor(\n (firstTouch.clientX + secondTouch.clientX) / 2,\n (firstTouch.clientY + secondTouch.clientY) / 2,\n scrollerRect\n );\n updateLiveGestureZoomTarget(\n (pinchState.startZoomScale * distance) / pinchState.startDistance,\n anchor\n );\n };\n\n const handleTouchEnd = () => {\n if (touchPinchStateRef.current) {\n touchPinchStateRef.current = null;\n commitLiveGestureZoom();\n }\n };\n\n const handleTouchCancel = () => {\n if (touchPinchStateRef.current) {\n clearLiveGestureZoom();\n }\n };\n\n const handleGestureStart = (event: Event) => {\n const gestureEvent = event as WebKitGestureEvent;\n event.preventDefault();\n const scrollerRect = scroller.getBoundingClientRect();\n gestureScrollerRectRef.current = scrollerRect;\n const anchor = resolveEventAnchor(\n gestureEvent.clientX,\n gestureEvent.clientY,\n scrollerRect\n );\n clearLiveZoomCommitTimer();\n safariPinchStartZoomRef.current = committedZoomScaleRef.current;\n updateLiveGestureZoomState({\n anchor,\n baseZoomScale: committedZoomScaleRef.current,\n targetZoomScale: committedZoomScaleRef.current\n });\n gestureZoomScaleRef.current = committedZoomScaleRef.current;\n };\n\n const handleGestureChange = (event: Event) => {\n const gestureEvent = event as WebKitGestureEvent;\n const startZoomScale = safariPinchStartZoomRef.current;\n if (startZoomScale === null || !Number.isFinite(gestureEvent.scale) || gestureEvent.scale <= 0) {\n return;\n }\n\n event.preventDefault();\n const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();\n gestureScrollerRectRef.current = scrollerRect;\n const anchor = resolveEventAnchor(\n gestureEvent.clientX,\n gestureEvent.clientY,\n scrollerRect\n );\n updateLiveGestureZoomTarget(startZoomScale * gestureEvent.scale, anchor);\n };\n\n const handleGestureEnd = () => {\n if (safariPinchStartZoomRef.current !== null) {\n safariPinchStartZoomRef.current = null;\n commitLiveGestureZoom();\n }\n };\n\n const handleGestureCancel = () => {\n if (safariPinchStartZoomRef.current !== null) {\n clearLiveGestureZoom();\n }\n };\n\n scroller.addEventListener(\"wheel\", handleWheel, { passive: false });\n scroller.addEventListener(\"touchstart\", handleTouchStart, { passive: false });\n scroller.addEventListener(\"touchmove\", handleTouchMove, { passive: false });\n scroller.addEventListener(\"touchend\", handleTouchEnd);\n scroller.addEventListener(\"touchcancel\", handleTouchCancel);\n scroller.addEventListener(\"gesturestart\", handleGestureStart as EventListener, { passive: false });\n scroller.addEventListener(\"gesturechange\", handleGestureChange as EventListener, { passive: false });\n scroller.addEventListener(\"gestureend\", handleGestureEnd as EventListener);\n\n return () => {\n scroller.removeEventListener(\"wheel\", handleWheel);\n scroller.removeEventListener(\"touchstart\", handleTouchStart);\n scroller.removeEventListener(\"touchmove\", handleTouchMove);\n scroller.removeEventListener(\"touchend\", handleTouchEnd);\n scroller.removeEventListener(\"touchcancel\", handleTouchCancel);\n scroller.removeEventListener(\"gesturestart\", handleGestureStart as EventListener);\n scroller.removeEventListener(\"gesturechange\", handleGestureChange as EventListener);\n scroller.removeEventListener(\"gestureend\", handleGestureEnd as EventListener);\n handleGestureCancel();\n };\n }, [\n clearLiveGestureZoom,\n clearLiveZoomCommitTimer,\n commitLiveGestureZoom,\n enableGestureZoom,\n experimentalCanvas,\n scheduleLiveGestureZoomCommit,\n updateLiveGestureZoomState,\n updateLiveGestureZoomTarget\n ]);\n\n React.useEffect(() => {\n if (!openTableMenu) {\n return;\n }\n\n function handlePointerDown(event: PointerEvent) {\n if (tableMenuRef.current?.contains(event.target as Node)) {\n return;\n }\n setOpenTableMenu(null);\n }\n\n function handleKeyDown(event: KeyboardEvent) {\n if (event.key === \"Escape\") {\n setOpenTableMenu(null);\n }\n }\n\n window.addEventListener(\"pointerdown\", handlePointerDown);\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n window.removeEventListener(\"pointerdown\", handlePointerDown);\n window.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [openTableMenu]);\n\n const resolvePointerCellFromGeometry = React.useCallback((clientX: number, clientY: number): XlsxCellAddress | null => {\n const wrapper = wrapperRef.current;\n const scroller = scrollRef.current;\n const visibleRowsCurrent = visibleRowsRef.current;\n const visibleColsCurrent = visibleColsRef.current;\n const rowPrefixSumsCurrent = rowPrefixSumsRef.current;\n const colPrefixSumsCurrent = colPrefixSumsRef.current;\n\n if (!wrapper || !scroller || visibleRowsCurrent.length === 0 || visibleColsCurrent.length === 0) {\n return null;\n }\n\n const scrollerRect = cachedScrollerRectRef.current ?? scroller.getBoundingClientRect();\n if (\n clientX < scrollerRect.left ||\n clientX > scrollerRect.right ||\n clientY < scrollerRect.top ||\n clientY > scrollerRect.bottom\n ) {\n return null;\n }\n\n const pointerOffsetX = clientX - scrollerRect.left;\n const pointerOffsetY = clientY - scrollerRect.top;\n const localX = pointerOffsetX + (pointerOffsetX >= frozenPaneRight ? scroller.scrollLeft : 0);\n const localY = pointerOffsetY + (pointerOffsetY >= frozenPaneBottom ? scroller.scrollTop : 0);\n const rowContentOffset = localY - displayHeaderHeight;\n const colContentOffset = localX - displayRowHeaderWidth;\n let geometryCell: XlsxCellAddress | null = null;\n\n if (localY >= displayHeaderHeight && localX < displayRowHeaderWidth) {\n const rowIndex = findIndexForOffsetPrefix(rowPrefixSumsCurrent, rowContentOffset);\n const actualRow = visibleRowsCurrent[rowIndex];\n if (actualRow !== undefined && firstVisibleColRef.current !== undefined) {\n geometryCell = { row: actualRow, col: firstVisibleColRef.current };\n }\n } else if (localY < displayHeaderHeight && localX >= displayRowHeaderWidth) {\n const colIndex = findIndexForOffsetPrefix(colPrefixSumsCurrent, colContentOffset);\n const actualCol = visibleColsCurrent[colIndex];\n if (actualCol !== undefined && firstVisibleRowRef.current !== undefined) {\n geometryCell = { row: firstVisibleRowRef.current, col: actualCol };\n }\n } else if (localY >= displayHeaderHeight && localX >= displayRowHeaderWidth) {\n const rowIndex = findIndexForOffsetPrefix(rowPrefixSumsCurrent, rowContentOffset);\n const colIndex = findIndexForOffsetPrefix(colPrefixSumsCurrent, colContentOffset);\n const actualRow = visibleRowsCurrent[rowIndex];\n const actualCol = visibleColsCurrent[colIndex];\n if (actualRow !== undefined && actualCol !== undefined) {\n geometryCell = { row: actualRow, col: actualCol };\n }\n }\n\n return geometryCell;\n }, [displayHeaderHeight, displayRowHeaderWidth, frozenPaneBottom, frozenPaneRight]);\n\n const resolvePointerCellFromHitTest = React.useCallback((clientX: number, clientY: number): XlsxCellAddress | null => {\n const elementsAtPoint = typeof document.elementsFromPoint === \"function\"\n ? document.elementsFromPoint(clientX, clientY)\n : [document.elementFromPoint(clientX, clientY)].filter((element): element is Element => Boolean(element));\n for (const element of elementsAtPoint) {\n if (!(element instanceof HTMLElement)) {\n continue;\n }\n\n const cell = parseCellAddressAttribute(element.closest<HTMLElement>(\"[data-xlsx-cell]\")?.getAttribute(\"data-xlsx-cell\") ?? null);\n if (cell) {\n return cell;\n }\n\n const colHeader = element.closest<HTMLElement>(\"[data-xlsx-col-header]\");\n if (colHeader && firstVisibleRowRef.current !== undefined) {\n const actualCol = Number(colHeader.getAttribute(\"data-xlsx-col-header\"));\n if (Number.isInteger(actualCol) && actualCol >= 0) {\n return { row: firstVisibleRowRef.current, col: actualCol };\n }\n }\n\n const rowElement = element.closest<HTMLTableRowElement>(\"tr[data-xlsx-row]\");\n if (rowElement && firstVisibleColRef.current !== undefined) {\n const actualRow = Number(rowElement.getAttribute(\"data-xlsx-row\"));\n if (Number.isInteger(actualRow) && actualRow >= 0) {\n return { row: actualRow, col: firstVisibleColRef.current };\n }\n }\n }\n\n return null;\n }, []);\n\n const resolvePointerCellFromClient = React.useCallback((clientX: number, clientY: number): XlsxCellAddress | null => {\n const geometryCell = resolvePointerCellFromGeometry(clientX, clientY);\n if (geometryCell) {\n return resolveMergeAnchorCell(geometryCell);\n }\n\n const resolvedCell = resolvePointerCellFromHitTest(clientX, clientY);\n return resolvedCell ? resolveMergeAnchorCell(resolvedCell) : null;\n }, [resolveMergeAnchorCell, resolvePointerCellFromGeometry, resolvePointerCellFromHitTest]);\n\n const resolveDraggedSelectionCell = React.useCallback((\n dragState: {\n axis: \"cell\" | \"column\" | \"row\";\n contentScaleX: number;\n contentScaleY: number;\n originCell: XlsxCellAddress;\n originContentX: number;\n originContentY: number;\n startClientX: number;\n startClientY: number;\n },\n clientX: number,\n clientY: number\n ): XlsxCellAddress | null => {\n const geometryCell = resolvePointerCellFromGeometry(clientX, clientY);\n const hitCell = resolvePointerCellFromHitTest(clientX, clientY);\n const actualRow =\n geometryCell?.row ??\n (hitCell && rowIndexByActual.has(hitCell.row)\n ? hitCell.row\n : undefined);\n const actualCol = geometryCell?.col ?? hitCell?.col;\n if (actualRow === undefined || actualCol === undefined) {\n return null;\n }\n\n if (dragState.axis === \"row\") {\n return { row: actualRow, col: dragState.originCell.col };\n }\n\n if (dragState.axis === \"column\") {\n return { row: dragState.originCell.row, col: actualCol };\n }\n\n return { row: actualRow, col: actualCol };\n }, [resolvePointerCellFromGeometry, resolvePointerCellFromHitTest, rowIndexByActual]);\n\n const resolveCellPointerOrigin = React.useCallback((\n cell: XlsxCellAddress,\n rect: DOMRect,\n clientX: number,\n clientY: number\n ) => {\n const rowIndex = rowIndexByActual.get(cell.row);\n const colIndex = colIndexByActual.get(cell.col);\n if (rowIndex === undefined || colIndex === undefined) {\n return null;\n }\n\n const displayWidth = displayEffectiveColWidths[colIndex] ?? displayDefaultColWidth;\n const displayHeight = displayEffectiveRowHeights[rowIndex] ?? displayDefaultRowHeight;\n const contentScaleX = Math.max(0.0001, rect.width > 0 ? rect.width / displayWidth : 1);\n const contentScaleY = Math.max(0.0001, rect.height > 0 ? rect.height / displayHeight : 1);\n\n return {\n contentScaleX,\n contentScaleY,\n originContentX:\n (colPrefixSums[colIndex] ?? 0)\n + clampContentOffset((clientX - rect.left) / contentScaleX, displayWidth),\n originContentY:\n (rowPrefixSums[rowIndex] ?? 0)\n + clampContentOffset((clientY - rect.top) / contentScaleY, displayHeight)\n };\n }, [colIndexByActual, colPrefixSums, displayDefaultColWidth, displayDefaultRowHeight, displayEffectiveColWidths, displayEffectiveRowHeights, rowIndexByActual, rowPrefixSums]);\n\n const resolveCellPointerOriginFromClient = React.useCallback((\n cell: XlsxCellAddress,\n clientX: number,\n clientY: number\n ) => {\n const scroller = scrollRef.current;\n const rowIndex = rowIndexByActual.get(cell.row);\n const colIndex = colIndexByActual.get(cell.col);\n if (!scroller || rowIndex === undefined || colIndex === undefined) {\n return null;\n }\n\n const scrollerRect = cachedScrollerRectRef.current ?? scroller.getBoundingClientRect();\n const pointerOffsetX = clientX - scrollerRect.left;\n const pointerOffsetY = clientY - scrollerRect.top;\n const localX = pointerOffsetX + (pointerOffsetX >= frozenPaneRight ? scroller.scrollLeft : 0);\n const localY = pointerOffsetY + (pointerOffsetY >= frozenPaneBottom ? scroller.scrollTop : 0);\n const displayWidth = displayEffectiveColWidths[colIndex] ?? displayDefaultColWidth;\n const displayHeight = displayEffectiveRowHeights[rowIndex] ?? displayDefaultRowHeight;\n\n return {\n contentScaleX: 1,\n contentScaleY: 1,\n originContentX:\n (colPrefixSums[colIndex] ?? 0)\n + clampContentOffset(localX - displayRowHeaderWidth - (colPrefixSums[colIndex] ?? 0), displayWidth),\n originContentY:\n (rowPrefixSums[rowIndex] ?? 0)\n + clampContentOffset(localY - displayHeaderHeight - (rowPrefixSums[rowIndex] ?? 0), displayHeight)\n };\n }, [\n colIndexByActual,\n colPrefixSums,\n displayDefaultColWidth,\n displayDefaultRowHeight,\n displayEffectiveColWidths,\n displayEffectiveRowHeights,\n displayHeaderHeight,\n displayRowHeaderWidth,\n frozenPaneBottom,\n frozenPaneRight,\n rowIndexByActual,\n rowPrefixSums\n ]);\n\n const resolveRowPointerOrigin = React.useCallback((\n actualRow: number,\n rect: DOMRect,\n clientY: number\n ) => {\n const rowIndex = rowIndexByActual.get(actualRow);\n if (rowIndex === undefined) {\n return null;\n }\n\n const displayHeight = displayEffectiveRowHeights[rowIndex] ?? displayDefaultRowHeight;\n const contentScaleY = Math.max(0.0001, rect.height > 0 ? rect.height / displayHeight : 1);\n return {\n contentScaleX: 1,\n contentScaleY,\n originContentX: colPrefixSums[0] ?? 0,\n originContentY:\n (rowPrefixSums[rowIndex] ?? 0)\n + clampContentOffset((clientY - rect.top) / contentScaleY, displayHeight)\n };\n }, [colPrefixSums, displayDefaultRowHeight, displayEffectiveRowHeights, rowIndexByActual, rowPrefixSums]);\n\n const resolveColumnPointerOrigin = React.useCallback((\n actualCol: number,\n rect: DOMRect,\n clientX: number\n ) => {\n const colIndex = colIndexByActual.get(actualCol);\n if (colIndex === undefined) {\n return null;\n }\n\n const displayWidth = displayEffectiveColWidths[colIndex] ?? displayDefaultColWidth;\n const contentScaleX = Math.max(0.0001, rect.width > 0 ? rect.width / displayWidth : 1);\n return {\n contentScaleX,\n contentScaleY: 1,\n originContentX:\n (colPrefixSums[colIndex] ?? 0)\n + clampContentOffset((clientX - rect.left) / contentScaleX, displayWidth),\n originContentY: rowPrefixSums[0] ?? 0\n };\n }, [colIndexByActual, colPrefixSums, displayDefaultColWidth, displayEffectiveColWidths, rowPrefixSums]);\n\n const applyColumnPreview = React.useCallback((actualCol: number, widthPx: number | null) => {\n const colElement = colElementRefs.current.get(actualCol);\n if (colElement) {\n colElement.style.width = widthPx === null ? \"\" : `${widthPx}px`;\n }\n\n const baseIndex = visibleCols.indexOf(actualCol);\n const baseWidth = baseIndex >= 0 ? (displayEffectiveColWidths[baseIndex] ?? displayDefaultColWidth) : displayDefaultColWidth;\n const previewWidth = widthPx ?? baseWidth;\n const baseTotalWidth = displayEffectiveColWidths.reduce((sum, width) => sum + width, 0) + displayRowHeaderWidth;\n const widthDelta = previewWidth - baseWidth;\n if (tableRef.current) {\n tableRef.current.style.width = `${baseTotalWidth + widthDelta}px`;\n }\n }, [displayDefaultColWidth, displayEffectiveColWidths, displayRowHeaderWidth, visibleCols]);\n\n const applyRowPreview = React.useCallback((actualRow: number, heightPx: number | null) => {\n const rowElement =\n rowElementRefs.current.get(actualRow) ??\n wrapperRef.current?.querySelector<HTMLTableRowElement>(`tr[data-xlsx-row=\"${actualRow}\"]`) ??\n null;\n if (rowElement) {\n rowElementRefs.current.set(actualRow, rowElement);\n rowElement.style.height = heightPx === null ? \"\" : `${heightPx}px`;\n }\n }, []);\n\n React.useEffect(() => {\n pendingSelectionDragCleanupRef.current?.();\n selectionDragCleanupRef.current?.();\n fillDragCleanupRef.current?.();\n chartInteractionCleanupRef.current?.();\n imageInteractionCleanupRef.current?.();\n pendingSelectionDragCleanupRef.current = null;\n selectionDragCleanupRef.current = null;\n fillDragCleanupRef.current = null;\n chartInteractionCleanupRef.current = null;\n imageInteractionCleanupRef.current = null;\n pendingSelectionDragRef.current = null;\n selectionDragRef.current = null;\n fillDragRef.current = null;\n chartInteractionRef.current = null;\n imageInteractionRef.current = null;\n pendingResizePreviewRef.current = null;\n columnPreviewRef.current = null;\n rowPreviewRef.current = null;\n setResizeGuide(null);\n clearGlobalCursor();\n setEditingCell(null);\n setEditingValue(\"\");\n setFillPreviewRange(null);\n chartPreviewRectRef.current = null;\n setChartPreviewRect(null);\n imagePreviewRectRef.current = null;\n setImagePreviewRect(null);\n selectionPreviewRangeRef.current = null;\n setInteractionMode(\"idle\");\n }, [activeSheetIndex, clearGlobalCursor, revision]);\n\n const focusGridElement = React.useCallback((element: HTMLDivElement, options?: FocusOptions) => {\n if (document.activeElement !== element) {\n element.focus(options ?? { preventScroll: true });\n }\n }, []);\n\n const focusGrid = React.useCallback((options?: FocusOptions) => {\n const scroller = scrollRef.current;\n if (scroller) {\n gridKeyboardActiveRef.current = true;\n focusGridElement(scroller, options);\n }\n }, [focusGridElement]);\n\n React.useEffect(() => {\n const handleDocumentPointerDown = (event: PointerEvent) => {\n const scroller = scrollRef.current;\n if (!scroller || !(event.target instanceof Node) || !scroller.contains(event.target)) {\n gridKeyboardActiveRef.current = false;\n return;\n }\n\n if (isInteractiveFocusTarget(event.target, scroller)) {\n gridKeyboardActiveRef.current = false;\n return;\n }\n\n gridKeyboardActiveRef.current = true;\n focusGridElement(scroller);\n };\n\n const handleDocumentKeyDown = (event: KeyboardEvent) => {\n if (!gridKeyboardActiveRef.current || event.defaultPrevented) {\n return;\n }\n\n const scroller = scrollRef.current;\n const interactiveTarget = getInteractiveFocusTarget(event.target);\n if (!scroller || (interactiveTarget && interactiveTarget !== scroller)) {\n return;\n }\n\n gridKeyboardHandlerRef.current?.(event);\n };\n\n document.addEventListener(\"pointerdown\", handleDocumentPointerDown, true);\n document.addEventListener(\"keydown\", handleDocumentKeyDown);\n return () => {\n document.removeEventListener(\"pointerdown\", handleDocumentPointerDown, true);\n document.removeEventListener(\"keydown\", handleDocumentKeyDown);\n };\n }, [focusGridElement]);\n\n const openHyperlink = React.useCallback((target?: string | null, location?: string | null) => {\n const internalTarget = parseInternalSheetLink(location ?? target);\n if (internalTarget) {\n const targetSheetIndex = sheets.findIndex((sheet) => sheet.name === internalTarget.sheetName);\n if (targetSheetIndex >= 0) {\n if (targetSheetIndex === activeSheetIndex) {\n selectCell(internalTarget.cell);\n } else {\n setPendingNavigation({\n cell: internalTarget.cell,\n sheetIndex: targetSheetIndex\n });\n setActiveSheetIndex(targetSheetIndex);\n }\n return;\n }\n }\n\n const externalTarget = target && !target.startsWith(\"#\") ? target : null;\n if (externalTarget && typeof window !== \"undefined\") {\n window.open(externalTarget, \"_blank\", \"noopener,noreferrer\");\n }\n }, [activeSheetIndex, selectCell, setActiveSheetIndex, sheets]);\n\n const startEditing = React.useCallback(\n (cell: XlsxCellAddress, initialValue?: string) => {\n if (readOnly) {\n return;\n }\n\n selectCell(cell);\n setEditingCell(cell);\n setEditingValue(initialValue ?? getControllerCellDisplayValue(cell));\n },\n [getControllerCellDisplayValue, readOnly, selectCell]\n );\n\n const commitEditing = React.useCallback(() => {\n if (!editingCell) {\n return;\n }\n\n if (readOnly) {\n editingCellRef.current = null;\n setEditingCell(null);\n setEditingValue(\"\");\n focusGrid();\n return;\n }\n\n setCellValue(editingCell, editingValue);\n editingCellRef.current = null;\n setEditingCell(null);\n setEditingValue(\"\");\n focusGrid();\n }, [editingCell, editingValue, focusGrid, readOnly, setCellValue]);\n\n const cancelEditing = React.useCallback(() => {\n editingCellRef.current = null;\n setEditingCell(null);\n setEditingValue(\"\");\n focusGrid();\n }, [focusGrid]);\n\n const handleEditingBlur = React.useCallback(() => {\n if (!editingCellRef.current) {\n return;\n }\n\n commitEditing();\n }, [commitEditing]);\n\n const handleEditingNavigate = React.useCallback((key: string, value: string) => {\n const cell = editingCellRef.current;\n if (!cell) {\n return;\n }\n\n const currentRowIndex = rowIndexByActual.get(cell.row);\n const currentColIndex = colIndexByActual.get(cell.col);\n if (currentRowIndex === undefined || currentColIndex === undefined) {\n if (!readOnlyRef.current) {\n setCellValue(cell, value);\n }\n editingCellRef.current = null;\n setEditingCell(null);\n setEditingValue(\"\");\n focusGrid();\n return;\n }\n\n let nextRowIndex = currentRowIndex;\n let nextColIndex = currentColIndex;\n switch (key) {\n case \"ArrowDown\":\n nextRowIndex += 1;\n break;\n case \"ArrowLeft\":\n nextColIndex -= 1;\n break;\n case \"ArrowRight\":\n nextColIndex += 1;\n break;\n case \"ArrowUp\":\n nextRowIndex -= 1;\n break;\n default:\n return;\n }\n\n if (!readOnlyRef.current) {\n setCellValue(cell, value);\n }\n editingCellRef.current = null;\n setEditingCell(null);\n setEditingValue(\"\");\n moveSelection(nextRowIndex, nextColIndex, false);\n focusGrid();\n }, [colIndexByActual, focusGrid, rowIndexByActual, setCellValue, visibleCols, visibleRows]);\n\n React.useEffect(() => {\n commitEditingRef.current = commitEditing;\n }, [commitEditing]);\n\n const [, startBatchTransition] = React.useTransition();\n const [, startSelectionTransition] = React.useTransition();\n const [asyncViewportRowBatch, setAsyncViewportRowBatch] = React.useState<WorksheetBatchWindow | null>(null);\n const viewportRequest = React.useMemo(() => {\n const rowIndices = experimentalCanvas\n ? (() => {\n if (visibleRows.length === 0 || drawingViewport.height <= 0) {\n return [] as number[];\n }\n\n const indices = new Set<number>(frozenRowVirtualIndices);\n const viewportStart = Math.max(0, drawingViewport.top - displayHeaderHeight - CANVAS_VIEWPORT_OVERSCAN_PX);\n const viewportEnd = Math.max(\n viewportStart,\n drawingViewport.top + drawingViewport.height - displayHeaderHeight + CANVAS_VIEWPORT_OVERSCAN_PX\n );\n const startIndex = findIndexForOffsetPrefix(rowPrefixSums, viewportStart);\n const endIndex = findIndexForOffsetPrefix(rowPrefixSums, viewportEnd);\n for (let index = Math.max(0, startIndex); index <= Math.max(startIndex, endIndex); index += 1) {\n indices.add(index);\n }\n\n return Array.from(indices).sort((left, right) => left - right);\n })()\n : domExpandedWindow.rowIndices;\n const firstVirtualRowIndex = rowIndices[0];\n const lastVirtualRowIndex = rowIndices[rowIndices.length - 1];\n const firstVisibleRow = firstVirtualRowIndex === undefined ? undefined : visibleRows[firstVirtualRowIndex];\n const lastVisibleRow = lastVirtualRowIndex === undefined ? undefined : visibleRows[lastVirtualRowIndex];\n if (firstVisibleRow === undefined || lastVisibleRow === undefined || lastVisibleRow < firstVisibleRow) {\n return null;\n }\n\n const overscan = 48;\n const startRow = Math.max(0, firstVisibleRow - overscan);\n const endRow = lastVisibleRow + overscan;\n return {\n endRow,\n startRow\n };\n }, [\n displayHeaderHeight,\n domExpandedWindow.rowIndices,\n drawingViewport.height,\n drawingViewport.top,\n experimentalCanvas,\n frozenRowVirtualIndices,\n rowPrefixSums,\n visibleRows\n ]);\n\n const syncViewportRowBatch = React.useMemo<WorksheetBatchWindow | null>(() => {\n if (!shouldVirtualizeRows || !worksheet || getRowsBatchAsync || !viewportRequest) {\n return null;\n }\n\n const worksheetWithRowsBatch = worksheet as WorksheetWithRowsBatch;\n if (typeof worksheetWithRowsBatch.getRowsBatch !== \"function\") {\n return null;\n }\n\n try {\n const rows = worksheetWithRowsBatch.getRowsBatch(viewportRequest.startRow, viewportRequest.endRow - viewportRequest.startRow + 1, {\n includeFormulas: true,\n includeHyperlinks: true,\n includeMergeInfo: true,\n includeStyles: true,\n useFormattedValues: true\n });\n localizeRows(worksheet, rows, locale);\n return buildWorksheetBatchWindow(rows as unknown[] | null, activeSheet ?? null, viewportRequest.startRow, viewportRequest.endRow);\n } catch {\n return null;\n }\n }, [activeSheet, getRowsBatchAsync, shouldVirtualizeRows, viewportRequest, worksheet, locale]);\n\n React.useEffect(() => {\n if (!shouldVirtualizeRows || !getRowsBatchAsync || !activeSheet || !viewportRequest) {\n setAsyncViewportRowBatch(null);\n return;\n }\n\n if (\n asyncViewportRowBatch\n && viewportRequest.startRow >= asyncViewportRowBatch.startRow\n && viewportRequest.endRow <= asyncViewportRowBatch.endRow\n ) {\n return;\n }\n\n let isCurrent = true;\n void getRowsBatchAsync(\n activeSheet.workbookSheetIndex,\n viewportRequest.startRow,\n viewportRequest.endRow - viewportRequest.startRow + 1\n )\n .then((rows) => {\n if (!isCurrent) {\n return;\n }\n\n const nextBatch = buildWorksheetBatchWindow(\n rows,\n activeSheet,\n viewportRequest.startRow,\n viewportRequest.endRow\n );\n startBatchTransition(() => {\n setAsyncViewportRowBatch(nextBatch);\n });\n })\n .catch(() => {\n if (!isCurrent) {\n return;\n }\n\n startBatchTransition(() => {\n setAsyncViewportRowBatch(null);\n });\n });\n\n return () => {\n isCurrent = false;\n };\n }, [activeSheet, asyncViewportRowBatch, getRowsBatchAsync, shouldVirtualizeRows, startBatchTransition, viewportRequest]);\n\n const viewportRowBatch = getRowsBatchAsync ? asyncViewportRowBatch : syncViewportRowBatch;\n\n React.useEffect(() => {\n setAsyncViewportRowBatch(null);\n }, [activeSheetIndex, revision, locale]);\n\n const sparklinesByCell = React.useMemo(() => {\n const map = new Map<string, XlsxSheetData[\"sparklines\"][number]>();\n for (const sparkline of activeSheet?.sparklines ?? []) {\n map.set(`${sparkline.target.row}:${sparkline.target.col}`, sparkline);\n }\n return map;\n }, [activeSheet?.sparklines]);\n const chartRangeHighlights = React.useMemo(\n () => collectChartRangeHighlights(selectedChart, selectedChartElement, sheets, workbook, paletteIsDark(palette)),\n [palette, selectedChart, selectedChartElement, sheets, workbook]\n );\n const activeSheetChartHighlights = React.useMemo(\n () => activeSheet\n ? chartRangeHighlights.filter((highlight) => highlight.workbookSheetIndex === activeSheet.workbookSheetIndex)\n : [],\n [activeSheet, chartRangeHighlights]\n );\n\n const cellRenderCacheInvalidationKey = React.useMemo(\n () =>\n [\n activeSheetChartHighlights,\n activeSheetIndex,\n displayColLimit,\n displayRowLimit,\n getCellStyle,\n palette,\n revision,\n locale,\n selectedChartElement,\n selectedChartId,\n viewportRowBatch,\n worksheet,\n zoomFactor\n ] as const,\n [\n activeSheetChartHighlights,\n activeSheetIndex,\n displayColLimit,\n displayRowLimit,\n getCellStyle,\n palette,\n revision,\n locale,\n selectedChartElement,\n selectedChartId,\n viewportRowBatch,\n worksheet,\n zoomFactor\n ]\n );\n const cellRenderCacheInvalidationRef = React.useRef<typeof cellRenderCacheInvalidationKey | null>(null);\n if (cellRenderCacheInvalidationRef.current !== cellRenderCacheInvalidationKey) {\n cellRenderCacheRef.current.clear();\n cellBaseRawStyleCacheRef.current.clear();\n mergedBorderOverlayCacheRef.current.clear();\n cellRenderCacheInvalidationRef.current = cellRenderCacheInvalidationKey;\n }\n\n const getCellData = React.useCallback((row: number, col: number): CellRenderData => {\n const cacheKey = `${row}:${col}`;\n const cached = cellRenderCacheRef.current.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const batchCoversRow = viewportRowBatch ? row >= viewportRowBatch.startRow && row <= viewportRowBatch.endRow : false;\n const batchedCell = batchCoversRow ? viewportRowBatch?.cells.get(cacheKey) : undefined;\n const worksheetMergedSecondary = worksheet?.isMergedSecondary(row, col) ?? false;\n\n if (!worksheet && !batchedCell) {\n const emptyData: CellRenderData = {\n isMergedSecondary: false,\n style: scaleCssProperties({\n backgroundColor: resolveSheetSurface(activeSheet, palette),\n borderBottom: \"none\",\n borderRight: \"none\",\n boxShadow: activeSheet?.showGridLines ? buildGridlineShadow(palette.border) : undefined,\n padding: DEFAULT_CELL_PADDING,\n verticalAlign: \"bottom\",\n whiteSpace: \"nowrap\"\n }, zoomFactor),\n value: \"\"\n };\n cellRenderCacheRef.current.set(cacheKey, emptyData);\n return emptyData;\n }\n\n if (batchedCell?.isMergedSecondary || worksheetMergedSecondary) {\n const mergedSecondaryData: CellRenderData = {\n isMergedSecondary: true,\n style: {},\n value: \"\"\n };\n cellRenderCacheRef.current.set(cacheKey, mergedSecondaryData);\n return mergedSecondaryData;\n }\n\n const merge = worksheet\n ? (worksheet.getMergeSpan(row, col) as { colSpan?: number; rowSpan?: number } | null | undefined)\n : batchCoversRow\n ? batchedCell?.mergeSpan\n : null;\n const resolveBaseResolvedStyle = (targetRow: number, targetCol: number) => {\n const targetCacheKey = `${targetRow}:${targetCol}`;\n const cachedBaseStyle = cellBaseRawStyleCacheRef.current.get(targetCacheKey);\n if (cachedBaseStyle !== undefined) {\n return cachedBaseStyle;\n }\n\n const targetBatchCoversRow = viewportRowBatch\n ? targetRow >= viewportRowBatch.startRow && targetRow <= viewportRowBatch.endRow\n : false;\n const targetBatchedCell = targetBatchCoversRow\n ? viewportRowBatch?.cells.get(targetCacheKey)\n : undefined;\n const targetInheritedStyle = resolveInheritedCellStyle(activeSheet, targetRow, targetCol);\n const targetWorksheetStyle = targetBatchCoversRow\n ? targetBatchedCell?.style ?? null\n : (worksheet?.getCellStyleAt(targetRow, targetCol) as Record<string, unknown> | null | undefined) ?? null;\n const targetRawStyle = mergeResolvedCellStyle(targetInheritedStyle, targetWorksheetStyle, {\n replaceXfSubtrees: true\n });\n const targetTable = getTableAtCell(effectiveTables, targetRow, targetCol);\n const targetTableStyle = resolveTableCellStyle(targetTable, targetRow, targetCol, activeSheet);\n const resolvedStyle = targetTableStyle\n ? mergeResolvedCellStyle(\n mergeResolvedCellStyle(targetRawStyle, targetTableStyle),\n resolveCellTableOverrides(targetRawStyle, activeSheet, targetRow, targetCol)\n )\n : targetRawStyle;\n cellBaseRawStyleCacheRef.current.set(targetCacheKey, resolvedStyle);\n return resolvedStyle;\n };\n\n const table = getTableAtCell(effectiveTables, row, col);\n const mergeRowSpan = Math.max(1, merge?.rowSpan ?? 1);\n const mergeColSpan = Math.max(1, merge?.colSpan ?? 1);\n let mergedBorderOverlay: Record<string, unknown> | null = null;\n if (mergeRowSpan > 1 || mergeColSpan > 1) {\n const mergedBorderOverlayCacheKey = `${row}:${col}:${mergeRowSpan}:${mergeColSpan}`;\n const cachedOverlay = mergedBorderOverlayCacheRef.current.get(mergedBorderOverlayCacheKey);\n if (cachedOverlay !== undefined) {\n mergedBorderOverlay = cachedOverlay;\n } else {\n mergedBorderOverlay = resolveMergedRegionBorderOverlay(\n row,\n col,\n merge,\n (targetRow, targetCol) => resolveBaseResolvedStyle(targetRow, targetCol)\n );\n mergedBorderOverlayCacheRef.current.set(mergedBorderOverlayCacheKey, mergedBorderOverlay);\n }\n }\n\n const baseResolvedStyle = resolveBaseResolvedStyle(row, col);\n const resolvedMergedStyle = mergeResolvedCellStyle(baseResolvedStyle, mergedBorderOverlay);\n let displayValue: string | null = null;\n const readDisplayValue = () => {\n if (displayValue === null) {\n displayValue = batchCoversRow || !worksheet\n ? batchedCell?.value ?? \"\"\n : getCellDisplayValue(worksheet, row, col, activeSheet, locale);\n }\n return displayValue;\n };\n const conditionalCellStyle = resolveConditionalCellStyleForCell(\n row,\n col,\n activeSheet,\n () => (worksheet ? getCellNumericValue(worksheet, row, col) : getBatchedCellNumericValue(batchedCell)),\n readDisplayValue,\n worksheet,\n viewportRowBatch?.cells\n );\n const finalResolvedStyle = mergeResolvedCellStyle(resolvedMergedStyle, conditionalCellStyle);\n const alignment = finalResolvedStyle?.alignment as Record<string, unknown> | undefined;\n const textRotationDeg = resolveSpreadsheetTextRotation(alignment?.rotation ?? alignment?.textRotation);\n const headerRowCount = table ? Math.max(table.headerRowCount, 1) : 0;\n const rawHyperlink = batchCoversRow\n ? batchedCell?.hyperlink\n : (worksheet?.getHyperlinkAt(row, col) as\n | { location?: string; target?: string; tooltip?: string }\n | null\n | undefined);\n const sparkline = sparklinesByCell.get(cacheKey) ?? null;\n const sparklineValues = sparkline && worksheet\n ? (\n sparkline.range.start.row === sparkline.range.end.row\n ? Array.from(\n { length: Math.abs(sparkline.range.end.col - sparkline.range.start.col) + 1 },\n (_, index) => getCellNumericValue(\n worksheet,\n sparkline.range.start.row,\n Math.min(sparkline.range.start.col, sparkline.range.end.col) + index\n )\n )\n : Array.from(\n { length: Math.abs(sparkline.range.end.row - sparkline.range.start.row) + 1 },\n (_, index) => getCellNumericValue(\n worksheet,\n Math.min(sparkline.range.start.row, sparkline.range.end.row) + index,\n sparkline.range.start.col\n )\n )\n )\n : null;\n const checkboxState = finalResolvedStyle?.cellControl && worksheet\n ? getCellBooleanValue(worksheet, row, col)\n : null;\n const chartHighlight = resolveCellChartHighlight({ row, col }, activeSheetChartHighlights);\n const nextData: CellRenderData = {\n canvas: undefined,\n chartHighlight,\n checkboxState,\n colSpan: merge?.colSpan,\n conditionalDataBar: worksheet\n ? resolveConditionalDataBarForCell(\n row,\n col,\n worksheet,\n activeSheet,\n conditionalFormatMetricsCacheRef.current\n )\n : null,\n conditionalColorScale: worksheet\n ? resolveConditionalColorScaleForCell(\n row,\n col,\n worksheet,\n activeSheet,\n conditionalFormatMetricsCacheRef.current\n )\n : null,\n conditionalIcon: worksheet\n ? resolveConditionalIconForCell(\n row,\n col,\n worksheet,\n activeSheet,\n conditionalFormatMetricsCacheRef.current\n )\n : null,\n hyperlink: rawHyperlink ?? null,\n isMergedSecondary: false,\n shrinkToFit: alignment?.shrinkToFit === true,\n isTableHeader: Boolean(table && row >= table.start.row && row < table.start.row + headerRowCount),\n rowSpan: merge?.rowSpan,\n sparkline: sparkline && sparklineValues ? { config: sparkline, values: sparklineValues } : null,\n style: scaleCssProperties(buildCellStyle(finalResolvedStyle, palette, activeSheet?.themePalette, {\n generalAlignment: worksheet ? getCellGeneralAlignment(worksheet, row, col) : batchedCell?.generalAlignment,\n showGridLines: activeSheet?.showGridLines\n }), zoomFactor),\n textRotationDeg,\n validation: resolveCellDataValidation(row, col, activeSheet),\n value: sparkline\n ? \"\"\n : checkboxState !== null\n ? \"\"\n : readDisplayValue()\n };\n\n if (getCellStyle) {\n const styleOverrides = getCellStyle({\n cell: { row, col },\n hasChartHighlight: Boolean(nextData.chartHighlight),\n hasConditionalFormat: Boolean(\n nextData.conditionalColorScale || nextData.conditionalDataBar || nextData.conditionalIcon\n ),\n hasHyperlink: Boolean(nextData.hyperlink),\n hasValidation: Boolean(nextData.validation),\n isMerged: Boolean(nextData.colSpan || nextData.rowSpan),\n isTableHeader: Boolean(nextData.isTableHeader),\n resolvedStyle: { ...nextData.style },\n sheetName: activeSheet?.name ?? \"\",\n value: nextData.value,\n workbookSheetIndex: activeSheet?.workbookSheetIndex ?? -1\n });\n if (styleOverrides) {\n nextData.style = { ...nextData.style, ...styleOverrides };\n // getCellStyle is the final styling layer, so a host-provided\n // background replaces the cell's background fill — including a\n // conditional color-scale fill, since both target the full-cell\n // background. Data bars and icon sets are overlays drawn on top and\n // are intentionally left intact.\n if (\n nextData.conditionalColorScale &&\n (styleOverrides.backgroundColor !== undefined || styleOverrides.background !== undefined)\n ) {\n nextData.conditionalColorScale = null;\n }\n }\n }\n\n nextData.canvas = buildCanvasCellStyleCache(nextData.style);\n\n // Publish the base render data before resolving text overflow. The overflow\n // walk asks neighbouring cells for their data, and a neighbour whose text\n // overflows towards this cell asks for ours in turn; caching first turns\n // that mutual lookup into a cache hit instead of unbounded recursion. The\n // spill fields below are attached to this same cached object.\n cellRenderCacheRef.current.set(cacheKey, nextData);\n\n if (canCellTextOverflow(nextData)) {\n const startColIndex = colIndexByActual.get(col);\n if (startColIndex !== undefined) {\n const cellColSpan = Math.max(1, nextData.colSpan ?? 1);\n let endColIndex = startColIndex;\n while (endColIndex + 1 < visibleCols.length && visibleCols[endColIndex + 1]! < col + cellColSpan) {\n endColIndex += 1;\n }\n const horizontalPadding = getHorizontalPadding(nextData.style.padding);\n const textWidth = measureTextWidth(nextData.value, nextData.style);\n const requiredWidth = textWidth + horizontalPadding + 2;\n const baseWidth = Math.max(\n displayEffectiveColWidths[startColIndex] ?? displayDefaultColWidth,\n sumPrefixRange(colPrefixSums, startColIndex, endColIndex)\n );\n const align = nextData.style.textAlign;\n const spillSide = align === \"right\" || align === \"end\" ? \"left\" : \"right\";\n let availableWidth = baseWidth;\n\n if (requiredWidth > availableWidth) {\n const nextColStep = spillSide === \"left\" ? -1 : 1;\n let nextColIndex = spillSide === \"left\" ? startColIndex - 1 : endColIndex + 1;\n while (nextColIndex >= 0 && nextColIndex < visibleCols.length) {\n const nextActualCol = visibleCols[nextColIndex];\n if (nextActualCol === undefined) {\n break;\n }\n\n const neighborData = getCellData(row, nextActualCol);\n if (!canReceiveOverflowText(neighborData)) {\n break;\n }\n\n availableWidth += displayEffectiveColWidths[nextColIndex] ?? displayDefaultColWidth;\n if (requiredWidth <= availableWidth) {\n break;\n }\n\n nextColIndex += nextColStep;\n }\n\n if (availableWidth > baseWidth) {\n nextData.spillWidth = Math.max(0, availableWidth - horizontalPadding);\n nextData.spillSide = spillSide;\n nextData.spillOffsetX = spillSide === \"left\"\n ? Math.min(0, baseWidth - availableWidth)\n : 0;\n }\n }\n }\n }\n\n if (nextData.shrinkToFit && nextData.value.length > 0 && nextData.style.whiteSpace !== \"pre-wrap\") {\n const startColIndex = colIndexByActual.get(col);\n if (startColIndex !== undefined) {\n const cellColSpan = Math.max(1, nextData.colSpan ?? 1);\n let endColIndex = startColIndex;\n while (endColIndex + 1 < visibleCols.length && visibleCols[endColIndex + 1]! < col + cellColSpan) {\n endColIndex += 1;\n }\n const horizontalPadding = getHorizontalPadding(nextData.style.padding);\n const trailingInset = (nextData.conditionalIcon ? 18 * zoomFactor : 0) + (nextData.isTableHeader ? 16 * zoomFactor : 0);\n const availableWidth = Math.max(\n displayEffectiveColWidths[startColIndex] ?? displayDefaultColWidth,\n sumPrefixRange(colPrefixSums, startColIndex, endColIndex)\n );\n const availableTextWidth = Math.max(0, availableWidth - horizontalPadding - trailingInset);\n if (availableTextWidth > 0) {\n const textWidth = measureTextWidth(nextData.value, nextData.style);\n if (textWidth > availableTextWidth) {\n const baseFontSizePx = resolveCanvasFontSizePx(nextData.style, 12 * zoomFactor);\n const minimumFontSizePx = Math.max(6 * zoomFactor, baseFontSizePx * 0.4);\n let lowerBound = minimumFontSizePx;\n let upperBound = baseFontSizePx;\n let bestFontSizePx = minimumFontSizePx;\n\n while (upperBound - lowerBound > 0.25) {\n const candidateFontSizePx = (lowerBound + upperBound) / 2;\n const candidateStyle = {\n ...nextData.style,\n font: undefined,\n fontSize: candidateFontSizePx\n } as React.CSSProperties;\n const candidateWidth = measureTextWidth(nextData.value, candidateStyle);\n if (candidateWidth <= availableTextWidth) {\n bestFontSizePx = candidateFontSizePx;\n lowerBound = candidateFontSizePx;\n } else {\n upperBound = candidateFontSizePx;\n }\n }\n\n nextData.shrinkToFitFontSizePx = Math.min(baseFontSizePx, bestFontSizePx);\n }\n }\n }\n }\n\n return nextData;\n }, [\n activeSheet,\n activeSheetChartHighlights,\n cellRenderCacheInvalidationKey,\n locale,\n colIndexByActual,\n colPrefixSums,\n displayDefaultColWidth,\n displayEffectiveColWidths,\n effectiveTables,\n getCellStyle,\n palette,\n sparklinesByCell,\n viewportRowBatch,\n visibleCols,\n worksheet,\n zoomFactor\n ]);\n\n React.useEffect(() => {\n conditionalFormatMetricsCacheRef.current.clear();\n }, [activeSheet?.conditionalFormatRules, activeSheet?.workbookSheetIndex, revision]);\n\n const selectionOverlay = React.useMemo(() => {\n if (!displayedSelection) {\n return null;\n }\n\n const normalized = normalizeRange(displayedSelection);\n const startRowIndex = rowIndexByActual.get(normalized.start.row);\n const endRowIndex = rowIndexByActual.get(normalized.end.row);\n const startColIndex = colIndexByActual.get(normalized.start.col);\n const endColIndex = colIndexByActual.get(normalized.end.col);\n\n if (\n startRowIndex === undefined ||\n endRowIndex === undefined ||\n startColIndex === undefined ||\n endColIndex === undefined\n ) {\n return null;\n }\n\n return {\n height: sumPrefixRange(rowPrefixSums, startRowIndex, endRowIndex),\n left: displayRowHeaderWidth + sumPrefixRange(colPrefixSums, 0, startColIndex - 1),\n top: displayHeaderHeight + sumPrefixRange(rowPrefixSums, 0, startRowIndex - 1),\n width: sumPrefixRange(colPrefixSums, startColIndex, endColIndex)\n };\n }, [colIndexByActual, colPrefixSums, displayHeaderHeight, displayRowHeaderWidth, displayedSelection, rowIndexByActual, rowPrefixSums]);\n const resolvedSelectionOverlay = selectionOverlay;\n const rangeHighlightRects = React.useMemo(() => highlightedCellRanges.map(({ highlight, index, range }) => {\n const rows = getHighlightAxisExtent(visibleRows, rowPrefixSums, range.start.row, range.end.row);\n const cols = getHighlightAxisExtent(visibleCols, colPrefixSums, range.start.col, range.end.col);\n return {\n highlight,\n index,\n range,\n rect: rows && cols ? {\n height: rows.end - rows.start,\n left: displayRowHeaderWidth + cols.start,\n top: displayHeaderHeight + rows.start,\n width: cols.end - cols.start\n } : null\n };\n }), [colPrefixSums, displayHeaderHeight, displayRowHeaderWidth, highlightedCellRanges, rowPrefixSums, visibleCols, visibleRows]);\n\n React.useEffect(() => {\n let requiredRows = 0;\n let requiredCols = 0;\n for (const { highlight, range } of highlightedCellRanges) {\n if (highlight.kind !== \"columns\") {\n requiredRows = Math.max(requiredRows, Math.min(XLSX_MAX_ROWS, range.end.row + OPEN_GRID_ROW_PADDING + 1));\n }\n if (highlight.kind !== \"rows\") {\n requiredCols = Math.max(requiredCols, Math.min(XLSX_MAX_COLUMNS, range.end.col + OPEN_GRID_COL_PADDING + 1));\n }\n }\n setDisplayRowLimit((current) => Math.max(current, requiredRows));\n setDisplayColLimit((current) => Math.max(current, requiredCols));\n }, [activeSheet?.maxUsedCol, activeSheet?.maxUsedRow, highlightedCellRanges, activeSheetIndex, isWorkerBacked]);\n\n React.useEffect(() => {\n const scroller = scrollRef.current;\n const target = rangeHighlightRects.find(({ highlight }) => highlight.autoScroll);\n if (!scroller || !target?.rect || scrolledHighlightRef.current === target.highlight || isLoading\n || scroller.clientWidth === 0 || scroller.clientHeight === 0\n || (target.highlight.kind !== \"columns\" && displayRowLimit <= target.range.end.row)\n || (target.highlight.kind !== \"rows\" && displayColLimit <= target.range.end.col)) {\n return;\n }\n const { highlight, rect } = target;\n if (highlight.kind !== \"columns\") {\n scroller.scrollTop = getHighlightScrollOffset(rect.top, rect.top + rect.height, frozenPaneBottom, scroller.clientHeight, scroller.scrollTop);\n }\n if (highlight.kind !== \"rows\") {\n scroller.scrollLeft = getHighlightScrollOffset(rect.left, rect.left + rect.width, frozenPaneRight, scroller.clientWidth, scroller.scrollLeft);\n }\n scrolledHighlightRef.current = highlight;\n syncDrawingViewport(scroller, { immediate: true });\n }, [displayColLimit, displayRowLimit, drawingViewport.height, drawingViewport.width, frozenPaneBottom, frozenPaneRight, isLoading, rangeHighlightRects, syncDrawingViewport]);\n const { fill: selectionFill, header: selectionHeaderSurface, stroke: selectionStroke } = React.useMemo(() => resolveSelectionColors({\n palette,\n selectionColor,\n selectionFillColor,\n selectionHeaderColor\n }), [palette, selectionColor, selectionFillColor, selectionHeaderColor]);\n const virtualCols = React.useMemo<RenderedAxisItem[]>(\n () =>\n domExpandedWindow.colIndices.map((index) => {\n const virtualCol = colVirtualItemByIndex.get(index);\n return {\n end: virtualCol?.end ?? (colPrefixSums[index + 1] ?? 0),\n index,\n key: visibleCols[index] ?? index,\n size: virtualCol?.size ?? (displayEffectiveColWidths[index] ?? displayDefaultColWidth),\n start: virtualCol?.start ?? (colPrefixSums[index] ?? 0)\n };\n }),\n [colPrefixSums, colVirtualItemByIndex, displayDefaultColWidth, displayEffectiveColWidths, domExpandedWindow.colIndices, visibleCols]\n );\n const renderedCols = React.useMemo<RenderedColumn[]>(\n () => {\n const columns: RenderedColumn[] = [];\n virtualCols.forEach((virtualCol) => {\n const actualCol = visibleCols[virtualCol.index];\n if (actualCol === undefined) {\n return;\n }\n\n columns.push({\n actualCol,\n key: actualCol,\n size: virtualCol.size,\n virtualIndex: virtualCol.index\n });\n });\n return columns;\n },\n [virtualCols, visibleCols]\n );\n const canvasBaseRowIndices = React.useMemo(() => {\n if (visibleRows.length === 0 || drawingViewport.height <= 0) {\n return [] as number[];\n }\n\n const indices = new Set<number>(frozenRowVirtualIndices);\n const viewportStart = Math.max(0, drawingViewport.top - displayHeaderHeight - CANVAS_VIEWPORT_OVERSCAN_PX);\n const viewportEnd = Math.max(\n viewportStart,\n drawingViewport.top + drawingViewport.height - displayHeaderHeight + CANVAS_VIEWPORT_OVERSCAN_PX\n );\n const startIndex = findIndexForOffsetPrefix(rowPrefixSums, viewportStart);\n const endIndex = findIndexForOffsetPrefix(rowPrefixSums, viewportEnd);\n for (let index = Math.max(0, startIndex); index <= Math.max(startIndex, endIndex); index += 1) {\n indices.add(index);\n }\n\n return Array.from(indices).sort((left, right) => left - right);\n }, [\n displayHeaderHeight,\n drawingViewport.height,\n drawingViewport.top,\n frozenRowVirtualIndices,\n rowPrefixSums,\n visibleRows.length\n ]);\n const canvasBaseColIndices = React.useMemo(() => {\n if (visibleCols.length === 0 || drawingViewport.width <= 0) {\n return [] as number[];\n }\n\n const indices = new Set<number>(frozenColVirtualIndices);\n const viewportStart = Math.max(0, drawingViewport.left - displayRowHeaderWidth - CANVAS_VIEWPORT_OVERSCAN_PX);\n const viewportEnd = Math.max(\n viewportStart,\n drawingViewport.left + drawingViewport.width - displayRowHeaderWidth + CANVAS_VIEWPORT_OVERSCAN_PX\n );\n const startIndex = findIndexForOffsetPrefix(colPrefixSums, viewportStart);\n const endIndex = findIndexForOffsetPrefix(colPrefixSums, viewportEnd);\n for (let index = Math.max(0, startIndex); index <= Math.max(startIndex, endIndex); index += 1) {\n indices.add(index);\n }\n\n return Array.from(indices).sort((left, right) => left - right);\n }, [\n colPrefixSums,\n displayRowHeaderWidth,\n drawingViewport.left,\n drawingViewport.width,\n frozenColVirtualIndices,\n visibleCols.length\n ]);\n const canvasExpandedWindow = React.useMemo(\n () => expandMergeAwareWindow(canvasBaseRowIndices, canvasBaseColIndices, indexedMergedRegions),\n [canvasBaseColIndices, canvasBaseRowIndices, indexedMergedRegions]\n );\n const canvasVisibleRowItems = React.useMemo(\n () =>\n canvasExpandedWindow.rowIndices.map((index) => ({\n actualRow: visibleRows[index] ?? index,\n index,\n size: displayEffectiveRowHeights[index] ?? displayDefaultRowHeight,\n start: rowPrefixSums[index] ?? 0\n })),\n [canvasExpandedWindow.rowIndices, displayDefaultRowHeight, displayEffectiveRowHeights, rowPrefixSums, visibleRows]\n );\n const canvasVisibleColItems = React.useMemo(\n () =>\n canvasExpandedWindow.colIndices.map((index) => ({\n actualCol: visibleCols[index] ?? index,\n index,\n size: displayEffectiveColWidths[index] ?? displayDefaultColWidth,\n start: colPrefixSums[index] ?? 0\n })),\n [canvasExpandedWindow.colIndices, colPrefixSums, displayDefaultColWidth, displayEffectiveColWidths, visibleCols]\n );\n const canvasPaneAxisItems = React.useMemo(() => {\n const frozenRowIndexSet = new Set(frozenRows\n .map((actualRow) => rowIndexByActual.get(actualRow))\n .filter((index): index is number => index !== undefined));\n const frozenColIndexSet = new Set(frozenCols\n .map((actualCol) => colIndexByActual.get(actualCol))\n .filter((index): index is number => index !== undefined));\n\n const frozenRowItems = canvasVisibleRowItems.filter((item) => frozenRowIndexSet.has(item.index));\n const scrollRowItems = canvasVisibleRowItems.filter((item) => !frozenRowIndexSet.has(item.index));\n const frozenColItems = canvasVisibleColItems.filter((item) => frozenColIndexSet.has(item.index));\n const scrollColItems = canvasVisibleColItems.filter((item) => !frozenColIndexSet.has(item.index));\n\n return {\n corner: {\n cols: frozenColItems,\n rows: frozenRowItems\n },\n left: {\n cols: frozenColItems,\n rows: scrollRowItems\n },\n scroll: {\n cols: scrollColItems,\n rows: scrollRowItems\n },\n top: {\n cols: scrollColItems,\n rows: frozenRowItems\n }\n } satisfies Record<FrozenDrawingPane, {\n cols: Array<{ actualCol: number; index: number; size: number; start: number }>;\n rows: Array<{ actualRow: number; index: number; size: number; start: number }>;\n }>;\n }, [canvasVisibleColItems, canvasVisibleRowItems, colIndexByActual, frozenCols, frozenRows, rowIndexByActual]);\n const totalContentWidth = colPrefixSums[colPrefixSums.length - 1] ?? 0;\n const leadingColumnSpacerWidth = shouldVirtualizeCols ? (virtualCols[0]?.start ?? 0) : 0;\n const trailingColumnSpacerWidth = shouldVirtualizeCols\n ? totalContentWidth - (virtualCols[virtualCols.length - 1]?.end ?? 0)\n : 0;\n const imageRects = React.useMemo(\n () =>\n showImages\n ? images.map((image) => ({\n image,\n rect:\n imagePreviewRect && imagePreviewRect.id === image.id\n ? imagePreviewRect.rect\n : resolveImageRect(image, visibleRows, visibleCols, displayEffectiveRowHeights, displayEffectiveColWidths, {\n actualColPrefixSums,\n actualRowPrefixSums,\n colIndexByActual,\n colPrefixSums,\n headerHeight: displayHeaderHeight,\n rowIndexByActual,\n rowHeaderWidth: displayRowHeaderWidth,\n rowPrefixSums,\n zoomFactor\n })\n }))\n : [],\n [\n colIndexByActual,\n colPrefixSums,\n actualColPrefixSums,\n actualRowPrefixSums,\n displayHeaderHeight,\n displayEffectiveColWidths,\n displayEffectiveRowHeights,\n displayRowHeaderWidth,\n imagePreviewRect,\n images,\n rowIndexByActual,\n rowPrefixSums,\n showImages,\n visibleCols,\n visibleRows,\n zoomFactor\n ]\n );\n const shapeRects = React.useMemo(\n () =>\n showImages\n ? shapes.map((shape) => ({\n rect: resolveAnchoredRect(shape.anchor, visibleRows, visibleCols, displayEffectiveRowHeights, displayEffectiveColWidths, {\n actualColPrefixSums,\n actualRowPrefixSums,\n colIndexByActual,\n colPrefixSums,\n headerHeight: displayHeaderHeight,\n rowIndexByActual,\n rowHeaderWidth: displayRowHeaderWidth,\n rowPrefixSums,\n zoomFactor\n }),\n shape\n }))\n : [],\n [\n colIndexByActual,\n colPrefixSums,\n actualColPrefixSums,\n actualRowPrefixSums,\n displayHeaderHeight,\n displayEffectiveColWidths,\n displayEffectiveRowHeights,\n displayRowHeaderWidth,\n rowIndexByActual,\n rowPrefixSums,\n shapes,\n showImages,\n visibleCols,\n visibleRows,\n zoomFactor\n ]\n );\n const formControlRects = React.useMemo(\n () =>\n showImages\n ? formControls\n .filter((control) => !control.hidden)\n .map((control) => ({\n control,\n rect: resolveAnchoredRect(control.anchor, visibleRows, visibleCols, displayEffectiveRowHeights, displayEffectiveColWidths, {\n actualColPrefixSums,\n actualRowPrefixSums,\n colIndexByActual,\n colPrefixSums,\n headerHeight: displayHeaderHeight,\n rowIndexByActual,\n rowHeaderWidth: displayRowHeaderWidth,\n rowPrefixSums,\n zoomFactor\n })\n }))\n : [],\n [\n actualColPrefixSums,\n actualRowPrefixSums,\n colIndexByActual,\n colPrefixSums,\n displayHeaderHeight,\n displayEffectiveColWidths,\n displayEffectiveRowHeights,\n displayRowHeaderWidth,\n formControls,\n rowIndexByActual,\n rowPrefixSums,\n showImages,\n visibleCols,\n visibleRows,\n zoomFactor\n ]\n );\n const chartRects = React.useMemo(\n () =>\n showImages\n ? charts.map((chart) => ({\n chart,\n rect:\n chartPreviewRect && chartPreviewRect.id === chart.id\n ? chartPreviewRect.rect\n : resolveAnchoredRect(chart.anchor, visibleRows, visibleCols, displayEffectiveRowHeights, displayEffectiveColWidths, {\n actualColPrefixSums,\n actualRowPrefixSums,\n colIndexByActual,\n colPrefixSums,\n headerHeight: displayHeaderHeight,\n rowIndexByActual,\n rowHeaderWidth: displayRowHeaderWidth,\n rowPrefixSums,\n zoomFactor\n })\n }))\n : [],\n [\n actualColPrefixSums,\n actualRowPrefixSums,\n chartPreviewRect,\n charts,\n colIndexByActual,\n colPrefixSums,\n displayHeaderHeight,\n displayEffectiveColWidths,\n displayEffectiveRowHeights,\n displayRowHeaderWidth,\n rowIndexByActual,\n rowPrefixSums,\n showImages,\n visibleCols,\n visibleRows,\n zoomFactor\n ]\n );\n const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);\n const shouldBakeCanvasImages = Boolean(shouldBakeCanvasStaticDrawings && !renderImage && !renderImageSelection);\n const bakedShapeRects = React.useMemo(\n () => (shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => !shape.hyperlink) : []),\n [shapeRects, shouldBakeCanvasStaticDrawings]\n );\n const domShapeRects = React.useMemo(\n () => (shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => shape.hyperlink) : shapeRects),\n [shapeRects, shouldBakeCanvasStaticDrawings]\n );\n const domFormControlRects = formControlRects;\n const bakedImageRects = React.useMemo(\n () => (\n shouldBakeCanvasImages\n ? imageRects.filter(({ image }) => !image.hyperlink && selectedImageId !== image.id)\n : []\n ),\n [imageRects, selectedImageId, shouldBakeCanvasImages]\n );\n const domImageRects = React.useMemo(\n () => (\n shouldBakeCanvasImages\n ? imageRects.filter(({ image }) => image.hyperlink || selectedImageId === image.id)\n : imageRects\n ),\n [imageRects, selectedImageId, shouldBakeCanvasImages]\n );\n const hasCanvasDomDrawingOverlays = Boolean(\n showImages\n && (\n chartRects.length > 0\n || domShapeRects.length > 0\n || domFormControlRects.length > 0\n || domImageRects.length > 0\n )\n );\n const bakedCanvasDrawingSignature = React.useMemo(() => {\n if (!shouldBakeCanvasStaticDrawings) {\n return \"\";\n }\n\n const shapeSignature = bakedShapeRects\n .map(({ rect, shape }) => `s:${shape.id}:${shape.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`)\n .join(\"|\");\n const imageSignature = bakedImageRects\n .map(({ image, rect }) => `i:${image.id}:${image.src}:${image.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`)\n .join(\"|\");\n return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${imageSignature}`;\n }, [\n bakedImageRects,\n bakedShapeRects,\n canvasImageLoadVersion,\n revision,\n shouldBakeCanvasStaticDrawings\n ]);\n\n const resolveMountedCellOverlayRect = React.useCallback((element: HTMLElement) => {\n const wrapper = wrapperRef.current;\n if (!wrapper) {\n return null;\n }\n\n const wrapperRect = wrapper.getBoundingClientRect();\n const elementRect = element.getBoundingClientRect();\n const logicalWidth = wrapper.offsetWidth || 1;\n const logicalHeight = wrapper.offsetHeight || 1;\n const scaleX = Math.max(0.0001, wrapperRect.width > 0 ? wrapperRect.width / logicalWidth : 1);\n const scaleY = Math.max(0.0001, wrapperRect.height > 0 ? wrapperRect.height / logicalHeight : 1);\n\n return {\n height: elementRect.height / scaleY,\n left: (elementRect.left - wrapperRect.left) / scaleX,\n top: (elementRect.top - wrapperRect.top) / scaleY,\n width: elementRect.width / scaleX\n };\n }, []);\n\n const resolveMountedCellOverlayRectForAddress = React.useCallback((cell: XlsxCellAddress) => {\n const wrapper = wrapperRef.current;\n if (!wrapper) {\n return null;\n }\n\n const element = wrapper.querySelector<HTMLElement>(`[data-xlsx-cell=\"${cell.row}:${cell.col}\"]`);\n if (!element) {\n return null;\n }\n\n return resolveMountedCellOverlayRect(element);\n }, [resolveMountedCellOverlayRect]);\n\n const resolveGeometryOverlayRect = React.useCallback((range: XlsxCellRange) => {\n const normalized = normalizeRange(range);\n const isSingleCellSelection = normalized.start.row === normalized.end.row && normalized.start.col === normalized.end.col;\n const mergedRange = isSingleCellSelection ? findMergedRange(normalized.start) : null;\n const startCell = mergedRange?.start ?? normalized.start;\n const endCell = mergedRange?.end ?? normalized.end;\n const startRowIndex = rowIndexByActual.get(startCell.row);\n const endRowIndex = rowIndexByActual.get(endCell.row);\n const startColIndex = colIndexByActual.get(startCell.col);\n const endColIndex = colIndexByActual.get(endCell.col);\n\n if (\n startRowIndex === undefined ||\n endRowIndex === undefined ||\n startColIndex === undefined ||\n endColIndex === undefined\n ) {\n return null;\n }\n\n let left = displayRowHeaderWidth + sumPrefixRange(colPrefixSums, 0, startColIndex - 1);\n let top = displayHeaderHeight + sumPrefixRange(rowPrefixSums, 0, startRowIndex - 1);\n let width = sumPrefixRange(colPrefixSums, startColIndex, endColIndex);\n let height = sumPrefixRange(rowPrefixSums, startRowIndex, endRowIndex);\n\n const columnPreview = columnPreviewRef.current;\n if (columnPreview) {\n const previewIndex = colIndexByActual.get(columnPreview.actualIndex);\n if (previewIndex !== undefined) {\n const baseWidth = displayEffectiveColWidths[previewIndex] ?? displayDefaultColWidth;\n const widthDelta = columnPreview.size - baseWidth;\n if (previewIndex < startColIndex) {\n left += widthDelta;\n }\n if (previewIndex >= startColIndex && previewIndex <= endColIndex) {\n width += widthDelta;\n }\n }\n }\n\n const rowPreview = rowPreviewRef.current;\n if (rowPreview) {\n const previewIndex = rowIndexByActual.get(rowPreview.actualIndex);\n if (previewIndex !== undefined) {\n const baseHeight = displayEffectiveRowHeights[previewIndex] ?? displayDefaultRowHeight;\n const heightDelta = rowPreview.size - baseHeight;\n if (previewIndex < startRowIndex) {\n top += heightDelta;\n }\n if (previewIndex >= startRowIndex && previewIndex <= endRowIndex) {\n height += heightDelta;\n }\n }\n }\n\n return {\n height: Math.max(0, height),\n left: Math.max(displayRowHeaderWidth, left),\n top: Math.max(displayHeaderHeight, top),\n width: Math.max(0, width)\n };\n }, [colIndexByActual, colPrefixSums, displayDefaultColWidth, displayDefaultRowHeight, displayEffectiveColWidths, displayEffectiveRowHeights, displayHeaderHeight, displayRowHeaderWidth, findMergedRange, rowIndexByActual, rowPrefixSums]);\n\n const resolveMountedRangeOverlayRect = React.useCallback((range: XlsxCellRange, geometryRect: {\n height: number;\n left: number;\n top: number;\n width: number;\n }) => {\n const normalized = normalizeRange(range);\n const startRect = resolveMountedCellOverlayRectForAddress(normalized.start);\n const topRightRect = resolveMountedCellOverlayRectForAddress({ row: normalized.start.row, col: normalized.end.col });\n const bottomLeftRect = resolveMountedCellOverlayRectForAddress({ row: normalized.end.row, col: normalized.start.col });\n const endRect = resolveMountedCellOverlayRectForAddress(normalized.end);\n\n const leftRect = startRect ?? bottomLeftRect;\n const topRect = startRect ?? topRightRect;\n const rightRect = topRightRect ?? endRect;\n const bottomRect = bottomLeftRect ?? endRect;\n\n const left = leftRect ? leftRect.left : geometryRect.left;\n const top = topRect ? topRect.top : geometryRect.top;\n const right = rightRect ? rightRect.left + rightRect.width : geometryRect.left + geometryRect.width;\n const bottom = bottomRect ? bottomRect.top + bottomRect.height : geometryRect.top + geometryRect.height;\n\n return {\n height: Math.max(0, bottom - top),\n left: Math.max(displayRowHeaderWidth, left),\n top: Math.max(displayHeaderHeight, top),\n width: Math.max(0, right - left)\n };\n }, [displayHeaderHeight, displayRowHeaderWidth, resolveMountedCellOverlayRectForAddress]);\n\n const resolveDragPreviewRect = React.useCallback((range: XlsxCellRange) => {\n const dragState = selectionDragRef.current;\n if (!dragState || !dragState.didDrag || dragState.axis !== \"cell\" || !dragState.originOverlayRect) {\n return null;\n }\n\n const rangeRect = resolveGeometryOverlayRect(range);\n if (!rangeRect) {\n return null;\n }\n\n const originRect = dragState.originOverlayRect;\n const normalized = normalizeRange(range);\n if (rangesEqual(normalized, { start: dragState.originCell, end: dragState.originCell })) {\n return originRect;\n }\n\n const originCell = dragState.originCell;\n const originOnLeft = originCell.col === normalized.start.col;\n const originOnTop = originCell.row === normalized.start.row;\n const adjustedRangeRect = resolveMountedRangeOverlayRect(range, rangeRect);\n const geometryRight = adjustedRangeRect.left + adjustedRangeRect.width;\n const geometryBottom = adjustedRangeRect.top + adjustedRangeRect.height;\n const originRight = originRect.left + originRect.width;\n const originBottom = originRect.top + originRect.height;\n const left = originOnLeft ? originRect.left : Math.min(originRect.left, adjustedRangeRect.left);\n const top = originOnTop ? originRect.top : Math.min(originRect.top, adjustedRangeRect.top);\n const right = originOnLeft ? Math.max(originRight, geometryRight) : originRight;\n const bottom = originOnTop ? Math.max(originBottom, geometryBottom) : originBottom;\n\n return {\n height: Math.max(originRect.height, bottom - top),\n left,\n top,\n width: Math.max(originRect.width, right - left)\n };\n }, [resolveGeometryOverlayRect, resolveMountedRangeOverlayRect]);\n\n const resolveOverlayRect = React.useCallback((range: XlsxCellRange) => {\n const normalized = normalizeRange(range);\n if (normalized.start.row === normalized.end.row && normalized.start.col === normalized.end.col) {\n const rect = resolveMountedCellOverlayRectForAddress(normalized.start);\n if (rect) {\n return rect;\n }\n }\n\n const geometryRect = resolveGeometryOverlayRect(range);\n if (geometryRect) {\n return resolveMountedRangeOverlayRect(range, geometryRect);\n }\n\n return null;\n }, [\n resolveGeometryOverlayRect,\n resolveMountedRangeOverlayRect,\n ]);\n\n const resolveCellDisplayRect = React.useCallback((cell: XlsxCellAddress) => {\n const rowIndex = rowIndexByActual.get(cell.row);\n const colIndex = colIndexByActual.get(cell.col);\n if (rowIndex === undefined || colIndex === undefined) {\n return null;\n }\n\n const cellData = getCellData(cell.row, cell.col);\n const colSpan = Math.max(1, cellData.colSpan ?? 1);\n const rowSpan = Math.max(1, cellData.rowSpan ?? 1);\n let endActualCol = Math.min(displayColLimit - 1, cell.col + colSpan - 1);\n let endActualRow = Math.min(displayRowLimit - 1, cell.row + rowSpan - 1);\n\n if (worksheet && (colSpan > 1 || rowSpan > 1)) {\n for (let nextCol = cell.col + 1; nextCol < displayColLimit; nextCol += 1) {\n const nextAnchor = resolveMergeAnchorCell({ row: cell.row, col: nextCol });\n if (nextAnchor.row !== cell.row || nextAnchor.col !== cell.col) {\n break;\n }\n endActualCol = nextCol;\n }\n for (let nextRow = cell.row + 1; nextRow < displayRowLimit; nextRow += 1) {\n const nextAnchor = resolveMergeAnchorCell({ row: nextRow, col: cell.col });\n if (nextAnchor.row !== cell.row || nextAnchor.col !== cell.col) {\n break;\n }\n endActualRow = nextRow;\n }\n }\n\n return {\n height: sumPrefixRange(actualRowPrefixSums, cell.row, endActualRow),\n left: displayRowHeaderWidth + sumPrefixRange(actualColPrefixSums, 0, cell.col - 1),\n top: displayHeaderHeight + sumPrefixRange(actualRowPrefixSums, 0, cell.row - 1),\n width: sumPrefixRange(actualColPrefixSums, cell.col, endActualCol)\n };\n }, [\n actualColPrefixSums,\n actualRowPrefixSums,\n colIndexByActual,\n displayColLimit,\n displayHeaderHeight,\n displayRowLimit,\n displayRowHeaderWidth,\n getCellData,\n resolveMergeAnchorCell,\n rowIndexByActual,\n worksheet\n ]);\n\n const openTableMenuState = React.useMemo(() => {\n if (!openTableMenu) {\n return null;\n }\n\n const table = effectiveTables.find((entry) => entry.name === openTableMenu.tableName) ?? null;\n const column = getTableHeaderColumn(table, openTableMenu.row, openTableMenu.col);\n const wrapper = wrapperRef.current;\n if (!table || !column || !wrapper) {\n return null;\n }\n\n const cell = wrapper.querySelector<HTMLElement>(`[data-xlsx-cell=\"${openTableMenu.row}:${openTableMenu.col}\"]`);\n if (cell) {\n return {\n column,\n left: cell.offsetLeft + cell.offsetWidth - 170,\n table,\n top: cell.offsetTop + cell.offsetHeight - 2\n };\n }\n\n const rect = resolveCellDisplayRect({ row: openTableMenu.row, col: openTableMenu.col });\n if (!rect) {\n return null;\n }\n\n return {\n column,\n left: rect.left + rect.width - 170,\n table,\n top: rect.top + rect.height - 2\n };\n }, [effectiveTables, openTableMenu, resolveCellDisplayRect]);\n\n const applyHeaderSelection = React.useCallback((range: XlsxCellRange | null) => {\n if (experimentalCanvas) {\n return;\n }\n\n const normalized = range ? normalizeRange(range) : null;\n\n rowHeaderCellRefs.current.forEach((element, actualRow) => {\n element.style.backgroundColor = normalized && actualRow >= normalized.start.row && actualRow <= normalized.end.row\n ? \"var(--xlsx-selection-header)\"\n : palette.rowHeaderSurface;\n });\n\n colHeaderCellRefs.current.forEach((element, actualCol) => {\n element.style.backgroundColor = normalized && actualCol >= normalized.start.col && actualCol <= normalized.end.col\n ? \"var(--xlsx-selection-header)\"\n : palette.headerSurface;\n });\n }, [experimentalCanvas, palette.headerSurface, palette.rowHeaderSurface]);\n\n const setRowHeaderRef = React.useCallback((actualRow: number, element: HTMLTableCellElement | null) => {\n if (experimentalCanvas) {\n rowHeaderCellRefs.current.delete(actualRow);\n return;\n }\n\n if (element) {\n rowHeaderCellRefs.current.set(actualRow, element);\n const range = selectionPreviewRangeRef.current ?? displayedSelectionRef.current;\n element.style.backgroundColor = range && actualRow >= range.start.row && actualRow <= range.end.row\n ? \"var(--xlsx-selection-header)\"\n : palette.rowHeaderSurface;\n return;\n }\n\n rowHeaderCellRefs.current.delete(actualRow);\n }, [experimentalCanvas, palette.rowHeaderSurface]);\n\n const setColHeaderRef = React.useCallback((actualCol: number, element: HTMLTableCellElement | null) => {\n if (experimentalCanvas) {\n colHeaderCellRefs.current.delete(actualCol);\n return;\n }\n\n if (element) {\n colHeaderCellRefs.current.set(actualCol, element);\n const range = selectionPreviewRangeRef.current ?? displayedSelectionRef.current;\n element.style.backgroundColor = range && actualCol >= range.start.col && actualCol <= range.end.col\n ? \"var(--xlsx-selection-header)\"\n : palette.headerSurface;\n return;\n }\n\n colHeaderCellRefs.current.delete(actualCol);\n }, [experimentalCanvas, palette.headerSurface]);\n\n const applyPreviewOverlay = React.useCallback((\n range: XlsxCellRange | null,\n options?: { preferGeometry?: boolean; updateHeaderSelection?: boolean }\n ) => {\n const overlays = experimentalCanvas\n ? [...selectionPaneOverlayRefs.current.values()]\n : [selectionOverlayRef.current];\n if (!range) {\n if (options?.updateHeaderSelection !== false) {\n applyHeaderSelection(range);\n }\n return;\n }\n\n const nextRect =\n experimentalCanvas || options?.preferGeometry\n ? resolveGeometryOverlayRect(range) ?? resolveOverlayRect(range)\n : selectionDragRef.current?.didDrag || fillDragRef.current\n ? resolveDragPreviewRect(range) ?? resolveGeometryOverlayRect(range) ?? resolveOverlayRect(range)\n : resolveOverlayRect(range);\n if (!nextRect) {\n if (options?.updateHeaderSelection !== false) {\n applyHeaderSelection(range);\n }\n return;\n }\n\n for (const overlay of overlays) {\n if (overlay) {\n overlay.style.left = `${nextRect.left}px`;\n overlay.style.top = `${nextRect.top}px`;\n overlay.style.width = `${nextRect.width}px`;\n overlay.style.height = `${nextRect.height}px`;\n overlay.style.opacity = \"1\";\n overlay.style.visibility = \"visible\";\n }\n }\n const fillHandles = experimentalCanvas\n ? [...selectionPaneFillHandleRefs.current.values()]\n : [fillHandleRef.current];\n for (const fillHandle of fillHandles) {\n if (fillHandle) {\n fillHandle.style.left = `${nextRect.left + nextRect.width - (4 * zoomFactor)}px`;\n fillHandle.style.top = `${nextRect.top + nextRect.height - (4 * zoomFactor)}px`;\n }\n }\n if (options?.updateHeaderSelection !== false) {\n applyHeaderSelection(range);\n }\n }, [applyHeaderSelection, experimentalCanvas, resolveDragPreviewRect, resolveGeometryOverlayRect, resolveOverlayRect, zoomFactor]);\n\n const applyPreviewOverlayFromElement = React.useCallback((element: HTMLElement, range: XlsxCellRange) => {\n if (experimentalCanvas || !isSameCell(range.start, range.end)) {\n applyPreviewOverlay(range);\n return;\n }\n const overlay = selectionOverlayRef.current;\n if (!overlay) {\n return;\n }\n\n const nextRect = resolveMountedCellOverlayRect(element) ?? resolveOverlayRect(range);\n if (!nextRect) {\n applyHeaderSelection(range);\n return;\n }\n\n overlay.style.left = `${nextRect.left}px`;\n overlay.style.top = `${nextRect.top}px`;\n overlay.style.width = `${nextRect.width}px`;\n overlay.style.height = `${nextRect.height}px`;\n overlay.style.opacity = \"1\";\n overlay.style.visibility = \"visible\";\n const fillHandle = fillHandleRef.current;\n if (fillHandle) {\n fillHandle.style.left = `${nextRect.left + nextRect.width - (4 * zoomFactor)}px`;\n fillHandle.style.top = `${nextRect.top + nextRect.height - (4 * zoomFactor)}px`;\n }\n applyHeaderSelection(range);\n }, [applyHeaderSelection, applyPreviewOverlay, experimentalCanvas, resolveMountedCellOverlayRect, resolveOverlayRect, zoomFactor]);\n\n const syncActiveValidationOverlay = React.useCallback((cell: XlsxCellAddress | null) => {\n const overlay = activeValidationOverlayRef.current;\n if (!overlay || !cell || editingCellRef.current || selectionDragRef.current || fillDragRef.current) {\n if (overlay) {\n overlay.style.opacity = \"0\";\n overlay.style.visibility = \"hidden\";\n }\n return;\n }\n\n const cellData = getCellData(cell.row, cell.col);\n const shouldShow = cellData.validation?.validationType === \"list\" && cellData.validation.showDropdown;\n const rect = shouldShow ? resolveOverlayRect({ start: cell, end: cell }) : null;\n if (!rect) {\n overlay.style.opacity = \"0\";\n overlay.style.visibility = \"hidden\";\n return;\n }\n\n overlay.style.left = `${rect.left + rect.width - (16 * zoomFactor)}px`;\n overlay.style.top = `${rect.top + (rect.height / 2)}px`;\n overlay.style.opacity = \"1\";\n overlay.style.visibility = \"visible\";\n }, [getCellData, resolveOverlayRect, zoomFactor]);\n\n const commitSelectionRange = React.useCallback((range: XlsxCellRange) => {\n gridKeyboardActiveRef.current = true;\n const normalized = normalizeRange(range);\n if (\n selectionRef.current &&\n rangesEqual(selectionRef.current, normalized) &&\n isSameCell(activeCellRef.current, normalized.end) &&\n selectedChartIdRef.current === null &&\n selectedImageIdRef.current === null\n ) {\n return;\n }\n\n pendingSelectionCommitRef.current = normalized;\n if (selectionCommitFrameRef.current !== null) {\n return;\n }\n\n selectionCommitFrameRef.current = window.requestAnimationFrame(() => {\n selectionCommitFrameRef.current = null;\n const pendingRange = pendingSelectionCommitRef.current;\n pendingSelectionCommitRef.current = null;\n if (!pendingRange) {\n return;\n }\n\n if (\n selectionRef.current &&\n rangesEqual(selectionRef.current, pendingRange) &&\n isSameCell(activeCellRef.current, pendingRange.end) &&\n selectedChartIdRef.current === null &&\n selectedImageIdRef.current === null\n ) {\n return;\n }\n\n startSelectionTransition(() => {\n selectRange(pendingRange);\n });\n });\n }, [selectRange, startSelectionTransition]);\n\n const refreshOverlayFromCurrentSelection = React.useCallback(() => {\n if (displayedSelectionRef.current) {\n applyPreviewOverlay(displayedSelectionRef.current);\n }\n }, [applyPreviewOverlay]);\n const resolveResizeGuidePositionFromClient = React.useCallback((\n type: \"column\" | \"row\",\n clientPosition: number\n ) => {\n const scroller = scrollRef.current;\n const scrollerRect = scroller?.getBoundingClientRect();\n if (!scroller || !scrollerRect) {\n return null;\n }\n\n if (type === \"column\") {\n return scroller.scrollLeft + (clientPosition - scrollerRect.left);\n }\n\n return scroller.scrollTop + (clientPosition - scrollerRect.top);\n }, []);\n\n React.useEffect(() => {\n function handlePointerMove(event: PointerEvent) {\n const state = resizeStateRef.current;\n if (!state) {\n return;\n }\n\n const size =\n state.type === \"column\"\n ? Math.max(displayDefaultColWidth / 2, state.initialPx + (event.clientX - state.startPosition))\n : Math.max(displayDefaultRowHeight / 1.5, state.initialPx + (event.clientY - state.startPosition));\n pendingResizePreviewRef.current = {\n actualIndex: state.actualIndex,\n size,\n type: state.type\n };\n\n if (resizeFrameRef.current !== null) {\n return;\n }\n\n resizeFrameRef.current = window.requestAnimationFrame(() => {\n resizeFrameRef.current = null;\n const preview = pendingResizePreviewRef.current;\n if (!preview) {\n return;\n }\n\n if (preview.type === \"column\") {\n columnPreviewRef.current = { actualIndex: preview.actualIndex, size: preview.size };\n rowPreviewRef.current = null;\n if (displayedSelectionRef.current) {\n applyPreviewOverlay(displayedSelectionRef.current, { preferGeometry: true, updateHeaderSelection: false });\n }\n const position = resolveResizeGuidePositionFromClient(\"column\", event.clientX);\n setResizeGuide(position === null ? null : { position, type: \"column\" });\n setGlobalCursor(\"col-resize\");\n return;\n }\n\n rowPreviewRef.current = { actualIndex: preview.actualIndex, size: preview.size };\n columnPreviewRef.current = null;\n if (displayedSelectionRef.current) {\n applyPreviewOverlay(displayedSelectionRef.current, { preferGeometry: true, updateHeaderSelection: false });\n }\n const position = resolveResizeGuidePositionFromClient(\"row\", event.clientY);\n setResizeGuide(position === null ? null : { position, type: \"row\" });\n setGlobalCursor(\"row-resize\");\n });\n }\n\n function handlePointerUp(event: PointerEvent) {\n if (resizeStateRef.current?.pointerId === event.pointerId) {\n const resizeState = resizeStateRef.current;\n const preview = pendingResizePreviewRef.current;\n resizeStateRef.current = null;\n pendingResizePreviewRef.current = null;\n columnPreviewRef.current = null;\n rowPreviewRef.current = null;\n setInteractionMode(\"idle\");\n setResizeGuide(null);\n clearGlobalCursor();\n document.body.style.userSelect = \"\";\n\n if (resizeFrameRef.current !== null) {\n window.cancelAnimationFrame(resizeFrameRef.current);\n resizeFrameRef.current = null;\n }\n\n if (preview && preview.actualIndex === resizeState.actualIndex && preview.type === resizeState.type) {\n if (preview.type === \"column\") {\n controller.resizeColumn(preview.actualIndex, preview.size / zoomFactor);\n } else {\n controller.resizeRow(preview.actualIndex, preview.size / zoomFactor);\n }\n }\n }\n }\n\n window.addEventListener(\"pointermove\", handlePointerMove);\n window.addEventListener(\"pointerup\", handlePointerUp);\n window.addEventListener(\"pointercancel\", handlePointerUp);\n\n return () => {\n window.removeEventListener(\"pointermove\", handlePointerMove);\n window.removeEventListener(\"pointerup\", handlePointerUp);\n window.removeEventListener(\"pointercancel\", handlePointerUp);\n clearGlobalCursor();\n if (resizeFrameRef.current !== null) {\n window.cancelAnimationFrame(resizeFrameRef.current);\n resizeFrameRef.current = null;\n }\n };\n }, [\n applyPreviewOverlay,\n clearGlobalCursor,\n controller,\n displayDefaultColWidth,\n displayDefaultRowHeight,\n resolveResizeGuidePositionFromClient,\n setGlobalCursor,\n zoomFactor\n ]);\n\n function buildDraggedSelectionRange(\n dragState: { anchor: XlsxCellAddress; axis: \"cell\" | \"column\" | \"row\" },\n cell: XlsxCellAddress\n ): XlsxCellRange | null {\n if (dragState.axis === \"row\") {\n if (firstVisibleCol === undefined || lastVisibleCol === undefined) {\n return null;\n }\n\n return normalizeRange({\n start: { row: dragState.anchor.row, col: firstVisibleCol },\n end: { row: cell.row, col: lastVisibleCol }\n });\n }\n\n if (dragState.axis === \"column\") {\n if (firstVisibleRow === undefined || lastVisibleRow === undefined) {\n return null;\n }\n\n return normalizeRange({\n start: { row: firstVisibleRow, col: dragState.anchor.col },\n end: { row: lastVisibleRow, col: cell.col }\n });\n }\n\n return resolveCellSelectionRange({ start: dragState.anchor, end: cell });\n }\n\n function updateSelectionDragPreview(clientX: number, clientY: number) {\n const dragState = selectionDragRef.current;\n if (!dragState) {\n return;\n }\n\n const nextCell = resolveDraggedSelectionCell(dragState, clientX, clientY);\n if (!nextCell) {\n return;\n }\n\n const nextRange = buildDraggedSelectionRange(dragState, nextCell);\n if (!nextRange || rangesEqual(nextRange, dragState.previewRange)) {\n return;\n }\n\n dragState.previewRange = nextRange;\n selectionPreviewRangeRef.current = nextRange;\n displayedSelectionRef.current = nextRange;\n applyPreviewOverlay(nextRange);\n }\n\n function clearPendingSelectionDrag() {\n const cleanup = pendingSelectionDragCleanupRef.current;\n pendingSelectionDragCleanupRef.current = null;\n pendingSelectionDragRef.current = null;\n cleanup?.();\n }\n\n function finishPendingSelectionDrag() {\n const pendingState = pendingSelectionDragRef.current;\n clearPendingSelectionDrag();\n if (pendingState && !pendingState.committedOnPointerDown) {\n commitSelectionRange(pendingState.previewRange);\n }\n }\n\n function promotePendingSelectionDrag(clientX: number, clientY: number) {\n const pendingState = pendingSelectionDragRef.current;\n if (!pendingState) {\n return;\n }\n\n clearPendingSelectionDrag();\n cachedScrollerRectRef.current = scrollRef.current?.getBoundingClientRect() ?? null;\n pendingState.didDrag = true;\n selectionDragRef.current = pendingState;\n selectionPreviewRangeRef.current = pendingState.previewRange;\n displayedSelectionRef.current = pendingState.previewRange;\n setInteractionMode(\"select\");\n document.body.style.userSelect = \"none\";\n installSelectionDragListeners(pendingState.pointerId);\n updateSelectionDragPreview(clientX, clientY);\n }\n\n function installPendingSelectionDragListeners(pointerId: number, target: Element) {\n const existingCleanup = pendingSelectionDragCleanupRef.current;\n pendingSelectionDragCleanupRef.current = null;\n existingCleanup?.();\n\n const pointerTarget = target as Element & {\n hasPointerCapture?: (pointerId: number) => boolean;\n releasePointerCapture?: (pointerId: number) => void;\n setPointerCapture?: (pointerId: number) => void;\n };\n\n try {\n pointerTarget.setPointerCapture?.(pointerId);\n } catch {\n // Pointer capture can fail if the initiating element unmounts before the browser applies it.\n }\n\n const handlePointerMove = (event: Event) => {\n const pointerEvent = event as PointerEvent;\n if (pointerEvent.pointerId !== pointerId) {\n return;\n }\n\n const pendingState = pendingSelectionDragRef.current;\n if (!pendingState) {\n return;\n }\n\n const deltaX = Math.abs(pointerEvent.clientX - pendingState.startClientX);\n const deltaY = Math.abs(pointerEvent.clientY - pendingState.startClientY);\n if (deltaX < SELECTION_DRAG_THRESHOLD_PX && deltaY < SELECTION_DRAG_THRESHOLD_PX) {\n return;\n }\n\n event.preventDefault();\n promotePendingSelectionDrag(pointerEvent.clientX, pointerEvent.clientY);\n };\n\n const handlePointerEnd = (event: Event) => {\n const pointerEvent = event as PointerEvent;\n if (pointerEvent.pointerId !== pointerId) {\n return;\n }\n\n finishPendingSelectionDrag();\n };\n\n target.addEventListener(\"pointermove\", handlePointerMove);\n target.addEventListener(\"pointerup\", handlePointerEnd);\n target.addEventListener(\"pointercancel\", handlePointerEnd);\n pendingSelectionDragCleanupRef.current = () => {\n target.removeEventListener(\"pointermove\", handlePointerMove);\n target.removeEventListener(\"pointerup\", handlePointerEnd);\n target.removeEventListener(\"pointercancel\", handlePointerEnd);\n try {\n if (pointerTarget.hasPointerCapture?.(pointerId)) {\n pointerTarget.releasePointerCapture?.(pointerId);\n }\n } catch {\n // Pointer capture may already be released by the browser on pointerup/cancel.\n }\n };\n }\n\n function installSelectionDragListeners(pointerId: number) {\n selectionDragCleanupRef.current?.();\n let pendingClientPoint: { x: number; y: number } | null = null;\n let pointerMoveFrame: number | null = null;\n\n const flushPointerMove = () => {\n pointerMoveFrame = null;\n const pendingPoint = pendingClientPoint;\n pendingClientPoint = null;\n if (!pendingPoint) {\n return;\n }\n\n const dragState = selectionDragRef.current;\n if (!dragState) {\n return;\n }\n\n if (!dragState.didDrag) {\n const deltaX = Math.abs(pendingPoint.x - dragState.startClientX);\n const deltaY = Math.abs(pendingPoint.y - dragState.startClientY);\n if (deltaX < SELECTION_DRAG_THRESHOLD_PX && deltaY < SELECTION_DRAG_THRESHOLD_PX) {\n return;\n }\n\n dragState.didDrag = true;\n }\n\n updateSelectionDragPreview(pendingPoint.x, pendingPoint.y);\n };\n\n const handlePointerMove = (event: PointerEvent) => {\n if (event.pointerId !== pointerId) {\n return;\n }\n pendingClientPoint = { x: event.clientX, y: event.clientY };\n if (pointerMoveFrame !== null) {\n return;\n }\n pointerMoveFrame = window.requestAnimationFrame(flushPointerMove);\n };\n\n const finishSelectionDrag = (event: PointerEvent) => {\n if (event.pointerId !== pointerId) {\n return;\n }\n if (pointerMoveFrame !== null) {\n window.cancelAnimationFrame(pointerMoveFrame);\n pointerMoveFrame = null;\n }\n pendingClientPoint = null;\n\n const dragState = selectionDragRef.current;\n const nextCell = dragState ? resolveDraggedSelectionCell(dragState, event.clientX, event.clientY) : null;\n let nextRange = dragState?.previewRange ?? null;\n if (dragState?.didDrag && nextCell && dragState) {\n nextRange = buildDraggedSelectionRange(dragState, nextCell);\n }\n\n selectionDragRef.current = null;\n cachedScrollerRectRef.current = null;\n if (nextRange && (dragState?.didDrag || !dragState?.committedOnPointerDown)) {\n selectionPreviewRangeRef.current = nextRange;\n displayedSelectionRef.current = nextRange;\n if (dragState?.axis === \"column\") {\n const normalized = normalizeRange(nextRange);\n axisSelectionRef.current = {\n axis: \"column\",\n startCol: normalized.start.col,\n endCol: normalized.end.col\n };\n } else if (dragState?.axis === \"row\") {\n const normalized = normalizeRange(nextRange);\n axisSelectionRef.current = {\n axis: \"row\",\n startRow: normalized.start.row,\n endRow: normalized.end.row\n };\n } else {\n axisSelectionRef.current = null;\n }\n commitSelectionRange(nextRange);\n } else if (!nextRange) {\n selectionPreviewRangeRef.current = null;\n }\n setInteractionMode(\"idle\");\n document.body.style.cursor = \"\";\n document.body.style.userSelect = \"\";\n selectionDragCleanupRef.current?.();\n selectionDragCleanupRef.current = null;\n };\n\n window.addEventListener(\"pointermove\", handlePointerMove);\n window.addEventListener(\"pointerup\", finishSelectionDrag);\n window.addEventListener(\"pointercancel\", finishSelectionDrag);\n selectionDragCleanupRef.current = () => {\n if (pointerMoveFrame !== null) {\n window.cancelAnimationFrame(pointerMoveFrame);\n pointerMoveFrame = null;\n }\n pendingClientPoint = null;\n window.removeEventListener(\"pointermove\", handlePointerMove);\n window.removeEventListener(\"pointerup\", finishSelectionDrag);\n window.removeEventListener(\"pointercancel\", finishSelectionDrag);\n };\n }\n\n function installFillDragListeners(pointerId: number, sourceRange: XlsxCellRange) {\n fillDragCleanupRef.current?.();\n let pendingClientPoint: { x: number; y: number } | null = null;\n let pointerMoveFrame: number | null = null;\n\n const flushPointerMove = () => {\n pointerMoveFrame = null;\n const pendingPoint = pendingClientPoint;\n pendingClientPoint = null;\n if (!pendingPoint) {\n return;\n }\n const nextCell = resolvePointerCellFromClient(pendingPoint.x, pendingPoint.y);\n if (nextCell) {\n updateFillPreview(nextCell);\n }\n };\n\n const handlePointerMove = (event: PointerEvent) => {\n if (event.pointerId !== pointerId) {\n return;\n }\n pendingClientPoint = { x: event.clientX, y: event.clientY };\n if (pointerMoveFrame !== null) {\n return;\n }\n pointerMoveFrame = window.requestAnimationFrame(flushPointerMove);\n };\n\n const finishFillDrag = (event: PointerEvent) => {\n if (event.pointerId !== pointerId) {\n return;\n }\n if (pointerMoveFrame !== null) {\n window.cancelAnimationFrame(pointerMoveFrame);\n pointerMoveFrame = null;\n }\n pendingClientPoint = null;\n\n const nextCell = resolvePointerCellFromClient(event.clientX, event.clientY);\n if (nextCell) {\n updateFillPreview(nextCell);\n }\n\n const nextRange = fillDragRef.current?.previewRange ?? sourceRange;\n fillDragRef.current = null;\n cachedScrollerRectRef.current = null;\n setInteractionMode(\"idle\");\n setFillPreviewRange(null);\n document.body.style.cursor = \"\";\n document.body.style.userSelect = \"\";\n fillSelection(nextRange);\n fillDragCleanupRef.current?.();\n fillDragCleanupRef.current = null;\n };\n\n window.addEventListener(\"pointermove\", handlePointerMove);\n window.addEventListener(\"pointerup\", finishFillDrag);\n window.addEventListener(\"pointercancel\", finishFillDrag);\n fillDragCleanupRef.current = () => {\n if (pointerMoveFrame !== null) {\n window.cancelAnimationFrame(pointerMoveFrame);\n pointerMoveFrame = null;\n }\n pendingClientPoint = null;\n window.removeEventListener(\"pointermove\", handlePointerMove);\n window.removeEventListener(\"pointerup\", finishFillDrag);\n window.removeEventListener(\"pointercancel\", finishFillDrag);\n };\n }\n\n React.useLayoutEffect(() => {\n const overlayRange = selectionPreviewRangeRef.current ?? displayedSelection;\n if (!overlayRange || !wrapperRef.current) {\n applyHeaderSelection(null);\n for (const overlay of [selectionOverlayRef.current, ...selectionPaneOverlayRefs.current.values()]) {\n if (overlay) {\n overlay.style.opacity = \"0\";\n overlay.style.visibility = \"hidden\";\n }\n }\n return;\n }\n\n applyPreviewOverlay(overlayRange);\n }, [applyHeaderSelection, applyPreviewOverlay, displayedSelection, revision]);\n\n React.useLayoutEffect(() => {\n syncActiveValidationOverlay(activeCell);\n }, [activeCell, editingCell, revision, syncActiveValidationOverlay]);\n\n const closeContextMenu = React.useCallback((restoreFocus = true) => {\n setContextMenu(null);\n if (restoreFocus) focusGrid({ preventScroll: true });\n }, [focusGrid]);\n\n function getContextExtent(kind: \"row\" | \"column\" | \"sheet\", range: XlsxCellRange): XlsxCellRange {\n return {\n start: {\n row: kind === \"row\" ? range.start.row : firstVisibleRow ?? 0,\n col: kind === \"column\" ? range.start.col : firstVisibleCol ?? 0\n },\n end: {\n row: kind === \"row\" ? range.end.row : lastVisibleRow ?? 0,\n col: kind === \"column\" ? range.end.col : lastVisibleCol ?? 0\n }\n };\n }\n\n function selectContextExtent(kind: \"row\" | \"column\" | \"sheet\", range: XlsxCellRange) {\n const nextRange = getContextExtent(kind, range);\n axisSelectionRef.current = kind === \"row\"\n ? { axis: \"row\", startRow: nextRange.start.row, endRow: nextRange.end.row }\n : kind === \"column\"\n ? { axis: \"column\", startCol: nextRange.start.col, endCol: nextRange.end.col }\n : null;\n selectRange(nextRange);\n }\n\n function openContextMenu(cell: XlsxCellAddress, kind: XlsxContextMenuContext[\"kind\"], position: { x: number; y: number }) {\n if (!enableContextMenu || !activeSheet) return;\n if (kind === \"cell\") cell = resolveMergeAnchorCell(cell);\n if (editingCellRef.current) commitEditingRef.current();\n const current = normalizedSelection;\n const inside = current && cell.row >= current.start.row && cell.row <= current.end.row &&\n cell.col >= current.start.col && cell.col <= current.end.col;\n let range = inside ? current : { start: cell, end: cell };\n if (kind !== \"cell\") {\n const sameAxis = axisSelectionRef.current?.axis === kind;\n range = getContextExtent(kind, sameAxis && inside ? range : { start: cell, end: cell });\n selectContextExtent(kind, range);\n } else if (!inside) {\n axisSelectionRef.current = null;\n selectCell(cell);\n }\n setOpenTableMenu(null);\n setContextMenu({\n context: { kind, cell, selection: range, sheetIndex: activeSheetIndex, sheetName: activeSheet.name, readOnly },\n position\n });\n }\n\n const handleCellDoubleClick = React.useCallback((cell: XlsxCellAddress) => {\n startEditing(cell);\n }, [startEditing]);\n\n const handleCellClick = React.useCallback((cell: XlsxCellAddress, cellData: CellRenderData) => {\n if (!cellData.hyperlink) {\n return;\n }\n\n openHyperlink(cellData.hyperlink.target, cellData.hyperlink.location);\n }, [openHyperlink]);\n\n const handleCellPointerDown = React.useCallback((\n event: React.PointerEvent<HTMLTableCellElement>,\n cell: XlsxCellAddress\n ) => {\n if (event.button !== 0) {\n return;\n }\n\n event.preventDefault();\n focusGrid();\n axisSelectionRef.current = null;\n const targetCell = resolveMergeAnchorCell(\n event.currentTarget.colSpan > 1 || event.currentTarget.rowSpan > 1\n ? resolvePointerCellFromGeometry(event.clientX, event.clientY) ?? cell\n : cell\n );\n const currentSelection = selectionRef.current;\n const anchor = event.shiftKey && currentSelection ? currentSelection.start : targetCell;\n const initialRange = resolveCellSelectionRange({ start: anchor, end: targetCell });\n const isActive = isSameCell(activeCellRef.current, targetCell);\n const committedOnPointerDown = !isActive || !editingCellRef.current;\n if (editingCellRef.current && !isActive) {\n commitEditingRef.current();\n }\n const pointerOrigin =\n targetCell.row === cell.row && targetCell.col === cell.col\n ? resolveCellPointerOrigin(cell, event.currentTarget.getBoundingClientRect(), event.clientX, event.clientY)\n : resolveCellPointerOriginFromClient(targetCell, event.clientX, event.clientY);\n const originOverlayRect =\n targetCell.row === cell.row && targetCell.col === cell.col\n ? resolveMountedCellOverlayRect(event.currentTarget)\n : resolveOverlayRect(initialRange);\n if (!pointerOrigin) {\n return;\n }\n\n startCellSelection(\n event.currentTarget,\n event.pointerId,\n anchor,\n \"cell\",\n targetCell,\n pointerOrigin,\n originOverlayRect,\n committedOnPointerDown,\n initialRange,\n event.clientX,\n event.clientY\n );\n if (targetCell.row === cell.row && targetCell.col === cell.col) {\n applyPreviewOverlayFromElement(event.currentTarget, initialRange);\n } else {\n applyPreviewOverlay(initialRange);\n }\n if (committedOnPointerDown) {\n commitSelectionRange(initialRange);\n }\n }, [\n applyPreviewOverlay,\n applyPreviewOverlayFromElement,\n commitSelectionRange,\n focusGrid,\n resolveCellPointerOrigin,\n resolveCellPointerOriginFromClient,\n resolveMountedCellOverlayRect,\n resolveOverlayRect,\n resolveCellSelectionRange,\n resolveMergeAnchorCell,\n resolvePointerCellFromGeometry\n ]);\n\n const handleRowPointerDown = React.useCallback((\n event: React.PointerEvent<HTMLTableCellElement>,\n actualRow: number\n ) => {\n if (event.button !== 0 || firstVisibleCol === undefined || lastVisibleCol === undefined) {\n return;\n }\n\n event.preventDefault();\n focusGrid();\n const currentSelection = selectionRef.current;\n const anchorRow = event.shiftKey && currentSelection ? currentSelection.start.row : actualRow;\n const initialRange = normalizeRange({\n start: { row: anchorRow, col: firstVisibleCol },\n end: { row: actualRow, col: lastVisibleCol }\n });\n const pointerOrigin = resolveRowPointerOrigin(actualRow, event.currentTarget.getBoundingClientRect(), event.clientY);\n if (!pointerOrigin) {\n return;\n }\n\n startCellSelection(\n event.currentTarget,\n event.pointerId,\n { row: anchorRow, col: firstVisibleCol },\n \"row\",\n { row: actualRow, col: firstVisibleCol },\n pointerOrigin,\n null,\n true,\n initialRange,\n event.clientX,\n event.clientY\n );\n axisSelectionRef.current = { axis: \"row\", startRow: anchorRow, endRow: actualRow };\n commitSelectionRange(initialRange);\n }, [commitSelectionRange, firstVisibleCol, focusGrid, lastVisibleCol, resolveRowPointerOrigin]);\n\n const handleColumnPointerDown = React.useCallback((\n event: React.PointerEvent<HTMLTableCellElement>,\n actualCol: number\n ) => {\n if (event.button !== 0 || firstVisibleRow === undefined || lastVisibleRow === undefined) {\n return;\n }\n\n event.preventDefault();\n focusGrid();\n const currentSelection = selectionRef.current;\n const anchorCol =\n event.shiftKey && currentSelection ? currentSelection.start.col : actualCol;\n const initialRange = normalizeRange({\n start: { row: firstVisibleRow, col: anchorCol },\n end: { row: lastVisibleRow, col: actualCol }\n });\n const pointerOrigin = resolveColumnPointerOrigin(actualCol, event.currentTarget.getBoundingClientRect(), event.clientX);\n if (!pointerOrigin) {\n return;\n }\n\n startCellSelection(\n event.currentTarget,\n event.pointerId,\n { row: firstVisibleRow, col: anchorCol },\n \"column\",\n { row: firstVisibleRow, col: actualCol },\n pointerOrigin,\n null,\n true,\n initialRange,\n event.clientX,\n event.clientY\n );\n axisSelectionRef.current = { axis: \"column\", startCol: anchorCol, endCol: actualCol };\n commitSelectionRange(initialRange);\n }, [commitSelectionRange, firstVisibleRow, focusGrid, lastVisibleRow, resolveColumnPointerOrigin]);\n\n const handleRowResizePointerDown = React.useCallback((\n event: React.PointerEvent<HTMLDivElement>,\n actualRow: number,\n rowHeight: number\n ) => {\n if (!canResizeHeaders) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n startRowResize(event.pointerId, actualRow, rowHeight, event.clientY);\n }, [canResizeHeaders]);\n\n const renderCellAdornment = React.useCallback((cell: XlsxCellAddress) => {\n const table = getTableAtCell(effectiveTables, cell.row, cell.col);\n const tableColumn = getTableHeaderColumn(table, cell.row, cell.col);\n if (!table || !tableColumn) {\n return null;\n }\n\n const direction =\n sortState && sortState.tableName === table.name && sortState.columnIndex === tableColumn.index\n ? sortState.direction\n : null;\n const triggerIcon = direction === \"ascending\" ? \"▲\" : direction === \"descending\" ? \"▼\" : \"▾\";\n\n if (renderTableHeaderMenu) {\n return renderTableHeaderMenu({\n cell,\n column: tableColumn,\n direction,\n sortAscending: () => sortTable(table.name, tableColumn.index, \"ascending\"),\n sortDescending: () => sortTable(table.name, tableColumn.index, \"descending\"),\n table,\n triggerIcon,\n triggerProps: {\n \"aria-haspopup\": \"menu\",\n \"aria-label\": `Open menu for ${tableColumn.name}`,\n onClick: (event) => {\n event.stopPropagation();\n },\n onPointerDown: (event) => {\n event.stopPropagation();\n },\n style: resolveTableHeaderTriggerStyle(palette, zoomFactor),\n type: \"button\"\n }\n });\n }\n\n return (\n <button\n onPointerDown={(event) => {\n event.preventDefault();\n event.stopPropagation();\n }}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n setOpenTableMenu((current) =>\n current &&\n current.tableName === table.name &&\n current.row === cell.row &&\n current.col === cell.col\n ? null\n : { col: cell.col, row: cell.row, tableName: table.name }\n );\n }}\n style={{\n ...resolveTableHeaderTriggerStyle(palette, zoomFactor)\n }}\n type=\"button\"\n >\n {triggerIcon}\n </button>\n );\n }, [effectiveTables, palette, renderTableHeaderMenu, sortState, sortTable, zoomFactor]);\n const resolveCanvasColumnHeaderRect = React.useCallback((actualCol: number) => {\n const colIndex = colIndexByActual.get(actualCol);\n if (colIndex === undefined) {\n return null;\n }\n\n const width = displayEffectiveColWidths[colIndex] ?? displayDefaultColWidth;\n return {\n left: stickyLeftByCol.get(actualCol) ?? (displayRowHeaderWidth + (colPrefixSums[colIndex] ?? 0) - drawingViewport.left),\n width\n };\n }, [\n colIndexByActual,\n colPrefixSums,\n displayDefaultColWidth,\n displayEffectiveColWidths,\n displayRowHeaderWidth,\n drawingViewport.left,\n stickyLeftByCol\n ]);\n\n const resolveCanvasRowHeaderRect = React.useCallback((actualRow: number) => {\n const rowIndex = rowIndexByActual.get(actualRow);\n if (rowIndex === undefined) {\n return null;\n }\n\n const height = displayEffectiveRowHeights[rowIndex] ?? displayDefaultRowHeight;\n return {\n height,\n top: stickyTopByRow.get(actualRow) ?? (displayHeaderHeight + (rowPrefixSums[rowIndex] ?? 0) - drawingViewport.top)\n };\n }, [\n displayDefaultRowHeight,\n displayEffectiveRowHeights,\n displayHeaderHeight,\n drawingViewport.top,\n rowIndexByActual,\n rowPrefixSums,\n stickyTopByRow\n ]);\n const scrollBodyViewportWidth = Math.max(0, drawingViewport.width - frozenPaneRight);\n const scrollBodyViewportHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);\n const canvasScrollBufferX = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportWidth);\n const canvasScrollBufferY = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportHeight);\n const scrollBodyCanvasLeft = frozenPaneRight - canvasScrollBufferX;\n const scrollBodyCanvasTop = frozenPaneBottom - canvasScrollBufferY;\n const scrollBodyCanvasWidth = scrollBodyViewportWidth + canvasScrollBufferX * 2;\n const scrollBodyCanvasHeight = scrollBodyViewportHeight + canvasScrollBufferY * 2;\n const topBodyCanvasWidth = scrollBodyCanvasWidth;\n const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);\n const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);\n const leftBodyCanvasHeight = scrollBodyCanvasHeight;\n const cornerBodyCanvasWidth = leftBodyCanvasWidth;\n const cornerBodyCanvasHeight = topBodyCanvasHeight;\n const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;\n const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;\n const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;\n const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;\n const canvasColumnHeaderCells = React.useMemo(\n () =>\n canvasVisibleColItems.flatMap<CanvasStickyColumnHeaderCell>((column) => {\n const rect = resolveCanvasColumnHeaderRect(column.actualCol);\n if (!rect) {\n return [];\n }\n\n const isFrozen = stickyLeftByCol.has(column.actualCol);\n return [{\n actualCol: column.actualCol,\n height: displayHeaderHeight,\n isFrozen,\n left: rect.left,\n localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : scrollBodyCanvasLeft),\n width: rect.width\n }];\n }),\n [\n canvasVisibleColItems,\n displayHeaderHeight,\n displayRowHeaderWidth,\n resolveCanvasColumnHeaderRect,\n scrollBodyCanvasLeft,\n stickyLeftByCol\n ]\n );\n const canvasRowHeaderCells = React.useMemo(\n () =>\n canvasVisibleRowItems.flatMap<CanvasStickyRowHeaderCell>((row) => {\n const rect = resolveCanvasRowHeaderRect(row.actualRow);\n if (!rect) {\n return [];\n }\n\n const isFrozen = stickyTopByRow.has(row.actualRow);\n return [{\n actualRow: row.actualRow,\n height: rect.height,\n isFrozen,\n localTop: rect.top - (isFrozen ? displayHeaderHeight : scrollBodyCanvasTop),\n top: rect.top\n }];\n }),\n [\n canvasVisibleRowItems,\n displayHeaderHeight,\n resolveCanvasRowHeaderRect,\n scrollBodyCanvasTop,\n stickyTopByRow\n ]\n );\n const resolveCanvasColumnResizeTarget = React.useCallback((clientX: number) => {\n if (!canResizeHeaders) {\n return null;\n }\n\n const scrollerRect = scrollRef.current?.getBoundingClientRect();\n if (!scrollerRect) {\n return null;\n }\n\n const localX = clientX - scrollerRect.left;\n for (const column of canvasColumnHeaderCells) {\n if (Math.abs(localX - (column.left + column.width)) <= CANVAS_RESIZE_HIT_SLOP_PX) {\n return { actualCol: column.actualCol, width: column.width };\n }\n }\n\n return null;\n }, [canResizeHeaders, canvasColumnHeaderCells]);\n const resolveCanvasColumnHeaderTarget = React.useCallback((clientX: number) => {\n const scrollerRect = scrollRef.current?.getBoundingClientRect();\n if (!scrollerRect) {\n return null;\n }\n\n const localX = clientX - scrollerRect.left;\n for (const column of canvasColumnHeaderCells) {\n if (localX >= column.left && localX <= column.left + column.width) {\n return column.actualCol;\n }\n }\n\n return null;\n }, [canvasColumnHeaderCells]);\n\n const resolveCanvasRowResizeTarget = React.useCallback((clientY: number) => {\n if (!canResizeHeaders) {\n return null;\n }\n\n const scrollerRect = scrollRef.current?.getBoundingClientRect();\n if (!scrollerRect) {\n return null;\n }\n\n const localY = clientY - scrollerRect.top;\n for (const row of canvasRowHeaderCells) {\n if (Math.abs(localY - (row.top + row.height)) <= CANVAS_RESIZE_HIT_SLOP_PX) {\n return { actualRow: row.actualRow, height: row.height };\n }\n }\n\n return null;\n }, [canResizeHeaders, canvasRowHeaderCells]);\n const resolveCanvasRowHeaderTarget = React.useCallback((clientY: number) => {\n const scrollerRect = scrollRef.current?.getBoundingClientRect();\n if (!scrollerRect) {\n return null;\n }\n\n const localY = clientY - scrollerRect.top;\n for (const row of canvasRowHeaderCells) {\n if (localY >= row.top && localY <= row.top + row.height) {\n return row.actualRow;\n }\n }\n\n return null;\n }, [canvasRowHeaderCells]);\n\n const handleCanvasColumnHeaderPointerMove = React.useCallback((event: React.PointerEvent<HTMLCanvasElement>) => {\n if (resizeStateRef.current?.type === \"column\") {\n event.currentTarget.style.cursor = \"col-resize\";\n return;\n }\n event.currentTarget.style.cursor = resolveCanvasColumnResizeTarget(event.clientX) ? \"col-resize\" : \"default\";\n }, [resolveCanvasColumnResizeTarget]);\n\n const handleCanvasRowHeaderPointerMove = React.useCallback((event: React.PointerEvent<HTMLCanvasElement>) => {\n if (resizeStateRef.current?.type === \"row\") {\n event.currentTarget.style.cursor = \"row-resize\";\n return;\n }\n event.currentTarget.style.cursor = resolveCanvasRowResizeTarget(event.clientY) ? \"row-resize\" : \"default\";\n }, [resolveCanvasRowResizeTarget]);\n\n const handleCanvasHeaderPointerLeave = React.useCallback((event: React.PointerEvent<HTMLCanvasElement>) => {\n if (resizeStateRef.current) {\n event.currentTarget.style.cursor = resizeStateRef.current.type === \"column\" ? \"col-resize\" : \"row-resize\";\n return;\n }\n event.currentTarget.style.cursor = \"default\";\n }, []);\n\n const handleCanvasBodyPointerDown = React.useCallback((event: React.PointerEvent<HTMLCanvasElement>) => {\n if (event.button !== 0) {\n return;\n }\n\n const cell = resolvePointerCellFromClient(event.clientX, event.clientY);\n if (!cell) {\n return;\n }\n\n event.preventDefault();\n focusGrid();\n axisSelectionRef.current = null;\n const currentSelection = selectionRef.current;\n const anchor = event.shiftKey && currentSelection ? currentSelection.start : cell;\n const initialRange = resolveCellSelectionRange({ start: anchor, end: cell });\n const isActive = isSameCell(activeCellRef.current, cell);\n const committedOnPointerDown = !isActive || !editingCellRef.current;\n if (editingCellRef.current && !isActive) {\n commitEditingRef.current();\n }\n const rowIndex = rowIndexByActual.get(cell.row);\n const colIndex = colIndexByActual.get(cell.col);\n if (rowIndex === undefined || colIndex === undefined) {\n return;\n }\n\n startCellSelection(\n event.currentTarget,\n event.pointerId,\n anchor,\n \"cell\",\n cell,\n {\n contentScaleX: 1,\n contentScaleY: 1,\n originContentX: colPrefixSums[colIndex] ?? 0,\n originContentY: rowPrefixSums[rowIndex] ?? 0\n },\n resolveOverlayRect(initialRange),\n committedOnPointerDown,\n initialRange,\n event.clientX,\n event.clientY\n );\n applyPreviewOverlay(initialRange);\n if (committedOnPointerDown) {\n commitSelectionRange(initialRange);\n }\n }, [\n applyPreviewOverlay,\n colIndexByActual,\n colPrefixSums,\n commitSelectionRange,\n focusGrid,\n resolveOverlayRect,\n resolveCellSelectionRange,\n resolvePointerCellFromClient,\n rowIndexByActual,\n rowPrefixSums,\n startCellSelection\n ]);\n\n const handleCanvasBodyClick = React.useCallback((event: React.MouseEvent<HTMLCanvasElement>) => {\n const cell = resolvePointerCellFromClient(event.clientX, event.clientY);\n if (!cell) {\n return;\n }\n\n handleCellClick(cell, getCellData(cell.row, cell.col));\n }, [getCellData, handleCellClick, resolvePointerCellFromClient]);\n\n const handleCanvasBodyDoubleClick = React.useCallback((event: React.MouseEvent<HTMLCanvasElement>) => {\n if (readOnly) {\n return;\n }\n\n const cell = resolvePointerCellFromClient(event.clientX, event.clientY);\n if (!cell) {\n return;\n }\n\n startEditing(cell);\n }, [readOnly, resolvePointerCellFromClient, startEditing]);\n\n const handleCanvasColumnHeaderPointerDown = React.useCallback((event: React.PointerEvent<HTMLCanvasElement>) => {\n if (event.button !== 0 || firstVisibleRow === undefined || lastVisibleRow === undefined) {\n return;\n }\n\n const resizeTarget = resolveCanvasColumnResizeTarget(event.clientX);\n if (resizeTarget) {\n event.preventDefault();\n event.stopPropagation();\n startColumnResize(event.pointerId, resizeTarget.actualCol, resizeTarget.width, event.clientX);\n return;\n }\n\n const actualCol = resolveCanvasColumnHeaderTarget(event.clientX);\n if (actualCol === null) {\n return;\n }\n\n event.preventDefault();\n focusGrid();\n const currentSelection = selectionRef.current;\n const anchorCol = event.shiftKey && currentSelection ? currentSelection.start.col : actualCol;\n const initialRange = normalizeRange({\n start: { row: firstVisibleRow, col: anchorCol },\n end: { row: lastVisibleRow, col: actualCol }\n });\n const anchorColIndex = colIndexByActual.get(anchorCol);\n if (anchorColIndex === undefined) {\n return;\n }\n\n startCellSelection(\n event.currentTarget,\n event.pointerId,\n { row: firstVisibleRow, col: anchorCol },\n \"column\",\n { row: firstVisibleRow, col: actualCol },\n {\n contentScaleX: 1,\n contentScaleY: 1,\n originContentX: colPrefixSums[anchorColIndex] ?? 0,\n originContentY: rowPrefixSums[0] ?? 0\n },\n null,\n true,\n initialRange,\n event.clientX,\n event.clientY\n );\n axisSelectionRef.current = { axis: \"column\", startCol: anchorCol, endCol: actualCol };\n commitSelectionRange(initialRange);\n }, [\n colIndexByActual,\n colPrefixSums,\n commitSelectionRange,\n firstVisibleRow,\n focusGrid,\n lastVisibleRow,\n resolveCanvasColumnHeaderTarget,\n resolveCanvasColumnResizeTarget,\n rowPrefixSums,\n startCellSelection\n ]);\n\n const handleCanvasRowHeaderPointerDown = React.useCallback((event: React.PointerEvent<HTMLCanvasElement>) => {\n if (event.button !== 0 || firstVisibleCol === undefined || lastVisibleCol === undefined) {\n return;\n }\n\n const resizeTarget = resolveCanvasRowResizeTarget(event.clientY);\n if (resizeTarget) {\n event.preventDefault();\n event.stopPropagation();\n startRowResize(event.pointerId, resizeTarget.actualRow, resizeTarget.height, event.clientY);\n return;\n }\n\n const actualRow = resolveCanvasRowHeaderTarget(event.clientY);\n if (actualRow === null) {\n return;\n }\n\n event.preventDefault();\n focusGrid();\n const currentSelection = selectionRef.current;\n const anchorRow = event.shiftKey && currentSelection ? currentSelection.start.row : actualRow;\n const initialRange = normalizeRange({\n start: { row: anchorRow, col: firstVisibleCol },\n end: { row: actualRow, col: lastVisibleCol }\n });\n const anchorRowIndex = rowIndexByActual.get(anchorRow);\n if (anchorRowIndex === undefined) {\n return;\n }\n\n startCellSelection(\n event.currentTarget,\n event.pointerId,\n { row: anchorRow, col: firstVisibleCol },\n \"row\",\n { row: actualRow, col: firstVisibleCol },\n {\n contentScaleX: 1,\n contentScaleY: 1,\n originContentX: colPrefixSums[0] ?? 0,\n originContentY: rowPrefixSums[anchorRowIndex] ?? 0\n },\n null,\n true,\n initialRange,\n event.clientX,\n event.clientY\n );\n axisSelectionRef.current = { axis: \"row\", startRow: anchorRow, endRow: actualRow };\n commitSelectionRange(initialRange);\n }, [\n colPrefixSums,\n commitSelectionRange,\n firstVisibleCol,\n focusGrid,\n lastVisibleCol,\n resolveCanvasRowHeaderTarget,\n resolveCanvasRowResizeTarget,\n rowIndexByActual,\n rowPrefixSums,\n startCellSelection\n ]);\n\n const getCanvasImage = React.useCallback((image: XlsxImage) => {\n if (typeof Image === \"undefined\") {\n return null;\n }\n\n const cacheKey = image.id;\n const cached = canvasImageCacheRef.current.get(cacheKey);\n if (cached && cached.src === image.src) {\n return cached.loaded && !cached.failed ? cached.image : null;\n }\n\n const imageElement = new Image();\n const entry = {\n failed: false,\n image: imageElement,\n loaded: false,\n src: image.src\n };\n canvasImageCacheRef.current.set(cacheKey, entry);\n imageElement.onload = () => {\n entry.loaded = true;\n setCanvasImageLoadVersion((version) => version + 1);\n };\n imageElement.onerror = () => {\n entry.failed = true;\n setCanvasImageLoadVersion((version) => version + 1);\n };\n imageElement.src = image.src;\n return null;\n }, []);\n\n React.useLayoutEffect(() => {\n if (!experimentalCanvas) {\n return;\n }\n\n const canvasProfileTarget = typeof window !== \"undefined\"\n ? (window as unknown as {\n __xlsxCanvasProfile?: Array<{\n bodyMs: number;\n culledCells: number;\n dirtyRects: number;\n lookedUpCells: number;\n paintedCells: number;\n repaintBody: boolean;\n repaintHeaders: boolean;\n renderToLayoutMs: number;\n totalMs: number;\n }>;\n }).__xlsxCanvasProfile\n : undefined;\n const canvasProfileStart = canvasProfileTarget ? performance.now() : 0;\n let canvasProfileBodyStart = 0;\n let canvasProfileBodyMs = 0;\n let canvasProfileCulledCells = 0;\n let canvasProfileDirtyRects = 0;\n let canvasProfileLookedUpCells = 0;\n let canvasProfilePaintedCells = 0;\n\n const dpr = typeof window === \"undefined\" ? 1 : Math.max(1, window.devicePixelRatio || 1);\n\n function configureCanvas(\n canvas: HTMLCanvasElement | null,\n width: number,\n height: number,\n options?: { clear?: boolean }\n ) {\n if (!canvas) {\n return null;\n }\n\n const nextWidth = Math.max(1, Math.round(width * dpr));\n const nextHeight = Math.max(1, Math.round(height * dpr));\n if (canvas.width !== nextWidth) {\n canvas.width = nextWidth;\n }\n if (canvas.height !== nextHeight) {\n canvas.height = nextHeight;\n }\n if (canvas.style.width !== `${width}px`) {\n canvas.style.width = `${width}px`;\n }\n if (canvas.style.height !== `${height}px`) {\n canvas.style.height = `${height}px`;\n }\n const context = canvas.getContext(\"2d\", { alpha: false });\n if (!context) {\n return null;\n }\n\n context.setTransform(dpr, 0, 0, dpr, 0, 0);\n if (options?.clear !== false) {\n context.clearRect(0, 0, width, height);\n }\n return context;\n }\n\n const bodyWidth = Math.max(0, drawingViewport.width);\n const bodyHeight = Math.max(0, drawingViewport.height);\n const headerHeight = Math.min(displayHeaderHeight, bodyHeight);\n const rowHeaderWidth = Math.min(displayRowHeaderWidth, bodyWidth);\n const visibleRange = selectionPreviewRangeRef.current ?? displayedSelection;\n const normalizedVisibleRange = visibleRange ? normalizeRange(visibleRange) : null;\n const rangeSignature = buildRangeSignature(normalizedVisibleRange);\n const nextBodyCanvasSignature: PaintedBodyCanvasSignature = {\n activeSheet: activeSheet ?? null,\n bakedDrawingSignature: bakedCanvasDrawingSignature,\n bodyHeight,\n bodyWidth,\n colSignature: buildRenderedAxisSignature(canvasVisibleColItems, (item) => item.index),\n getCellData,\n headerHeight,\n palette,\n rowHeaderWidth,\n rowSignature: buildRenderedAxisSignature(canvasVisibleRowItems, (item) => item.index),\n viewportLeft: drawingViewport.left,\n viewportTop: drawingViewport.top,\n zoomFactor\n };\n const nextHeaderCanvasSignature: PaintedHeaderCanvasSignature = {\n activeSheet: activeSheet ?? null,\n bodyHeight,\n bodyWidth,\n colSignature: nextBodyCanvasSignature.colSignature,\n headerHeight,\n palette,\n rangeSignature,\n rowHeaderWidth,\n rowSignature: nextBodyCanvasSignature.rowSignature,\n viewportLeft: drawingViewport.left,\n viewportTop: drawingViewport.top,\n zoomFactor\n };\n const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;\n const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;\n const previousPaintedViewport = paintedDrawingViewportRef.current;\n const shouldRepaintBody = !(\n previousBodyCanvasSignature\n && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet\n && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature\n && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight\n && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth\n && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature\n && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData\n && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight\n && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette\n && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth\n && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature\n && previousBodyCanvasSignature.viewportLeft === nextBodyCanvasSignature.viewportLeft\n && previousBodyCanvasSignature.viewportTop === nextBodyCanvasSignature.viewportTop\n && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor\n );\n const shouldRepaintHeaders = !(\n previousHeaderCanvasSignature\n && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet\n && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight\n && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth\n && previousHeaderCanvasSignature.colSignature === nextHeaderCanvasSignature.colSignature\n && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight\n && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette\n && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature\n && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth\n && previousHeaderCanvasSignature.rowSignature === nextHeaderCanvasSignature.rowSignature\n && previousHeaderCanvasSignature.viewportLeft === nextHeaderCanvasSignature.viewportLeft\n && previousHeaderCanvasSignature.viewportTop === nextHeaderCanvasSignature.viewportTop\n && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor\n );\n const canBlitBody = Boolean(\n shouldRepaintBody\n && previousBodyCanvasSignature\n && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet\n && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature\n && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight\n && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth\n && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData\n && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight\n && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette\n && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth\n && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor\n );\n const canBlitTopHeader = Boolean(\n shouldRepaintHeaders\n && previousHeaderCanvasSignature\n && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet\n && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight\n && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth\n && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight\n && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette\n && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature\n && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth\n && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor\n );\n const canBlitLeftHeader = Boolean(\n shouldRepaintHeaders\n && previousHeaderCanvasSignature\n && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet\n && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight\n && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth\n && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight\n && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette\n && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature\n && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth\n && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor\n );\n if (!shouldRepaintBody && !shouldRepaintHeaders) {\n return;\n }\n const paneBounds: Record<FrozenDrawingPane, { height: number; left: number; top: number; width: number }> = {\n corner: {\n height: cornerBodyCanvasHeight,\n left: displayRowHeaderWidth,\n top: displayHeaderHeight,\n width: cornerBodyCanvasWidth\n },\n left: {\n height: leftBodyCanvasHeight,\n left: displayRowHeaderWidth,\n top: scrollBodyCanvasTop,\n width: leftBodyCanvasWidth\n },\n scroll: {\n height: scrollBodyCanvasHeight,\n left: scrollBodyCanvasLeft,\n top: scrollBodyCanvasTop,\n width: scrollBodyCanvasWidth\n },\n top: {\n height: topBodyCanvasHeight,\n left: scrollBodyCanvasLeft,\n top: displayHeaderHeight,\n width: topBodyCanvasWidth\n }\n };\n const bodyContexts: Record<FrozenDrawingPane, CanvasRenderingContext2D | null> = shouldRepaintBody\n ? {\n corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth, cornerBodyCanvasHeight, { clear: !canBlitBody }),\n left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth, leftBodyCanvasHeight, { clear: !canBlitBody }),\n scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth, scrollBodyCanvasHeight, { clear: !canBlitBody }),\n top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth, topBodyCanvasHeight, { clear: !canBlitBody })\n }\n : {\n corner: null,\n left: null,\n scroll: null,\n top: null\n };\n const topHeaderContexts = shouldRepaintHeaders\n ? {\n frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth, headerHeight),\n scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth, headerHeight, { clear: !canBlitTopHeader })\n }\n : {\n frozen: null,\n scroll: null\n };\n const leftHeaderContexts = shouldRepaintHeaders\n ? {\n frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight),\n scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight, { clear: !canBlitLeftHeader })\n }\n : {\n frozen: null,\n scroll: null\n };\n const cornerContext = shouldRepaintHeaders\n ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight)\n : null;\n if (\n (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner))\n || (\n shouldRepaintHeaders\n && (\n !cornerContext\n || (topFrozenHeaderCanvasWidth > 0 && !topHeaderContexts.frozen)\n || (topScrollHeaderCanvasWidth > 0 && !topHeaderContexts.scroll)\n || (leftFrozenHeaderCanvasHeight > 0 && !leftHeaderContexts.frozen)\n || (leftScrollHeaderCanvasHeight > 0 && !leftHeaderContexts.scroll)\n )\n )\n ) {\n return;\n }\n const showGridLines = activeSheet?.showGridLines ?? true;\n const sheetSurface = resolveSheetSurface(activeSheet, palette);\n const deferredSpillTextsByPane: Record<FrozenDrawingPane, Array<{\n align: CanvasTextAlign;\n clipHeight: number;\n clipLeft: number;\n clipTop: number;\n clipWidth: number;\n color: string;\n ellipsize: boolean;\n font: string;\n maxWidth: number;\n text: string;\n textDecoration?: string;\n textX: number;\n textY: number;\n lineThroughY: number;\n underlineY: number;\n }>> = {\n corner: [],\n left: [],\n scroll: [],\n top: []\n };\n const bodyDirtyRectsByPane: Record<FrozenDrawingPane, CanvasDirtyRect[]> = {\n corner: [],\n left: [],\n scroll: [],\n top: []\n };\n let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth, headerHeight);\n let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight);\n type BakedCanvasDrawingEntry =\n | {\n kind: \"image\";\n image: XlsxImage;\n rect: XlsxImageRect;\n zIndex: number;\n }\n | {\n kind: \"shape\";\n rect: XlsxImageRect;\n shape: XlsxShape;\n zIndex: number;\n };\n const bakedCanvasDrawingEntries: BakedCanvasDrawingEntry[] = shouldBakeCanvasStaticDrawings\n ? [\n ...bakedShapeRects.map(({ rect, shape }) => ({\n kind: \"shape\" as const,\n rect,\n shape,\n zIndex: shape.zIndex\n })),\n ...bakedImageRects.map(({ image, rect }) => ({\n image,\n kind: \"image\" as const,\n rect,\n zIndex: image.zIndex\n }))\n ].sort((left, right) => left.zIndex - right.zIndex)\n : [];\n const resolveBakedCanvasLocalRect = (rect: XlsxImageRect, pane: FrozenDrawingPane) => {\n const bounds = paneBounds[pane];\n const scrollX = pane === \"scroll\" || pane === \"top\" ? drawingViewport.left : 0;\n const scrollY = pane === \"scroll\" || pane === \"left\" ? drawingViewport.top : 0;\n return {\n height: rect.height,\n left: rect.left - scrollX - bounds.left,\n top: rect.top - scrollY - bounds.top,\n width: rect.width\n };\n };\n const drawBakedShapeText = (\n context: CanvasRenderingContext2D,\n shape: XlsxShape,\n left: number,\n top: number,\n width: number,\n height: number\n ) => {\n if (shape.paragraphs.length === 0) {\n return;\n }\n\n const inset = shape.textBox?.insetPx;\n const paddingLeft = (inset?.left ?? 6) * zoomFactor;\n const paddingRight = (inset?.right ?? 6) * zoomFactor;\n const paddingTop = (inset?.top ?? 4) * zoomFactor;\n const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;\n const textLeft = left + paddingLeft;\n const textTop = top + paddingTop;\n const textWidth = Math.max(0, width - paddingLeft - paddingRight);\n const textHeight = Math.max(0, height - paddingTop - paddingBottom);\n if (textWidth <= 0 || textHeight <= 0) {\n return;\n }\n\n const lineMetrics = shape.paragraphs.map((paragraph) => {\n const lineHeight = Math.max(\n 12 * zoomFactor,\n ...paragraph.runs.map((run) => ((run.fontSizePt ?? 11) * 96 / 72) * zoomFactor * 1.2)\n );\n const widthPx = paragraph.runs.reduce((total, run) => {\n const fontSize = ((run.fontSizePt ?? 11) * 96 / 72) * zoomFactor;\n context.font = `${run.italic ? \"italic \" : \"\"}${run.bold ? \"700 \" : \"400 \"}${fontSize}px ${run.fontFamily ?? \"Calibri, sans-serif\"}`;\n return total + measureCanvasTextWidth(context, run.text);\n }, 0);\n return { lineHeight, widthPx };\n });\n const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);\n let y = textTop;\n if (shape.textBox?.verticalAlign === \"middle\") {\n y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);\n } else if (shape.textBox?.verticalAlign === \"bottom\") {\n y = textTop + Math.max(0, textHeight - totalTextHeight);\n }\n\n context.save();\n context.beginPath();\n context.rect(textLeft, textTop, textWidth, textHeight);\n context.clip();\n context.textBaseline = \"middle\";\n shape.paragraphs.forEach((paragraph, paragraphIndex) => {\n const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };\n let x = textLeft;\n const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? \"left\";\n if (align === \"center\") {\n x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);\n } else if (align === \"right\") {\n x = textLeft + Math.max(0, textWidth - metric.widthPx);\n }\n\n paragraph.runs.forEach((run) => {\n const fontSize = ((run.fontSizePt ?? 11) * 96 / 72) * zoomFactor;\n context.font = `${run.italic ? \"italic \" : \"\"}${run.bold ? \"700 \" : \"400 \"}${fontSize}px ${run.fontFamily ?? \"Calibri, sans-serif\"}`;\n context.fillStyle = run.color ?? \"#000000\";\n context.textAlign = \"left\";\n const textY = y + metric.lineHeight / 2;\n context.fillText(run.text, x, textY);\n if (run.underline && run.text.length > 0) {\n const textWidthPx = measureCanvasTextWidth(context, run.text);\n context.strokeStyle = run.color ?? \"#000000\";\n context.lineWidth = Math.max(1, zoomFactor * 0.75);\n context.beginPath();\n context.moveTo(x, textY + Math.max(2, fontSize * 0.28));\n context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));\n context.stroke();\n }\n x += measureCanvasTextWidth(context, run.text);\n });\n y += metric.lineHeight;\n });\n context.restore();\n };\n const drawBakedShape = (context: CanvasRenderingContext2D, shape: XlsxShape, rect: XlsxImageRect) => {\n const fillColor = shape.fill?.none ? \"transparent\" : (shape.fill?.color ?? \"transparent\");\n const strokeColor = shape.stroke?.none ? \"transparent\" : (shape.stroke?.color ?? \"transparent\");\n const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === \"line\" ? 2 : 1)) * zoomFactor);\n const vectorShape = resolveShapeVector(shape);\n const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);\n\n context.save();\n context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);\n if (shape.rotationDeg) {\n context.rotate((shape.rotationDeg * Math.PI) / 180);\n }\n context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);\n context.globalAlpha *= opacity;\n context.lineWidth = lineWidth;\n context.strokeStyle = strokeColor;\n context.fillStyle = fillColor;\n applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);\n\n const localLeft = -rect.width / 2;\n const localTop = -rect.height / 2;\n if (vectorShape && typeof Path2D !== \"undefined\") {\n context.save();\n context.translate(localLeft, localTop);\n context.scale(\n rect.width / Math.max(1, vectorShape.viewBox.width),\n rect.height / Math.max(1, vectorShape.viewBox.height)\n );\n const path = getCachedCanvasPath2D(vectorShape.path);\n if (!path) {\n context.restore();\n context.restore();\n return;\n }\n if (fillColor !== \"transparent\") {\n context.fill(path);\n }\n if (strokeColor !== \"transparent\" && lineWidth > 0) {\n context.stroke(path);\n }\n context.restore();\n } else if (shape.geometry === \"ellipse\") {\n context.beginPath();\n context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);\n if (fillColor !== \"transparent\") {\n context.fill();\n }\n if (strokeColor !== \"transparent\" && lineWidth > 0) {\n context.stroke();\n }\n } else {\n const radius = shape.geometry === \"roundRect\" ? 12 * zoomFactor : 0;\n drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);\n if (fillColor !== \"transparent\") {\n context.fill();\n }\n if (strokeColor !== \"transparent\" && lineWidth > 0) {\n context.stroke();\n }\n }\n\n drawBakedShapeText(context, shape, localLeft, localTop, rect.width, rect.height);\n context.restore();\n };\n const drawBakedCanvasDrawings = (\n pane: FrozenDrawingPane,\n context: CanvasRenderingContext2D,\n dirtyRects: CanvasDirtyRect[]\n ) => {\n if (bakedCanvasDrawingEntries.length === 0 || dirtyRects.length === 0) {\n return;\n }\n\n for (const entry of bakedCanvasDrawingEntries) {\n if (resolveDrawingPane(entry.rect) !== pane) {\n continue;\n }\n const localRect = resolveBakedCanvasLocalRect(entry.rect, pane);\n if (\n localRect.left + localRect.width < 0\n || localRect.top + localRect.height < 0\n || localRect.left > paneBounds[pane].width\n || localRect.top > paneBounds[pane].height\n || !intersectsCanvasDirtyRects(localRect.left, localRect.top, localRect.width, localRect.height, dirtyRects)\n ) {\n continue;\n }\n\n if (entry.kind === \"shape\") {\n drawBakedShape(context, entry.shape, localRect);\n } else {\n const imageElement = getCanvasImage(entry.image);\n if (imageElement) {\n context.drawImage(imageElement, localRect.left, localRect.top, localRect.width, localRect.height);\n }\n }\n }\n };\n\n const cellPaneOrder: FrozenDrawingPane[] = [\"scroll\", \"top\", \"left\", \"corner\"];\n if (shouldRepaintBody) {\n canvasProfileBodyStart = canvasProfileTarget ? performance.now() : 0;\n for (const pane of Object.keys(bodyContexts) as FrozenDrawingPane[]) {\n const context = bodyContexts[pane];\n const bounds = paneBounds[pane];\n if (!context || bounds.width <= 0 || bounds.height <= 0) {\n continue;\n }\n\n let dirtyRects = buildFullCanvasDirtyRect(bounds.width, bounds.height);\n if (canBlitBody) {\n const bufferCanvas = getBodyBlitBufferCanvas(pane);\n const deltaX = pane === \"scroll\" || pane === \"top\"\n ? drawingViewport.left - previousPaintedViewport.left\n : 0;\n const deltaY = pane === \"scroll\" || pane === \"left\"\n ? drawingViewport.top - previousPaintedViewport.top\n : 0;\n if (bufferCanvas) {\n const blittedDirtyRects = blitCanvasWithScrollDelta(\n pane === \"corner\"\n ? cornerBodyCanvasRef.current!\n : pane === \"left\"\n ? leftBodyCanvasRef.current!\n : pane === \"top\"\n ? topBodyCanvasRef.current!\n : scrollBodyCanvasRef.current!,\n context,\n bufferCanvas,\n dpr,\n bounds.width,\n bounds.height,\n deltaX,\n deltaY\n );\n if (blittedDirtyRects) {\n dirtyRects = blittedDirtyRects;\n }\n }\n } else {\n context.clearRect(0, 0, bounds.width, bounds.height);\n }\n\n bodyDirtyRectsByPane[pane] = dirtyRects;\n canvasProfileDirtyRects += dirtyRects.length;\n if (dirtyRects.length === 0) {\n continue;\n }\n\n context.fillStyle = sheetSurface;\n for (const dirtyRect of dirtyRects) {\n context.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);\n }\n }\n\n for (const pane of cellPaneOrder) {\n const paneContext = bodyContexts[pane];\n const paneBoundsForCell = paneBounds[pane];\n const paneDirtyRects = bodyDirtyRectsByPane[pane];\n const paneAxisItems = canvasPaneAxisItems[pane];\n if (\n !paneContext\n || paneDirtyRects.length === 0\n || paneBoundsForCell.width <= 0\n || paneBoundsForCell.height <= 0\n || paneAxisItems.rows.length === 0\n || paneAxisItems.cols.length === 0\n ) {\n continue;\n }\n let hasPendingGridlinePath = false;\n const enqueueGridlinePath = () => {\n if (!hasPendingGridlinePath) {\n paneContext.beginPath();\n hasPendingGridlinePath = true;\n }\n };\n const flushPendingGridlines = () => {\n if (!hasPendingGridlinePath) {\n return;\n }\n paneContext.strokeStyle = palette.border;\n paneContext.lineWidth = 1;\n paneContext.stroke();\n hasPendingGridlinePath = false;\n };\n const drawnMergedAnchorKeys = new Set<string>();\n const deferredDoubleBorders: Array<{\n side: \"top\" | \"right\" | \"bottom\" | \"left\";\n rect: { left: number; top: number; width: number; height: number };\n border: CanvasBorderDeclaration;\n }> = [];\n const drawCellBorder = (\n side: \"top\" | \"right\" | \"bottom\" | \"left\",\n rect: { left: number; top: number; width: number; height: number },\n border: CanvasBorderDeclaration\n ) => {\n if (border.style === \"double\") {\n deferredDoubleBorders.push({ side, rect, border });\n } else {\n strokeCanvasBorderSide(paneContext, side, rect, border);\n }\n };\n for (const rowItem of paneAxisItems.rows) {\n for (const colItem of paneAxisItems.cols) {\n const cell = { row: rowItem.actualRow, col: colItem.actualCol };\n const anchorCell = resolveMergeAnchorCell(cell);\n const anchorKey = `${anchorCell.row}:${anchorCell.col}`;\n let drawCell = anchorCell;\n\n const drawRowIndex = rowIndexByActual.get(drawCell.row);\n const drawColIndex = colIndexByActual.get(drawCell.col);\n if (drawRowIndex === undefined || drawColIndex === undefined) {\n continue;\n }\n\n const baseCellLeft = displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);\n const baseCellTop = displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);\n const useFrozenHorizontalPosition = pane === \"left\" || pane === \"corner\";\n const useFrozenVerticalPosition = pane === \"top\" || pane === \"corner\";\n const roughLocalRect = {\n height: displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size,\n left: (\n useFrozenHorizontalPosition\n ? (stickyLeftByCol.get(drawCell.col) ?? (baseCellLeft - drawingViewport.left))\n : (baseCellLeft - drawingViewport.left)\n ) - paneBoundsForCell.left,\n top: (\n useFrozenVerticalPosition\n ? (stickyTopByRow.get(drawCell.row) ?? (baseCellTop - drawingViewport.top))\n : (baseCellTop - drawingViewport.top)\n ) - paneBoundsForCell.top,\n width: displayEffectiveColWidths[drawColIndex] ?? colItem.size\n };\n const isMergedSecondaryProbe = anchorCell.row !== cell.row || anchorCell.col !== cell.col;\n if (\n !isMergedSecondaryProbe\n && !intersectsCanvasDirtyRects(\n roughLocalRect.left - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,\n roughLocalRect.top - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,\n roughLocalRect.width + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,\n roughLocalRect.height + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,\n paneDirtyRects\n )\n ) {\n canvasProfileCulledCells += 1;\n continue;\n }\n\n canvasProfileLookedUpCells += 1;\n let cellData = getCellData(drawCell.row, drawCell.col);\n if ((cellData.colSpan || cellData.rowSpan) && drawnMergedAnchorKeys.has(anchorKey)) {\n continue;\n }\n if (cellData.isMergedSecondary) {\n continue;\n }\n if (cellData.colSpan || cellData.rowSpan) {\n drawnMergedAnchorKeys.add(anchorKey);\n }\n\n const displayRect = cellData.colSpan || cellData.rowSpan\n ? resolveCellDisplayRect(drawCell)\n : null;\n const baseLeft = displayRect?.left ?? baseCellLeft;\n const baseTop = displayRect?.top ?? baseCellTop;\n const localRect = {\n height: displayRect?.height ?? (displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size),\n left: (\n useFrozenHorizontalPosition\n ? (stickyLeftByCol.get(drawCell.col) ?? (baseLeft - drawingViewport.left))\n : (baseLeft - drawingViewport.left)\n ) - paneBoundsForCell.left,\n top: (\n useFrozenVerticalPosition\n ? (stickyTopByRow.get(drawCell.row) ?? (baseTop - drawingViewport.top))\n : (baseTop - drawingViewport.top)\n ) - paneBoundsForCell.top,\n width: displayRect?.width ?? (displayEffectiveColWidths[drawColIndex] ?? colItem.size)\n };\n const spillOffsetX = Math.min(0, cellData.spillOffsetX ?? 0);\n const drawableLeft = localRect.left + spillOffsetX;\n const drawableWidth = Math.max(localRect.width, cellData.spillWidth ?? 0);\n\n if (\n drawableLeft + drawableWidth < 0\n || localRect.top + localRect.height < 0\n || drawableLeft > paneBoundsForCell.width\n || localRect.top > paneBoundsForCell.height\n ) {\n continue;\n }\n if (!intersectsCanvasDirtyRects(drawableLeft, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {\n canvasProfileCulledCells += 1;\n continue;\n }\n\n canvasProfilePaintedCells += 1;\n const cellStyle = cellData.style;\n const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);\n const fillColor = cellData.conditionalColorScale?.color\n ?? (typeof cellStyle.backgroundColor === \"string\" ? cellStyle.backgroundColor : sheetSurface);\n const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === \"string\"\n ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage)\n : null;\n if (cellData.chartHighlight) {\n flushPendingGridlines();\n }\n paneContext.fillStyle = gradientFill ?? fillColor;\n paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);\n if (cellData.chartHighlight) {\n paneContext.fillStyle = cellData.chartHighlight.fillColor;\n paneContext.fillRect(\n localRect.left + 1,\n localRect.top + 1,\n Math.max(0, localRect.width - 2),\n Math.max(0, localRect.height - 2)\n );\n }\n\n if (cellData.conditionalDataBar) {\n const barLeft = localRect.left + 4 * zoomFactor;\n const barTop = localRect.top + 4 * zoomFactor;\n const barWidth = Math.max(0, (localRect.width - (8 * zoomFactor)) * (cellData.conditionalDataBar.widthPercent / 100));\n const barHeight = Math.max(0, localRect.height - (8 * zoomFactor));\n if (barWidth > 0 && barHeight > 0) {\n paneContext.fillStyle = resolveCanvasDataBarFill(\n paneContext,\n barLeft,\n barTop,\n barWidth,\n barHeight,\n cellData.conditionalDataBar\n );\n paneContext.fillRect(barLeft, barTop, barWidth, barHeight);\n if (cellData.conditionalDataBar.border !== false && cellData.conditionalDataBar.borderColor) {\n paneContext.strokeStyle = cellData.conditionalDataBar.borderColor;\n paneContext.lineWidth = 1;\n paneContext.strokeRect(barLeft + 0.5, barTop + 0.5, Math.max(0, barWidth - 1), Math.max(0, barHeight - 1));\n }\n }\n }\n\n const topBorder = canvasCellStyle.topBorder;\n const rightBorder = canvasCellStyle.rightBorder;\n const bottomBorder = canvasCellStyle.bottomBorder;\n const leftBorder = canvasCellStyle.leftBorder;\n const rightNeighborCol = visibleCols[drawColIndex + Math.max(1, cellData.colSpan ?? 1)];\n const rightNeighborCell = rightNeighborCol === undefined\n ? null\n : { row: drawCell.row, col: rightNeighborCol };\n const rightNeighborData = rightNeighborCell\n ? getCellData(rightNeighborCell.row, rightNeighborCell.col)\n : null;\n let rightNeighborLeftBorder = rightNeighborData?.isMergedSecondary\n ? null\n : (rightNeighborData?.canvas?.leftBorder ?? parseCanvasBorderDeclaration(rightNeighborData?.style.borderLeft));\n if (rightNeighborLeftBorder == null && rightNeighborCell && rightNeighborData?.isMergedSecondary) {\n const rightNeighborAnchorCell = resolveMergeAnchorCell(rightNeighborCell);\n if (rightNeighborAnchorCell.col === rightNeighborCell.col) {\n const rightNeighborAnchorData = getCellData(rightNeighborAnchorCell.row, rightNeighborAnchorCell.col);\n rightNeighborLeftBorder = rightNeighborAnchorData.isMergedSecondary\n ? null\n : (rightNeighborAnchorData.canvas?.leftBorder\n ?? parseCanvasBorderDeclaration(rightNeighborAnchorData.style.borderLeft));\n }\n }\n const bottomNeighborRow = visibleRows[drawRowIndex + Math.max(1, cellData.rowSpan ?? 1)];\n const bottomNeighborCell = bottomNeighborRow === undefined\n ? null\n : { row: bottomNeighborRow, col: drawCell.col };\n const bottomNeighborData = bottomNeighborCell\n ? getCellData(bottomNeighborCell.row, bottomNeighborCell.col)\n : null;\n let bottomNeighborTopBorder = bottomNeighborData?.isMergedSecondary\n ? null\n : (bottomNeighborData?.canvas?.topBorder ?? parseCanvasBorderDeclaration(bottomNeighborData?.style.borderTop));\n if (bottomNeighborTopBorder == null && bottomNeighborCell && bottomNeighborData?.isMergedSecondary) {\n const bottomNeighborAnchorCell = resolveMergeAnchorCell(bottomNeighborCell);\n if (bottomNeighborAnchorCell.row === bottomNeighborCell.row) {\n const bottomNeighborAnchorData = getCellData(bottomNeighborAnchorCell.row, bottomNeighborAnchorCell.col);\n bottomNeighborTopBorder = bottomNeighborAnchorData.isMergedSecondary\n ? null\n : (bottomNeighborAnchorData.canvas?.topBorder\n ?? parseCanvasBorderDeclaration(bottomNeighborAnchorData.style.borderTop));\n }\n }\n const resolvedRightBorder = resolveCanvasBoundaryBorder(rightBorder, rightNeighborLeftBorder);\n const resolvedBottomBorder = resolveCanvasBoundaryBorder(bottomBorder, bottomNeighborTopBorder);\n\n if (showGridLines && (!resolvedRightBorder || !resolvedBottomBorder)) {\n enqueueGridlinePath();\n if (!resolvedRightBorder) {\n paneContext.moveTo(localRect.left + localRect.width - 0.5, localRect.top);\n paneContext.lineTo(localRect.left + localRect.width - 0.5, localRect.top + localRect.height);\n }\n if (!resolvedBottomBorder) {\n paneContext.moveTo(localRect.left, localRect.top + localRect.height - 0.5);\n paneContext.lineTo(localRect.left + localRect.width, localRect.top + localRect.height - 0.5);\n }\n }\n\n if (topBorder || resolvedRightBorder || resolvedBottomBorder || leftBorder || cellData.chartHighlight) {\n flushPendingGridlines();\n }\n\n if (topBorder && drawRowIndex === 0) {\n drawCellBorder(\"top\", localRect, topBorder);\n }\n if (resolvedRightBorder) {\n if (resolvedRightBorder.style === \"double\" && resolvedRightBorder === rightNeighborLeftBorder) {\n drawCellBorder(\"left\", { ...localRect, left: localRect.left + localRect.width }, resolvedRightBorder);\n } else {\n drawCellBorder(\"right\", localRect, resolvedRightBorder);\n }\n }\n if (resolvedBottomBorder) {\n if (resolvedBottomBorder.style === \"double\" && resolvedBottomBorder === bottomNeighborTopBorder) {\n drawCellBorder(\"top\", { ...localRect, top: localRect.top + localRect.height }, resolvedBottomBorder);\n } else {\n drawCellBorder(\"bottom\", localRect, resolvedBottomBorder);\n }\n }\n if (leftBorder && drawColIndex === 0) {\n drawCellBorder(\"left\", localRect, leftBorder);\n }\n if (cellData.chartHighlight) {\n const highlightBorder = {\n color: cellData.chartHighlight.strokeColor,\n style: \"solid\",\n width: Math.max(1, zoomFactor)\n };\n if (cellData.chartHighlight.borderTop) {\n strokeCanvasBorderSide(paneContext, \"top\", localRect, highlightBorder);\n }\n if (cellData.chartHighlight.borderRight) {\n strokeCanvasBorderSide(paneContext, \"right\", localRect, highlightBorder);\n }\n if (cellData.chartHighlight.borderBottom) {\n strokeCanvasBorderSide(paneContext, \"bottom\", localRect, highlightBorder);\n }\n if (cellData.chartHighlight.borderLeft) {\n strokeCanvasBorderSide(paneContext, \"left\", localRect, highlightBorder);\n }\n }\n\n const rawText = cellData.value ?? \"\";\n const shouldDrawCanvasContent = (\n cellData.checkboxState != null\n || cellData.sparkline\n || rawText.length > 0\n || cellData.conditionalIcon\n || cellData.isTableHeader\n );\n if (!shouldDrawCanvasContent) {\n continue;\n }\n\n const padding = canvasCellStyle.padding;\n const contentLeft = localRect.left + padding.left;\n const contentTop = localRect.top + padding.top;\n const contentWidth = Math.max(0, localRect.width - padding.left - padding.right);\n const contentHeight = Math.max(0, localRect.height - padding.top - padding.bottom);\n if (contentWidth <= 0 || contentHeight <= 0) {\n continue;\n }\n\n const activeFontSizePx = cellData.shrinkToFitFontSizePx\n ?? resolveCanvasFontSizePx(cellStyle, 12 * zoomFactor);\n const textClipOverscan = Math.max(\n 1,\n zoomFactor * 1.5,\n activeFontSizePx * (cellData.textRotationDeg ? 0.75 : 0.18)\n );\n const textClip = resolveCellTextClipOverscan(\n textClipOverscan,\n canvasCellStyle.usesWrappedText || rawText.includes(\"\\n\")\n );\n\n flushPendingGridlines();\n paneContext.save();\n paneContext.beginPath();\n paneContext.rect(\n contentLeft - textClip.horizontal,\n contentTop - textClip.vertical,\n contentWidth + (textClip.horizontal * 2),\n contentHeight + (textClip.vertical * 2)\n );\n paneContext.clip();\n paneContext.font = cellData.shrinkToFitFontSizePx\n ? resolveCanvasFontWithPxSize(cellStyle, cellData.shrinkToFitFontSizePx)\n : canvasCellStyle.baseFont;\n paneContext.fillStyle = canvasCellStyle.textColor;\n paneContext.textBaseline = \"middle\";\n\n if (cellData.checkboxState != null) {\n const boxSize = Math.min(14 * zoomFactor, contentWidth, contentHeight);\n const boxLeft = localRect.left + (localRect.width - boxSize) / 2;\n const boxTop = localRect.top + (localRect.height - boxSize) / 2;\n paneContext.strokeStyle = paletteIsDark(palette) ? \"#cbd5e1\" : \"#475569\";\n paneContext.lineWidth = 1.25;\n paneContext.strokeRect(boxLeft, boxTop, boxSize, boxSize);\n if (cellData.checkboxState) {\n paneContext.fillStyle = paletteIsDark(palette) ? \"#60a5fa\" : \"#2563eb\";\n paneContext.fillRect(boxLeft + 1.5, boxTop + 1.5, Math.max(0, boxSize - 3), Math.max(0, boxSize - 3));\n paneContext.strokeStyle = paletteIsDark(palette) ? \"#020617\" : \"#ffffff\";\n paneContext.lineWidth = 1.5;\n paneContext.beginPath();\n paneContext.moveTo(boxLeft + (boxSize * 0.24), boxTop + (boxSize * 0.56));\n paneContext.lineTo(boxLeft + (boxSize * 0.45), boxTop + (boxSize * 0.76));\n paneContext.lineTo(boxLeft + (boxSize * 0.8), boxTop + (boxSize * 0.3));\n paneContext.stroke();\n }\n } else if (cellData.sparkline) {\n const sparkline = cellData.sparkline.config;\n const sparklineValues = cellData.sparkline.values;\n const points = sparklineValues\n .map((value, index) => ({ index, value }))\n .filter((entry): entry is { index: number; value: number } => typeof entry.value === \"number\" && Number.isFinite(entry.value));\n if (points.length > 0) {\n const negativeColor = sparkline.negativeColor ?? \"#c2410c\";\n const seriesColor = sparkline.color ?? \"#2563eb\";\n const markerColor = sparkline.markerColor ?? seriesColor;\n const sparkLeft = contentLeft + 1;\n const sparkTop = contentTop + 2;\n const sparkWidth = Math.max(1, contentWidth - 2);\n const sparkHeight = Math.max(1, contentHeight - 4);\n\n if (sparkline.type === \"winLoss\") {\n const normalizedValues = points.map((entry) => ({ ...entry, value: entry.value >= 0 ? 1 : -1 }));\n const segmentWidth = Math.max(4 * zoomFactor, sparkWidth / Math.max(normalizedValues.length * 1.9, 1));\n const gap = normalizedValues.length > 1\n ? (sparkWidth - segmentWidth * normalizedValues.length) / (normalizedValues.length - 1)\n : 0;\n const positiveY = sparkTop + Math.max(1.5 * zoomFactor, sparkHeight * 0.18);\n const negativeY = sparkTop + sparkHeight - Math.max(1.5 * zoomFactor, sparkHeight * 0.18);\n paneContext.strokeStyle = seriesColor;\n paneContext.lineCap = \"round\";\n paneContext.lineWidth = Math.max(1.5, 1.8 * zoomFactor);\n normalizedValues.forEach((entry, index) => {\n const left = sparkLeft + index * (segmentWidth + Math.max(0, gap));\n const y = entry.value >= 0 ? positiveY : negativeY;\n paneContext.beginPath();\n paneContext.moveTo(left, y);\n paneContext.lineTo(left + segmentWidth, y);\n paneContext.stroke();\n });\n } else if (sparkline.type === \"column\") {\n const minValue = Math.min(0, ...points.map((entry) => entry.value));\n const maxValue = Math.max(0, ...points.map((entry) => entry.value));\n const zeroY = sparkTop + sparkHeight - clampSparklineValue(0, minValue, maxValue) * sparkHeight;\n const barWidth = Math.max(2 * zoomFactor, sparkWidth / Math.max(points.length * 1.8, 1));\n const gap = points.length > 1 ? (sparkWidth - barWidth * points.length) / (points.length - 1) : 0;\n paneContext.strokeStyle = palette.border;\n paneContext.lineWidth = 1;\n paneContext.beginPath();\n paneContext.moveTo(sparkLeft, zeroY);\n paneContext.lineTo(sparkLeft + sparkWidth, zeroY);\n paneContext.stroke();\n points.forEach((entry, index) => {\n const left = sparkLeft + index * (barWidth + Math.max(0, gap));\n const y = sparkTop + sparkHeight - clampSparklineValue(entry.value, minValue, maxValue) * sparkHeight;\n const top = Math.min(y, zeroY);\n const barHeight = Math.max(1, Math.abs(y - zeroY));\n paneContext.fillStyle = entry.value < 0 ? negativeColor : seriesColor;\n paneContext.fillRect(left, top, barWidth, barHeight);\n });\n } else if (points.length > 1) {\n const minValue = Math.min(...points.map((entry) => entry.value));\n const maxValue = Math.max(...points.map((entry) => entry.value));\n const xStep = points.length > 1 ? sparkWidth / (points.length - 1) : 0;\n paneContext.strokeStyle = seriesColor;\n paneContext.lineCap = \"round\";\n paneContext.lineJoin = \"round\";\n paneContext.lineWidth = Math.max(1.2, 1.6 * zoomFactor);\n paneContext.beginPath();\n points.forEach((entry, index) => {\n const x = sparkLeft + index * xStep;\n const y = sparkTop + sparkHeight - clampSparklineValue(entry.value, minValue, maxValue) * sparkHeight;\n if (index === 0) {\n paneContext.moveTo(x, y);\n } else {\n paneContext.lineTo(x, y);\n }\n });\n paneContext.stroke();\n\n if (sparkline.markers) {\n const highValue = Math.max(...points.map((entry) => entry.value));\n const lowValue = Math.min(...points.map((entry) => entry.value));\n points.forEach((entry, index) => {\n const x = sparkLeft + index * xStep;\n const y = sparkTop + sparkHeight - clampSparklineValue(entry.value, minValue, maxValue) * sparkHeight;\n let fill = markerColor;\n if (entry.value === highValue && sparkline.highColor) {\n fill = sparkline.highColor;\n } else if (entry.value === lowValue && sparkline.lowColor) {\n fill = sparkline.lowColor;\n } else if (index === 0 && sparkline.firstColor) {\n fill = sparkline.firstColor;\n } else if (index === points.length - 1 && sparkline.lastColor) {\n fill = sparkline.lastColor;\n } else if (entry.value < 0 && sparkline.negative && sparkline.negativeColor) {\n fill = sparkline.negativeColor;\n }\n paneContext.fillStyle = fill;\n paneContext.beginPath();\n paneContext.arc(x, y, Math.max(1.25, 1.75 * zoomFactor), 0, Math.PI * 2);\n paneContext.fill();\n });\n }\n }\n }\n } else {\n const align = canvasCellStyle.textAlign;\n paneContext.textAlign = align;\n const leadingInset = cellData.conditionalIcon ? 18 * zoomFactor : 0;\n const trailingInset = cellData.isTableHeader ? 16 * zoomFactor : 0;\n const textLeft = contentLeft + leadingInset;\n const textWidth = Math.max(0, contentWidth - leadingInset - trailingInset);\n const textX = align === \"right\"\n ? textLeft + textWidth\n : align === \"center\"\n ? textLeft + textWidth / 2\n : textLeft;\n const spillMaxWidth = cellData.spillWidth && cellData.spillWidth > 0\n ? Math.max(0, cellData.spillWidth - trailingInset)\n : null;\n const maxTextWidth = spillMaxWidth ?? textWidth;\n const textColor = canvasCellStyle.textColor;\n const shouldEllipsizeText = canvasCellStyle.textOverflowEllipsis;\n const shouldWrapText = canvasCellStyle.usesWrappedText || rawText.includes(\"\\n\");\n const singleLineHeight = cellData.shrinkToFitFontSizePx\n ? resolveCanvasLineHeight(cellStyle, cellData.shrinkToFitFontSizePx)\n : resolveCanvasLineHeight(cellStyle, 12 * zoomFactor);\n\n if (cellData.textRotationDeg && rawText.length > 0) {\n drawRotatedCellText(paneContext, rawText, cellData.textRotationDeg, {\n left: textLeft, top: contentTop, width: maxTextWidth, height: contentHeight\n }, align, cellStyle.verticalAlign, singleLineHeight);\n } else if (shouldWrapText) {\n const wrappedLines = wrapCanvasText(paneContext, rawText, maxTextWidth);\n const lineHeight = cellData.shrinkToFitFontSizePx\n ? resolveCanvasLineHeight(cellStyle, cellData.shrinkToFitFontSizePx)\n : resolveCanvasLineHeight(cellStyle, 12 * zoomFactor);\n const textBlockHeight = wrappedLines.length * lineHeight;\n const verticalAlign = cellStyle.verticalAlign;\n let textBlockTop = contentTop;\n if (verticalAlign === \"middle\") {\n textBlockTop = contentTop + ((contentHeight - textBlockHeight) / 2);\n } else if (verticalAlign !== \"top\") {\n textBlockTop = contentTop + contentHeight - textBlockHeight;\n }\n\n wrappedLines.forEach((line, lineIndex) => {\n const textY = textBlockTop + (lineIndex * lineHeight) + (lineHeight / 2);\n paneContext.fillText(line, textX, textY);\n drawCanvasTextDecorations(paneContext, {\n align,\n color: textColor,\n decoration: canvasCellStyle.textDecoration,\n lineThroughY: textY,\n maxWidth: maxTextWidth,\n text: line,\n textX,\n underlineY: textY + Math.max(2, lineHeight * 0.24),\n zoomFactor\n });\n });\n } else if (spillMaxWidth != null) {\n const text = shouldEllipsizeText ? truncateCanvasText(paneContext, rawText, maxTextWidth) : rawText;\n const textY = resolveCanvasTextMiddleY(cellStyle.verticalAlign, contentTop, contentHeight, singleLineHeight);\n const spillClipLeft = cellData.spillSide === \"left\"\n ? contentLeft + contentWidth - spillMaxWidth - textClipOverscan\n : contentLeft - textClipOverscan;\n deferredSpillTextsByPane[pane].push({\n align,\n clipHeight: contentHeight + (textClipOverscan * 2),\n clipLeft: spillClipLeft,\n clipTop: contentTop - textClipOverscan,\n clipWidth: spillMaxWidth + (textClipOverscan * 2),\n color: textColor,\n ellipsize: shouldEllipsizeText,\n font: paneContext.font,\n maxWidth: spillMaxWidth,\n text,\n textDecoration: canvasCellStyle.textDecoration,\n textX,\n textY,\n lineThroughY: textY,\n underlineY: textY + (6 * zoomFactor)\n });\n } else {\n const text = cellData.shrinkToFit\n ? rawText\n : shouldEllipsizeText\n ? truncateCanvasText(paneContext, rawText, maxTextWidth)\n : rawText;\n const textY = resolveCanvasTextMiddleY(cellStyle.verticalAlign, contentTop, contentHeight, singleLineHeight);\n paneContext.fillText(text, textX, textY);\n drawCanvasTextDecorations(paneContext, {\n align,\n color: textColor,\n decoration: canvasCellStyle.textDecoration,\n ellipsize: shouldEllipsizeText,\n lineThroughY: textY,\n maxWidth: maxTextWidth,\n text,\n textX,\n underlineY: textY + 6 * zoomFactor,\n zoomFactor\n });\n }\n }\n\n if (cellData.conditionalIcon) {\n const iconSize = 10 * zoomFactor;\n const iconX = localRect.left + padding.left;\n const iconY = localRect.top + (localRect.height / 2);\n drawCanvasConditionalIcon(paneContext, cellData.conditionalIcon, iconX + (iconSize / 2), iconY, iconSize);\n }\n\n if (cellData.isTableHeader) {\n paneContext.fillStyle = palette.mutedText;\n paneContext.textAlign = \"center\";\n paneContext.fillText(\"▾\", localRect.left + localRect.width - (10 * zoomFactor), localRect.top + (localRect.height / 2));\n }\n\n paneContext.restore();\n }\n }\n flushPendingGridlines();\n for (const { side, rect, border } of deferredDoubleBorders) {\n strokeCanvasBorderSide(paneContext, side, rect, border);\n }\n }\n\n for (const pane of cellPaneOrder) {\n const paneContext = bodyContexts[pane];\n if (!paneContext) {\n continue;\n }\n for (const spillText of deferredSpillTextsByPane[pane]) {\n paneContext.save();\n paneContext.beginPath();\n paneContext.rect(spillText.clipLeft, spillText.clipTop, spillText.clipWidth, spillText.clipHeight);\n paneContext.clip();\n paneContext.font = spillText.font;\n paneContext.fillStyle = spillText.color;\n paneContext.textAlign = spillText.align;\n paneContext.textBaseline = \"middle\";\n paneContext.fillText(spillText.text, spillText.textX, spillText.textY);\n drawCanvasTextDecorations(paneContext, {\n align: spillText.align,\n color: spillText.color,\n decoration: spillText.textDecoration,\n ellipsize: spillText.ellipsize,\n lineThroughY: spillText.lineThroughY,\n maxWidth: spillText.maxWidth,\n text: spillText.text,\n textX: spillText.textX,\n underlineY: spillText.underlineY,\n zoomFactor\n });\n paneContext.restore();\n }\n }\n\n for (const pane of cellPaneOrder) {\n const paneContext = bodyContexts[pane];\n if (!paneContext) {\n continue;\n }\n drawBakedCanvasDrawings(pane, paneContext, bodyDirtyRectsByPane[pane]);\n }\n canvasProfileBodyMs = canvasProfileTarget ? performance.now() - canvasProfileBodyStart : 0;\n }\n\n if (shouldRepaintHeaders && cornerContext) {\n const topFrozenHeaderContext = topHeaderContexts.frozen;\n const topScrollHeaderContext = topHeaderContexts.scroll;\n const leftFrozenHeaderContext = leftHeaderContexts.frozen;\n const leftScrollHeaderContext = leftHeaderContexts.scroll;\n\n if (canBlitTopHeader) {\n const bufferCanvas = getHeaderBlitBufferCanvas(\"top\");\n if (bufferCanvas && topScrollHeaderContext && topScrollHeaderCanvasRef.current) {\n const blittedDirtyRects = blitCanvasWithScrollDelta(\n topScrollHeaderCanvasRef.current,\n topScrollHeaderContext,\n bufferCanvas,\n dpr,\n topScrollHeaderCanvasWidth,\n headerHeight,\n drawingViewport.left - previousPaintedViewport.left,\n 0\n );\n if (blittedDirtyRects) {\n topScrollHeaderDirtyRects = blittedDirtyRects;\n }\n }\n }\n if (canBlitLeftHeader) {\n const bufferCanvas = getHeaderBlitBufferCanvas(\"left\");\n if (bufferCanvas && leftScrollHeaderContext && leftScrollHeaderCanvasRef.current) {\n const blittedDirtyRects = blitCanvasWithScrollDelta(\n leftScrollHeaderCanvasRef.current,\n leftScrollHeaderContext,\n bufferCanvas,\n dpr,\n rowHeaderWidth,\n leftScrollHeaderCanvasHeight,\n 0,\n drawingViewport.top - previousPaintedViewport.top\n );\n if (blittedDirtyRects) {\n leftScrollHeaderDirtyRects = blittedDirtyRects;\n }\n }\n }\n\n if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth > 0) {\n topFrozenHeaderContext.fillStyle = palette.headerSurface;\n topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth, headerHeight);\n }\n if (topScrollHeaderContext && topScrollHeaderCanvasWidth > 0) {\n topScrollHeaderContext.fillStyle = palette.headerSurface;\n for (const dirtyRect of topScrollHeaderDirtyRects) {\n topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);\n }\n }\n if (topFrozenHeaderContext) {\n topFrozenHeaderContext.strokeStyle = palette.border;\n topFrozenHeaderContext.lineWidth = 1;\n }\n if (topScrollHeaderContext) {\n topScrollHeaderContext.strokeStyle = palette.border;\n topScrollHeaderContext.lineWidth = 1;\n }\n for (const column of canvasColumnHeaderCells) {\n const paneContext = column.isFrozen ? topFrozenHeaderContext : topScrollHeaderContext;\n if (!paneContext) {\n continue;\n }\n if (\n column.localLeft + column.width < 0\n || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth : topScrollHeaderCanvasWidth)\n ) {\n continue;\n }\n if (\n !column.isFrozen\n && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)\n ) {\n continue;\n }\n\n const selected =\n normalizedVisibleRange\n && column.actualCol >= normalizedVisibleRange.start.col\n && column.actualCol <= normalizedVisibleRange.end.col;\n paneContext.fillStyle = selected ? selectionHeaderSurface : palette.headerSurface;\n paneContext.fillRect(column.localLeft, 0, column.width, column.height);\n paneContext.strokeStyle = palette.border;\n paneContext.beginPath();\n paneContext.moveTo(column.localLeft + column.width - 0.5, 0);\n paneContext.lineTo(column.localLeft + column.width - 0.5, column.height);\n paneContext.moveTo(column.localLeft, column.height - 0.5);\n paneContext.lineTo(column.localLeft + column.width, column.height - 0.5);\n paneContext.stroke();\n paneContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;\n paneContext.fillStyle = palette.headerText;\n paneContext.textAlign = \"center\";\n paneContext.textBaseline = \"middle\";\n paneContext.fillText(\n columnLabel(column.actualCol),\n column.localLeft + (column.width / 2),\n column.height / 2\n );\n }\n\n if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight > 0) {\n leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;\n leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight);\n }\n if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight > 0) {\n leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;\n for (const dirtyRect of leftScrollHeaderDirtyRects) {\n leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);\n }\n }\n if (leftFrozenHeaderContext) {\n leftFrozenHeaderContext.strokeStyle = palette.border;\n leftFrozenHeaderContext.lineWidth = 1;\n }\n if (leftScrollHeaderContext) {\n leftScrollHeaderContext.strokeStyle = palette.border;\n leftScrollHeaderContext.lineWidth = 1;\n }\n for (const row of canvasRowHeaderCells) {\n const paneContext = row.isFrozen ? leftFrozenHeaderContext : leftScrollHeaderContext;\n if (!paneContext) {\n continue;\n }\n if (\n row.localTop + row.height < 0\n || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight : leftScrollHeaderCanvasHeight)\n ) {\n continue;\n }\n if (\n !row.isFrozen\n && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)\n ) {\n continue;\n }\n\n const selected =\n normalizedVisibleRange\n && row.actualRow >= normalizedVisibleRange.start.row\n && row.actualRow <= normalizedVisibleRange.end.row;\n paneContext.fillStyle = selected ? selectionHeaderSurface : palette.rowHeaderSurface;\n paneContext.fillRect(0, row.localTop, rowHeaderWidth, row.height);\n paneContext.beginPath();\n paneContext.moveTo(0, row.localTop + row.height - 0.5);\n paneContext.lineTo(rowHeaderWidth, row.localTop + row.height - 0.5);\n paneContext.moveTo(rowHeaderWidth - 0.5, row.localTop);\n paneContext.lineTo(rowHeaderWidth - 0.5, row.localTop + row.height);\n paneContext.stroke();\n paneContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;\n paneContext.fillStyle = palette.headerText;\n paneContext.textAlign = \"center\";\n paneContext.textBaseline = \"middle\";\n paneContext.fillText(`${row.actualRow + 1}`, rowHeaderWidth / 2, row.localTop + (row.height / 2));\n }\n\n cornerContext.fillStyle = palette.headerSurface;\n cornerContext.fillRect(0, 0, rowHeaderWidth, headerHeight);\n cornerContext.strokeStyle = palette.border;\n cornerContext.lineWidth = 1;\n cornerContext.beginPath();\n cornerContext.moveTo(rowHeaderWidth - 0.5, 0);\n cornerContext.lineTo(rowHeaderWidth - 0.5, headerHeight);\n cornerContext.moveTo(0, headerHeight - 0.5);\n cornerContext.lineTo(rowHeaderWidth, headerHeight - 0.5);\n cornerContext.stroke();\n }\n paintedDrawingViewportRef.current = drawingViewport;\n if (shouldRepaintBody) {\n paintedBodyCanvasSignatureRef.current = nextBodyCanvasSignature;\n }\n if (shouldRepaintHeaders) {\n paintedHeaderCanvasSignatureRef.current = nextHeaderCanvasSignature;\n }\n applyCanvasViewportCompensation();\n if (canvasProfileTarget) {\n canvasProfileTarget.push({\n bodyMs: canvasProfileBodyMs,\n culledCells: canvasProfileCulledCells,\n dirtyRects: canvasProfileDirtyRects,\n lookedUpCells: canvasProfileLookedUpCells,\n paintedCells: canvasProfilePaintedCells,\n repaintBody: shouldRepaintBody,\n repaintHeaders: shouldRepaintHeaders,\n renderToLayoutMs: canvasProfileStart - xlsxGridRenderStart,\n totalMs: performance.now() - canvasProfileStart\n });\n if (canvasProfileTarget.length > 500) {\n canvasProfileTarget.splice(0, canvasProfileTarget.length - 500);\n }\n }\n }, [\n activeSheet,\n applyCanvasViewportCompensation,\n bakedCanvasDrawingSignature,\n bakedImageRects,\n bakedShapeRects,\n canvasColumnHeaderCells,\n canvasPaneAxisItems,\n canvasRowHeaderCells,\n canvasVisibleColItems,\n canvasVisibleRowItems,\n colIndexByActual,\n colPrefixSums,\n displayEffectiveColWidths,\n displayEffectiveRowHeights,\n displayHeaderHeight,\n displayRowHeaderWidth,\n displayedSelection,\n drawingViewport.left,\n drawingViewport.top,\n drawingViewport.height,\n drawingViewport.width,\n experimentalCanvas,\n frozenPaneBottom,\n frozenPaneRight,\n getCellData,\n getCanvasImage,\n getBodyBlitBufferCanvas,\n getHeaderBlitBufferCanvas,\n palette,\n resolveCellDisplayRect,\n resolveMergeAnchorCell,\n rowIndexByActual,\n rowPrefixSums,\n selectionHeaderSurface,\n shouldBakeCanvasStaticDrawings,\n stickyLeftByCol,\n stickyTopByRow,\n visibleCols,\n visibleRows,\n zoomFactor\n ]);\n\n const startChartMove = React.useCallback((\n event: React.PointerEvent<HTMLElement>,\n chart: XlsxChart,\n rect: XlsxImageRect\n ) => {\n if (event.button !== 0) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n focusGrid();\n selectChart(chart.id);\n\n if (readOnlyRef.current || chart.editable === false) {\n return;\n }\n\n event.currentTarget.setPointerCapture?.(event.pointerId);\n chartInteractionRef.current = {\n baseRect: rect,\n chartId: chart.id,\n didMove: false,\n pointerId: event.pointerId,\n startClientX: event.clientX,\n startClientY: event.clientY,\n type: \"move\"\n };\n chartPreviewRectRef.current = { id: chart.id, rect };\n setChartPreviewRect({ id: chart.id, rect });\n setInteractionMode(\"select\");\n document.body.style.cursor = \"move\";\n document.body.style.userSelect = \"none\";\n installChartInteractionListenersRef.current(event.pointerId);\n }, [focusGrid, selectChart]);\n\n const startChartResize = React.useCallback((\n event: React.PointerEvent<HTMLElement>,\n chart: XlsxChart,\n rect: XlsxImageRect,\n handle: XlsxImageResizeHandlePosition\n ) => {\n if (readOnlyRef.current || chart.editable === false || event.button !== 0) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n focusGrid();\n selectChart(chart.id);\n event.currentTarget.setPointerCapture?.(event.pointerId);\n chartInteractionRef.current = {\n baseRect: rect,\n chartId: chart.id,\n didMove: false,\n handle,\n pointerId: event.pointerId,\n startClientX: event.clientX,\n startClientY: event.clientY,\n type: \"resize\"\n };\n chartPreviewRectRef.current = { id: chart.id, rect };\n setChartPreviewRect({ id: chart.id, rect });\n setInteractionMode(\"select\");\n document.body.style.cursor = String(IMAGE_HANDLE_CURSOR[handle]);\n document.body.style.userSelect = \"none\";\n installChartInteractionListenersRef.current(event.pointerId);\n }, [focusGrid, selectChart]);\n\n const startImageMove = React.useCallback((\n event: React.PointerEvent<HTMLElement>,\n image: XlsxImage,\n rect: XlsxImageRect\n ) => {\n if (event.button !== 0) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n focusGrid();\n selectImage(image.id);\n\n if (readOnlyRef.current || image.editable === false) {\n return;\n }\n\n event.currentTarget.setPointerCapture?.(event.pointerId);\n imageInteractionRef.current = {\n baseRect: rect,\n didMove: false,\n imageId: image.id,\n pointerId: event.pointerId,\n startClientX: event.clientX,\n startClientY: event.clientY,\n type: \"move\"\n };\n imagePreviewRectRef.current = { id: image.id, rect };\n setImagePreviewRect({ id: image.id, rect });\n setInteractionMode(\"select\");\n document.body.style.cursor = \"move\";\n document.body.style.userSelect = \"none\";\n installImageInteractionListenersRef.current(event.pointerId);\n }, [focusGrid, selectImage]);\n\n const startImageResize = React.useCallback((\n event: React.PointerEvent<HTMLElement>,\n image: XlsxImage,\n rect: XlsxImageRect,\n handle: XlsxImageResizeHandlePosition\n ) => {\n if (readOnlyRef.current || image.editable === false || event.button !== 0) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n focusGrid();\n selectImage(image.id);\n event.currentTarget.setPointerCapture?.(event.pointerId);\n imageInteractionRef.current = {\n baseRect: rect,\n didMove: false,\n handle,\n imageId: image.id,\n pointerId: event.pointerId,\n startClientX: event.clientX,\n startClientY: event.clientY,\n type: \"resize\"\n };\n imagePreviewRectRef.current = { id: image.id, rect };\n setImagePreviewRect({ id: image.id, rect });\n setInteractionMode(\"select\");\n document.body.style.cursor = String(IMAGE_HANDLE_CURSOR[handle]);\n document.body.style.userSelect = \"none\";\n installImageInteractionListenersRef.current(event.pointerId);\n }, [focusGrid, selectImage]);\n\n const handleImageClick = React.useCallback((image: XlsxImage) => {\n if (skipNextImageClickRef.current === image.id) {\n skipNextImageClickRef.current = null;\n return;\n }\n\n if (image.hyperlink) {\n openHyperlink(image.hyperlink);\n }\n }, [openHyperlink]);\n\n const handleShapeClick = React.useCallback((shape: XlsxShape) => {\n if (shape.hyperlink) {\n openHyperlink(shape.hyperlink);\n }\n }, [openHyperlink]);\n\n const handleChartClick = React.useCallback((chart: XlsxChart) => {\n if (skipNextChartClickRef.current === chart.id) {\n skipNextChartClickRef.current = null;\n return;\n }\n\n selectChart(chart.id);\n }, [selectChart]);\n\n const handleChartElementPointerDown = React.useCallback((selection: XlsxChartElementSelection) => {\n skipNextChartClickRef.current = selection.chartId;\n selectChartElement(selection);\n }, [selectChartElement]);\n\n const handleChartElementDoubleClick = React.useCallback((selection: XlsxChartElementSelection) => {\n selectChartElement(selection);\n }, [selectChartElement]);\n\n if (isLoading) {\n return <>{renderLoading(loadingComponent, loadingState, palette)}</>;\n }\n\n if (isLoadDeferred) {\n return <>{renderDeferredLoad({ ...controller, canLoadDeferred, continueDeferredLoad, deferredLoadFileSize, isLoadDeferred }, palette)}</>;\n }\n\n if (error) {\n if (error instanceof XlsxFileSizeLimitExceededError) {\n return (\n <>\n {renderFileTooLarge(\n fileTooLargeState,\n {\n displayFileName,\n fileSizeBytes: error.fileSizeBytes,\n maxFileSizeBytes: error.maxFileSizeBytes\n },\n palette\n )}\n </>\n );\n }\n\n return <>{renderError(errorState, error, palette)}</>;\n }\n\n if (!activeSheet && activeTab?.kind === \"chartsheet\") {\n return (\n <div\n style={{\n alignItems: \"stretch\",\n backgroundColor: palette.canvas,\n display: \"flex\",\n flex: 1,\n justifyContent: \"center\",\n minHeight: 0,\n minWidth: 0,\n padding: 16\n }}\n >\n <div\n style={{\n backgroundColor: palette.surface,\n border: `1px solid ${palette.border}`,\n borderRadius: 12,\n display: \"grid\",\n gap: 16,\n gridTemplateColumns: charts.length > 1 ? \"repeat(auto-fit, minmax(320px, 1fr))\" : \"minmax(320px, 1fr)\",\n padding: 16,\n width: \"100%\"\n }}\n >\n {charts.length > 0 ? charts.map((chart) => {\n const sourceSize = experimentalOfficeCharts && chart.anchor.kind !== \"two-cell\" ? chart.anchor.sizeEmu : undefined;\n const chartsheetRect = {\n height: sourceSize?.cy ? emuToPixels(sourceSize.cy) : 320,\n left: 0,\n top: 0,\n width: sourceSize?.cx ? emuToPixels(sourceSize.cx) : 640\n };\n return (\n <div key={chart.id} style={{\n minHeight: sourceSize ? undefined : 320,\n width: sourceSize ? chartsheetRect.width : undefined,\n maxWidth: \"100%\",\n aspectRatio: sourceSize ? `${chartsheetRect.width}/${chartsheetRect.height}` : undefined,\n position: \"relative\"\n }}>\n {isChartsLoading\n ? renderChartLoadingNode(renderChartLoading, chart, chartsheetRect)\n : (\n <MemoChartSvg\n experimentalOfficeCharts={experimentalOfficeCharts}\n chart={chart}\n loadingFallback={(stage) => renderChartLoadingNode(renderChartLoading, chart, chartsheetRect, stage)}\n onChartElementDoubleClick={handleChartElementDoubleClick}\n onChartElementPointerDown={handleChartElementPointerDown}\n palette={palette}\n rect={chartsheetRect}\n selectedChartElement={selectedChartElement}\n />\n )}\n </div>\n );\n }) : (\n <div\n style={{\n alignItems: \"center\",\n color: palette.mutedText,\n display: \"flex\",\n fontSize: 13,\n justifyContent: \"center\",\n minHeight: 320\n }}\n >\n Chartsheet metadata is present, but no embedded chart payload was exposed for this tab.\n </div>\n )}\n </div>\n </div>\n );\n }\n\n if (!activeSheet) {\n return <>{renderEmpty(emptyState, palette)}</>;\n }\n\n const virtualRows = domExpandedWindow.rowIndices.map((index) => {\n const virtualRow = rowVirtualItemByIndex.get(index);\n return {\n end: virtualRow?.end ?? (rowPrefixSums[index + 1] ?? 0),\n index,\n key: visibleRows[index] ?? index,\n size: virtualRow?.size ?? (displayEffectiveRowHeights[index] ?? displayDefaultRowHeight),\n start: virtualRow?.start ?? (rowPrefixSums[index] ?? 0)\n };\n });\n const totalHeight = shouldUseDomVirtualizer && shouldVirtualizeRows\n ? rowVirtualizer.getTotalSize()\n : (rowPrefixSums[rowPrefixSums.length - 1] ?? 0);\n const totalWidth = totalContentWidth + displayRowHeaderWidth;\n const sheetContentHeight = displayHeaderHeight + totalHeight;\n const isLiveZooming = liveGestureZoom !== null && zoomScale === liveGestureZoom.baseZoomScale;\n const liveZoomScale = isLiveZooming\n ? Math.max(0.1, liveGestureZoom.targetZoomScale / liveGestureZoom.baseZoomScale)\n : 1;\n const liveZoomScrollLeft = scrollRef.current?.scrollLeft ?? drawingViewport.left;\n const liveZoomScrollTop = scrollRef.current?.scrollTop ?? drawingViewport.top;\n const liveZoomTranslateX = isLiveZooming\n ? (liveZoomScrollLeft + liveGestureZoom.anchor.x) * (1 - liveZoomScale)\n : 0;\n const liveZoomTranslateY = isLiveZooming\n ? (liveZoomScrollTop + liveGestureZoom.anchor.y) * (1 - liveZoomScale)\n : 0;\n const headerLabelLiveScale = isLiveZooming ? liveZoomScale : 1;\n const selectionBorderWidth = Math.max(1, zoomFactor);\n const shouldAnimateCanvasSelection = experimentalCanvas && enableCanvasSelectionAnimation;\n const shouldAnimateCanvasSelectionGeometry = shouldAnimateCanvasSelection && liveGestureZoom === null;\n const canvasSelectionTransition = shouldAnimateCanvasSelectionGeometry\n ? \"left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1), width 120ms cubic-bezier(0.22, 1, 0.36, 1), height 120ms cubic-bezier(0.22, 1, 0.36, 1), opacity 100ms linear\"\n : \"none\";\n const rowColSpan = renderedCols.length + 1 + (leadingColumnSpacerWidth > 0 ? 1 : 0) + (trailingColumnSpacerWidth > 0 ? 1 : 0);\n const gutterSeparatorShadow = `inset -1px 0 0 ${palette.border}, inset 0 -1px 0 ${palette.border}`;\n const maxDrawingOverlayZIndex = Math.max(\n 0,\n ...shapes.map((shape) => shape.zIndex + 20),\n ...formControls.map((control) => control.zIndex + 20),\n ...images.map((image) => image.zIndex + 22),\n ...charts.map((chart) => chart.zIndex + 22)\n );\n const rowHeaderOverlayZIndex = Math.max(35, maxDrawingOverlayZIndex + 1);\n const canvasHeaderOverlayZIndex = Math.max(50, rowHeaderOverlayZIndex + 1);\n const stickyHeaderOverlayZIndex = canvasHeaderOverlayZIndex + 1;\n const cornerHeaderOverlayZIndex = canvasHeaderOverlayZIndex + 2;\n const frozenRowHeaderOverlayZIndex = rowHeaderOverlayZIndex;\n const headerCellStyle = scaleCssProperties({\n backgroundColor: palette.headerSurface,\n borderBottom: \"none\",\n borderRight: \"none\",\n boxSizing: \"border-box\",\n boxShadow: gutterSeparatorShadow,\n color: palette.headerText,\n fontSize: \"11px\",\n fontWeight: 600,\n height: HEADER_HEIGHT,\n overflow: \"hidden\",\n padding: \"2px 4px\",\n position: \"sticky\",\n textAlign: \"center\",\n top: 0,\n userSelect: \"none\",\n whiteSpace: \"nowrap\",\n zIndex: canvasHeaderOverlayZIndex\n }, zoomFactor);\n const columnResizeHandleStyle = scaleCssProperties({\n backgroundColor: \"transparent\",\n cursor: \"col-resize\",\n position: \"absolute\",\n right: -8,\n top: 0,\n width: 16,\n height: \"100%\",\n zIndex: 5\n }, zoomFactor);\n const canvasBodyViewportLayerStyle: React.CSSProperties = {\n height: 0,\n left: 0,\n overflow: \"visible\",\n pointerEvents: \"none\",\n position: \"sticky\",\n top: 0,\n width: 0\n };\n const canvasHeaderViewportLayerStyle: React.CSSProperties = {\n height: 0,\n left: 0,\n overflow: \"visible\",\n pointerEvents: \"none\",\n position: \"sticky\",\n top: 0,\n width: 0,\n zIndex: canvasHeaderOverlayZIndex\n };\n const canvasBodyBaseStyle: React.CSSProperties = {\n cursor: \"cell\",\n pointerEvents: \"auto\",\n position: \"absolute\",\n transformOrigin: \"0 0\",\n transition: \"none\",\n zIndex: 10\n };\n const canvasScrollBodyStyle: React.CSSProperties = {\n ...canvasBodyBaseStyle,\n display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? \"block\" : \"none\",\n left: scrollBodyCanvasLeft,\n top: scrollBodyCanvasTop\n };\n const canvasTopBodyStyle: React.CSSProperties = {\n ...canvasBodyBaseStyle,\n display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? \"block\" : \"none\",\n left: scrollBodyCanvasLeft,\n top: displayHeaderHeight,\n zIndex: 30\n };\n const canvasLeftBodyStyle: React.CSSProperties = {\n ...canvasBodyBaseStyle,\n display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? \"block\" : \"none\",\n left: displayRowHeaderWidth,\n top: scrollBodyCanvasTop,\n zIndex: 30\n };\n const canvasCornerBodyStyle: React.CSSProperties = {\n ...canvasBodyBaseStyle,\n display: cornerBodyCanvasWidth > 0 && cornerBodyCanvasHeight > 0 ? \"block\" : \"none\",\n left: displayRowHeaderWidth,\n top: displayHeaderHeight,\n zIndex: 31\n };\n const canvasHeaderBaseStyle: React.CSSProperties = {\n cursor: \"default\",\n pointerEvents: \"auto\",\n position: \"absolute\",\n transformOrigin: \"0 0\"\n };\n const canvasTopFrozenHeaderStyle: React.CSSProperties = {\n ...canvasHeaderBaseStyle,\n display: topFrozenHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? \"block\" : \"none\",\n left: displayRowHeaderWidth,\n top: 0,\n zIndex: stickyHeaderOverlayZIndex\n };\n const canvasTopScrollHeaderStyle: React.CSSProperties = {\n ...canvasHeaderBaseStyle,\n display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? \"block\" : \"none\",\n left: scrollBodyCanvasLeft,\n top: 0,\n zIndex: canvasHeaderOverlayZIndex\n };\n const canvasLeftFrozenHeaderStyle: React.CSSProperties = {\n ...canvasHeaderBaseStyle,\n display: leftFrozenHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? \"block\" : \"none\",\n left: 0,\n top: displayHeaderHeight,\n zIndex: stickyHeaderOverlayZIndex\n };\n const canvasLeftScrollHeaderStyle: React.CSSProperties = {\n ...canvasHeaderBaseStyle,\n display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? \"block\" : \"none\",\n left: 0,\n top: scrollBodyCanvasTop,\n zIndex: canvasHeaderOverlayZIndex\n };\n const canvasCornerHeaderStyle: React.CSSProperties = {\n display: drawingViewport.width > 0 && drawingViewport.height > 0 ? \"block\" : \"none\",\n left: 0,\n pointerEvents: \"none\",\n position: \"absolute\",\n top: 0,\n transformOrigin: \"0 0\",\n zIndex: cornerHeaderOverlayZIndex\n };\n const editingOverlayRect = experimentalCanvas && editingCell\n ? resolveCellDisplayRect(editingCell)\n : null;\n const activeCellAdornment = experimentalCanvas && activeCell\n ? renderCellAdornment(activeCell)\n : null;\n const activeCellAdornmentRect = activeCell && activeCellAdornment\n ? resolveCellDisplayRect(activeCell)\n : null;\n const activeCellOverlayCell = renderActiveCellOverlay && activeCell\n ? resolveMergeAnchorCell(activeCell)\n : null;\n const activeCellOverlayRect = activeCellOverlayCell\n ? resolveCellDisplayRect(activeCellOverlayCell)\n : null;\n const activeCellOverlay = activeCellOverlayCell && activeCellOverlayRect\n ? renderActiveCellOverlay?.({ cell: activeCellOverlayCell, rect: activeCellOverlayRect })\n : null;\n function resolveDrawingPane(rect: XlsxImageRect) {\n return resolveFrozenDrawingPane(\n rect,\n frozenRows,\n frozenCols,\n displayActualRowHeights,\n displayActualColWidths,\n activeSheet?.freezePanes ?? null,\n stickyTopByRow,\n stickyLeftByCol,\n {\n defaultColWidth: displayDefaultColWidth,\n defaultRowHeight: displayDefaultRowHeight,\n headerHeight: displayHeaderHeight,\n rowHeaderWidth: displayRowHeaderWidth\n }\n );\n }\n\n function renderShapeDrawing(shape: XlsxShape, rect: XlsxImageRect, pane: FrozenDrawingPane) {\n const drawingPane = resolveDrawingPane(rect);\n if (drawingPane !== pane) {\n return null;\n }\n const hasMeasuredViewport = drawingViewport.width > 0 && drawingViewport.height > 0;\n if (pane === \"scroll\" && hasMeasuredViewport && !rectIntersectsViewport(rect, drawingViewport)) {\n return null;\n }\n\n const isFrozenDrawing = pane !== \"scroll\";\n const inset = shape.textBox?.insetPx;\n const groupScaleX = shape.scaleX ?? 1;\n const groupScaleY = shape.scaleY ?? 1;\n const strokeScale = Math.max(groupScaleX, groupScaleY);\n const textScale = strokeScale * zoomFactor;\n const textWidth = groupScaleX !== 0 ? rect.width / groupScaleX : rect.width;\n const textHeight = groupScaleY !== 0 ? rect.height / groupScaleY : rect.height;\n const vectorShape = resolveShapeVector(shape);\n const strokeColor = shape.stroke?.none ? \"transparent\" : (shape.stroke?.color ?? \"transparent\");\n const scaledStrokeWidth = (shape.stroke?.widthPx ?? (shape.geometry === \"line\" ? 2 : 1)) * strokeScale * zoomFactor;\n const headMarkerId = `${shape.id}-${pane}-head-marker`;\n const tailMarkerId = `${shape.id}-${pane}-tail-marker`;\n const headMarker = vectorShape\n ? resolveShapeLineEndMarker(\n shape.stroke?.headEndType,\n headMarkerId,\n strokeColor,\n scaledStrokeWidth,\n rect,\n vectorShape.viewBox\n )\n : null;\n const tailMarker = vectorShape\n ? resolveShapeLineEndMarker(\n shape.stroke?.tailEndType,\n tailMarkerId,\n strokeColor,\n scaledStrokeWidth,\n rect,\n vectorShape.viewBox\n )\n : null;\n const style = {\n ...buildShapeContainerStyle(shape, rect, zoomFactor),\n ...(vectorShape ? {\n backgroundColor: \"transparent\",\n border: \"none\"\n } : null)\n };\n\n return (\n <div\n key={`${pane}-${shape.id}`}\n onClick={() => handleShapeClick(shape)}\n style={{\n ...style,\n contain: \"layout paint\",\n cursor: shape.hyperlink ? \"pointer\" : \"default\",\n left: style.left,\n pointerEvents: shape.hyperlink ? \"auto\" : \"none\",\n top: style.top,\n zIndex: isFrozenDrawing ? shape.zIndex + 20 : style.zIndex\n }}\n title={shape.description}\n >\n {vectorShape ? (\n <svg\n aria-hidden=\"true\"\n preserveAspectRatio=\"none\"\n style={{\n height: \"100%\",\n inset: 0,\n position: \"absolute\",\n width: \"100%\"\n }}\n viewBox={`0 0 ${vectorShape.viewBox.width} ${vectorShape.viewBox.height}`}\n >\n {headMarker || tailMarker ? <defs>{headMarker}{tailMarker}</defs> : null}\n <path\n d={vectorShape.path}\n fill={shape.fill?.none ? \"transparent\" : (shape.fill?.color ?? \"transparent\")}\n fillOpacity={shape.fill?.opacity ?? 1}\n markerEnd={tailMarker ? `url(#${tailMarkerId})` : undefined}\n markerStart={headMarker ? `url(#${headMarkerId})` : undefined}\n stroke={strokeColor}\n strokeOpacity={shape.stroke?.opacity ?? 1}\n strokeWidth={scaledStrokeWidth}\n vectorEffect=\"non-scaling-stroke\"\n />\n </svg>\n ) : null}\n <div\n style={{\n color: \"#000000\",\n display: \"flex\",\n flex: 1,\n flexDirection: \"column\",\n gap: 2 * zoomFactor,\n height: textHeight,\n justifyContent:\n shape.textBox?.verticalAlign === \"middle\"\n ? \"center\"\n : shape.textBox?.verticalAlign === \"bottom\"\n ? \"flex-end\"\n : \"flex-start\",\n paddingBottom: (inset?.bottom ?? 4) * zoomFactor,\n paddingLeft: (inset?.left ?? 6) * zoomFactor,\n paddingRight: (inset?.right ?? 6) * zoomFactor,\n paddingTop: (inset?.top ?? 4) * zoomFactor,\n pointerEvents: \"none\",\n position: \"relative\",\n transform:\n groupScaleX !== 1 || groupScaleY !== 1\n ? `scale(${groupScaleX}, ${groupScaleY})`\n : undefined,\n transformOrigin: \"top left\",\n width: textWidth,\n zIndex: 1\n }}\n >\n {shape.paragraphs.map((paragraph, index) => renderShapeParagraph(\n paragraph,\n index,\n shape.textBox?.horizontalAlign ?? \"left\",\n textScale\n ))}\n </div>\n </div>\n );\n }\n\n function renderFormControlDrawing(control: XlsxFormControl, rect: XlsxImageRect, pane: FrozenDrawingPane) {\n const drawingPane = resolveDrawingPane(rect);\n if (drawingPane !== pane) {\n return null;\n }\n\n const hasMeasuredViewport = drawingViewport.width > 0 && drawingViewport.height > 0;\n if (pane === \"scroll\" && hasMeasuredViewport && !rectIntersectsViewport(rect, drawingViewport)) {\n return null;\n }\n\n const isFrozenDrawing = pane !== \"scroll\";\n const controlLabel = resolveFormControlLabel(control);\n const fontSizePx = Math.max(9 * zoomFactor, ((control.fontSizePt ?? 9) * 96 / 72) * zoomFactor);\n const stroke = paletteIsDark(palette) ? \"#cbd5e1\" : \"#475569\";\n const textColor = control.textColor ?? \"#000000\";\n const canMutateControl = !readOnly && control.controlIndex !== undefined && [\n \"checkbox\",\n \"dropdown\",\n \"listbox\",\n \"radio\",\n \"scrollbar\",\n \"spinner\"\n ].includes(control.kind);\n const canActivateButton = !readOnly && control.kind === \"button\" && Boolean(onFormControlAction);\n const isInteractiveControl = canMutateControl || canActivateButton;\n const controlItems = formControlItemsById.get(control.id) ?? [];\n const commonStyle: React.CSSProperties = {\n alignItems: control.kind === \"group-box\" ? \"stretch\" : \"center\",\n color: textColor,\n contain: \"layout paint\",\n cursor: isInteractiveControl ? \"pointer\" : undefined,\n display: \"flex\",\n fontFamily: control.fontFamily ?? \"Calibri, sans-serif\",\n fontSize: fontSizePx,\n fontWeight: 400,\n gap: 4 * zoomFactor,\n height: rect.height,\n justifyContent:\n control.textAlign === \"center\"\n ? \"center\"\n : control.textAlign === \"right\"\n ? \"flex-end\"\n : \"flex-start\",\n left: rect.left,\n lineHeight: 1.2,\n overflow: \"hidden\",\n pointerEvents: isInteractiveControl ? \"auto\" : \"none\",\n position: \"absolute\",\n top: rect.top,\n width: rect.width,\n zIndex: isFrozenDrawing ? control.zIndex + 20 : control.zIndex\n };\n\n const stopControlPointerEvent = (event: React.SyntheticEvent) => {\n event.stopPropagation();\n };\n const commitControlKind = (\n kind: XlsxFormControlKindInput,\n nextControl: Partial<XlsxFormControl>\n ) => {\n if (readOnly || control.controlIndex === undefined) {\n return false;\n }\n const didUpdate = updateFormControl(control.controlIndex, { kind }, control.sheetIndex);\n if (didUpdate) {\n onFormControlChange?.({\n control: { ...control, ...nextControl },\n previousControl: control,\n type: \"change\"\n });\n }\n return didUpdate;\n };\n const setControlState = (state: XlsxFormControl[\"state\"] = \"unchecked\") => {\n const kind = buildFormControlKindInput(control);\n if (kind?.kind === \"checkbox\") {\n return commitControlKind(\n { ...kind, state },\n { checked: state === \"checked\", state }\n );\n }\n if (kind?.kind === \"optionButton\" && state !== \"mixed\") {\n return commitControlKind(\n { ...kind, state },\n { checked: state === \"checked\", state }\n );\n }\n return false;\n };\n const setControlSelected = (selected: number | number[] | undefined) => {\n const kind = buildFormControlKindInput(control);\n if (kind?.kind === \"dropdown\") {\n const nextSelected = typeof selected === \"number\" ? selected : undefined;\n return commitControlKind(\n { ...kind, selected: nextSelected },\n { selected: nextSelected }\n );\n }\n if (kind?.kind === \"listBox\") {\n const nextSelected = Array.isArray(selected)\n ? selected\n : typeof selected === \"number\" ? [selected] : [];\n return commitControlKind(\n { ...kind, selected: nextSelected },\n { selected: nextSelected }\n );\n }\n return false;\n };\n const setControlValue = (value: number) => {\n if (!Number.isFinite(value)) {\n return false;\n }\n const kind = buildFormControlKindInput(control);\n if (kind?.kind !== \"scrollbar\" && kind?.kind !== \"spinner\") {\n return false;\n }\n const nextValue = Math.max(kind.min, Math.min(kind.max, value));\n return commitControlKind({ ...kind, value: nextValue }, { value: nextValue });\n };\n const activateControl = () => {\n if (!readOnly && control.kind === \"button\") {\n onFormControlAction?.({ control, type: \"activate\" });\n }\n };\n\n if (renderFormControl) {\n return (\n <React.Fragment key={`${pane}-${control.id}`}>\n {renderFormControl({\n activate: activateControl,\n checked: isFormControlChecked(control),\n control,\n disabled: !isInteractiveControl,\n items: controlItems,\n label: controlLabel,\n mixed: isFormControlMixed(control),\n readOnly,\n rect,\n setSelected: setControlSelected,\n setState: setControlState,\n setValue: setControlValue,\n stopPropagation: stopControlPointerEvent,\n style: commonStyle,\n zoomFactor\n })}\n </React.Fragment>\n );\n }\n\n if (control.kind === \"group-box\") {\n const hasLabel = controlLabel.length > 0;\n return (\n <div\n key={`${pane}-${control.id}`}\n style={{\n ...commonStyle,\n padding: `${hasLabel ? Math.max(7, fontSizePx * 0.5) : 0}px 0 0`,\n position: \"absolute\"\n }}\n >\n <div\n style={{\n border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,\n borderRadius: 2 * zoomFactor,\n height: \"100%\",\n position: \"relative\",\n width: \"100%\"\n }}\n >\n {controlLabel ? (\n <span\n style={{\n backgroundColor: SHEET_SURFACE,\n left: 8 * zoomFactor,\n maxWidth: `calc(100% - ${16 * zoomFactor}px)`,\n padding: `0 ${4 * zoomFactor}px`,\n position: \"absolute\",\n top: -(fontSizePx * 0.6),\n whiteSpace: \"nowrap\"\n }}\n >\n {controlLabel}\n </span>\n ) : null}\n </div>\n </div>\n );\n }\n\n if (control.kind === \"radio\") {\n const checked = isFormControlChecked(control);\n return (\n <button\n aria-disabled={readOnly || control.controlIndex === undefined}\n aria-checked={checked}\n disabled={!readOnly && control.controlIndex === undefined}\n key={`${pane}-${control.id}`}\n onClick={(event) => {\n event.stopPropagation();\n if (!checked) {\n setControlState(\"checked\");\n }\n }}\n onPointerDown={stopControlPointerEvent}\n role=\"radio\"\n style={{\n ...commonStyle,\n background: \"transparent\",\n border: 0,\n boxSizing: \"border-box\",\n padding: `0 ${Math.max(1, zoomFactor)}px`\n }}\n tabIndex={readOnly ? -1 : undefined}\n type=\"button\"\n >\n {renderRadioControl(checked, palette, zoomFactor)}\n {controlLabel ? <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{controlLabel}</span> : null}\n </button>\n );\n }\n\n if (control.kind === \"checkbox\") {\n const checked = isFormControlChecked(control);\n const mixed = isFormControlMixed(control);\n return (\n <button\n aria-disabled={readOnly || control.controlIndex === undefined}\n aria-checked={mixed ? \"mixed\" : checked}\n disabled={!readOnly && control.controlIndex === undefined}\n key={`${pane}-${control.id}`}\n onClick={(event) => {\n event.stopPropagation();\n const nextState = checked && !mixed ? \"unchecked\" : \"checked\";\n setControlState(nextState);\n }}\n onPointerDown={stopControlPointerEvent}\n role=\"checkbox\"\n style={{\n ...commonStyle,\n background: \"transparent\",\n border: 0,\n boxSizing: \"border-box\",\n padding: `0 ${Math.max(1, zoomFactor)}px`\n }}\n tabIndex={readOnly ? -1 : undefined}\n type=\"button\"\n >\n {renderCheckboxControl(checked, palette, zoomFactor, mixed)}\n {controlLabel ? <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{controlLabel}</span> : null}\n </button>\n );\n }\n\n if (control.kind === \"button\") {\n return (\n <button\n aria-disabled={readOnly || !onFormControlAction}\n disabled={!readOnly && !onFormControlAction}\n key={`${pane}-${control.id}`}\n onClick={(event) => {\n event.stopPropagation();\n activateControl();\n }}\n onPointerDown={stopControlPointerEvent}\n style={{\n ...commonStyle,\n background: \"linear-gradient(180deg, #f8fafc 0%, #e2e8f0 100%)\",\n border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,\n borderRadius: 4 * zoomFactor,\n boxSizing: \"border-box\",\n justifyContent: \"center\",\n padding: `0 ${6 * zoomFactor}px`,\n textAlign: \"center\"\n }}\n tabIndex={readOnly ? -1 : undefined}\n type=\"button\"\n >\n {controlLabel}\n </button>\n );\n }\n\n if (control.kind === \"dropdown\") {\n const selected = typeof control.selected === \"number\" ? control.selected : undefined;\n return (\n <select\n aria-disabled={readOnly || control.controlIndex === undefined || controlItems.length === 0}\n aria-label={controlLabel || control.name || \"Dropdown form control\"}\n disabled={!readOnly && (control.controlIndex === undefined || controlItems.length === 0)}\n key={`${pane}-${control.id}`}\n onChange={(event) => {\n event.stopPropagation();\n const nextSelected = event.currentTarget.value === \"\" ? undefined : Number(event.currentTarget.value);\n setControlSelected(nextSelected);\n }}\n onClick={stopControlPointerEvent}\n onPointerDown={stopControlPointerEvent}\n style={{\n ...commonStyle,\n backgroundColor: SHEET_SURFACE,\n border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,\n borderRadius: 2 * zoomFactor,\n boxSizing: \"border-box\",\n padding: `0 ${4 * zoomFactor}px`\n }}\n tabIndex={readOnly ? -1 : undefined}\n value={selected === undefined ? \"\" : String(selected)}\n >\n {selected === undefined ? <option value=\"\">{controlLabel}</option> : null}\n {controlItems.map((item, index) => <option key={index} value={index}>{item}</option>)}\n </select>\n );\n }\n\n if (control.kind === \"listbox\") {\n const selected = Array.isArray(control.selected)\n ? control.selected\n : control.selected === undefined ? [] : [control.selected];\n const allowsMultipleSelection = (control.selection ?? \"single\") !== \"single\";\n return (\n <select\n aria-disabled={readOnly || control.controlIndex === undefined || controlItems.length === 0}\n aria-label={controlLabel || control.name || \"List box form control\"}\n disabled={!readOnly && (control.controlIndex === undefined || controlItems.length === 0)}\n key={`${pane}-${control.id}`}\n multiple={allowsMultipleSelection}\n onChange={(event) => {\n event.stopPropagation();\n const nextSelected = Array.from(event.currentTarget.selectedOptions, (option) => Number(option.value));\n setControlSelected(nextSelected);\n }}\n onClick={stopControlPointerEvent}\n onPointerDown={stopControlPointerEvent}\n size={Math.max(2, Math.min(controlItems.length || 2, Math.floor(rect.height / Math.max(1, fontSizePx * 1.45))))}\n style={{\n ...commonStyle,\n alignItems: undefined,\n backgroundColor: SHEET_SURFACE,\n border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,\n borderRadius: 2 * zoomFactor,\n boxSizing: \"border-box\",\n display: \"block\",\n padding: 0\n }}\n tabIndex={readOnly ? -1 : undefined}\n value={allowsMultipleSelection ? selected.map(String) : selected[0] === undefined ? \"\" : String(selected[0])}\n >\n {controlItems.map((item, index) => <option key={index} value={index}>{item}</option>)}\n </select>\n );\n }\n\n if (control.kind === \"scrollbar\") {\n const min = control.min ?? 0;\n const max = Math.max(min, control.max ?? 100);\n return (\n <input\n aria-disabled={readOnly || control.controlIndex === undefined}\n aria-label={controlLabel || control.name || \"Scrollbar form control\"}\n disabled={!readOnly && control.controlIndex === undefined}\n key={`${pane}-${control.id}`}\n max={max}\n min={min}\n onChange={(event) => {\n event.stopPropagation();\n setControlValue(event.currentTarget.valueAsNumber);\n }}\n onClick={stopControlPointerEvent}\n onPointerDown={stopControlPointerEvent}\n step={Math.max(1, control.increment ?? 1)}\n style={{\n ...commonStyle,\n accentColor: paletteIsDark(palette) ? \"#60a5fa\" : \"#2563eb\",\n boxSizing: \"border-box\",\n padding: 0,\n writingMode: control.horizontal === false ? \"vertical-lr\" : undefined\n }}\n tabIndex={readOnly ? -1 : undefined}\n type=\"range\"\n value={Math.max(min, Math.min(max, control.value ?? min))}\n />\n );\n }\n\n if (control.kind === \"spinner\") {\n const min = control.min ?? 0;\n const max = Math.max(min, control.max ?? 100);\n return (\n <input\n aria-disabled={readOnly || control.controlIndex === undefined}\n aria-label={controlLabel || control.name || \"Spinner form control\"}\n disabled={!readOnly && control.controlIndex === undefined}\n key={`${pane}-${control.id}`}\n max={max}\n min={min}\n onChange={(event) => {\n event.stopPropagation();\n setControlValue(event.currentTarget.valueAsNumber);\n }}\n onClick={stopControlPointerEvent}\n onPointerDown={stopControlPointerEvent}\n step={Math.max(1, control.increment ?? 1)}\n style={{\n ...commonStyle,\n backgroundColor: SHEET_SURFACE,\n border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,\n borderRadius: 2 * zoomFactor,\n boxSizing: \"border-box\",\n padding: `0 ${4 * zoomFactor}px`\n }}\n tabIndex={readOnly ? -1 : undefined}\n type=\"number\"\n value={Math.max(min, Math.min(max, control.value ?? min))}\n />\n );\n }\n\n return (\n <div\n key={`${pane}-${control.id}`}\n style={{\n ...commonStyle,\n border: control.kind === \"editbox\" || control.kind === \"unknown\"\n ? `${Math.max(1, zoomFactor)}px solid ${stroke}`\n : undefined,\n borderRadius: 2 * zoomFactor,\n boxSizing: \"border-box\",\n padding: `0 ${Math.max(1, zoomFactor)}px`\n }}\n >\n {controlLabel ? <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{controlLabel}</span> : null}\n </div>\n );\n }\n\n function renderImageDrawing(\n image: XlsxImage,\n rect: XlsxImageRect,\n pane: FrozenDrawingPane\n ) {\n const drawingPane = resolveDrawingPane(rect);\n if (drawingPane !== pane) {\n return null;\n }\n const hasMeasuredViewport = drawingViewport.width > 0 && drawingViewport.height > 0;\n if (pane === \"scroll\" && hasMeasuredViewport && !rectIntersectsViewport(rect, drawingViewport)) {\n return null;\n }\n\n const isFrozenDrawing = pane !== \"scroll\";\n const canEditImage = !readOnly && image.editable !== false;\n const drawingSelectionSurface = paletteIsDark(palette) ? palette.canvas : DRAWING_SELECTION_HANDLE_FILL;\n const style: React.CSSProperties = {\n contain: \"layout paint\",\n height: rect.height,\n left: rect.left,\n overflow: \"hidden\",\n pointerEvents: \"none\",\n position: \"absolute\",\n top: rect.top,\n width: rect.width,\n zIndex: isFrozenDrawing ? image.zIndex + 20 : image.zIndex\n };\n const defaultNode = (\n <img\n alt={image.description ?? image.name ?? \"\"}\n draggable={false}\n src={image.src}\n style={{\n display: \"block\",\n height: \"100%\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n width: \"100%\"\n }}\n />\n );\n const selectionNode = selectedImageId === image.id ? (\n <div\n style={{\n ...style,\n contain: \"layout\",\n overflow: \"visible\",\n pointerEvents: \"none\",\n zIndex: isFrozenDrawing ? image.zIndex + 22 : image.zIndex + 2\n }}\n >\n {renderImageSelection\n ? renderImageSelection({\n defaultNode: (\n <div\n style={{\n border: `${Math.max(1, zoomFactor)}px solid ${DRAWING_SELECTION_STROKE}`,\n boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${drawingSelectionSurface}`,\n boxSizing: \"border-box\",\n inset: 0,\n pointerEvents: \"none\",\n position: \"absolute\"\n }}\n >\n {canEditImage\n ? IMAGE_HANDLE_POSITIONS.map((position) => (\n <div\n key={position}\n onPointerDown={(event) => startImageResize(event, image, rect, position)}\n style={resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)}\n />\n ))\n : null}\n </div>\n ),\n getHandleProps: (position) => ({\n onPointerDown: (event) => {\n if (canEditImage) {\n startImageResize(event, image, rect, position);\n }\n },\n style: canEditImage\n ? resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)\n : { ...resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor), display: \"none\" }\n }),\n image,\n rect\n })\n : (\n <div\n style={{\n border: `${Math.max(1, zoomFactor)}px solid ${DRAWING_SELECTION_STROKE}`,\n boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${drawingSelectionSurface}`,\n boxSizing: \"border-box\",\n inset: 0,\n pointerEvents: \"none\",\n position: \"absolute\"\n }}\n >\n {canEditImage\n ? IMAGE_HANDLE_POSITIONS.map((position) => (\n <div\n key={position}\n onPointerDown={(event) => startImageResize(event, image, rect, position)}\n style={resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)}\n />\n ))\n : null}\n </div>\n )}\n </div>\n ) : null;\n\n return (\n <React.Fragment key={`${pane}-${image.id}`}>\n {renderImage\n ? <div style={style}>{renderImage({ defaultNode, image, rect, style })}</div>\n : <div style={style}>{defaultNode}</div>}\n <div\n onClick={() => handleImageClick(image)}\n onPointerDown={(event) => startImageMove(event, image, rect)}\n style={{\n ...style,\n background: \"transparent\",\n cursor: canEditImage && selectedImageId === image.id ? \"move\" : image.hyperlink ? \"pointer\" : \"cell\",\n pointerEvents: \"auto\",\n zIndex: isFrozenDrawing ? image.zIndex + 21 : image.zIndex + 1\n }}\n />\n {selectionNode}\n </React.Fragment>\n );\n }\n\n function renderChartDrawing(\n chart: XlsxChart,\n rect: XlsxImageRect,\n pane: FrozenDrawingPane\n ) {\n const drawingPane = resolveDrawingPane(rect);\n if (drawingPane !== pane) {\n return null;\n }\n const hasMeasuredViewport = drawingViewport.width > 0 && drawingViewport.height > 0;\n if (pane === \"scroll\" && hasMeasuredViewport && !rectIntersectsViewport(rect, drawingViewport)) {\n return null;\n }\n\n const isFrozenDrawing = pane !== \"scroll\";\n const canEditChart = !readOnly && chart.editable !== false;\n const drawingSelectionSurface = paletteIsDark(palette) ? palette.canvas : DRAWING_SELECTION_HANDLE_FILL;\n const style: React.CSSProperties = {\n contain: \"layout paint\",\n height: rect.height,\n left: rect.left,\n overflow: \"hidden\",\n pointerEvents: \"none\",\n position: \"absolute\",\n top: rect.top,\n width: rect.width,\n zIndex: isFrozenDrawing ? chart.zIndex + 20 : chart.zIndex\n };\n const selectionNode = selectedChartId === chart.id ? (\n <div\n style={{\n ...style,\n contain: \"layout\",\n overflow: \"visible\",\n pointerEvents: \"none\",\n zIndex: isFrozenDrawing ? chart.zIndex + 22 : chart.zIndex + 2\n }}\n >\n <div\n style={{\n border: `${Math.max(1, zoomFactor)}px solid ${DRAWING_SELECTION_STROKE}`,\n boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${drawingSelectionSurface}`,\n boxSizing: \"border-box\",\n inset: 0,\n pointerEvents: \"none\",\n position: \"absolute\"\n }}\n >\n {canEditChart\n ? IMAGE_HANDLE_POSITIONS.map((position) => (\n <div\n key={position}\n onPointerDown={(event) => startChartResize(event, chart, rect, position)}\n style={resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)}\n />\n ))\n : null}\n </div>\n </div>\n ) : null;\n\n return (\n <React.Fragment key={`${pane}-${chart.id}`}>\n <div\n onClick={() => handleChartClick(chart)}\n onPointerDown={(event) => startChartMove(event, chart, rect)}\n style={{\n ...style,\n cursor: canEditChart && selectedChartId === chart.id ? \"move\" : \"cell\",\n pointerEvents: \"auto\"\n }}\n >\n {isChartsLoading\n ? renderChartLoadingNode(renderChartLoading, chart, rect)\n : (\n <MemoChartSvg\n experimentalOfficeCharts={experimentalOfficeCharts}\n chart={chart}\n loadingFallback={(stage) => renderChartLoadingNode(renderChartLoading, chart, rect, stage)}\n onChartElementDoubleClick={handleChartElementDoubleClick}\n onChartElementPointerDown={handleChartElementPointerDown}\n palette={palette}\n rect={rect}\n selectedChartElement={selectedChartElement}\n />\n )}\n </div>\n {selectionNode}\n </React.Fragment>\n );\n }\n\n const scrollOverlayStyle: React.CSSProperties = {\n inset: 0,\n pointerEvents: \"none\",\n position: \"absolute\",\n zIndex: 20\n };\n const topOverlayStyle: React.CSSProperties = {\n height: 0,\n overflow: \"visible\",\n pointerEvents: \"none\",\n position: \"sticky\",\n top: 0,\n width: 0,\n zIndex: 25\n };\n const leftOverlayStyle: React.CSSProperties = {\n height: 0,\n left: 0,\n overflow: \"visible\",\n pointerEvents: \"none\",\n position: \"sticky\",\n width: 0,\n zIndex: 25\n };\n const cornerOverlayStyle: React.CSSProperties = {\n height: 0,\n left: 0,\n overflow: \"visible\",\n pointerEvents: \"none\",\n position: \"sticky\",\n top: 0,\n width: 0,\n zIndex: 26\n };\n const canvasOverlayPaneBaseStyle: React.CSSProperties = {\n overflow: \"hidden\",\n pointerEvents: \"none\",\n position: \"absolute\"\n };\n const canvasScrollOverlayPaneStyle: React.CSSProperties = {\n ...canvasOverlayPaneBaseStyle,\n display: scrollBodyViewportWidth > 0 && scrollBodyViewportHeight > 0 ? \"block\" : \"none\",\n height: scrollBodyViewportHeight,\n left: frozenPaneRight,\n top: frozenPaneBottom,\n width: scrollBodyViewportWidth,\n zIndex: 20\n };\n const canvasTopOverlayPaneStyle: React.CSSProperties = {\n ...canvasOverlayPaneBaseStyle,\n display: scrollBodyViewportWidth > 0 && topBodyCanvasHeight > 0 ? \"block\" : \"none\",\n height: topBodyCanvasHeight,\n left: frozenPaneRight,\n top: displayHeaderHeight,\n width: scrollBodyViewportWidth,\n zIndex: 35\n };\n const canvasLeftOverlayPaneStyle: React.CSSProperties = {\n ...canvasOverlayPaneBaseStyle,\n display: leftBodyCanvasWidth > 0 && scrollBodyViewportHeight > 0 ? \"block\" : \"none\",\n height: scrollBodyViewportHeight,\n left: displayRowHeaderWidth,\n top: frozenPaneBottom,\n width: leftBodyCanvasWidth,\n zIndex: 35\n };\n const canvasCornerOverlayPaneStyle: React.CSSProperties = {\n ...canvasOverlayPaneBaseStyle,\n display: cornerBodyCanvasWidth > 0 && cornerBodyCanvasHeight > 0 ? \"block\" : \"none\",\n height: cornerBodyCanvasHeight,\n left: displayRowHeaderWidth,\n top: displayHeaderHeight,\n width: cornerBodyCanvasWidth,\n zIndex: 36\n };\n const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;\n const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;\n const canReusePaneDrawingNodes =\n previousPaneDrawingNodes !== null\n && previousPaneDrawingNodes.experimentalOfficeCharts === experimentalOfficeCharts\n && previousPaneDrawingNodes.showImages === showImages\n && previousPaneDrawingNodes.updateFormControl === updateFormControl\n && previousPaneDrawingNodes.chartRects === chartRects\n && previousPaneDrawingNodes.formControlRects === domFormControlRects\n && previousPaneDrawingNodes.formControlItemsById === formControlItemsById\n && previousPaneDrawingNodes.shapeRects === domShapeRects\n && previousPaneDrawingNodes.imageRects === domImageRects\n && previousPaneDrawingNodes.selectedChartId === selectedChartId\n && previousPaneDrawingNodes.selectedImageId === selectedImageId\n && previousPaneDrawingNodes.readOnly === readOnly\n && previousPaneDrawingNodes.onFormControlAction === onFormControlAction\n && previousPaneDrawingNodes.onFormControlChange === onFormControlChange\n && previousPaneDrawingNodes.selectionStroke === selectionStroke\n && previousPaneDrawingNodes.renderChartLoading === renderChartLoading\n && previousPaneDrawingNodes.renderFormControl === renderFormControl\n && previousPaneDrawingNodes.renderImage === renderImage\n && previousPaneDrawingNodes.renderImageSelection === renderImageSelection\n && previousPaneDrawingNodes.isChartsLoading === isChartsLoading\n && previousPaneDrawingNodes.selectedChartElement === selectedChartElement\n && previousPaneDrawingNodes.palette === palette\n && previousPaneDrawingNodes.drawingViewportSignature === drawingViewportCacheSignature;\n const paneDrawingNodes = canReusePaneDrawingNodes\n ? previousPaneDrawingNodes.value\n : (!showImages\n ? {\n corner: null,\n left: null,\n scroll: null,\n top: null\n }\n : {\n corner: (\n <>\n {chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, \"corner\"))}\n {domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, \"corner\"))}\n {domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, \"corner\"))}\n {domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, \"corner\"))}\n </>\n ),\n left: (\n <>\n {chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, \"left\"))}\n {domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, \"left\"))}\n {domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, \"left\"))}\n {domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, \"left\"))}\n </>\n ),\n scroll: (\n <>\n {chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, \"scroll\"))}\n {domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, \"scroll\"))}\n {domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, \"scroll\"))}\n {domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, \"scroll\"))}\n </>\n ),\n top: (\n <>\n {chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, \"top\"))}\n {domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, \"top\"))}\n {domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, \"top\"))}\n {domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, \"top\"))}\n </>\n )\n }) satisfies Record<FrozenDrawingPane, React.ReactNode>;\n\n if (!canReusePaneDrawingNodes) {\n paneDrawingNodesCacheRef.current = {\n chartRects,\n drawingViewportSignature: drawingViewportCacheSignature,\n formControlItemsById,\n formControlRects: domFormControlRects,\n imageRects: domImageRects,\n isChartsLoading,\n onFormControlAction,\n onFormControlChange,\n palette,\n readOnly,\n renderChartLoading,\n experimentalOfficeCharts,\n renderFormControl,\n renderImage,\n renderImageSelection,\n selectedChartElement,\n selectedChartId,\n selectedImageId,\n selectionStroke,\n shapeRects: domShapeRects,\n showImages,\n updateFormControl,\n value: paneDrawingNodes\n };\n }\n\n function startColumnResize(pointerId: number, actualCol: number, widthPx: number, startX: number) {\n if (!canResizeHeaders) {\n return;\n }\n\n resizeStateRef.current = {\n actualIndex: actualCol,\n initialPx: widthPx,\n pointerId,\n startPosition: startX,\n type: \"column\"\n };\n columnPreviewRef.current = { actualIndex: actualCol, size: widthPx };\n rowPreviewRef.current = null;\n const position = resolveResizeGuidePositionFromClient(\"column\", startX);\n setResizeGuide(position === null ? null : { position, type: \"column\" });\n setInteractionMode(\"select\");\n setGlobalCursor(\"col-resize\");\n document.body.style.userSelect = \"none\";\n }\n\n function startRowResize(pointerId: number, actualRow: number, heightPx: number, startY: number) {\n if (!canResizeHeaders) {\n return;\n }\n\n resizeStateRef.current = {\n actualIndex: actualRow,\n initialPx: heightPx,\n pointerId,\n startPosition: startY,\n type: \"row\"\n };\n rowPreviewRef.current = { actualIndex: actualRow, size: heightPx };\n columnPreviewRef.current = null;\n const position = resolveResizeGuidePositionFromClient(\"row\", startY);\n setResizeGuide(position === null ? null : { position, type: \"row\" });\n setInteractionMode(\"select\");\n setGlobalCursor(\"row-resize\");\n document.body.style.userSelect = \"none\";\n }\n\n function startCellSelection(\n target: Element,\n pointerId: number,\n anchor: XlsxCellAddress,\n axis: \"cell\" | \"column\" | \"row\",\n originCell: XlsxCellAddress,\n pointerOrigin: {\n contentScaleX: number;\n contentScaleY: number;\n originContentX: number;\n originContentY: number;\n },\n originOverlayRect: { height: number; left: number; top: number; width: number } | null,\n committedOnPointerDown: boolean,\n initialRange: XlsxCellRange,\n startClientX: number,\n startClientY: number\n ) {\n clearPendingSelectionDrag();\n const previewRange = normalizeRange(initialRange);\n pendingSelectionDragRef.current = {\n anchor,\n axis,\n contentScaleX: pointerOrigin.contentScaleX,\n contentScaleY: pointerOrigin.contentScaleY,\n committedOnPointerDown,\n didDrag: false,\n originCell,\n originOverlayRect,\n originContentX: pointerOrigin.originContentX,\n originContentY: pointerOrigin.originContentY,\n pointerId,\n previewRange,\n startClientX,\n startClientY\n };\n selectionPreviewRangeRef.current = previewRange;\n displayedSelectionRef.current = previewRange;\n applyPreviewOverlay(previewRange);\n installPendingSelectionDragListeners(pointerId, target);\n }\n\n function resolveFillRange(sourceRange: XlsxCellRange, cell: XlsxCellAddress): XlsxCellRange {\n const normalizedSource = normalizeRange(sourceRange);\n if (isCellInRange(cell, normalizedSource)) {\n return normalizedSource;\n }\n\n const distanceAbove = Math.max(0, normalizedSource.start.row - cell.row);\n const distanceBelow = Math.max(0, cell.row - normalizedSource.end.row);\n const distanceLeft = Math.max(0, normalizedSource.start.col - cell.col);\n const distanceRight = Math.max(0, cell.col - normalizedSource.end.col);\n const verticalDistance = Math.max(distanceAbove, distanceBelow);\n const horizontalDistance = Math.max(distanceLeft, distanceRight);\n\n if (verticalDistance >= horizontalDistance) {\n return normalizeRange({\n start: {\n row: distanceAbove > 0 ? cell.row : normalizedSource.start.row,\n col: normalizedSource.start.col\n },\n end: {\n row: distanceBelow > 0 ? cell.row : normalizedSource.end.row,\n col: normalizedSource.end.col\n }\n });\n }\n\n return normalizeRange({\n start: {\n row: normalizedSource.start.row,\n col: distanceLeft > 0 ? cell.col : normalizedSource.start.col\n },\n end: {\n row: normalizedSource.end.row,\n col: distanceRight > 0 ? cell.col : normalizedSource.end.col\n }\n });\n }\n\n function startFillDrag(pointerId: number, sourceRange: XlsxCellRange) {\n if (readOnly) {\n return;\n }\n\n cachedScrollerRectRef.current = scrollRef.current?.getBoundingClientRect() ?? null;\n const normalizedSource = normalizeRange(sourceRange);\n fillDragRef.current = {\n pointerId,\n previewRange: normalizedSource,\n sourceRange: normalizedSource\n };\n setFillPreviewRange(normalizedSource);\n setInteractionMode(\"fill\");\n document.body.style.cursor = \"crosshair\";\n document.body.style.userSelect = \"none\";\n installFillDragListeners(pointerId, normalizedSource);\n }\n\n function updateFillPreview(cell: XlsxCellAddress) {\n const fillState = fillDragRef.current;\n if (!fillState) {\n return;\n }\n\n const nextRange = resolveFillRange(fillState.sourceRange, cell);\n fillState.previewRange = nextRange;\n applyPreviewOverlay(nextRange);\n }\n\n function installImageInteractionListeners(pointerId: number) {\n imageInteractionCleanupRef.current?.();\n\n const resolveInteractionRect = (\n interaction: NonNullable<typeof imageInteractionRef.current>,\n clientX: number,\n clientY: number\n ) => {\n const deltaX = clientX - interaction.startClientX;\n const deltaY = clientY - interaction.startClientY;\n return clampImageRect(\n interaction.type === \"move\"\n ? {\n ...interaction.baseRect,\n left: interaction.baseRect.left + deltaX,\n top: interaction.baseRect.top + deltaY\n }\n : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, displayImageMinSize),\n {\n contentOffsetLeft: displayRowHeaderWidth,\n contentOffsetTop: displayHeaderHeight,\n minSizePx: displayImageMinSize\n }\n );\n };\n\n const handlePointerMove = (event: PointerEvent) => {\n if (event.pointerId !== pointerId) {\n return;\n }\n\n const interaction = imageInteractionRef.current;\n if (!interaction) {\n return;\n }\n\n const deltaX = event.clientX - interaction.startClientX;\n const deltaY = event.clientY - interaction.startClientY;\n if (!interaction.didMove && (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3)) {\n interaction.didMove = true;\n }\n const nextRect = resolveInteractionRect(interaction, event.clientX, event.clientY);\n scheduleImagePreviewRect({ id: interaction.imageId, rect: nextRect });\n };\n\n const cleanup = () => {\n window.removeEventListener(\"pointermove\", handlePointerMove);\n window.removeEventListener(\"pointerup\", handlePointerUp);\n window.removeEventListener(\"pointercancel\", handlePointerUp);\n };\n\n const handlePointerUp = (event: PointerEvent) => {\n if (event.pointerId !== pointerId) {\n return;\n }\n\n const interaction = imageInteractionRef.current;\n if (imagePreviewFrameRef.current !== null) {\n window.cancelAnimationFrame(imagePreviewFrameRef.current);\n imagePreviewFrameRef.current = null;\n }\n const pendingPreview = pendingImagePreviewRef.current;\n pendingImagePreviewRef.current = null;\n if (pendingPreview) {\n imagePreviewRectRef.current = pendingPreview;\n setImagePreviewRect(pendingPreview);\n }\n const finalRect = interaction\n ? resolveInteractionRect(interaction, event.clientX, event.clientY)\n : null;\n imageInteractionRef.current = null;\n imageInteractionCleanupRef.current = null;\n setInteractionMode(\"idle\");\n document.body.style.cursor = \"\";\n document.body.style.userSelect = \"\";\n cleanup();\n if (interaction) {\n if (interaction.didMove) {\n skipNextImageClickRef.current = interaction.imageId;\n }\n if (interaction.didMove && finalRect) {\n setImageRect(interaction.imageId, toLogicalRect(finalRect), {\n columnWidths: actualColWidths,\n rowHeights: actualRowHeights\n });\n }\n }\n imagePreviewRectRef.current = null;\n setImagePreviewRect(null);\n };\n\n window.addEventListener(\"pointermove\", handlePointerMove);\n window.addEventListener(\"pointerup\", handlePointerUp);\n window.addEventListener(\"pointercancel\", handlePointerUp);\n imageInteractionCleanupRef.current = cleanup;\n }\n\n function installChartInteractionListeners(pointerId: number) {\n chartInteractionCleanupRef.current?.();\n\n const resolveInteractionRect = (\n interaction: NonNullable<typeof chartInteractionRef.current>,\n clientX: number,\n clientY: number\n ) => {\n const deltaX = clientX - interaction.startClientX;\n const deltaY = clientY - interaction.startClientY;\n return clampImageRect(\n interaction.type === \"move\"\n ? {\n ...interaction.baseRect,\n left: interaction.baseRect.left + deltaX,\n top: interaction.baseRect.top + deltaY\n }\n : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, 48 * zoomFactor),\n {\n contentOffsetLeft: displayRowHeaderWidth,\n contentOffsetTop: displayHeaderHeight,\n minSizePx: 48 * zoomFactor\n }\n );\n };\n\n const handlePointerMove = (event: PointerEvent) => {\n if (event.pointerId !== pointerId) {\n return;\n }\n\n const interaction = chartInteractionRef.current;\n if (!interaction) {\n return;\n }\n\n const deltaX = event.clientX - interaction.startClientX;\n const deltaY = event.clientY - interaction.startClientY;\n if (!interaction.didMove && (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3)) {\n interaction.didMove = true;\n }\n const nextRect = resolveInteractionRect(interaction, event.clientX, event.clientY);\n scheduleChartPreviewRect({ id: interaction.chartId, rect: nextRect });\n };\n\n const cleanup = () => {\n window.removeEventListener(\"pointermove\", handlePointerMove);\n window.removeEventListener(\"pointerup\", handlePointerUp);\n window.removeEventListener(\"pointercancel\", handlePointerUp);\n };\n\n const handlePointerUp = (event: PointerEvent) => {\n if (event.pointerId !== pointerId) {\n return;\n }\n\n const interaction = chartInteractionRef.current;\n if (chartPreviewFrameRef.current !== null) {\n window.cancelAnimationFrame(chartPreviewFrameRef.current);\n chartPreviewFrameRef.current = null;\n }\n const pendingPreview = pendingChartPreviewRef.current;\n pendingChartPreviewRef.current = null;\n if (pendingPreview) {\n chartPreviewRectRef.current = pendingPreview;\n setChartPreviewRect(pendingPreview);\n }\n const finalRect = interaction\n ? resolveInteractionRect(interaction, event.clientX, event.clientY)\n : null;\n chartInteractionRef.current = null;\n chartInteractionCleanupRef.current = null;\n setInteractionMode(\"idle\");\n document.body.style.cursor = \"\";\n document.body.style.userSelect = \"\";\n cleanup();\n if (interaction) {\n if (interaction.didMove) {\n skipNextChartClickRef.current = interaction.chartId;\n }\n if (interaction.didMove && finalRect) {\n setChartRect(interaction.chartId, toLogicalRect(finalRect), {\n columnWidths: actualColWidths,\n rowHeights: actualRowHeights\n });\n }\n }\n chartPreviewRectRef.current = null;\n setChartPreviewRect(null);\n };\n\n window.addEventListener(\"pointermove\", handlePointerMove);\n window.addEventListener(\"pointerup\", handlePointerUp);\n window.addEventListener(\"pointercancel\", handlePointerUp);\n chartInteractionCleanupRef.current = cleanup;\n }\n\n function resolveCurrentCell() {\n if (activeCell && rowIndexByActual.has(activeCell.row) && colIndexByActual.has(activeCell.col)) {\n return activeCell;\n }\n if (firstVisibleRow === undefined || firstVisibleCol === undefined) {\n return null;\n }\n return { row: firstVisibleRow, col: firstVisibleCol };\n }\n\n function ensureCellVisible(rowIndex: number, colIndex: number) {\n const scroller = scrollRef.current;\n if (!scroller) {\n return;\n }\n\n let nextScrollTop = scroller.scrollTop;\n let nextScrollLeft = scroller.scrollLeft;\n const rowStart = displayHeaderHeight + (rowPrefixSums[rowIndex] ?? 0);\n const rowEnd = displayHeaderHeight + (rowPrefixSums[rowIndex + 1] ?? rowStart);\n const colStart = displayRowHeaderWidth + (colPrefixSums[colIndex] ?? 0);\n const colEnd = displayRowHeaderWidth + (colPrefixSums[colIndex + 1] ?? colStart);\n\n if (rowEnd > frozenPaneBottom) {\n const visibleTop = scroller.scrollTop + frozenPaneBottom;\n const visibleBottom = scroller.scrollTop + scroller.clientHeight;\n if (rowStart < visibleTop) {\n nextScrollTop = rowStart - frozenPaneBottom;\n } else if (rowEnd > visibleBottom) {\n nextScrollTop = rowEnd - scroller.clientHeight;\n }\n }\n\n if (colEnd > frozenPaneRight) {\n const visibleLeft = scroller.scrollLeft + frozenPaneRight;\n const visibleRight = scroller.scrollLeft + scroller.clientWidth;\n if (colStart < visibleLeft) {\n nextScrollLeft = colStart - frozenPaneRight;\n } else if (colEnd > visibleRight) {\n nextScrollLeft = colEnd - scroller.clientWidth;\n }\n }\n\n nextScrollTop = Math.max(0, Math.min(nextScrollTop, scroller.scrollHeight - scroller.clientHeight));\n nextScrollLeft = Math.max(0, Math.min(nextScrollLeft, scroller.scrollWidth - scroller.clientWidth));\n\n if (nextScrollTop !== scroller.scrollTop) {\n scroller.scrollTop = nextScrollTop;\n }\n if (nextScrollLeft !== scroller.scrollLeft) {\n scroller.scrollLeft = nextScrollLeft;\n }\n syncDrawingViewport(scroller, { immediate: true });\n }\n\n function moveSelection(nextRowIndex: number, nextColIndex: number, extend: boolean) {\n const clampedRowIndex = Math.max(0, Math.min(nextRowIndex, visibleRows.length - 1));\n const clampedColIndex = Math.max(0, Math.min(nextColIndex, visibleCols.length - 1));\n const nextRow = visibleRows[clampedRowIndex];\n const nextCol = visibleCols[clampedColIndex];\n if (nextRow === undefined || nextCol === undefined) {\n return;\n }\n\n const nextRange = { start: { row: nextRow, col: nextCol }, end: { row: nextRow, col: nextCol } };\n axisSelectionRef.current = null;\n selectionPreviewRangeRef.current = null;\n displayedSelectionRef.current = nextRange;\n applyPreviewOverlay(nextRange);\n selectCell({ row: nextRow, col: nextCol }, extend ? { extend: true } : undefined);\n ensureCellVisible(clampedRowIndex, clampedColIndex);\n }\n\n function resolvePageRowIndex(currentRowIndex: number, direction: 1 | -1) {\n const scroller = scrollRef.current;\n const viewportHeight = Math.max(\n displayDefaultRowHeight,\n (scroller?.clientHeight ?? displayDefaultRowHeight * 20) - frozenPaneBottom\n );\n const currentOffset = rowPrefixSums[currentRowIndex] ?? 0;\n const targetOffset = direction > 0\n ? currentOffset + viewportHeight\n : currentOffset - viewportHeight;\n return Math.max(0, Math.min(findIndexForOffsetPrefix(rowPrefixSums, targetOffset), visibleRows.length - 1));\n }\n\n function resolvePageColIndex(currentColIndex: number, direction: 1 | -1) {\n const scroller = scrollRef.current;\n const viewportWidth = Math.max(\n displayDefaultColWidth,\n (scroller?.clientWidth ?? displayDefaultColWidth * 8) - frozenPaneRight\n );\n const currentOffset = colPrefixSums[currentColIndex] ?? 0;\n const targetOffset = direction > 0\n ? currentOffset + viewportWidth\n : currentOffset - viewportWidth;\n return Math.max(0, Math.min(findIndexForOffsetPrefix(colPrefixSums, targetOffset), visibleCols.length - 1));\n }\n\n function resolveColumnSelectionNavigation(eventKey: string) {\n const axisSelection = axisSelectionRef.current;\n if (axisSelection?.axis !== \"column\" || firstVisibleRow === undefined) {\n return null;\n }\n\n const startCol = Math.min(axisSelection.startCol, axisSelection.endCol);\n const endCol = Math.max(axisSelection.startCol, axisSelection.endCol);\n const targetCol = eventKey === \"ArrowLeft\"\n ? startCol - 1\n : eventKey === \"ArrowRight\"\n ? endCol + 1\n : null;\n if (targetCol === null) {\n return null;\n }\n\n const rowIndex = rowIndexByActual.get(firstVisibleRow);\n const colIndex = colIndexByActual.get(targetCol);\n if (rowIndex === undefined || colIndex === undefined) {\n return null;\n }\n\n return { colIndex, rowIndex };\n }\n\n function resolveRowSelectionNavigation(eventKey: string) {\n const axisSelection = axisSelectionRef.current;\n if (axisSelection?.axis !== \"row\" || firstVisibleCol === undefined) {\n return null;\n }\n\n const startRow = Math.min(axisSelection.startRow, axisSelection.endRow);\n const endRow = Math.max(axisSelection.startRow, axisSelection.endRow);\n const targetRow = eventKey === \"ArrowUp\"\n ? startRow - 1\n : eventKey === \"ArrowDown\"\n ? endRow + 1\n : null;\n if (targetRow === null) {\n return null;\n }\n\n const rowIndex = rowIndexByActual.get(targetRow);\n const colIndex = colIndexByActual.get(firstVisibleCol);\n if (rowIndex === undefined || colIndex === undefined) {\n return null;\n }\n\n return { colIndex, rowIndex };\n }\n\n function handleGridKeyDown(event: React.KeyboardEvent<HTMLDivElement> | KeyboardEvent) {\n if (editingCell || contextMenu) {\n return;\n }\n\n if (enableContextMenu && (event.key === \"ContextMenu\" || (event.shiftKey && event.key === \"F10\"))) {\n const cell = resolveCurrentCell();\n const rect = scrollRef.current?.getBoundingClientRect();\n if (cell && rect) {\n event.preventDefault();\n const overlay = resolveOverlayRect({ start: cell, end: cell });\n openContextMenu(cell, \"cell\", {\n x: rect.left + (overlay?.left ?? displayRowHeaderWidth) - (scrollRef.current?.scrollLeft ?? 0),\n y: rect.top + (overlay ? overlay.top + overlay.height : displayHeaderHeight) - (scrollRef.current?.scrollTop ?? 0)\n });\n }\n return;\n }\n\n if (!readOnly && (event.metaKey || event.ctrlKey) && !event.altKey) {\n const normalizedKey = event.key.toLowerCase();\n if (normalizedKey === \"z\" && event.shiftKey) {\n event.preventDefault();\n redo();\n return;\n }\n\n if (normalizedKey === \"z\") {\n event.preventDefault();\n undo();\n return;\n }\n\n if (normalizedKey === \"y\") {\n event.preventDefault();\n redo();\n return;\n }\n }\n\n const currentCell = resolveCurrentCell();\n if (!currentCell) {\n return;\n }\n\n const currentRowIndex = rowIndexByActual.get(currentCell.row) ?? 0;\n const currentColIndex = colIndexByActual.get(currentCell.col) ?? 0;\n const isCommandNavigation = event.ctrlKey || event.metaKey;\n\n if (!readOnly && isPrintableKey(event)) {\n event.preventDefault();\n startEditing(currentCell, event.key);\n return;\n }\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n if (!event.shiftKey && !isCommandNavigation) {\n const target = resolveRowSelectionNavigation(event.key);\n if (target) {\n moveSelection(target.rowIndex, target.colIndex, false);\n break;\n }\n }\n moveSelection(\n isCommandNavigation\n ? (rowIndexByActual.get(resolveLastUsedVisibleIndex(visibleRows, activeSheet?.maxUsedRow ?? -1)) ?? visibleRows.length - 1)\n : currentRowIndex + 1,\n currentColIndex,\n event.shiftKey\n );\n break;\n case \"ArrowUp\":\n event.preventDefault();\n if (!event.shiftKey && !isCommandNavigation) {\n const target = resolveRowSelectionNavigation(event.key);\n if (target) {\n moveSelection(target.rowIndex, target.colIndex, false);\n break;\n }\n }\n moveSelection(\n isCommandNavigation\n ? (rowIndexByActual.get(resolveFirstUsedVisibleIndex(visibleRows, activeSheet?.minUsedRow ?? -1)) ?? 0)\n : currentRowIndex - 1,\n currentColIndex,\n event.shiftKey\n );\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n if (!event.shiftKey && !isCommandNavigation) {\n const target = resolveColumnSelectionNavigation(event.key);\n if (target) {\n moveSelection(target.rowIndex, target.colIndex, false);\n break;\n }\n }\n moveSelection(\n currentRowIndex,\n isCommandNavigation\n ? (colIndexByActual.get(resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1)) ?? 0)\n : currentColIndex - 1,\n event.shiftKey\n );\n break;\n case \"ArrowRight\":\n event.preventDefault();\n if (!event.shiftKey && !isCommandNavigation) {\n const target = resolveColumnSelectionNavigation(event.key);\n if (target) {\n moveSelection(target.rowIndex, target.colIndex, false);\n break;\n }\n }\n moveSelection(\n currentRowIndex,\n isCommandNavigation\n ? (colIndexByActual.get(resolveLastUsedVisibleIndex(visibleCols, activeSheet?.maxUsedCol ?? -1)) ?? visibleCols.length - 1)\n : currentColIndex + 1,\n event.shiftKey\n );\n break;\n case \"Home\":\n event.preventDefault();\n moveSelection(\n isCommandNavigation\n ? (rowIndexByActual.get(resolveFirstUsedVisibleIndex(visibleRows, activeSheet?.minUsedRow ?? -1)) ?? 0)\n : currentRowIndex,\n colIndexByActual.get(resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1)) ?? 0,\n event.shiftKey\n );\n break;\n case \"End\":\n event.preventDefault();\n moveSelection(\n isCommandNavigation\n ? (rowIndexByActual.get(resolveLastUsedVisibleIndex(visibleRows, activeSheet?.maxUsedRow ?? -1)) ?? visibleRows.length - 1)\n : currentRowIndex,\n colIndexByActual.get(resolveLastUsedVisibleIndex(visibleCols, activeSheet?.maxUsedCol ?? -1)) ?? visibleCols.length - 1,\n event.shiftKey\n );\n break;\n case \"PageDown\":\n event.preventDefault();\n if (event.altKey) {\n moveSelection(currentRowIndex, resolvePageColIndex(currentColIndex, 1), event.shiftKey);\n break;\n }\n moveSelection(resolvePageRowIndex(currentRowIndex, 1), currentColIndex, event.shiftKey);\n break;\n case \"PageUp\":\n event.preventDefault();\n if (event.altKey) {\n moveSelection(currentRowIndex, resolvePageColIndex(currentColIndex, -1), event.shiftKey);\n break;\n }\n moveSelection(resolvePageRowIndex(currentRowIndex, -1), currentColIndex, event.shiftKey);\n break;\n case \"Tab\":\n event.preventDefault();\n if (event.shiftKey) {\n moveSelection(\n currentColIndex > 0 ? currentRowIndex : currentRowIndex - 1,\n currentColIndex > 0 ? currentColIndex - 1 : visibleCols.length - 1,\n false\n );\n } else {\n moveSelection(\n currentColIndex < visibleCols.length - 1 ? currentRowIndex : currentRowIndex + 1,\n currentColIndex < visibleCols.length - 1 ? currentColIndex + 1 : 0,\n false\n );\n }\n break;\n case \"Enter\":\n event.preventDefault();\n if (event.metaKey || event.ctrlKey || event.altKey) {\n break;\n }\n if (event.shiftKey) {\n moveSelection(currentRowIndex - 1, currentColIndex, false);\n break;\n }\n moveSelection(currentRowIndex + 1, currentColIndex, false);\n break;\n case \"Backspace\":\n case \"Delete\":\n if (!readOnly) {\n event.preventDefault();\n clearSelectedCells();\n }\n break;\n case \"F2\":\n if (!readOnly) {\n event.preventDefault();\n startEditing(currentCell);\n }\n break;\n default:\n break;\n }\n }\n\n gridKeyboardHandlerRef.current = handleGridKeyDown;\n\n const scrollerViewportProps: XlsxScrollerRenderProps[\"viewportProps\"] = {\n ref: scrollRef,\n \"aria-colcount\": Math.max(activeSheet?.colCount ?? 0, displayColLimit),\n \"aria-keyshortcuts\": \"ArrowUp ArrowDown ArrowLeft ArrowRight Home End PageUp PageDown Control+Home Control+End\",\n \"aria-label\": activeSheet ? `${activeSheet.name} worksheet grid` : \"Workbook grid\",\n \"aria-readonly\": readOnly,\n \"aria-rowcount\": Math.max(activeSheet?.rowCount ?? 0, displayRowLimit),\n role: \"grid\",\n onScroll: handleScrollerScroll,\n onContextMenu: (event) => {\n if (!enableContextMenu || isInteractiveFocusTarget(event.target, event.currentTarget)) return;\n const rect = event.currentTarget.getBoundingClientRect();\n const isRowHeader = event.clientX - rect.left < displayRowHeaderWidth;\n const isColumnHeader = event.clientY - rect.top < displayHeaderHeight;\n const kind = isRowHeader && isColumnHeader ? \"sheet\" : isRowHeader ? \"row\" : isColumnHeader ? \"column\" : \"cell\";\n const cell = resolvePointerCellFromClient(event.clientX, event.clientY) ??\n (kind === \"sheet\" ? { row: firstVisibleRow ?? 0, col: firstVisibleCol ?? 0 } : null);\n if (!cell) return;\n event.preventDefault();\n openContextMenu(cell, kind, { x: event.clientX, y: event.clientY });\n },\n onCopy: (event) => {\n if (editingCell) {\n return;\n }\n\n const clipboard = event.clipboardData;\n const clipboardData = getClipboardData();\n if (!clipboardData) {\n return;\n }\n\n event.preventDefault();\n if (clipboard) {\n clipboard.setData(\"text/plain\", clipboardData.text);\n clipboard.setData(\"text/html\", clipboardData.html);\n clipboard.setData(INTERNAL_CLIPBOARD_MIME, clipboardData.structured);\n return;\n }\n\n void copySelectionToClipboard();\n },\n onPointerDownCapture: (event) => {\n if (event.button !== 0) {\n return;\n }\n if (isInteractiveFocusTarget(event.target, event.currentTarget)) {\n return;\n }\n\n gridKeyboardActiveRef.current = true;\n focusGridElement(event.currentTarget);\n },\n onClickCapture: (event) => {\n if (isInteractiveFocusTarget(event.target, event.currentTarget)) {\n return;\n }\n\n gridKeyboardActiveRef.current = true;\n focusGridElement(event.currentTarget);\n },\n onFocus: () => {\n gridKeyboardActiveRef.current = true;\n },\n onKeyDown: handleGridKeyDown,\n onPaste: (event) => {\n if (editingCell || readOnly) {\n return;\n }\n\n const clipboard = event.clipboardData;\n if (!clipboard) {\n event.preventDefault();\n void pasteFromClipboard();\n return;\n }\n\n const structuredPayload = clipboard.getData(INTERNAL_CLIPBOARD_MIME);\n const textPayload = clipboard.getData(\"text/plain\");\n if (!structuredPayload && !textPayload) {\n return;\n }\n\n event.preventDefault();\n if (structuredPayload) {\n pasteStructuredClipboardData(structuredPayload);\n return;\n }\n\n pasteText(textPayload);\n },\n tabIndex: 0,\n style: {\n [\"--xlsx-menu-active\" as string]: selectionFill,\n [\"--xlsx-menu-border\" as string]: palette.strongBorder,\n [\"--xlsx-menu-surface\" as string]: palette.surface,\n [\"--xlsx-selection-header\" as string]: selectionHeaderSurface,\n backgroundColor: palette.canvas,\n color: palette.text,\n cursor: resizeGuide?.type === \"column\" ? \"col-resize\" : resizeGuide?.type === \"row\" ? \"row-resize\" : undefined,\n flex: 1,\n height: \"100%\",\n minHeight: 0,\n minWidth: 0,\n outline: \"none\",\n overflow: \"auto\",\n width: \"100%\"\n }\n };\n\n const rangeHighlightNodes = rangeHighlightRects.flatMap(({ index, rect }) => rect ? [\n <div\n key={index}\n aria-hidden=\"true\"\n className={classNames(\"react-xlsx-range-highlight\", highlightClassName)}\n data-dark={paletteIsDark(palette) ? \"true\" : undefined}\n data-xlsx-range-highlight={index}\n style={{\n ...rect,\n [\"--xlsx-highlight-default-border-width\" as string]: `${selectionBorderWidth}px`,\n boxSizing: \"border-box\",\n pointerEvents: \"none\",\n position: \"absolute\",\n zIndex: maxDrawingOverlayZIndex + 1\n }}\n />\n ] : []);\n function renderSelectionOverlay(pane?: FrozenDrawingPane) {\n return (\n <>\n <div\n ref={pane ? (element) => {\n if (element) {\n selectionPaneOverlayRefs.current.set(pane, element);\n } else {\n selectionPaneOverlayRefs.current.delete(pane);\n }\n } : selectionOverlayRef}\n data-xlsx-selection-overlay=\"\"\n style={{\n backgroundColor: selectionFill,\n boxSizing: \"border-box\",\n boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,\n contain: \"layout paint\",\n height: resolvedSelectionOverlay?.height ?? 0,\n left: resolvedSelectionOverlay?.left ?? 0,\n opacity: resolvedSelectionOverlay ? 1 : 0,\n pointerEvents: \"none\",\n position: \"absolute\",\n top: resolvedSelectionOverlay?.top ?? 0,\n transition: canvasSelectionTransition,\n visibility: resolvedSelectionOverlay ? \"visible\" : \"hidden\",\n willChange: shouldAnimateCanvasSelectionGeometry ? \"left, top, width, height\" : undefined,\n width: resolvedSelectionOverlay?.width ?? 0,\n zIndex: 24\n }}\n />\n <div\n ref={pane ? (element) => {\n if (element) {\n selectionPaneFillHandleRefs.current.set(pane, element);\n } else {\n selectionPaneFillHandleRefs.current.delete(pane);\n }\n } : fillHandleRef}\n onPointerDown={(event) => {\n if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n startFillDrag(event.pointerId, normalizedSelection);\n }}\n style={{\n backgroundColor: selectionStroke,\n border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,\n contain: \"layout paint\",\n cursor: \"crosshair\",\n display: !readOnly && resolvedSelectionOverlay ? \"block\" : \"none\",\n height: 8 * zoomFactor,\n left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - (4 * zoomFactor) : 0,\n pointerEvents: \"auto\",\n position: \"absolute\",\n top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - (4 * zoomFactor) : 0,\n transition: shouldAnimateCanvasSelectionGeometry\n ? \"left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)\"\n : \"none\",\n willChange: shouldAnimateCanvasSelectionGeometry ? \"left, top\" : undefined,\n width: 8 * zoomFactor,\n zIndex: 25\n }}\n />\n </>\n );\n }\n\n const overlayPanes = [\n { pane: \"scroll\", style: canvasScrollOverlayPaneStyle, ref: canvasScrollOverlayContentRef, left: -drawingViewport.left - frozenPaneRight, top: -drawingViewport.top - frozenPaneBottom },\n { pane: \"top\", style: canvasTopOverlayPaneStyle, ref: canvasTopOverlayContentRef, left: -drawingViewport.left - frozenPaneRight, top: -displayHeaderHeight },\n { pane: \"left\", style: canvasLeftOverlayPaneStyle, ref: canvasLeftOverlayContentRef, left: -displayRowHeaderWidth, top: -drawingViewport.top - frozenPaneBottom },\n { pane: \"corner\", style: canvasCornerOverlayPaneStyle, ref: canvasCornerOverlayContentRef, left: -displayRowHeaderWidth, top: -displayHeaderHeight }\n ] as const;\n const selectionOverlayPanes = experimentalCanvas ? (\n <div style={{ ...canvasBodyViewportLayerStyle, zIndex: 40 }}>\n {overlayPanes.map(({ pane, style, left, top }) => (\n <div key={pane} style={style}>\n <div\n ref={(element) => {\n if (element) {\n selectionPaneContentRefs.current.set(pane, element);\n } else {\n selectionPaneContentRefs.current.delete(pane);\n }\n }}\n style={{\n height: sheetContentHeight,\n left: 0,\n position: \"absolute\",\n top: 0,\n transform: `translate(${left}px, ${top}px)`,\n transformOrigin: \"0 0\",\n width: totalWidth\n }}\n >\n {renderSelectionOverlay(pane)}\n </div>\n </div>\n ))}\n </div>\n ) : null;\n const drawingOverlayPanes = (experimentalCanvas && hasCanvasDomDrawingOverlays) || rangeHighlightNodes.length > 0 ? (\n <div style={{ ...canvasBodyViewportLayerStyle, zIndex: experimentalCanvas ? 23 : 34 }}>\n {overlayPanes.map(({ pane, style, ref, left, top }) => (\n <div key={pane} style={style}>\n <div\n ref={ref}\n style={{\n height: sheetContentHeight,\n left: 0,\n pointerEvents: \"none\",\n position: \"absolute\",\n top: 0,\n transform: `translate(${left}px, ${top}px)`,\n transformOrigin: \"0 0\",\n width: totalWidth\n }}\n >\n {experimentalCanvas ? paneDrawingNodes[pane] : null}\n {rangeHighlightNodes}\n </div>\n </div>\n ))}\n </div>\n ) : null;\n\n const scrollerContent = (\n <div\n style={{\n backgroundColor: resolveSheetSurface(activeSheet, palette),\n minHeight: \"100%\",\n minWidth: \"100%\",\n position: \"relative\",\n width: totalWidth,\n height: sheetContentHeight\n }}\n >\n <div\n ref={wrapperRef}\n style={{\n height: sheetContentHeight,\n left: 0,\n position: \"absolute\",\n top: 0,\n transform: !experimentalCanvas && isLiveZooming\n ? `translate3d(${liveZoomTranslateX}px, ${liveZoomTranslateY}px, 0) scale(${liveZoomScale})`\n : undefined,\n transformOrigin: \"0 0\",\n transition: \"none\",\n willChange: !experimentalCanvas && isLiveZooming ? \"transform\" : undefined,\n width: totalWidth\n }}\n >\n {showImages && !experimentalCanvas ? (\n <>\n <div style={topOverlayStyle}>{paneDrawingNodes.top}</div>\n <div style={leftOverlayStyle}>{paneDrawingNodes.left}</div>\n <div style={cornerOverlayStyle}>{paneDrawingNodes.corner}</div>\n <div style={scrollOverlayStyle}>{paneDrawingNodes.scroll}</div>\n </>\n ) : null}\n {drawingOverlayPanes}\n {selectionOverlayPanes}\n {experimentalCanvas ? (\n <>\n <div style={canvasBodyViewportLayerStyle}>\n <canvas\n ref={scrollBodyCanvasRef}\n onClick={handleCanvasBodyClick}\n onDoubleClick={handleCanvasBodyDoubleClick}\n onPointerDown={handleCanvasBodyPointerDown}\n style={canvasScrollBodyStyle}\n />\n <canvas\n ref={topBodyCanvasRef}\n onClick={handleCanvasBodyClick}\n onDoubleClick={handleCanvasBodyDoubleClick}\n onPointerDown={handleCanvasBodyPointerDown}\n style={canvasTopBodyStyle}\n />\n <canvas\n ref={leftBodyCanvasRef}\n onClick={handleCanvasBodyClick}\n onDoubleClick={handleCanvasBodyDoubleClick}\n onPointerDown={handleCanvasBodyPointerDown}\n style={canvasLeftBodyStyle}\n />\n <canvas\n ref={cornerBodyCanvasRef}\n onClick={handleCanvasBodyClick}\n onDoubleClick={handleCanvasBodyDoubleClick}\n onPointerDown={handleCanvasBodyPointerDown}\n style={canvasCornerBodyStyle}\n />\n </div>\n <div style={canvasHeaderViewportLayerStyle}>\n <canvas\n ref={topFrozenHeaderCanvasRef}\n onPointerLeave={handleCanvasHeaderPointerLeave}\n onPointerMove={handleCanvasColumnHeaderPointerMove}\n onPointerDown={handleCanvasColumnHeaderPointerDown}\n style={canvasTopFrozenHeaderStyle}\n />\n <canvas\n ref={topScrollHeaderCanvasRef}\n onPointerLeave={handleCanvasHeaderPointerLeave}\n onPointerMove={handleCanvasColumnHeaderPointerMove}\n onPointerDown={handleCanvasColumnHeaderPointerDown}\n style={canvasTopScrollHeaderStyle}\n />\n <canvas\n ref={leftFrozenHeaderCanvasRef}\n onPointerLeave={handleCanvasHeaderPointerLeave}\n onPointerMove={handleCanvasRowHeaderPointerMove}\n onPointerDown={handleCanvasRowHeaderPointerDown}\n style={canvasLeftFrozenHeaderStyle}\n />\n <canvas\n ref={leftScrollHeaderCanvasRef}\n onPointerLeave={handleCanvasHeaderPointerLeave}\n onPointerMove={handleCanvasRowHeaderPointerMove}\n onPointerDown={handleCanvasRowHeaderPointerDown}\n style={canvasLeftScrollHeaderStyle}\n />\n <canvas ref={cornerHeaderCanvasRef} style={canvasCornerHeaderStyle} />\n </div>\n {editingCell && editingOverlayRect ? (() => {\n const editingCellStyle = getCellData(editingCell.row, editingCell.col).style;\n const editingBackground =\n typeof editingCellStyle.backgroundColor === \"string\"\n ? editingCellStyle.backgroundColor\n : resolveSheetSurface(activeSheet, palette);\n const editingColor =\n typeof editingCellStyle.color === \"string\"\n ? editingCellStyle.color\n : resolveReadableTextColor(null, editingBackground, palette);\n return (\n <div\n style={{\n left: editingOverlayRect.left,\n position: \"absolute\",\n top: editingOverlayRect.top,\n width: editingOverlayRect.width,\n height: editingOverlayRect.height,\n zIndex: 28\n }}\n >\n <input\n ref={editingInputRef}\n autoFocus\n onBlur={handleEditingBlur}\n onChange={(event) => setEditingValue(event.target.value)}\n onKeyDown={(event) => {\n event.stopPropagation();\n if (event.key === \"Enter\") {\n event.preventDefault();\n commitEditing();\n return;\n }\n\n if (event.key === \"Escape\") {\n event.preventDefault();\n cancelEditing();\n return;\n }\n\n if (\n event.key === \"ArrowDown\" ||\n event.key === \"ArrowLeft\" ||\n event.key === \"ArrowRight\" ||\n event.key === \"ArrowUp\"\n ) {\n event.preventDefault();\n handleEditingNavigate(event.key, event.currentTarget.value);\n }\n }}\n style={{\n backgroundColor: editingBackground,\n border: 0,\n boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,\n boxSizing: \"border-box\",\n color: editingColor,\n display: \"block\",\n font: resolveCanvasFont(editingCellStyle, 12 * zoomFactor),\n height: \"100%\",\n margin: 0,\n minHeight: 0,\n outline: \"none\",\n overflow: \"hidden\",\n padding: scaleCssLengthExpression(DEFAULT_CELL_PADDING, zoomFactor),\n width: \"100%\"\n }}\n value={editingValue}\n />\n </div>\n );\n })() : null}\n {activeCellAdornment && activeCellAdornmentRect ? (\n <div\n style={{\n height: activeCellAdornmentRect.height,\n left: activeCellAdornmentRect.left,\n pointerEvents: \"none\",\n position: \"absolute\",\n top: activeCellAdornmentRect.top,\n width: activeCellAdornmentRect.width,\n zIndex: 27\n }}\n >\n <div style={{ height: \"100%\", pointerEvents: \"auto\", position: \"relative\", width: \"100%\" }}>\n {activeCellAdornment}\n </div>\n </div>\n ) : null}\n </>\n ) : (\n <table\n ref={tableRef}\n style={{\n borderCollapse: \"collapse\",\n color: \"#000000\",\n flex: \"0 0 auto\",\n tableLayout: \"fixed\",\n width: totalWidth\n }}\n >\n <colgroup>\n <col style={{ width: displayRowHeaderWidth }} />\n {leadingColumnSpacerWidth > 0 ? <col style={{ width: leadingColumnSpacerWidth }} /> : null}\n {renderedCols.map((column) => (\n <col\n key={column.key}\n ref={(element) => {\n if (element) {\n colElementRefs.current.set(column.actualCol, element);\n } else {\n colElementRefs.current.delete(column.actualCol);\n }\n }}\n style={{ width: column.size }}\n />\n ))}\n {trailingColumnSpacerWidth > 0 ? <col style={{ width: trailingColumnSpacerWidth }} /> : null}\n </colgroup>\n <thead style={{ position: \"sticky\", top: 0, zIndex: canvasHeaderOverlayZIndex }}>\n <tr>\n <th\n style={{\n ...headerCellStyle,\n backgroundColor: palette.headerSurface,\n left: 0,\n width: displayRowHeaderWidth,\n zIndex: cornerHeaderOverlayZIndex\n }}\n />\n {leadingColumnSpacerWidth > 0 ? (\n <th aria-hidden=\"true\" style={{ ...headerCellStyle, padding: 0, width: leadingColumnSpacerWidth }} />\n ) : null}\n {renderedCols.map((column) => (\n <th\n data-xlsx-col-header={column.actualCol}\n key={column.key}\n ref={(element) => setColHeaderRef(column.actualCol, element)}\n onPointerDown={(event) => handleColumnPointerDown(event, column.actualCol)}\n style={{\n ...headerCellStyle,\n left: stickyLeftByCol.get(column.actualCol),\n zIndex: stickyLeftByCol.has(column.actualCol) ? stickyHeaderOverlayZIndex : headerCellStyle.zIndex\n }}\n >\n <div style={{ position: \"relative\" }}>\n <span\n style={{\n display: \"inline-block\",\n transform: headerLabelLiveScale !== 1 ? `scale(${1 / headerLabelLiveScale})` : undefined,\n transformOrigin: \"center center\"\n }}\n >\n {columnLabel(column.actualCol)}\n </span>\n <div\n onPointerDown={(event) => {\n if (!canResizeHeaders) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n startColumnResize(\n event.pointerId,\n column.actualCol,\n column.size,\n event.clientX\n );\n }}\n style={columnResizeHandleStyle}\n />\n </div>\n </th>\n ))}\n {trailingColumnSpacerWidth > 0 ? (\n <th aria-hidden=\"true\" style={{ ...headerCellStyle, padding: 0, width: trailingColumnSpacerWidth }} />\n ) : null}\n </tr>\n </thead>\n <tbody>\n {virtualRows.map((virtualRow, index) => {\n const actualRow = visibleRows[virtualRow.index];\n if (actualRow === undefined) {\n return null;\n }\n\n const previousEnd = index === 0 ? 0 : (virtualRows[index - 1]?.end ?? 0);\n const gapHeight = Math.max(0, virtualRow.start - previousEnd);\n\n return (\n <React.Fragment key={`row-fragment-${virtualRow.key}`}>\n {gapHeight > 0 ? (\n <tr aria-hidden=\"true\" style={{ height: gapHeight }}>\n <td colSpan={rowColSpan} />\n </tr>\n ) : null}\n <MemoGridRow\n actualRow={actualRow}\n editingCell={editingCell}\n editingValue={editingValue}\n frozenRowHeaderZIndex={frozenRowHeaderOverlayZIndex}\n getCellData={getCellData}\n key={virtualRow.key}\n leadingSpacerWidth={leadingColumnSpacerWidth}\n onCellClick={handleCellClick}\n onCellDoubleClick={handleCellDoubleClick}\n onCellPointerDown={handleCellPointerDown}\n onEditingBlur={handleEditingBlur}\n onEditingCancel={cancelEditing}\n onEditingCommit={commitEditing}\n onEditingNavigate={handleEditingNavigate}\n onEditingValueChange={setEditingValue}\n headerLabelLiveScale={headerLabelLiveScale}\n onRowHeaderRef={setRowHeaderRef}\n onRowPointerDown={handleRowPointerDown}\n onRowResizePointerDown={handleRowResizePointerDown}\n palette={palette}\n readOnly={readOnly}\n renderCellAdornment={renderCellAdornment}\n rowHeight={virtualRow.size}\n rowHeaderZIndex={rowHeaderOverlayZIndex}\n rowHeaderWidth={displayRowHeaderWidth}\n stickyLeftByCol={stickyLeftByCol}\n stickyTop={stickyTopByRow.get(actualRow)}\n trailingSpacerWidth={trailingColumnSpacerWidth}\n visibleCols={renderedCols}\n zoomFactor={zoomFactor}\n />\n </React.Fragment>\n );\n })}\n {virtualRows.length > 0 && totalHeight - (virtualRows[virtualRows.length - 1]?.end ?? totalHeight) > 0 ? (\n <tr\n style={{\n height: totalHeight - (virtualRows[virtualRows.length - 1]?.end ?? totalHeight)\n }}\n >\n <td colSpan={rowColSpan} />\n </tr>\n ) : null}\n </tbody>\n </table>\n )}\n <div\n ref={canvasSelectionLayerRef}\n style={{\n height: 0,\n left: 0,\n overflow: \"visible\",\n pointerEvents: \"none\",\n position: \"absolute\",\n top: 0,\n transformOrigin: \"0 0\",\n transition: \"none\",\n width: 0,\n zIndex: 24\n }}\n >\n {!experimentalCanvas ? renderSelectionOverlay() : null}\n {activeCellOverlay != null && activeCellOverlayRect ? (\n <div\n data-xlsx-active-cell-overlay=\"\"\n style={{\n height: activeCellOverlayRect.height,\n left: activeCellOverlayRect.left,\n pointerEvents: \"none\",\n position: \"absolute\",\n top: activeCellOverlayRect.top,\n width: activeCellOverlayRect.width,\n zIndex: 24\n }}\n >\n {activeCellOverlay}\n </div>\n ) : null}\n <div\n ref={activeValidationOverlayRef}\n aria-hidden=\"true\"\n style={{\n alignItems: \"center\",\n color: palette.mutedText,\n display: \"inline-flex\",\n fontSize: 10 * zoomFactor,\n fontWeight: 700,\n height: 16 * zoomFactor,\n justifyContent: \"center\",\n opacity: 0,\n pointerEvents: \"none\",\n position: \"absolute\",\n transform: \"translateY(-50%)\",\n visibility: \"hidden\",\n width: 12 * zoomFactor,\n zIndex: 26\n }}\n >\n ▾\n </div>\n </div>\n {resizeGuide ? (\n <div\n aria-hidden=\"true\"\n style={{\n backgroundColor: selectionStroke,\n borderRadius: Math.max(999, 3 * zoomFactor),\n boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${palette.surface}`,\n height: resizeGuide.type === \"column\" ? Math.max(12, 14 * zoomFactor) : Math.max(3, 2 * zoomFactor),\n left: resizeGuide.type === \"column\"\n ? resizeGuide.position - Math.max(2, 1.5 * zoomFactor)\n : Math.max(3, 4 * zoomFactor),\n pointerEvents: \"none\",\n position: \"absolute\",\n top: resizeGuide.type === \"column\"\n ? Math.max(2 * zoomFactor, displayHeaderHeight - Math.max(14, 16 * zoomFactor))\n : resizeGuide.position - Math.max(2, 1.5 * zoomFactor),\n width: resizeGuide.type === \"column\" ? Math.max(3, 2 * zoomFactor) : Math.max(12, 14 * zoomFactor),\n zIndex: 52\n }}\n />\n ) : null}\n {!renderTableHeaderMenu && openTableMenuState ? (\n <div\n ref={tableMenuRef}\n style={{\n color: palette.text,\n left: Math.max(displayRowHeaderWidth + (4 * zoomFactor), openTableMenuState.left),\n position: \"absolute\",\n top: openTableMenuState.top,\n zIndex: 50\n }}\n >\n <DefaultTableHeaderMenu\n cell={{ col: openTableMenuState.column.index + openTableMenuState.table.start.col, row: openTableMenuState.table.start.row }}\n column={openTableMenuState.column}\n direction={\n sortState &&\n sortState.tableName === openTableMenuState.table.name &&\n sortState.columnIndex === openTableMenuState.column.index\n ? sortState.direction\n : null\n }\n sortAscending={() => {\n sortTable(openTableMenuState.table.name, openTableMenuState.column.index, \"ascending\");\n setOpenTableMenu(null);\n }}\n sortDescending={() => {\n sortTable(openTableMenuState.table.name, openTableMenuState.column.index, \"descending\");\n setOpenTableMenu(null);\n }}\n table={openTableMenuState.table}\n triggerIcon=\"▾\"\n triggerProps={{ type: \"button\" }}\n />\n </div>\n ) : null}\n </div>\n </div>\n );\n\n return (\n <div style={{ backgroundColor: palette.canvas, display: \"flex\", flex: 1, minHeight: 0, minWidth: 0 }}>\n {renderScroller\n ? renderScroller({ children: scrollerContent, viewportProps: scrollerViewportProps })\n : <div key={activeTabIndex} {...scrollerViewportProps}>{scrollerContent}</div>}\n {enableContextMenu && contextMenu && (\n <XlsxContextMenu\n key={`${contextMenu.position.x}:${contextMenu.position.y}`}\n {...contextMenu}\n controller={controller}\n palette={palette}\n closeMenu={closeContextMenu}\n editCell={() => startEditing(contextMenu.context.cell)}\n selectExtent={(kind) => selectContextExtent(kind, contextMenu.context.selection)}\n renderContextMenu={renderContextMenu}\n contextMenuClassName={contextMenuClassName}\n contextMenuStyle={contextMenuStyle}\n />\n )}\n </div>\n );\n}\n\nfunction XlsxViewerInner({\n allowResizeInReadOnly = false,\n autoScrollToHighlightedRanges = true,\n className,\n controller,\n emptyState,\n enableCanvasSelectionAnimation = true,\n enableGestureZoom = true,\n errorState,\n experimentalCanvas = true,\n experimentalOfficeCharts,\n fileTooLargeState,\n getCellStyle,\n headerBackgroundColor,\n headerTextColor,\n height,\n highlightClassName,\n highlightedRanges,\n isDark = false,\n loadingComponent,\n loadingState,\n onFormControlAction,\n onFormControlChange,\n renderChartLoading,\n renderContextMenu,\n enableContextMenu = true,\n contextMenuClassName,\n contextMenuStyle,\n renderFormControl,\n renderImage,\n renderImageSelection,\n renderScroller,\n renderTableHeaderMenu,\n renderActiveCellOverlay,\n rounded = true,\n selectionColor,\n selectionFillColor,\n selectionHeaderColor,\n showImages = true,\n showDefaultToolbar = true,\n toolbar\n}: XlsxViewerProps & {\n controller: XlsxViewerController;\n}) {\n const palette = useViewerPalette(isDark, headerBackgroundColor, headerTextColor);\n const { displayFileName, error } = controller;\n const appliedHighlightRef = React.useRef<{\n autoScroll: boolean;\n clear: XlsxViewerController[\"clearHighlightedRanges\"];\n file: ArrayBuffer | undefined;\n referenceKey: string;\n src: string | undefined;\n } | null>(null);\n const { clearHighlightedRanges, file, highlightRanges, isLoading, sheets, src } = controller;\n React.useEffect(() => {\n if (isLoading || highlightedRanges === undefined) {\n appliedHighlightRef.current = null;\n return;\n }\n if (highlightedRanges !== null && sheets.length === 0) {\n return;\n }\n const referenceKey = JSON.stringify(typeof highlightedRanges === \"string\" ? [highlightedRanges] : highlightedRanges ?? []);\n const previous = appliedHighlightRef.current;\n if (previous && previous.referenceKey === referenceKey && previous.autoScroll === autoScrollToHighlightedRanges\n && previous.file === file && previous.src === src && previous.clear === clearHighlightedRanges) {\n return;\n }\n appliedHighlightRef.current = { autoScroll: autoScrollToHighlightedRanges, clear: clearHighlightedRanges, file, referenceKey, src };\n if (highlightedRanges === null || !highlightRanges(highlightedRanges, { autoScroll: autoScrollToHighlightedRanges })) {\n clearHighlightedRanges();\n }\n }, [autoScrollToHighlightedRanges, clearHighlightedRanges, file, highlightRanges, highlightedRanges, isLoading, sheets.length, src]);\n const customFileTooLarge =\n error instanceof XlsxFileSizeLimitExceededError\n ? renderCustomFileTooLarge(\n fileTooLargeState,\n {\n displayFileName,\n fileSizeBytes: error.fileSizeBytes,\n maxFileSizeBytes: error.maxFileSizeBytes\n },\n palette\n )\n : undefined;\n\n return (\n <ViewerAppearanceContext.Provider value={{ headerBackgroundColor, headerTextColor, isDark }}>\n <ViewerContext.Provider value={controller}>\n {customFileTooLarge !== undefined ? (\n customFileTooLarge\n ) : (\n <div\n className={classNames(\"react-xlsx-viewer\", className)}\n style={{\n blockSize: height,\n backgroundColor: palette.surface,\n borderRadius: rounded ? 12 : 0,\n color: palette.text,\n display: \"flex\",\n flex: \"1 1 auto\",\n flexDirection: \"column\",\n inlineSize: \"100%\",\n isolation: \"isolate\",\n maxHeight: \"100%\",\n maxWidth: \"100%\",\n minHeight: 0,\n minWidth: 0,\n overflow: \"hidden\",\n position: \"relative\",\n width: \"100%\"\n }}\n >\n <style>{RANGE_HIGHLIGHT_STYLES}</style>\n {resolveToolbar(toolbar, showDefaultToolbar, controller, palette)}\n <div style={{ display: \"flex\", flex: 1, minHeight: 0, minWidth: 0 }}>\n <XlsxGrid\n allowResizeInReadOnly={allowResizeInReadOnly}\n controller={controller}\n emptyState={emptyState}\n enableCanvasSelectionAnimation={enableCanvasSelectionAnimation}\n enableGestureZoom={enableGestureZoom}\n errorState={errorState}\n experimentalCanvas={experimentalCanvas}\n experimentalOfficeCharts={experimentalOfficeCharts}\n fileTooLargeState={fileTooLargeState}\n getCellStyle={getCellStyle}\n highlightClassName={highlightClassName}\n loadingComponent={loadingComponent}\n loadingState={loadingState}\n onFormControlAction={onFormControlAction}\n onFormControlChange={onFormControlChange}\n palette={palette}\n renderChartLoading={renderChartLoading}\n renderContextMenu={renderContextMenu}\n enableContextMenu={enableContextMenu}\n contextMenuClassName={contextMenuClassName}\n contextMenuStyle={contextMenuStyle}\n renderFormControl={renderFormControl}\n renderImage={renderImage}\n renderImageSelection={renderImageSelection}\n renderScroller={renderScroller}\n renderTableHeaderMenu={renderTableHeaderMenu}\n renderActiveCellOverlay={renderActiveCellOverlay}\n selectionColor={selectionColor}\n selectionFillColor={selectionFillColor}\n selectionHeaderColor={selectionHeaderColor}\n showImages={showImages}\n />\n </div>\n </div>\n )}\n </ViewerContext.Provider>\n </ViewerAppearanceContext.Provider>\n );\n}\n\nfunction XlsxViewerWithInlineController(props: XlsxViewerProps) {\n const controller = useXlsxViewerController(props);\n return <XlsxViewerInner {...props} controller={controller} />;\n}\n\nfunction XlsxViewerProviderWithInlineController({\n children,\n isDark = false,\n ...options\n}: Omit<XlsxViewerProviderProps, \"controller\">) {\n const controller = useXlsxViewerController(options);\n return (\n <ViewerAppearanceContext.Provider value={{ isDark }}>\n <ViewerContext.Provider value={controller}>{children}</ViewerContext.Provider>\n </ViewerAppearanceContext.Provider>\n );\n}\n\nexport function XlsxViewerProvider({ children, controller, isDark = false, ...options }: XlsxViewerProviderProps) {\n if (controller) {\n return (\n <ViewerAppearanceContext.Provider value={{ isDark }}>\n <ViewerContext.Provider value={controller}>{children}</ViewerContext.Provider>\n </ViewerAppearanceContext.Provider>\n );\n }\n\n return (\n <XlsxViewerProviderWithInlineController {...options} isDark={isDark}>\n {children}\n </XlsxViewerProviderWithInlineController>\n );\n}\n\nexport function useXlsxViewer() {\n const context = React.useContext(ViewerContext);\n if (!context) {\n throw new Error(\"useXlsxViewer must be used inside XlsxViewer or XlsxViewerProvider.\");\n }\n\n return context;\n}\n\nexport function useXlsxViewerSelection(): XlsxViewerSelection {\n const {\n activeCell,\n activeCellAddress,\n clearSelection,\n selectedRangeAddress,\n selectCell,\n selectRange,\n selection\n } = useXlsxViewer();\n\n return React.useMemo(\n () => ({\n activeCell,\n activeCellAddress,\n clearSelection,\n selectedRangeAddress,\n selectCell,\n selectRange,\n selection\n }),\n [activeCell, activeCellAddress, clearSelection, selectedRangeAddress, selectCell, selectRange, selection]\n );\n}\n\nexport function useXlsxViewerZoom(): XlsxViewerZoom {\n const {\n canZoomIn,\n canZoomOut,\n defaultZoomScale,\n maxZoomScale,\n minZoomScale,\n resetZoom,\n setZoomScale,\n zoomIn,\n zoomOut,\n zoomScale\n } = useXlsxViewer();\n\n return React.useMemo(\n () => ({\n canZoomIn,\n canZoomOut,\n defaultZoomScale,\n maxZoomScale,\n minZoomScale,\n resetZoom,\n setZoomScale,\n zoomIn,\n zoomOut,\n zoomScale\n }),\n [\n canZoomIn,\n canZoomOut,\n defaultZoomScale,\n maxZoomScale,\n minZoomScale,\n resetZoom,\n setZoomScale,\n zoomIn,\n zoomOut,\n zoomScale\n ]\n );\n}\n\nexport function useXlsxViewerEditing(): XlsxViewerEditing {\n const {\n addFormControl,\n addSheet,\n canRedo,\n canUndo,\n clearSelectedCells,\n copySelectionToClipboard,\n defineNamedRange,\n fillSelection,\n formControls,\n getClipboardData,\n getCellDisplayValue,\n getCellFormula,\n getFormControlItems,\n getSheetFormControls,\n mergeSelection,\n pasteFromClipboard,\n pasteStructuredClipboardData,\n pasteText,\n removeActiveSheet,\n removeFormControl,\n readOnly,\n redo,\n selectedCellFormula,\n selectedChartFormula,\n selectedFormula,\n selectedFormulaTarget,\n selectedValue,\n setCellFormula,\n setCellStyle,\n setCellValue,\n setRangeStyle,\n setSelectedFormula,\n setSelectedCellFormula,\n setSelectedCellStyle,\n setSelectedCellValue,\n undo,\n unmergeSelection,\n updateFormControl\n } = useXlsxViewer();\n\n return React.useMemo(\n () => ({\n addFormControl,\n addSheet,\n canRedo,\n canUndo,\n clearSelectedCells,\n copySelectionToClipboard,\n defineNamedRange,\n fillSelection,\n formControls,\n getClipboardData,\n getCellDisplayValue,\n getCellFormula,\n getFormControlItems,\n getSheetFormControls,\n mergeSelection,\n pasteFromClipboard,\n pasteStructuredClipboardData,\n pasteText,\n removeActiveSheet,\n removeFormControl,\n readOnly,\n redo,\n selectedCellFormula,\n selectedChartFormula,\n selectedFormula,\n selectedFormulaTarget,\n selectedValue,\n setCellFormula,\n setCellStyle,\n setCellValue,\n setRangeStyle,\n setSelectedFormula,\n setSelectedCellFormula,\n setSelectedCellStyle,\n setSelectedCellValue,\n undo,\n unmergeSelection,\n updateFormControl\n }),\n [\n addFormControl,\n addSheet,\n canRedo,\n canUndo,\n clearSelectedCells,\n copySelectionToClipboard,\n defineNamedRange,\n fillSelection,\n formControls,\n getClipboardData,\n getCellDisplayValue,\n getCellFormula,\n getFormControlItems,\n getSheetFormControls,\n mergeSelection,\n pasteFromClipboard,\n pasteStructuredClipboardData,\n pasteText,\n removeActiveSheet,\n removeFormControl,\n readOnly,\n redo,\n selectedCellFormula,\n selectedChartFormula,\n selectedFormula,\n selectedFormulaTarget,\n selectedValue,\n setCellFormula,\n setCellStyle,\n setCellValue,\n setRangeStyle,\n setSelectedFormula,\n setSelectedCellFormula,\n setSelectedCellStyle,\n setSelectedCellValue,\n undo,\n unmergeSelection,\n updateFormControl\n ]\n );\n}\n\nexport function useXlsxViewerTables(): XlsxViewerTables {\n const { sortState, sortTable, tables } = useXlsxViewer();\n\n return React.useMemo(\n () => ({\n sortState,\n sortTable,\n tables\n }),\n [sortState, sortTable, tables]\n );\n}\n\nexport function useXlsxViewerImages(): XlsxViewerImages {\n const {\n charts,\n clearSelectedChart,\n clearSelectedChartElement,\n clearSelectedImage,\n getChartById,\n getChartSeriesFormula,\n getSheetCharts,\n getImageById,\n getSheetImages,\n images,\n isChartsLoading,\n moveChartBy,\n moveImageBy,\n readOnly,\n resizeChartBy,\n resizeImageBy,\n selectedChart,\n selectedChartElement,\n selectedChartFormula,\n selectedChartId,\n selectedImage,\n selectedImageId,\n selectChart,\n selectChartElement,\n selectImage,\n setChartSeriesFormula,\n setChartRect,\n setImageRect,\n updateChart\n } = useXlsxViewer();\n\n return React.useMemo(\n () => ({\n charts,\n clearSelectedChart,\n clearSelectedChartElement,\n clearSelectedImage,\n getChartById,\n getChartSeriesFormula,\n getSheetCharts,\n getImageById,\n getSheetImages,\n images,\n isChartsLoading,\n moveChartBy,\n moveImageBy,\n readOnly,\n resizeChartBy,\n resizeImageBy,\n selectedChart,\n selectedChartElement,\n selectedChartFormula,\n selectedChartId,\n selectedImage,\n selectedImageId,\n selectChart,\n selectChartElement,\n selectImage,\n setChartSeriesFormula,\n setChartRect,\n setImageRect,\n updateChart\n }),\n [\n charts,\n clearSelectedChart,\n clearSelectedChartElement,\n clearSelectedImage,\n getChartById,\n getChartSeriesFormula,\n getSheetCharts,\n getImageById,\n getSheetImages,\n images,\n isChartsLoading,\n moveChartBy,\n moveImageBy,\n readOnly,\n resizeChartBy,\n resizeImageBy,\n selectedChart,\n selectedChartElement,\n selectedChartFormula,\n selectedChartId,\n selectedImage,\n selectedImageId,\n selectChart,\n selectChartElement,\n selectImage,\n setChartSeriesFormula,\n setChartRect,\n setImageRect,\n updateChart\n ]\n );\n}\n\nexport function useXlsxViewerCharts(): XlsxViewerCharts {\n const {\n activeTab,\n activeTabIndex,\n charts,\n chartsheets,\n clearSelectedChart,\n clearSelectedChartElement,\n getChartById,\n getChartSeriesFormula,\n getChartsheetById,\n getSheetCharts,\n isChartsLoading,\n moveChartBy,\n readOnly,\n resizeChartBy,\n selectChart,\n selectedChart,\n selectedChartElement,\n selectedChartFormula,\n selectedChartId,\n selectChartElement,\n setActiveTabIndex,\n setChartSeriesFormula,\n setChartRect,\n tabs,\n updateChart\n } = useXlsxViewer();\n\n return React.useMemo(\n () => ({\n activeTab,\n activeTabIndex,\n charts,\n chartsheets,\n clearSelectedChart,\n clearSelectedChartElement,\n getChartById,\n getChartSeriesFormula,\n getChartsheetById,\n getSheetCharts,\n isChartsLoading,\n moveChartBy,\n readOnly,\n resizeChartBy,\n selectChart,\n selectedChart,\n selectedChartElement,\n selectedChartFormula,\n selectedChartId,\n selectChartElement,\n setActiveTabIndex,\n setChartSeriesFormula,\n setChartRect,\n tabs,\n updateChart\n }),\n [\n activeTab,\n activeTabIndex,\n charts,\n chartsheets,\n clearSelectedChart,\n clearSelectedChartElement,\n getChartById,\n getChartSeriesFormula,\n getChartsheetById,\n getSheetCharts,\n isChartsLoading,\n moveChartBy,\n readOnly,\n resizeChartBy,\n selectChart,\n selectedChart,\n selectedChartElement,\n selectedChartFormula,\n selectedChartId,\n selectChartElement,\n setActiveTabIndex,\n setChartSeriesFormula,\n setChartRect,\n tabs,\n updateChart\n ]\n );\n}\n\nexport function useXlsxViewerThumbnails(\n options: UseXlsxViewerThumbnailsOptions = {}\n): XlsxViewerThumbnails {\n const {\n getRowsBatchAsync,\n getSheetFormControls,\n getSheetImages,\n getSheetShapes,\n locale,\n workbook,\n sheets\n } = useXlsxViewer();\n const { headerBackgroundColor, headerTextColor, isDark } = React.useContext(ViewerAppearanceContext);\n const palette = useViewerPalette(isDark, headerBackgroundColor, headerTextColor);\n const includeHeaders = options.includeHeaders ?? true;\n const resolution = options.resolution;\n const thumbnailImageCacheRef = React.useRef(new Map<string, {\n failed: boolean;\n image: HTMLImageElement;\n loaded: boolean;\n src: string;\n }>());\n const workerThumbnailRowBatchCacheRef = React.useRef(new Map<XlsxSheetData, WorksheetBatchWindow>());\n const workerThumbnailRowBatchRequestRef = React.useRef(\n new Map<XlsxSheetData, ThumbnailWorkerRowBatchRequest>()\n );\n const isMountedRef = React.useRef(false);\n const [thumbnailImageLoadVersion, setThumbnailImageLoadVersion] = React.useState(0);\n const [workerThumbnailRowBatchVersion, setWorkerThumbnailRowBatchVersion] = React.useState(0);\n\n React.useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n React.useEffect(() => {\n workerThumbnailRowBatchCacheRef.current.clear();\n workerThumbnailRowBatchRequestRef.current.clear();\n setWorkerThumbnailRowBatchVersion((version) => version + 1);\n }, [locale]);\n\n React.useEffect(() => {\n const currentSheets = new Set(sheets);\n for (const sheet of workerThumbnailRowBatchCacheRef.current.keys()) {\n if (!currentSheets.has(sheet)) {\n workerThumbnailRowBatchCacheRef.current.delete(sheet);\n }\n }\n for (const sheet of workerThumbnailRowBatchRequestRef.current.keys()) {\n if (!currentSheets.has(sheet)) {\n workerThumbnailRowBatchRequestRef.current.delete(sheet);\n }\n }\n }, [sheets]);\n\n const getWorkerThumbnailRowBatch = React.useCallback((\n sheet: XlsxSheetData,\n startRow: number,\n endRow: number\n ) => {\n const cachedBatch = workerThumbnailRowBatchCacheRef.current.get(sheet);\n if (cachedBatch && startRow >= cachedBatch.startRow && endRow <= cachedBatch.endRow) {\n return cachedBatch;\n }\n if (!getRowsBatchAsync) {\n return null;\n }\n\n const pendingRequest = workerThumbnailRowBatchRequestRef.current.get(sheet);\n if (!pendingRequest) {\n const request = {\n endRow: Math.max(endRow, cachedBatch?.endRow ?? endRow),\n startRow: Math.min(startRow, cachedBatch?.startRow ?? startRow)\n };\n workerThumbnailRowBatchRequestRef.current.set(sheet, request);\n\n void getRowsBatchAsync(\n sheet.workbookSheetIndex,\n request.startRow,\n request.endRow - request.startRow + 1\n )\n .then((rows) => {\n if (workerThumbnailRowBatchRequestRef.current.get(sheet) !== request) {\n return;\n }\n workerThumbnailRowBatchRequestRef.current.delete(sheet);\n if (!rows) {\n return;\n }\n\n workerThumbnailRowBatchCacheRef.current.set(\n sheet,\n buildWorksheetBatchWindow(rows, sheet, request.startRow, request.endRow)\n );\n if (isMountedRef.current) {\n setWorkerThumbnailRowBatchVersion((version) => version + 1);\n }\n })\n .catch(() => {\n if (workerThumbnailRowBatchRequestRef.current.get(sheet) === request) {\n workerThumbnailRowBatchRequestRef.current.delete(sheet);\n }\n });\n }\n\n return null;\n }, [getRowsBatchAsync]);\n\n const getThumbnailImage = React.useCallback((image: XlsxImage) => {\n if (typeof Image === \"undefined\") {\n return null;\n }\n\n const cacheKey = image.id;\n const cached = thumbnailImageCacheRef.current.get(cacheKey);\n if (cached && cached.src === image.src) {\n return cached.loaded && !cached.failed ? cached.image : null;\n }\n\n const imageElement = new Image();\n const entry = {\n failed: false,\n image: imageElement,\n loaded: false,\n src: image.src\n };\n thumbnailImageCacheRef.current.set(cacheKey, entry);\n imageElement.onload = () => {\n entry.loaded = true;\n if (isMountedRef.current) {\n setThumbnailImageLoadVersion((version) => version + 1);\n }\n };\n imageElement.onerror = () => {\n entry.failed = true;\n if (isMountedRef.current) {\n setThumbnailImageLoadVersion((version) => version + 1);\n }\n };\n imageElement.src = image.src;\n return null;\n }, []);\n\n const thumbnails = React.useMemo<XlsxSheetThumbnail[]>(() => {\n return sheets.map((sheet, sheetIndex) => {\n const worksheet = workbook?.getSheet(sheet.workbookSheetIndex) ?? null;\n const sheetImages = getSheetImages(sheetIndex)\n .filter((image) => image.src)\n .sort((left, right) => left.zIndex - right.zIndex);\n const sheetShapes = getSheetShapes(sheetIndex);\n const sheetFormControls = getSheetFormControls(sheetIndex).filter((control) => !control.hidden);\n const drawingBounds = [\n ...sheetImages.map((image) => image.anchor),\n ...sheetShapes.map((shape) => shape.anchor),\n ...sheetFormControls.map((control) => control.anchor)\n ].reduce<{\n maxCol: number;\n maxRow: number;\n minCol: number;\n minRow: number;\n } | null>((bounds, anchor) => {\n const drawingBound = resolveAnchoredBounds(anchor) ?? {\n maxCol: 0,\n maxRow: 0,\n minCol: 0,\n minRow: 0\n };\n return {\n maxCol: Math.max(bounds?.maxCol ?? drawingBound.maxCol, drawingBound.maxCol),\n maxRow: Math.max(bounds?.maxRow ?? drawingBound.maxRow, drawingBound.maxRow),\n minCol: Math.min(bounds?.minCol ?? drawingBound.minCol, drawingBound.minCol),\n minRow: Math.min(bounds?.minRow ?? drawingBound.minRow, drawingBound.minRow)\n };\n }, null);\n const sheetMinUsedRow = (sheet.maxUsedRow ?? -1) >= (sheet.minUsedRow ?? 0) ? (sheet.minUsedRow ?? 0) : undefined;\n const sheetMinUsedCol = (sheet.maxUsedCol ?? -1) >= (sheet.minUsedCol ?? 0) ? (sheet.minUsedCol ?? 0) : undefined;\n const effectiveMinUsedRow = Math.max(0, Math.min(sheetMinUsedRow ?? drawingBounds?.minRow ?? 0, drawingBounds?.minRow ?? sheetMinUsedRow ?? 0));\n const effectiveMinUsedCol = Math.max(0, Math.min(sheetMinUsedCol ?? drawingBounds?.minCol ?? 0, drawingBounds?.minCol ?? sheetMinUsedCol ?? 0));\n const effectiveMaxUsedRow = Math.max(sheet.maxUsedRow ?? -1, drawingBounds?.maxRow ?? -1);\n const effectiveMaxUsedCol = Math.max(sheet.maxUsedCol ?? -1, drawingBounds?.maxCol ?? -1);\n const showGridLines = sheet.showGridLines ?? true;\n const hiddenRowSet = new Set(sheet.hiddenRows ?? []);\n const hiddenColSet = new Set(sheet.hiddenCols ?? []);\n const visibleRows = buildVisibleAxisIndices(\n sheet.visibleRows ?? [],\n Math.max(THUMBNAIL_FALLBACK_ROWS, effectiveMaxUsedRow + THUMBNAIL_FALLBACK_ROWS + 1),\n effectiveMaxUsedRow,\n hiddenRowSet\n );\n const visibleCols = buildVisibleAxisIndices(\n sheet.visibleCols ?? [],\n Math.max(THUMBNAIL_FALLBACK_COLS, effectiveMaxUsedCol + THUMBNAIL_FALLBACK_COLS + 1),\n effectiveMaxUsedCol,\n hiddenColSet\n );\n const resolveColumnWidthPx = (actualCol: number) => {\n if (worksheet && !worksheet.isColumnHidden(actualCol)) {\n const width = worksheet.getColumnWidth(actualCol);\n if (width !== undefined && width !== null) {\n return Math.max(\n resolveRenderedSheetAxisPixels(resolveSheetColumnWidthPixels(width, sheet.columnWidthCharacterWidthPx), showGridLines),\n DEFAULT_COL_WIDTH / 2\n );\n }\n }\n\n return Math.max(\n resolveRenderedSheetAxisPixels(\n sheet.colWidthOverridesPx[actualCol] ?? sheet.defaultColWidthPx ?? DEFAULT_COL_WIDTH,\n showGridLines\n ),\n DEFAULT_COL_WIDTH / 2\n );\n };\n const resolveRowHeightPx = (actualRow: number) => {\n if (worksheet && !worksheet.isRowHidden(actualRow)) {\n const height = worksheet.getRowHeight(actualRow);\n if (height !== undefined && height !== null) {\n return Math.max(\n resolveRenderedSheetAxisPixels(resolveSheetRowHeightPixels(height), showGridLines),\n DEFAULT_ROW_HEIGHT / 1.5\n );\n }\n }\n\n return Math.max(\n resolveRenderedSheetAxisPixels(\n sheet.rowHeightOverridesPx[actualRow] ?? sheet.defaultRowHeightPx ?? DEFAULT_ROW_HEIGHT,\n showGridLines\n ),\n DEFAULT_ROW_HEIGHT / 1.5\n );\n };\n const previewRows = resolveThumbnailAxisIndices({\n fallbackCount: THUMBNAIL_FALLBACK_ROWS,\n getSizePx: resolveRowHeightPx,\n maxCount: THUMBNAIL_MAX_ROWS,\n maxLogicalPixels: THUMBNAIL_MAX_SOURCE_HEIGHT_PX,\n maxUsedIndex: effectiveMaxUsedRow,\n minUsedIndex: effectiveMinUsedRow,\n precomputed: visibleRows\n });\n const previewCols = resolveThumbnailAxisIndices({\n fallbackCount: THUMBNAIL_FALLBACK_COLS,\n getSizePx: resolveColumnWidthPx,\n maxCount: THUMBNAIL_MAX_COLS,\n maxLogicalPixels: THUMBNAIL_MAX_SOURCE_WIDTH_PX,\n maxUsedIndex: effectiveMaxUsedCol,\n minUsedIndex: effectiveMinUsedCol,\n precomputed: visibleCols\n });\n const rowAxis = buildThumbnailAxisItems(previewRows, resolveRowHeightPx);\n const colAxis = buildThumbnailAxisItems(previewCols, resolveColumnWidthPx);\n const rowItemByActual = new Map(rowAxis.items.map((item) => [item.actualIndex, item]));\n const colItemByActual = new Map(colAxis.items.map((item) => [item.actualIndex, item]));\n const headerHeight = includeHeaders ? HEADER_HEIGHT : 0;\n const rowHeaderWidth = includeHeaders ? ROW_HEADER_WIDTH : 0;\n const sourceWidth = rowHeaderWidth + colAxis.totalSize;\n const sourceHeight = headerHeight + rowAxis.totalSize;\n const outputSize = resolveThumbnailOutputSize(sourceWidth, sourceHeight, resolution);\n const sourceRange: XlsxCellRange = {\n start: {\n col: previewCols[0] ?? 0,\n row: previewRows[0] ?? 0\n },\n end: {\n col: previewCols[previewCols.length - 1] ?? 0,\n row: previewRows[previewRows.length - 1] ?? 0\n }\n };\n const sparklineByCell = new Map<string, XlsxSheetData[\"sparklines\"][number]>(\n (sheet.sparklines ?? []).map((sparkline) => [`${sparkline.target.row}:${sparkline.target.col}`, sparkline])\n );\n const thumbnailImageRects = sheetImages\n .map((image) => ({\n image,\n rect: resolveThumbnailAnchoredRect(\n image.anchor,\n visibleRows,\n visibleCols,\n resolveRowHeightPx,\n resolveColumnWidthPx,\n previewRows,\n previewCols,\n {\n headerHeight,\n rowHeaderWidth\n }\n )\n }))\n .filter(({ rect }) => (\n rect.left + rect.width >= rowHeaderWidth\n && rect.top + rect.height >= headerHeight\n && rect.left <= sourceWidth\n && rect.top <= sourceHeight\n ));\n const thumbnailShapeRects = sheetShapes\n .map((shape) => ({\n rect: resolveThumbnailAnchoredRect(\n shape.anchor,\n visibleRows,\n visibleCols,\n resolveRowHeightPx,\n resolveColumnWidthPx,\n previewRows,\n previewCols,\n {\n headerHeight,\n rowHeaderWidth\n }\n ),\n shape\n }))\n .filter(({ rect }) => (\n rect.left + rect.width >= rowHeaderWidth\n && rect.top + rect.height >= headerHeight\n && rect.left <= sourceWidth\n && rect.top <= sourceHeight\n ));\n const thumbnailFormControlRects = sheetFormControls\n .map((control) => ({\n control,\n rect: resolveThumbnailAnchoredRect(\n control.anchor,\n visibleRows,\n visibleCols,\n resolveRowHeightPx,\n resolveColumnWidthPx,\n previewRows,\n previewCols,\n {\n headerHeight,\n rowHeaderWidth\n }\n )\n }))\n .filter(({ rect }) => (\n rect.left + rect.width >= rowHeaderWidth\n && rect.top + rect.height >= headerHeight\n && rect.left <= sourceWidth\n && rect.top <= sourceHeight\n ));\n const thumbnailDrawingEntries = [\n ...thumbnailShapeRects.map(({ rect, shape }) => ({\n kind: \"shape\" as const,\n rect,\n shape,\n zIndex: shape.zIndex\n })),\n ...thumbnailFormControlRects.map(({ control, rect }) => ({\n control,\n kind: \"formControl\" as const,\n rect,\n zIndex: control.zIndex\n })),\n ...thumbnailImageRects.map(({ image, rect }) => ({\n image,\n kind: \"image\" as const,\n rect,\n zIndex: image.zIndex\n }))\n ].sort((left, right) => left.zIndex - right.zIndex);\n\n const paint = (canvas: HTMLCanvasElement | null) => {\n if (!canvas) {\n return false;\n }\n\n const workerRowBatch = worksheet\n ? null\n : getWorkerThumbnailRowBatch(sheet, sourceRange.start.row, sourceRange.end.row);\n if (!worksheet && !workerRowBatch) {\n return false;\n }\n\n const context = canvas.getContext(\"2d\", { alpha: false });\n if (!context) {\n return false;\n }\n\n const dpr = typeof window === \"undefined\" ? 1 : Math.max(1, window.devicePixelRatio || 1);\n const outputWidth = outputSize.width;\n const outputHeight = outputSize.height;\n const scale = outputSize.scale;\n const deviceWidth = Math.max(1, Math.round(outputWidth * dpr));\n const deviceHeight = Math.max(1, Math.round(outputHeight * dpr));\n if (canvas.width !== deviceWidth) {\n canvas.width = deviceWidth;\n }\n if (canvas.height !== deviceHeight) {\n canvas.height = deviceHeight;\n }\n if (canvas.style.width !== `${outputWidth}px`) {\n canvas.style.width = `${outputWidth}px`;\n }\n if (canvas.style.height !== `${outputHeight}px`) {\n canvas.style.height = `${outputHeight}px`;\n }\n\n context.setTransform(dpr * scale, 0, 0, dpr * scale, 0, 0);\n context.clearRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));\n const thumbnailSheetSurface = resolveSheetSurface(sheet, palette);\n context.fillStyle = palette.canvas;\n context.fillRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));\n context.fillStyle = thumbnailSheetSurface;\n context.fillRect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));\n\n if (includeHeaders) {\n context.fillStyle = palette.headerSurface;\n context.fillRect(rowHeaderWidth, 0, Math.max(1, colAxis.totalSize), headerHeight);\n context.fillStyle = palette.rowHeaderSurface;\n context.fillRect(0, headerHeight, rowHeaderWidth, Math.max(1, rowAxis.totalSize));\n context.fillRect(0, 0, rowHeaderWidth, headerHeight);\n }\n\n const conditionalFormatMetricsCache = new Map<string, number[]>();\n const cellRenderCache = new Map<string, CellRenderData>();\n const getCellData = (row: number, col: number): CellRenderData => {\n const cacheKey = `${row}:${col}`;\n const cached = cellRenderCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const batchCoversRow = workerRowBatch\n ? row >= workerRowBatch.startRow && row <= workerRowBatch.endRow\n : false;\n const batchedCell = batchCoversRow ? workerRowBatch?.cells.get(cacheKey) : undefined;\n if (batchedCell?.isMergedSecondary || worksheet?.isMergedSecondary(row, col)) {\n const mergedSecondaryData: CellRenderData = {\n isMergedSecondary: true,\n style: {},\n value: \"\"\n };\n cellRenderCache.set(cacheKey, mergedSecondaryData);\n return mergedSecondaryData;\n }\n\n const merge = worksheet\n ? (worksheet.getMergeSpan(row, col) as { colSpan?: number; rowSpan?: number } | null | undefined)\n : batchedCell?.mergeSpan;\n const inheritedStyle = resolveInheritedCellStyle(sheet, row, col);\n const worksheetStyle = worksheet\n ? (worksheet.getCellStyleAt(row, col) as Record<string, unknown> | null | undefined) ?? null\n : batchedCell?.style ?? null;\n const mergedStyle = mergeResolvedCellStyle(inheritedStyle, worksheetStyle, { replaceXfSubtrees: true });\n const conditionalCellStyle = resolveConditionalCellStyleForCell(\n row,\n col,\n sheet,\n () => (worksheet ? getCellNumericValue(worksheet, row, col) : getBatchedCellNumericValue(batchedCell)),\n () => (worksheet ? getCellDisplayValue(worksheet, row, col, sheet, locale) : (batchedCell?.value ?? \"\")),\n worksheet,\n workerRowBatch?.cells\n );\n const finalResolvedStyle = mergeResolvedCellStyle(mergedStyle, conditionalCellStyle);\n const alignment = finalResolvedStyle?.alignment as Record<string, unknown> | undefined;\n const sparkline = sparklineByCell.get(cacheKey) ?? null;\n const getNumericValue = (targetRow: number, targetCol: number) => (\n worksheet\n ? getCellNumericValue(worksheet, targetRow, targetCol)\n : getBatchedCellNumericValue(workerRowBatch?.cells.get(`${targetRow}:${targetCol}`))\n );\n const sparklineValues = sparkline\n ? (\n sparkline.range.start.row === sparkline.range.end.row\n ? Array.from(\n { length: Math.abs(sparkline.range.end.col - sparkline.range.start.col) + 1 },\n (_, index) => getNumericValue(\n sparkline.range.start.row,\n Math.min(sparkline.range.start.col, sparkline.range.end.col) + index\n )\n )\n : Array.from(\n { length: Math.abs(sparkline.range.end.row - sparkline.range.start.row) + 1 },\n (_, index) => getNumericValue(\n Math.min(sparkline.range.start.row, sparkline.range.end.row) + index,\n sparkline.range.start.col\n )\n )\n )\n : null;\n const checkboxState = finalResolvedStyle?.cellControl\n ? worksheet\n ? getCellBooleanValue(worksheet, row, col)\n : getBatchedCellBooleanValue(batchedCell)\n : null;\n const nextData: CellRenderData = {\n canvas: undefined,\n checkboxState,\n colSpan: merge?.colSpan,\n conditionalColorScale: worksheet\n ? resolveConditionalColorScaleForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache)\n : null,\n conditionalDataBar: worksheet\n ? resolveConditionalDataBarForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache)\n : null,\n conditionalIcon: worksheet\n ? resolveConditionalIconForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache)\n : null,\n isMergedSecondary: false,\n rowSpan: merge?.rowSpan,\n sparkline: sparkline && sparklineValues ? { config: sparkline, values: sparklineValues } : null,\n style: buildCellStyle(finalResolvedStyle, palette, sheet.themePalette, {\n generalAlignment: worksheet ? getCellGeneralAlignment(worksheet, row, col) : batchedCell?.generalAlignment,\n showGridLines\n }),\n textRotationDeg: resolveSpreadsheetTextRotation(alignment?.rotation ?? alignment?.textRotation),\n value: sparkline\n ? \"\"\n : checkboxState !== null\n ? \"\"\n : worksheet\n ? getCellDisplayValue(worksheet, row, col, sheet, locale)\n : batchedCell?.value ?? \"\"\n };\n\n nextData.canvas = buildCanvasCellStyleCache(nextData.style);\n cellRenderCache.set(cacheKey, nextData);\n return nextData;\n };\n\n for (const rowItem of rowAxis.items) {\n for (const colItem of colAxis.items) {\n const cellData = getCellData(rowItem.actualIndex, colItem.actualIndex);\n if (cellData.isMergedSecondary) {\n continue;\n }\n\n const colSpan = Math.max(1, cellData.colSpan ?? 1);\n const rowSpan = Math.max(1, cellData.rowSpan ?? 1);\n let width = colItem.size;\n let height = rowItem.size;\n for (let colOffset = 1; colOffset < colSpan; colOffset += 1) {\n const nextCol = colItemByActual.get(colItem.actualIndex + colOffset);\n width += nextCol?.size ?? 0;\n }\n for (let rowOffset = 1; rowOffset < rowSpan; rowOffset += 1) {\n const nextRow = rowItemByActual.get(rowItem.actualIndex + rowOffset);\n height += nextRow?.size ?? 0;\n }\n\n const rect = {\n height,\n left: rowHeaderWidth + colItem.start,\n top: headerHeight + rowItem.start,\n width\n };\n const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellData.style);\n const gradientFill = typeof cellData.style.backgroundImage === \"string\"\n ? resolveCanvasGradientFill(context, rect, cellData.style.backgroundImage)\n : null;\n const fillColor = cellData.conditionalColorScale?.color\n ?? (typeof cellData.style.backgroundColor === \"string\" ? cellData.style.backgroundColor : thumbnailSheetSurface);\n context.fillStyle = gradientFill ?? fillColor;\n context.fillRect(rect.left, rect.top, rect.width, rect.height);\n\n if (cellData.conditionalDataBar) {\n const barLeft = rect.left + 4;\n const barTop = rect.top + 4;\n const barWidth = Math.max(0, (rect.width - 8) * (cellData.conditionalDataBar.widthPercent / 100));\n const barHeight = Math.max(0, rect.height - 8);\n if (barWidth > 0 && barHeight > 0) {\n context.fillStyle = resolveCanvasDataBarFill(\n context,\n barLeft,\n barTop,\n barWidth,\n barHeight,\n cellData.conditionalDataBar\n );\n context.fillRect(barLeft, barTop, barWidth, barHeight);\n if (cellData.conditionalDataBar.border !== false && cellData.conditionalDataBar.borderColor) {\n context.strokeStyle = cellData.conditionalDataBar.borderColor;\n context.lineWidth = 1;\n context.strokeRect(barLeft + 0.5, barTop + 0.5, Math.max(0, barWidth - 1), Math.max(0, barHeight - 1));\n }\n }\n }\n\n if (showGridLines) {\n context.strokeStyle = palette.border;\n context.lineWidth = 1;\n context.beginPath();\n if (colItem.start === 0) {\n context.moveTo(rect.left + 0.5, rect.top);\n context.lineTo(rect.left + 0.5, rect.top + rect.height);\n }\n if (rowItem.start === 0) {\n context.moveTo(rect.left, rect.top + 0.5);\n context.lineTo(rect.left + rect.width, rect.top + 0.5);\n }\n context.moveTo(rect.left + rect.width - 0.5, rect.top);\n context.lineTo(rect.left + rect.width - 0.5, rect.top + rect.height);\n context.moveTo(rect.left, rect.top + rect.height - 0.5);\n context.lineTo(rect.left + rect.width, rect.top + rect.height - 0.5);\n context.stroke();\n }\n\n if (canvasCellStyle.topBorder) {\n strokeCanvasBorderSide(context, \"top\", rect, canvasCellStyle.topBorder);\n }\n if (canvasCellStyle.rightBorder) {\n strokeCanvasBorderSide(context, \"right\", rect, canvasCellStyle.rightBorder);\n }\n if (canvasCellStyle.bottomBorder) {\n strokeCanvasBorderSide(context, \"bottom\", rect, canvasCellStyle.bottomBorder);\n }\n if (canvasCellStyle.leftBorder) {\n strokeCanvasBorderSide(context, \"left\", rect, canvasCellStyle.leftBorder);\n }\n\n const rawText = cellData.value ?? \"\";\n const shouldDrawThumbnailContent = (\n cellData.checkboxState != null\n || cellData.sparkline\n || rawText.length > 0\n || cellData.conditionalIcon\n );\n if (!shouldDrawThumbnailContent) {\n continue;\n }\n\n const padding = canvasCellStyle.padding;\n const contentLeft = rect.left + padding.left;\n const contentTop = rect.top + padding.top;\n const contentWidth = Math.max(0, rect.width - padding.left - padding.right);\n const contentHeight = Math.max(0, rect.height - padding.top - padding.bottom);\n if (contentWidth <= 0 || contentHeight <= 0) {\n continue;\n }\n\n context.save();\n context.beginPath();\n context.rect(contentLeft, contentTop, contentWidth, contentHeight);\n context.clip();\n context.font = canvasCellStyle.baseFont;\n context.fillStyle = canvasCellStyle.textColor;\n context.textBaseline = \"middle\";\n\n if (cellData.checkboxState != null) {\n const boxSize = Math.min(14, contentWidth, contentHeight);\n const boxLeft = rect.left + (rect.width - boxSize) / 2;\n const boxTop = rect.top + (rect.height - boxSize) / 2;\n context.strokeStyle = paletteIsDark(palette) ? \"#cbd5e1\" : \"#475569\";\n context.lineWidth = 1.25;\n context.strokeRect(boxLeft, boxTop, boxSize, boxSize);\n if (cellData.checkboxState) {\n context.fillStyle = paletteIsDark(palette) ? \"#60a5fa\" : \"#2563eb\";\n context.fillRect(boxLeft + 1.5, boxTop + 1.5, Math.max(0, boxSize - 3), Math.max(0, boxSize - 3));\n }\n } else if (cellData.sparkline) {\n const sparkline = cellData.sparkline.config;\n const sparklineValues = cellData.sparkline.values;\n const points = sparklineValues\n .map((value, index) => ({ index, value }))\n .filter((entry): entry is { index: number; value: number } => typeof entry.value === \"number\" && Number.isFinite(entry.value));\n if (points.length > 1) {\n const minValue = Math.min(...points.map((entry) => entry.value));\n const maxValue = Math.max(...points.map((entry) => entry.value));\n const sparkLeft = contentLeft + 1;\n const sparkTop = contentTop + 2;\n const sparkWidth = Math.max(1, contentWidth - 2);\n const sparkHeight = Math.max(1, contentHeight - 4);\n const xStep = points.length > 1 ? sparkWidth / (points.length - 1) : 0;\n context.strokeStyle = sparkline.color ?? \"#2563eb\";\n context.lineCap = \"round\";\n context.lineJoin = \"round\";\n context.lineWidth = 1.25;\n context.beginPath();\n points.forEach((entry, index) => {\n const x = sparkLeft + index * xStep;\n const y = sparkTop + sparkHeight - clampSparklineValue(entry.value, minValue, maxValue) * sparkHeight;\n if (index === 0) {\n context.moveTo(x, y);\n } else {\n context.lineTo(x, y);\n }\n });\n context.stroke();\n }\n } else if (rawText.length > 0) {\n const align = canvasCellStyle.textAlign;\n context.textAlign = align;\n const leadingInset = cellData.conditionalIcon ? 18 : 0;\n const textLeft = contentLeft + leadingInset;\n const maxTextWidth = Math.max(0, contentWidth - leadingInset);\n const textX = align === \"right\"\n ? textLeft + maxTextWidth\n : align === \"center\"\n ? textLeft + maxTextWidth / 2\n : textLeft;\n const singleLineHeight = resolveCanvasLineHeight(cellData.style, 12);\n\n if (cellData.textRotationDeg) {\n drawRotatedCellText(context, rawText, cellData.textRotationDeg, {\n left: textLeft, top: contentTop, width: maxTextWidth, height: contentHeight\n }, align, cellData.style.verticalAlign, singleLineHeight);\n } else if (canvasCellStyle.usesWrappedText || rawText.includes(\"\\n\")) {\n const lines = wrapCanvasText(context, rawText, maxTextWidth);\n const lineHeight = resolveCanvasLineHeight(cellData.style, 12);\n const textBlockHeight = lines.length * lineHeight;\n let textBlockTop = contentTop;\n if (cellData.style.verticalAlign === \"middle\") {\n textBlockTop = contentTop + ((contentHeight - textBlockHeight) / 2);\n } else if (cellData.style.verticalAlign !== \"top\") {\n textBlockTop = contentTop + contentHeight - textBlockHeight;\n }\n lines.forEach((line, lineIndex) => {\n context.fillText(line, textX, textBlockTop + (lineIndex * lineHeight) + (lineHeight / 2));\n });\n } else {\n const text = canvasCellStyle.textOverflowEllipsis\n ? truncateCanvasText(context, rawText, maxTextWidth)\n : rawText;\n context.fillText(text, textX, resolveCanvasTextMiddleY(cellData.style.verticalAlign, contentTop, contentHeight, singleLineHeight));\n }\n }\n\n if (cellData.conditionalIcon) {\n const iconSize = 10;\n const iconX = rect.left + padding.left;\n const iconY = rect.top + (rect.height / 2);\n drawCanvasConditionalIcon(context, cellData.conditionalIcon, iconX + (iconSize / 2), iconY, iconSize);\n }\n\n context.restore();\n }\n }\n\n if (thumbnailDrawingEntries.length > 0) {\n context.save();\n context.beginPath();\n context.rect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));\n context.clip();\n for (const entry of thumbnailDrawingEntries) {\n if (entry.kind === \"shape\") {\n drawStaticShape(context, entry.shape, entry.rect, 1);\n } else if (entry.kind === \"formControl\") {\n drawStaticFormControl(context, entry.control, entry.rect, palette, 1, thumbnailSheetSurface);\n } else {\n const imageElement = getThumbnailImage(entry.image);\n if (imageElement) {\n context.drawImage(imageElement, entry.rect.left, entry.rect.top, entry.rect.width, entry.rect.height);\n }\n }\n }\n context.restore();\n }\n\n if (includeHeaders) {\n context.strokeStyle = palette.border;\n context.lineWidth = 1;\n context.font = \"600 11px ui-sans-serif, system-ui, sans-serif\";\n context.fillStyle = palette.headerText;\n context.textBaseline = \"middle\";\n\n for (const colItem of colAxis.items) {\n const left = rowHeaderWidth + colItem.start;\n context.beginPath();\n context.moveTo(left + colItem.size - 0.5, 0);\n context.lineTo(left + colItem.size - 0.5, headerHeight);\n context.moveTo(left, headerHeight - 0.5);\n context.lineTo(left + colItem.size, headerHeight - 0.5);\n context.stroke();\n context.textAlign = \"center\";\n context.fillText(columnLabel(colItem.actualIndex), left + (colItem.size / 2), headerHeight / 2);\n }\n\n for (const rowItem of rowAxis.items) {\n const top = headerHeight + rowItem.start;\n context.beginPath();\n context.moveTo(0, top + rowItem.size - 0.5);\n context.lineTo(rowHeaderWidth, top + rowItem.size - 0.5);\n context.moveTo(rowHeaderWidth - 0.5, top);\n context.lineTo(rowHeaderWidth - 0.5, top + rowItem.size);\n context.stroke();\n context.textAlign = \"center\";\n context.fillText(`${rowItem.actualIndex + 1}`, rowHeaderWidth / 2, top + (rowItem.size / 2));\n }\n\n context.beginPath();\n context.moveTo(rowHeaderWidth - 0.5, 0);\n context.lineTo(rowHeaderWidth - 0.5, headerHeight);\n context.moveTo(0, headerHeight - 0.5);\n context.lineTo(rowHeaderWidth, headerHeight - 0.5);\n context.stroke();\n }\n\n return true;\n };\n\n const plan: SheetThumbnailRenderPlan = {\n aspectRatio: sourceWidth / Math.max(1, sourceHeight),\n contentHeight: rowAxis.totalSize,\n contentWidth: colAxis.totalSize,\n height: outputSize.height,\n paint,\n sourceRange,\n width: outputSize.width\n };\n\n return {\n ...plan,\n sheet,\n sheetIndex,\n workbookSheetIndex: sheet.workbookSheetIndex\n };\n });\n }, [\n getSheetFormControls,\n getSheetImages,\n getSheetShapes,\n getWorkerThumbnailRowBatch,\n getThumbnailImage,\n includeHeaders,\n palette,\n resolution,\n sheets,\n thumbnailImageLoadVersion,\n workerThumbnailRowBatchVersion,\n locale,\n workbook\n ]);\n\n const paintThumbnail = React.useCallback(\n (sheetIndex: number, canvas: HTMLCanvasElement | null) => thumbnails[sheetIndex]?.paint(canvas) ?? false,\n [thumbnails]\n );\n\n return React.useMemo(\n () => ({\n paintThumbnail,\n thumbnails\n }),\n [paintThumbnail, thumbnails]\n );\n}\n\nexport function XlsxViewer(props: XlsxViewerProps) {\n const contextController = React.useContext(ViewerContext);\n\n if (props.controller) {\n return <XlsxViewerInner {...props} controller={props.controller} />;\n }\n\n if (contextController) {\n return <XlsxViewerInner {...props} controller={contextController} />;\n }\n\n return <XlsxViewerWithInlineController {...props} />;\n}\n\nexport function DefaultXlsxToolbar() {\n const controller = useXlsxViewer();\n const { headerBackgroundColor, headerTextColor, isDark } = React.useContext(ViewerAppearanceContext);\n const palette = useViewerPalette(isDark, headerBackgroundColor, headerTextColor);\n return <DefaultToolbar controller={controller} palette={palette} />;\n}\n","import * as React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { XlsxViewer as InternalViewer } from \"./XlsxViewer\";\nimport { useXlsxViewerController } from \"./controller\";\nimport type { XlsxViewerController, XlsxViewerProps } from \"./types\";\nimport type { XlsxVanillaContent, XlsxVanillaViewer, XlsxVanillaViewerOptions } from \"./vanilla-types\";\n\nexport { XlsxFileSizeLimitExceededError } from \"./controller\";\nexport { parseXlsxRange } from \"./range-reference\";\nexport { initWasm, setWasmSource, type XlsxWasmSource } from \"./wasm\";\nexport type * from \"./vanilla-types\";\n\nfunction DomContent({ node }: { node: Node }) {\n const hostRef = React.useRef<HTMLSpanElement>(null);\n\n React.useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n\n host.replaceChildren(node);\n return () => {\n if (node.parentNode === host) host.removeChild(node);\n };\n }, [node]);\n\n return <span ref={hostRef} style={{ display: \"contents\" }} />;\n}\n\nfunction toReactContent(content: XlsxVanillaContent | undefined): React.ReactNode {\n return content !== null && typeof content === \"object\" ? <DomContent node={content} /> : content;\n}\n\nfunction toReactOptions(options: XlsxVanillaViewerOptions): XlsxViewerProps {\n const {\n emptyState,\n errorState,\n fileTooLargeState,\n loadingState,\n renderActiveCellOverlay,\n renderChartLoading,\n renderContextMenu,\n renderFormControl,\n renderImage,\n toolbar,\n ...viewerOptions\n } = options;\n\n return {\n ...viewerOptions,\n emptyState: toReactContent(emptyState),\n errorState: typeof errorState === \"function\"\n ? (error) => toReactContent(errorState(error))\n : toReactContent(errorState),\n fileTooLargeState: typeof fileTooLargeState === \"function\"\n ? ({ defaultNode: _defaultNode, ...props }) => toReactContent(fileTooLargeState(props))\n : toReactContent(fileTooLargeState),\n loadingState: toReactContent(loadingState),\n renderActiveCellOverlay: renderActiveCellOverlay\n ? (props) => toReactContent(renderActiveCellOverlay(props))\n : undefined,\n renderChartLoading: renderChartLoading\n ? ({ defaultNode: _defaultNode, ...props }) => toReactContent(renderChartLoading(props))\n : undefined,\n renderContextMenu: renderContextMenu\n ? ({ defaultNode: _defaultNode, ...props }) => toReactContent(renderContextMenu(props))\n : undefined,\n renderFormControl: renderFormControl\n ? (props) => toReactContent(renderFormControl({ ...props, stopPropagation: (event) => event.stopPropagation() }))\n : undefined,\n renderImage: renderImage\n ? ({ defaultNode: _defaultNode, ...props }) => toReactContent(renderImage(props))\n : undefined,\n toolbar: typeof toolbar === \"function\"\n ? (controller) => toReactContent(toolbar(controller))\n : toReactContent(toolbar)\n };\n}\n\nfunction VanillaViewerRoot({\n generation,\n options,\n publish\n}: {\n generation: number;\n options: XlsxVanillaViewerOptions;\n publish: (controller: XlsxViewerController, generation: number) => void;\n}) {\n const controller = useXlsxViewerController(options);\n const viewerOptions = React.useMemo(() => toReactOptions(options), [options]);\n\n React.useLayoutEffect(() => publish(controller, generation), [controller, generation, publish]);\n\n return <InternalViewer {...viewerOptions} controller={controller} />;\n}\n\nfunction mergeOptions(\n current: XlsxVanillaViewerOptions,\n next: Partial<XlsxVanillaViewerOptions>\n): XlsxVanillaViewerOptions {\n if (next.file !== undefined && next.src !== undefined) {\n throw new TypeError(\"Provide either file or src, not both.\");\n }\n\n const merged = { ...current, ...next };\n if (next.file !== undefined) merged.src = undefined;\n if (next.src !== undefined) merged.file = undefined;\n return merged;\n}\n\nfunction createViewerElement(container: Element | DocumentFragment) {\n const owner = container.ownerDocument;\n const element = owner.createElement(\"div\");\n element.style.display = \"contents\";\n container.appendChild(element);\n return element;\n}\n\nfunction createViewer(\n container: Element | DocumentFragment,\n initialOptions: XlsxVanillaViewerOptions = {}\n): XlsxVanillaViewer {\n let options = mergeOptions({}, initialOptions);\n const element = createViewerElement(container);\n const root = createRoot(element);\n const listeners = new Set<(controller: XlsxViewerController) => void>();\n let controller: XlsxViewerController | null = null;\n let destroyed = false;\n let notificationPending = false;\n let generation = 0;\n\n const publish = (next: XlsxViewerController, publishedGeneration: number) => {\n if (destroyed || publishedGeneration !== generation) return;\n controller = next;\n if (notificationPending) return;\n notificationPending = true;\n queueMicrotask(() => {\n notificationPending = false;\n if (destroyed || !controller) return;\n const current = controller;\n for (const listener of listeners) listener(current);\n });\n };\n\n const render = () => root.render(\n <VanillaViewerRoot key={generation} generation={generation} options={options} publish={publish} />\n );\n const assertActive = () => {\n if (destroyed) throw new Error(\"The viewer has been destroyed.\");\n };\n\n render();\n\n return {\n element,\n getController: () => controller,\n reload() {\n assertActive();\n generation += 1;\n controller = null;\n render();\n },\n setOptions(next) {\n assertActive();\n const updated = mergeOptions(options, next);\n if (updated.file !== options.file || updated.src !== options.src) {\n generation += 1;\n controller = null;\n }\n options = updated;\n render();\n },\n subscribe(listener) {\n assertActive();\n listeners.add(listener);\n if (controller) listener(controller);\n return () => listeners.delete(listener);\n },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n listeners.clear();\n controller = null;\n root.unmount();\n element.remove();\n }\n };\n}\n\nexport class XlsxViewer implements XlsxVanillaViewer {\n readonly element: HTMLElement;\n private readonly viewer: XlsxVanillaViewer;\n\n constructor(container: Element | DocumentFragment, options: XlsxVanillaViewerOptions = {}) {\n this.viewer = createViewer(container, options);\n this.element = this.viewer.element;\n }\n\n getController() { return this.viewer.getController(); }\n setOptions(options: Partial<XlsxVanillaViewerOptions>) { this.viewer.setOptions(options); }\n reload() { this.viewer.reload(); }\n subscribe(listener: (controller: XlsxViewerController) => void) { return this.viewer.subscribe(listener); }\n destroy() { this.viewer.destroy(); }\n}\n\nexport function createXlsxViewer(\n container: Element | DocumentFragment,\n options: XlsxVanillaViewerOptions = {}\n): XlsxViewer {\n return new XlsxViewer(container, options);\n}\n\nexport function __createControlledViewer(container: Element | DocumentFragment) {\n const element = createViewerElement(container);\n const root = createRoot(element);\n return {\n update(props: unknown) { root.render(<InternalViewer {...props as XlsxViewerProps} />); },\n destroy() { root.unmount(); element.remove(); }\n };\n}\n\nexport function __createContentNode(node: Node): unknown {\n return toReactContent(node);\n}\n\nexport function __createContentRoot(container: Element) {\n const root = createRoot(container);\n return {\n update(content: unknown) { root.render(content as React.ReactNode); },\n destroy() { root.unmount(); }\n };\n}\n"]}
|