@a3s-lab/office 0.11.0 → 0.12.0

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/README.md CHANGED
@@ -578,11 +578,32 @@ import {
578
578
  importOfficeFile,
579
579
  } from '@a3s-lab/office/core';
580
580
 
581
- const imported = await importOfficeFile(file);
581
+ const controller = new AbortController();
582
+ const imported = await importOfficeFile(file, {
583
+ signal: controller.signal,
584
+ onProgress: ({ stage, progress }) => {
585
+ console.info(stage, `${Math.round(progress * 100)}%`);
586
+ },
587
+ });
582
588
  const output = await createArtifactBlob(imported);
583
589
  const blankDeck = createArtifact('blank-presentation');
584
590
  ```
585
591
 
592
+ Import progress advances monotonically through `reading`, `parsing`,
593
+ `analyzing`, and `finalizing`. Calling `controller.abort()` rejects with an
594
+ `AbortError`; large reads and parser checkpoints yield so a host can keep the
595
+ progress and Cancel controls responsive.
596
+
597
+ Spreadsheet import and export preserve sparse worksheets up to the XLSX limit
598
+ of 1,048,576 rows by 16,384 columns. The logical `data.length`, `row`, and
599
+ `column` dimensions can be large while only populated indexes are materialized.
600
+ Virtual scrolling never emits `onChange`, and editing a far blank row creates
601
+ only that row. Data-validation regions remain compact in
602
+ `dataValidationRanges`; direct `dataVerification` entries take precedence.
603
+ Protection ranges, passwordless editable ranges, and conditional formatting
604
+ also remain compact and round-trip through native XLSX records without
605
+ allocating every covered cell.
606
+
586
607
  Use `downloadArtifact` to start a browser download or
587
608
  `createArtifactBlob` when your application owns upload and persistence.
588
609
  Imported DOCX artifacts are source-backed: safe source-only OPC parts,
@@ -1338,6 +1359,7 @@ without hard-coded return URLs.
1338
1359
 
1339
1360
  - [Live Playground](https://a3s-lab.github.io/Office/)
1340
1361
  - [Documentation center](https://a3s-lab.github.io/Office/docs/)
1362
+ - [A3S Office 0.12.0 documentation](https://a3s-lab.github.io/Office/docs/0.12.0/)
1341
1363
  - [A3S Office 0.11.0 documentation](https://a3s-lab.github.io/Office/docs/0.11.0/)
1342
1364
  - [A3S Office 0.10.0 documentation](https://a3s-lab.github.io/Office/docs/0.10.0/)
1343
1365
  - [A3S Office 0.9.2 documentation](https://a3s-lab.github.io/Office/docs/0.9.2/)
@@ -4,12 +4,12 @@ import { Workbook } from "@fortune-sheet/react";
4
4
  import { AlignCenter, AlignLeft, AlignRight, AlignVerticalJustifyCenter, AlignVerticalJustifyEnd, AlignVerticalJustifyStart, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, BarChart3, BetweenHorizontalEnd, BetweenHorizontalStart, BetweenVerticalEnd, BetweenVerticalStart, Bold, Bookmark, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, ClipboardPaste, Cloud, Columns3, Copy, CopyPlus, DecimalsArrowLeft, DecimalsArrowRight, Eraser, Eye, EyeOff, FileX2, Grid3X3, Italic, KeyRound, Layers2, Link2Off, ListFilter, Merge, MessageSquareX, MoveHorizontal, MoveVertical, Paintbrush, Palette, PanelLeft, PanelTop, PanelsTopLeft, Pencil, Percent, Plus, Printer, Redo2, RefreshCw, Rows3, Save, Scissors, Search, ShieldCheck, SortAsc, SortDesc, TableProperties, Trash2, Underline, Undo2, WrapText, X } from "lucide-react";
5
5
  import { Fragment as external_react_Fragment, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
6
6
  import { genarate, indexToColumnChar, update as core_update } from "@fortune-sheet/core";
7
- import { withoutEditableRange, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, editableRangeRequiresCredentials, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, spreadsheetConditionalThresholdValue, normalizeSpreadsheetConditionalVisualOptions, withSheetProtection, spreadsheetProtectionKey, defaultSpreadsheetColorScaleThresholds, effectiveSpreadsheetPageSetup, protectedSheetCount, SPREADSHEET_CONDITIONAL_ICON_SETS, withSheetSelectionPermissions, withEditableRange, spreadsheetConditionalComparisonNeedsUpperValue, createOfficeSpreadsheetCollaborationBinding as createWorkOfficeSpreadsheetCollaborationBinding, isSpreadsheetConditionalComparisonOperator, drawSpreadsheetConditionalIcon, spreadsheetConditionalIconForValue, defaultSpreadsheetConditionalIconThresholds, sheetProtectionAuthority, readOfficeSpreadsheetCollaboration as readWorkOfficeSpreadsheetCollaboration, spreadsheetConditionalThresholdsEqual, editableRangeCellCount, unlockedCellCount } from "./4476.js";
7
+ import { withoutEditableRange, readOfficeSpreadsheetCollaboration as readWorkOfficeSpreadsheetCollaboration, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, editableRangeRequiresCredentials, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, spreadsheetConditionalThresholdValue, normalizeSpreadsheetConditionalVisualOptions, withSheetProtection, spreadsheetProtectionKey, defaultSpreadsheetColorScaleThresholds, effectiveSpreadsheetPageSetup, protectedSheetCount, SPREADSHEET_CONDITIONAL_ICON_SETS, withSheetSelectionPermissions, withEditableRange, spreadsheetConditionalComparisonNeedsUpperValue, createOfficeSpreadsheetCollaborationBinding as createWorkOfficeSpreadsheetCollaborationBinding, isSpreadsheetConditionalComparisonOperator, drawSpreadsheetConditionalIcon, spreadsheetConditionalIconForValue, defaultSpreadsheetConditionalIconThresholds, sheetProtectionAuthority, replaceOfficeSpreadsheetCollaboration as replaceWorkOfficeSpreadsheetCollaboration, spreadsheetConditionalThresholdsEqual, editableRangeCellCount, unlockedCellCount } from "./4476.js";
8
+ import { workSpreadsheetChartSupportsGrouping, createSpreadsheetChartFromSelection, normalizeWorkSpreadsheetDataLabelPosition, normalizeSpreadsheetPrintTitleColumns, normalizeSpreadsheetPrintTitleRows, formulaHasStructuredReference, normalizeWorkSpreadsheetErrorBars, defaultPivotValueCaption, workSpreadsheetChartSupportsBarSpacing, normalizeWorkSpreadsheetChartGrouping, normalizeWorkSpreadsheetChartAxisGroup, normalizeWorkSpreadsheetScatterStyle, spreadsheetPivotAggregationLabel, workSpreadsheetChartAxisIsCategoryAxis, reconcileSpreadsheetChartPreviews, normalizeWorkSpreadsheetChartAxes, normalizeWorkSpreadsheetDoughnutHoleSize, normalizeWorkSpreadsheetChartLegendPosition, spreadsheetChartCount, workSpreadsheetChartTypeLabel, sparseArrayEntries, normalizeWorkSpreadsheetTrendline, spreadsheetPivotOutputContains, effectiveSpreadsheetCalculationSettings, normalizeWorkSpreadsheetDataLabels, spreadsheetPivotIntersects, normalizeSpreadsheetPrintArea, spreadsheetCellAddress, formatSpreadsheetCellRanges, normalizeWorkSpreadsheetChartLayout, spreadsheetFormulaRangeForCell, workSpreadsheetChartAxisIsValueAxis, normalizeWorkSpreadsheetChartSmoothLines, spreadsheetPivotFilterValueKey, normalizeWorkSpreadsheetBubbleScale, normalizeWorkSpreadsheetChartSeriesStyle, volatileSpreadsheetFormulaFunctions, sparseArrayIndexes, normalizeWorkSpreadsheetBubbleSizeRepresents, workSpreadsheetChartSupportsTrendlines, normalizeWorkSpreadsheetChartLegendOverlay, parseSpreadsheetChartReference, workSpreadsheetChartGroupingIsStacked, normalizeWorkSpreadsheetChartGapWidth, workSpreadsheetChartSupportsSmoothLines, refreshSpreadsheetPivotTables, createSpreadsheetPivotFromSelection, spreadsheetPivotCount, reconcileSpreadsheetPivots, sparseMatrixColumnCount, workSpreadsheetChartSupportsErrorBars, formulaHasExternalReference, isValidSpreadsheetDefinedName, spreadsheetPivotValidation, deleteSpreadsheetPivotTable, workSpreadsheetChartAxisDefaultLabelPosition, workSpreadsheetChartSupportsSeriesAnalysis, parseSpreadsheetCellRanges, spreadsheetPivotFields, spreadsheetPivotFilterItems, normalizeWorkSpreadsheetChartOverlap, spreadsheetFormulaRangeConflict, spreadsheetSheetsWithChartPreviews, workSpreadsheetChartUsesNumericXAxis, workSpreadsheetChartAxisShowsMajorGridlinesByDefault, workSpreadsheetChartSupportsAxes, normalizeWorkSpreadsheetCombinationSeriesType, normalizeWorkSpreadsheetRadarStyle } from "./8715.js";
8
9
  import { useOfficeTaskPaneEscape, OfficeCheckbox, CommittedOfficeNumberField, useOfficeDialog, useOfficeTaskPaneModal, createOfficeKernelClient, officeFontFamilies, CollectionState, OfficeNumberField, handleOfficeTaskPaneKeyDown } from "./0~7048.js";
9
10
  import { isOfficeShortcutBlocked, OfficeColorPicker } from "./0~3635.js";
10
11
  import { createOfficeEditorExtension, useOfficeEditorRuntime, useOfficeEditorKeyboardShortcuts } from "./0~3557.js";
11
12
  import { showToast } from "./6489.js";
12
- import { workSpreadsheetChartSupportsGrouping, createSpreadsheetChartFromSelection, normalizeWorkSpreadsheetDataLabelPosition, normalizeSpreadsheetPrintTitleRows, normalizeSpreadsheetPrintTitleColumns, formulaHasStructuredReference, normalizeWorkSpreadsheetErrorBars, defaultPivotValueCaption, workSpreadsheetChartSupportsBarSpacing, normalizeWorkSpreadsheetChartGrouping, normalizeWorkSpreadsheetChartAxisGroup, normalizeWorkSpreadsheetScatterStyle, spreadsheetPivotAggregationLabel, workSpreadsheetChartAxisIsCategoryAxis, reconcileSpreadsheetChartPreviews, normalizeWorkSpreadsheetChartAxes, normalizeWorkSpreadsheetDoughnutHoleSize, normalizeWorkSpreadsheetChartLegendPosition, spreadsheetChartCount, workSpreadsheetChartTypeLabel, normalizeWorkSpreadsheetTrendline, spreadsheetPivotOutputContains, effectiveSpreadsheetCalculationSettings, normalizeWorkSpreadsheetDataLabels, spreadsheetPivotIntersects, normalizeSpreadsheetPrintArea, spreadsheetCellAddress, formatSpreadsheetCellRanges, normalizeWorkSpreadsheetChartLayout, spreadsheetFormulaRangeForCell, workSpreadsheetChartAxisIsValueAxis, normalizeWorkSpreadsheetChartSmoothLines, spreadsheetPivotFilterValueKey, normalizeWorkSpreadsheetBubbleScale, normalizeWorkSpreadsheetChartSeriesStyle, volatileSpreadsheetFormulaFunctions, normalizeWorkSpreadsheetBubbleSizeRepresents, workSpreadsheetChartSupportsTrendlines, normalizeWorkSpreadsheetChartLegendOverlay, parseSpreadsheetChartReference, workSpreadsheetChartGroupingIsStacked, normalizeWorkSpreadsheetChartGapWidth, workSpreadsheetChartSupportsSmoothLines, refreshSpreadsheetPivotTables, createSpreadsheetPivotFromSelection, spreadsheetPivotCount, reconcileSpreadsheetPivots, workSpreadsheetChartSupportsErrorBars, formulaHasExternalReference, isValidSpreadsheetDefinedName, spreadsheetPivotValidation, deleteSpreadsheetPivotTable, workSpreadsheetChartAxisDefaultLabelPosition, workSpreadsheetChartSupportsSeriesAnalysis, parseSpreadsheetCellRanges, spreadsheetPivotFields, spreadsheetPivotFilterItems, normalizeWorkSpreadsheetChartOverlap, spreadsheetFormulaRangeConflict, spreadsheetSheetsWithChartPreviews, workSpreadsheetChartUsesNumericXAxis, workSpreadsheetChartAxisShowsMajorGridlinesByDefault, workSpreadsheetChartSupportsAxes, normalizeWorkSpreadsheetCombinationSeriesType, normalizeWorkSpreadsheetRadarStyle } from "./8715.js";
13
13
  import { unsupportedSpreadsheetFormulaFunctions } from "./0~work-spreadsheet-formula-support.js";
14
14
  import { WorkspaceContextMenu, isWorkspaceContextMenuKeyboardEvent, WorkOfficeZoomControls, WorkOfficeStatusBar, WorkOfficeRibbon, workspaceContextMenuPosition, OfficeSelect, WorkOfficeRibbonGroup, WorkOfficePreviewBar, WorkOfficeRibbonButton } from "./0~4595.js";
15
15
  import { moveOfficeMenuFocus, stepOfficeZoom, Popover, useOfficeRemoteParticipants, button_Button, officePresenceColor, useOfficePublishPresenceLocation, icon_button_IconButton, office_text_field_OfficeTextField, useDialogFocusScope, useOfficeEditorWheelZoom } from "./0~6090.js";
@@ -440,18 +440,10 @@ function cellKey(row, column) {
440
440
  return `${row}_${column}`;
441
441
  }
442
442
  function forEachCell(data, ranges, visit) {
443
- const maximumRow = data.length - 1;
444
- const maximumColumn = Math.max(0, ...data.map((cells)=>cells.length - 1));
445
- for (const range of ranges){
446
- const startRow = Math.max(0, Math.min(maximumRow, range.row[0]));
447
- const endRow = Math.max(0, Math.min(maximumRow, range.row[1]));
448
- const startColumn = Math.max(0, Math.min(maximumColumn, range.column[0]));
449
- const endColumn = Math.max(0, Math.min(maximumColumn, range.column[1]));
450
- for(let row = startRow; row <= endRow; row += 1)for(let column = startColumn; column <= endColumn; column += 1)visit({
451
- row,
452
- column
453
- });
454
- }
443
+ for (const [row, cells] of sparseArrayEntries(data))for (const [column] of sparseArrayEntries(cells))if (ranges.some((range)=>row >= range.row[0] && row <= range.row[1] && column >= range.column[0] && column <= range.column[1])) visit({
444
+ row,
445
+ column
446
+ });
455
447
  }
456
448
  function cellValue(data, row, column) {
457
449
  return data[row]?.[column]?.v ?? null;
@@ -547,11 +539,12 @@ function spreadsheetSelectionSummary(sheet, selection) {
547
539
  const rowEnd = Math.min(range.row[1] ?? rowStart, sheet.data.length - 1);
548
540
  const columnStart = range.column[0] ?? 0;
549
541
  const columnEnd = range.column[1] ?? columnStart;
550
- for(let rowIndex = rowStart; rowIndex <= rowEnd; rowIndex += 1){
542
+ for (const rowIndex of sparseArrayIndexes(sheet.data)){
543
+ if (rowIndex < rowStart || rowIndex > rowEnd) continue;
551
544
  const row = sheet.data[rowIndex];
552
- if (!row) continue;
553
- const populatedColumnEnd = Math.min(columnEnd, row.length - 1);
554
- for(let columnIndex = columnStart; columnIndex <= populatedColumnEnd; columnIndex += 1)collect(row[columnIndex]);
545
+ if (row) {
546
+ for (const columnIndex of sparseArrayIndexes(row))if (!(columnIndex < columnStart) && !(columnIndex > columnEnd)) collect(row[columnIndex]);
547
+ }
555
548
  }
556
549
  } else {
557
550
  const rowStart = range.row[0] ?? 0;
@@ -607,28 +600,66 @@ function spreadsheetSheetsWithFiniteSelections(sheets) {
607
600
  }));
608
601
  }
609
602
  function spreadsheetSheetsForFortune(sheets) {
610
- return structuredClone(sheets).map((sheet)=>{
611
- for (const merge of Object.values(sheet.config?.merge ?? {}))for(let rowIndex = merge.r; rowIndex < merge.r + merge.rs; rowIndex += 1){
612
- const row = sheet.data?.[rowIndex];
613
- if (row) for(let columnIndex = merge.c; columnIndex < merge.c + merge.cs; columnIndex += 1)row[columnIndex] = {
614
- ...row[columnIndex] ?? {},
615
- mc: rowIndex === merge.r && columnIndex === merge.c ? {
616
- ...merge
617
- } : {
618
- r: merge.r,
619
- c: merge.c
603
+ return sheets.map((sheet)=>{
604
+ const { celldata: sourceCellData, data, ...sourceMetadata } = sheet;
605
+ const projected = structuredClone(sourceMetadata);
606
+ const cells = new Map();
607
+ if (data) for (const rowIndex of sparseArrayIndexes(data)){
608
+ const row = data[rowIndex];
609
+ if (row) for (const columnIndex of sparseArrayIndexes(row)){
610
+ const cell = row[columnIndex];
611
+ if (null != cell) cells.set(`${rowIndex}_${columnIndex}`, {
612
+ r: rowIndex,
613
+ c: columnIndex,
614
+ v: structuredClone(cell)
615
+ });
616
+ }
617
+ }
618
+ else for (const entry of sourceCellData ?? [])cells.set(`${entry.r}_${entry.c}`, structuredClone(entry));
619
+ for (const merge of Object.values(projected.config?.merge ?? {}))for(let rowIndex = merge.r; rowIndex < merge.r + merge.rs; rowIndex += 1)for(let columnIndex = merge.c; columnIndex < merge.c + merge.cs; columnIndex += 1){
620
+ const key = `${rowIndex}_${columnIndex}`;
621
+ const current = cells.get(key);
622
+ cells.set(key, {
623
+ r: rowIndex,
624
+ c: columnIndex,
625
+ v: {
626
+ ...current?.v ?? {},
627
+ mc: rowIndex === merge.r && columnIndex === merge.c ? {
628
+ ...merge
629
+ } : {
630
+ r: merge.r,
631
+ c: merge.c
632
+ }
620
633
  }
621
- };
634
+ });
622
635
  }
623
636
  return {
624
- ...sheet,
625
- celldata: sheet.data ? sheet.data.flatMap((row, rowIndex)=>row.flatMap((cell, columnIndex)=>null == cell ? [] : [
626
- {
627
- r: rowIndex,
628
- c: columnIndex,
629
- v: cell
630
- }
631
- ])) : sheet.celldata ?? []
637
+ ...projected,
638
+ celldata: Array.from(cells.values()).sort((left, right)=>left.r - right.r || left.c - right.c)
639
+ };
640
+ });
641
+ }
642
+ function spreadsheetSheetsFromFortune(sheets, sourceSheets) {
643
+ return sheets.map((sheet, index)=>{
644
+ const source = (sheet.id ? sourceSheets.find((candidate)=>candidate.id === sheet.id) : void 0) ?? sourceSheets[index];
645
+ const cells = spreadsheetPopulatedCellData(sheet);
646
+ const { celldata: _cellData, data: _data, ...metadata } = sheet;
647
+ const useMatrix = source ? void 0 !== source.data : void 0 !== sheet.data;
648
+ if (!useMatrix) return {
649
+ ...metadata,
650
+ celldata: cells
651
+ };
652
+ const data = [];
653
+ data.length = Math.max(sheet.data?.length ?? 0, source?.data?.length ?? 0, positiveSpreadsheetDimension(sheet.row));
654
+ for (const { r: rowIndex, c: columnIndex, v: cell } of cells){
655
+ const row = data[rowIndex] ?? [];
656
+ data[rowIndex] = row;
657
+ row.length = Math.max(row.length, sheet.data?.[rowIndex]?.length ?? 0, source?.data?.[rowIndex]?.length ?? 0, positiveSpreadsheetDimension(sheet.column));
658
+ row[columnIndex] = cell;
659
+ }
660
+ return {
661
+ ...metadata,
662
+ data
632
663
  };
633
664
  });
634
665
  }
@@ -644,7 +675,9 @@ function finiteSpreadsheetSelection(selection) {
644
675
  };
645
676
  }
646
677
  function sameSpreadsheetWorkbookState(changed, rendered) {
647
- return JSON.stringify(changed.map(spreadsheetSheetWithoutTransientSelection)) === JSON.stringify(rendered.map(spreadsheetSheetWithoutTransientSelection));
678
+ const left = changed.map(spreadsheetSheetWithoutTransientSelection);
679
+ const right = rendered.map(spreadsheetSheetWithoutTransientSelection);
680
+ return sameSpreadsheetHistoryValue(left, right);
648
681
  }
649
682
  function sameSpreadsheetHistoryContent(left, right) {
650
683
  return sameSpreadsheetHistoryValue({
@@ -737,8 +770,33 @@ function finiteSpreadsheetIndex(value, fallback) {
737
770
  return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : fallback;
738
771
  }
739
772
  function spreadsheetSheetWithoutTransientSelection(sheet) {
740
- const { celldata: _cellData, luckysheet_select_save: _selection, luckysheet_selection_range: _range, ...content } = sheet;
741
- return content;
773
+ const { celldata: _cellData, calcChain: _calculationChain, data: _data, dynamicArray_compute: _dynamicArrayComputation, luckysheet_select_save: _selection, luckysheet_selection_range: _range, zoomRatio, ...content } = sheet;
774
+ return {
775
+ ...content,
776
+ ...void 0 === zoomRatio || 1 === zoomRatio ? {} : {
777
+ zoomRatio
778
+ },
779
+ celldata: spreadsheetPopulatedCellData(sheet)
780
+ };
781
+ }
782
+ function spreadsheetPopulatedCellData(sheet) {
783
+ const cells = [];
784
+ if (void 0 !== sheet.data) for (const rowIndex of sparseArrayIndexes(sheet.data)){
785
+ const row = sheet.data[rowIndex];
786
+ if (row) for (const columnIndex of sparseArrayIndexes(row)){
787
+ const cell = row[columnIndex];
788
+ if (null != cell) cells.push({
789
+ r: rowIndex,
790
+ c: columnIndex,
791
+ v: cell
792
+ });
793
+ }
794
+ }
795
+ else for (const cell of sheet.celldata ?? [])if (null != cell.v) cells.push(cell);
796
+ return cells.sort((left, right)=>left.r - right.r || left.c - right.c);
797
+ }
798
+ function positiveSpreadsheetDimension(value) {
799
+ return 'number' == typeof value && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
742
800
  }
743
801
  function sameSpreadsheetHistoryValue(left, right) {
744
802
  if (left === right) return true;
@@ -748,7 +806,9 @@ function sameSpreadsheetHistoryValue(left, right) {
748
806
  if (leftFormulaCell || rightFormulaCell) return Boolean(leftFormulaCell && rightFormulaCell && sameSpreadsheetFormulaHistoryCell(leftFormulaCell, rightFormulaCell));
749
807
  if (Array.isArray(left) || Array.isArray(right)) {
750
808
  if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
751
- return left.every((value, index)=>sameSpreadsheetHistoryValue(value, right[index]));
809
+ const leftKeys = Object.keys(left).filter((key)=>void 0 !== left[Number(key)]);
810
+ const rightKeys = Object.keys(right).filter((key)=>void 0 !== right[Number(key)]);
811
+ return leftKeys.length === rightKeys.length && leftKeys.every((key)=>Object.hasOwn(right, key) && sameSpreadsheetHistoryValue(left[Number(key)], right[Number(key)]));
752
812
  }
753
813
  if ('object' != typeof left || 'object' != typeof right) return false;
754
814
  const leftRecord = left;
@@ -1471,13 +1531,13 @@ function spreadsheetSelectionContainsFocus(selection, focus) {
1471
1531
  }
1472
1532
  function spreadsheetSheetBounds(sheet) {
1473
1533
  const dataRows = sheet.data?.length ?? 0;
1474
- const dataColumns = Math.max(0, ...sheet.data?.map((row)=>row?.length ?? 0) ?? []);
1534
+ const dataColumns = sparseMatrixColumnCount(sheet.data);
1475
1535
  return {
1476
- lastRow: positiveSpreadsheetDimension(sheet.row, dataRows, spreadsheetDefaultRows) - 1,
1477
- lastColumn: positiveSpreadsheetDimension(sheet.column, dataColumns, spreadsheetDefaultColumns) - 1
1536
+ lastRow: spreadsheet_keyboard_navigation_positiveSpreadsheetDimension(sheet.row, dataRows, spreadsheetDefaultRows) - 1,
1537
+ lastColumn: spreadsheet_keyboard_navigation_positiveSpreadsheetDimension(sheet.column, dataColumns, spreadsheetDefaultColumns) - 1
1478
1538
  };
1479
1539
  }
1480
- function positiveSpreadsheetDimension(configured, dataDimension, fallback) {
1540
+ function spreadsheet_keyboard_navigation_positiveSpreadsheetDimension(configured, dataDimension, fallback) {
1481
1541
  const value = Number(configured);
1482
1542
  if (Number.isFinite(value) && value > 0) return Math.max(1, Math.trunc(value));
1483
1543
  if (dataDimension > 0) return dataDimension;
@@ -1817,11 +1877,10 @@ function createSpreadsheetEditorExtensions() {
1817
1877
  return Boolean(sheet && (context.editable || context.view && 1 !== sheet.hide));
1818
1878
  },
1819
1879
  execute: (context, sheetId)=>{
1820
- if (!context.editable) {
1821
- const sheet = context.content.sheets.find((candidate)=>candidate.id === sheetId);
1822
- if (!sheet || 1 === sheet.hide) return false;
1823
- return context.view?.activateSheet(sheetId) ?? false;
1824
- }
1880
+ const sheet = context.content.sheets.find((candidate)=>candidate.id === sheetId);
1881
+ if (!sheet) return false;
1882
+ if (context.view && 1 !== sheet.hide) return context.view?.activateSheet(sheetId) ?? false;
1883
+ if (!context.editable) return false;
1825
1884
  return applySpreadsheetSheetChange(context, activateSpreadsheetSheet(context.content, sheetId));
1826
1885
  }
1827
1886
  },
@@ -2134,7 +2193,7 @@ function spreadsheetStructureExtent(context, axis) {
2134
2193
  const range = spreadsheetStructureRange(liveRange(context), axis);
2135
2194
  if (!sheet) return range[1] + 1;
2136
2195
  if ('row' === axis) return Math.max(sheet.row ?? 0, sheet.data?.length ?? 0, range[1] + 1);
2137
- const dataWidth = Math.max(0, ...(sheet.data ?? []).map((row)=>row.length));
2196
+ const dataWidth = sparseMatrixColumnCount(sheet.data);
2138
2197
  return Math.max(sheet.column ?? 0, dataWidth, range[1] + 1);
2139
2198
  }
2140
2199
  function clearSelectedCells(context, mode = 'contents') {
@@ -2757,7 +2816,7 @@ function scanSpreadsheetFormulas(content) {
2757
2816
  for (const sheet of content.sheets){
2758
2817
  summary.normalizedSharedFormulaGroups += sheet.formulaMetadata?.normalizedSharedFormulaGroups ?? 0;
2759
2818
  summary.normalizedSharedFormulaCells += sheet.formulaMetadata?.normalizedSharedFormulaCells ?? 0;
2760
- for (const [row, cells] of (sheet.data ?? []).entries())for (const [column, cell] of cells.entries()){
2819
+ for (const [row, cells] of sparseArrayEntries(sheet.data))for (const [column, cell] of sparseArrayEntries(cells)){
2761
2820
  if (!cell?.f) continue;
2762
2821
  const address = spreadsheetCellAddress(row, column);
2763
2822
  const location = `${sheet.name}!${address}`;
@@ -5459,17 +5518,11 @@ function spreadsheetFindMatches(sheet, query) {
5459
5518
  }
5460
5519
  function indexedSpreadsheetCells(sheet) {
5461
5520
  const indexed = new Map();
5462
- for(let row = 0; row < (sheet.data?.length ?? 0); row += 1){
5463
- const cells = sheet.data?.[row];
5464
- if (cells) for(let column = 0; column < cells.length; column += 1){
5465
- const cell = cells[column];
5466
- if (cell) indexed.set(`${row}:${column}`, {
5467
- row,
5468
- column,
5469
- cell
5470
- });
5471
- }
5472
- }
5521
+ for (const [row, cells] of sparseArrayEntries(sheet.data))for (const [column, cell] of sparseArrayEntries(cells))if (cell) indexed.set(`${row}:${column}`, {
5522
+ row,
5523
+ column,
5524
+ cell
5525
+ });
5473
5526
  for (const entry of sheet.celldata ?? []){
5474
5527
  if (!Number.isSafeInteger(entry.r) || !Number.isSafeInteger(entry.c) || entry.r < 0 || entry.c < 0 || !entry.v) continue;
5475
5528
  const key = `${entry.r}:${entry.c}`;
@@ -9643,7 +9696,7 @@ function SpreadsheetProtectionPanel({ content, onChange }) {
9643
9696
  if (ranges.some((range, index)=>index !== selectedIndex && range.name.trim().toLowerCase() === name.toLowerCase())) return void setError('当前工作表中已经存在这个区域名称。');
9644
9697
  if (editableRangeCellCount(parsed) > MAX_EDITABLE_RANGE_CELLS) return void setError('一次最多可设置 100,000 个可编辑单元格。');
9645
9698
  const maximumRow = Math.max(1, sheet.row ?? sheet.data?.length ?? 1) - 1;
9646
- const maximumColumn = Math.max(1, sheet.column ?? Math.max(0, ...(sheet.data ?? []).map((row)=>row.length))) - 1;
9699
+ const maximumColumn = Math.max(1, sheet.column ?? sparseMatrixColumnCount(sheet.data)) - 1;
9647
9700
  if (parsed.some((range)=>range.row[1] > maximumRow || range.column[1] > maximumColumn)) return void setError(`范围必须位于当前工作表的 ${maximumRow + 1} 行 × ${maximumColumn + 1} 列以内。`);
9648
9701
  const index = selectedIndex ?? ranges.length;
9649
9702
  updateSheet(withEditableRange(sheet, selectedIndex, {
@@ -10682,12 +10735,9 @@ function sparseSpreadsheetCells(sheet, maximumCells) {
10682
10735
  };
10683
10736
  if (sheet.data) {
10684
10737
  if (sheet.data.length > OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) return null;
10685
- for(let row = 0; row < sheet.data.length; row += 1){
10686
- const values = sheet.data[row];
10687
- if (values) {
10688
- if (values.length > 16384) return null;
10689
- for(let column = 0; column < values.length; column += 1)if (!appendCell(values[column], row, column)) return null;
10690
- }
10738
+ for (const [row, values] of sparseArrayEntries(sheet.data)){
10739
+ if (values.length > 16384) return null;
10740
+ for (const [column, cell] of sparseArrayEntries(values))if (!appendCell(cell, row, column)) return null;
10691
10741
  }
10692
10742
  } else for (const entry of sheet.celldata ?? [])if (!appendCell(entry.v, entry.r, entry.c)) return null;
10693
10743
  return cells.sort((left, right)=>left.row - right.row || left.column - right.column);
@@ -11316,9 +11366,7 @@ function spreadsheetCellReader(sheet) {
11316
11366
  }
11317
11367
  function spreadsheetUsedBounds(sheet) {
11318
11368
  let lastRow = Math.max((sheet.data?.length ?? 1) - 1, 0);
11319
- let lastColumn = Math.max(...sheet.data?.map((row)=>Math.max((row?.length ?? 1) - 1, 0)) ?? [
11320
- 0
11321
- ]);
11369
+ let lastColumn = Math.max(sparseMatrixColumnCount(sheet.data) - 1, 0);
11322
11370
  for (const cell of sheet.celldata ?? []){
11323
11371
  lastRow = Math.max(lastRow, cell.r);
11324
11372
  lastColumn = Math.max(lastColumn, cell.c);
@@ -11940,9 +11988,7 @@ function spreadsheetFormatPainterSheetBounds(sheet, target, pattern) {
11940
11988
  const requestedColumnCount = Math.max(target.column[0], target.column[1]) + pattern.columnCount;
11941
11989
  return {
11942
11990
  rowCount: positiveInteger(sheet.row) ?? Math.max(sheet.data?.length ?? 0, requestedRowCount),
11943
- columnCount: positiveInteger(sheet.column) ?? Math.max(...sheet.data?.map((row)=>row?.length ?? 0) ?? [
11944
- 0
11945
- ], requestedColumnCount)
11991
+ columnCount: positiveInteger(sheet.column) ?? Math.max(sparseMatrixColumnCount(sheet.data), requestedColumnCount)
11946
11992
  };
11947
11993
  }
11948
11994
  function positiveInteger(value) {
@@ -12035,6 +12081,7 @@ function useSpreadsheetCollaboration({ initialContent, onChange, session }) {
12035
12081
  const sessionRef = useRef(session);
12036
12082
  if (sessionRef.current !== session) throw new Error('SpreadsheetEditor collaboration sessions cannot be replaced while mounted. Remount the editor for another shared workbook.');
12037
12083
  const viewStateRef = useRef(spreadsheetCollaborationViewState(initialContent));
12084
+ const [derivedOrigin] = useState(()=>session.createOrigin('system', 'spreadsheet-calculation'));
12038
12085
  const [content, setContent] = useState(()=>spreadsheetContentWithCollaborationViewState(readWorkOfficeSpreadsheetCollaboration(session), viewStateRef.current));
12039
12086
  const contentRef = useRef(content);
12040
12087
  const bindingRef = useRef(void 0);
@@ -12042,15 +12089,15 @@ function useSpreadsheetCollaboration({ initialContent, onChange, session }) {
12042
12089
  const onChangeRef = useRef(onChange);
12043
12090
  onChangeRef.current = onChange;
12044
12091
  contentRef.current = content;
12092
+ const project = useCallback((sharedContent)=>{
12093
+ const projected = spreadsheetContentWithCollaborationViewState(sharedContent, viewStateRef.current);
12094
+ contentRef.current = projected;
12095
+ setContent(projected);
12096
+ return projected;
12097
+ }, []);
12045
12098
  useEffect(()=>{
12046
12099
  const binding = createWorkOfficeSpreadsheetCollaborationBinding(session);
12047
12100
  bindingRef.current = binding;
12048
- const project = (sharedContent)=>{
12049
- const projected = spreadsheetContentWithCollaborationViewState(sharedContent, viewStateRef.current);
12050
- contentRef.current = projected;
12051
- setContent(projected);
12052
- return projected;
12053
- };
12054
12101
  const unsubscribeContent = binding.subscribe(({ content: next })=>{
12055
12102
  onChangeRef.current(project(next));
12056
12103
  });
@@ -12069,11 +12116,12 @@ function useSpreadsheetCollaboration({ initialContent, onChange, session }) {
12069
12116
  binding.destroy();
12070
12117
  };
12071
12118
  }, [
12119
+ project,
12072
12120
  session
12073
12121
  ]);
12074
12122
  const commit = useCallback((next)=>{
12075
- updateSpreadsheetCollaborationViewState(viewStateRef.current, next);
12076
12123
  const previous = contentRef.current;
12124
+ updateSpreadsheetCollaborationViewStateForContentChange(viewStateRef.current, previous, next);
12077
12125
  const binding = bindingRef.current;
12078
12126
  if (binding?.replace(previous, next)) {
12079
12127
  contentRef.current = spreadsheetContentWithCollaborationViewState(binding.content(), viewStateRef.current);
@@ -12083,17 +12131,42 @@ function useSpreadsheetCollaboration({ initialContent, onChange, session }) {
12083
12131
  contentRef.current = projected;
12084
12132
  setContent(projected);
12085
12133
  }, []);
12134
+ const commitDerived = useCallback((next)=>{
12135
+ const previous = contentRef.current;
12136
+ const binding = bindingRef.current;
12137
+ if (binding && replaceWorkOfficeSpreadsheetCollaboration(session, previous, next, derivedOrigin)) {
12138
+ contentRef.current = spreadsheetContentWithCollaborationViewState(binding.content(), viewStateRef.current);
12139
+ return;
12140
+ }
12141
+ project(binding?.content() ?? previous);
12142
+ }, [
12143
+ derivedOrigin,
12144
+ project,
12145
+ session
12146
+ ]);
12086
12147
  const view = useMemo(()=>({
12087
12148
  activateSheet: (sheetId)=>{
12149
+ const sharedContent = bindingRef.current?.content() ?? contentRef.current;
12150
+ const sheet = sharedContent.sheets.find((candidate)=>candidate.id === sheetId && 1 !== candidate.hide);
12151
+ if (!sheet) return false;
12152
+ if (viewStateRef.current.activeSheetId === sheetId) return true;
12088
12153
  viewStateRef.current.activeSheetId = sheetId;
12154
+ project(sharedContent);
12155
+ return true;
12089
12156
  },
12090
12157
  select: (sheetId, selection)=>{
12091
12158
  viewStateRef.current.selections.set(sheetId, finiteSpreadsheetSelection(selection));
12092
12159
  },
12093
12160
  setZoom: (sheetId, zoomRatio)=>{
12161
+ const sharedContent = bindingRef.current?.content() ?? contentRef.current;
12162
+ if (!sharedContent.sheets.some((sheet)=>sheet.id === sheetId)) return;
12163
+ if (viewStateRef.current.zoomBySheet.get(sheetId) === zoomRatio) return;
12094
12164
  viewStateRef.current.zoomBySheet.set(sheetId, zoomRatio);
12165
+ project(sharedContent);
12095
12166
  }
12096
- }), []);
12167
+ }), [
12168
+ project
12169
+ ]);
12097
12170
  const history = useMemo(()=>({
12098
12171
  canRedo: bindingRef.current?.canRedo() ?? false,
12099
12172
  canUndo: bindingRef.current?.canUndo() ?? false,
@@ -12106,6 +12179,7 @@ function useSpreadsheetCollaboration({ initialContent, onChange, session }) {
12106
12179
  content,
12107
12180
  history,
12108
12181
  onChange: commit,
12182
+ onDerivedChange: commitDerived,
12109
12183
  readOnly: 'edit' !== session.mode,
12110
12184
  view
12111
12185
  };
@@ -12125,16 +12199,21 @@ function spreadsheetCollaborationViewState(content) {
12125
12199
  zoomBySheet
12126
12200
  };
12127
12201
  }
12128
- function updateSpreadsheetCollaborationViewState(view, content) {
12129
- pruneSpreadsheetCollaborationViewState(view, content);
12130
- const active = content.sheets.find((sheet)=>1 === sheet.status);
12202
+ function updateSpreadsheetCollaborationViewStateForContentChange(view, previous, next) {
12203
+ pruneSpreadsheetCollaborationViewState(view, next);
12204
+ if (!spreadsheetSheetVisibilityChanged(previous, next)) return;
12205
+ const active = next.sheets.find((sheet)=>1 === sheet.status && 1 !== sheet.hide);
12131
12206
  if (active?.id) view.activeSheetId = active.id;
12132
- for (const sheet of content.sheets){
12133
- if (!sheet.id) continue;
12134
- const selection = sheet.luckysheet_select_save?.at(-1);
12135
- if (selection) view.selections.set(sheet.id, finiteSpreadsheetSelection(selection));
12136
- if (void 0 !== sheet.zoomRatio) view.zoomBySheet.set(sheet.id, sheet.zoomRatio);
12137
- }
12207
+ }
12208
+ function spreadsheetSheetVisibilityChanged(previous, next) {
12209
+ if (previous.sheets.length !== next.sheets.length) return true;
12210
+ const previousById = new Map(previous.sheets.flatMap((sheet)=>sheet.id ? [
12211
+ [
12212
+ sheet.id,
12213
+ sheet.hide
12214
+ ]
12215
+ ] : []));
12216
+ return next.sheets.some((sheet)=>!sheet.id || !previousById.has(sheet.id) || previousById.get(sheet.id) !== sheet.hide);
12138
12217
  }
12139
12218
  function spreadsheetContentWithCollaborationViewState(content, view) {
12140
12219
  pruneSpreadsheetCollaborationViewState(view, content);
@@ -12194,10 +12273,11 @@ function CollaborativeSpreadsheetEditor(props) {
12194
12273
  collaborationHistory: shared.history,
12195
12274
  collaborationView: shared.view,
12196
12275
  onChange: shared.onChange,
12276
+ onDerivedChange: shared.onDerivedChange,
12197
12277
  preview: props.preview || shared.readOnly
12198
12278
  });
12199
12279
  }
12200
- function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHistory, collaborationView, kernelWasmUrl, preview, saveStatus = '已自动保存', fileActions, onChange, onAgentRequest }) {
12280
+ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHistory, collaborationView, kernelWasmUrl, preview, saveStatus = '已自动保存', fileActions, onChange, onDerivedChange, onAgentRequest }) {
12201
12281
  const materializedContent = useMemo(()=>refreshSpreadsheetPivotTables(content), [
12202
12282
  content
12203
12283
  ]);
@@ -12396,19 +12476,24 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
12396
12476
  const workbookSheetsRef = useRef(workbookSheets);
12397
12477
  workbookSheetsRef.current = displayedWorkbookSheets;
12398
12478
  const handleWorkbookChange = useCallback((sheets)=>{
12399
- if (ignoreChangeDuringExternalSync(sameSpreadsheetWorkbookState(sheets, workbookSheetsRef.current))) return void takeWorkbookOperations();
12479
+ const controlledSheets = spreadsheetSheetsFromFortune(sheets, contentRef.current.sheets);
12480
+ const matchesRenderedWorkbook = sameSpreadsheetWorkbookState(controlledSheets, workbookSheetsRef.current);
12481
+ if (ignoreChangeDuringExternalSync(matchesRenderedWorkbook)) return void takeWorkbookOperations();
12400
12482
  const operations = takeWorkbookOperations();
12401
- if (previewRef.current || !operations.length && !calculation.hasPendingResultPatches() && sameSpreadsheetWorkbookState(sheets, workbookSheetsRef.current)) return;
12402
- const withCharts = reconcileSpreadsheetChartPreviews(contentRef.current, sheets);
12483
+ const hasPendingResultPatches = calculation.hasPendingResultPatches();
12484
+ if (previewRef.current || matchesRenderedWorkbook && !hasPendingResultPatches) return;
12485
+ const withCharts = reconcileSpreadsheetChartPreviews(contentRef.current, controlledSheets);
12403
12486
  const next = reconcileSpreadsheetPivots(contentRef.current, withCharts.sheets);
12404
12487
  contentRef.current = next;
12405
12488
  acceptWorkbookContent(next);
12406
12489
  calculation.synchronizeWorkbook(next, operations);
12407
- spreadsheetCommandsRef.current?.setSpreadsheetContent(next);
12490
+ if (matchesRenderedWorkbook && onDerivedChange) onDerivedChange(next);
12491
+ else spreadsheetCommandsRef.current?.setSpreadsheetContent(next);
12408
12492
  }, [
12409
12493
  acceptWorkbookContent,
12410
12494
  calculation,
12411
12495
  ignoreChangeDuringExternalSync,
12496
+ onDerivedChange,
12412
12497
  takeWorkbookOperations
12413
12498
  ]);
12414
12499
  const handleWorkbookOperations = useCallback((operations)=>{
@@ -12514,14 +12599,14 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
12514
12599
  pasteSpreadsheetSelection(browserSpreadsheetClipboard, commands.pasteCells);
12515
12600
  return true;
12516
12601
  };
12517
- const activateReadOnlySpreadsheetSheet = useCallback((sheetId)=>{
12518
- if (!previewRef.current) return false;
12602
+ const activateLocalSpreadsheetSheet = useCallback((sheetId)=>{
12603
+ if (!previewRef.current && !collaborationView) return false;
12519
12604
  const sheet = contentRef.current.sheets.find((candidate)=>candidate.id === sheetId && 1 !== candidate.hide);
12520
12605
  if (!sheet) return false;
12521
12606
  activeSheetIdRef.current = sheetId;
12522
- collaborationView?.activateSheet(sheetId);
12607
+ if (collaborationView && !collaborationView.activateSheet(sheetId)) return false;
12523
12608
  setSelectionState(null);
12524
- setPreviewActiveSheetId(sheetId);
12609
+ if (previewRef.current) setPreviewActiveSheetId(sheetId);
12525
12610
  try {
12526
12611
  workbookRef.current?.activateSheet({
12527
12612
  id: sheetId
@@ -12564,8 +12649,8 @@ function SpreadsheetEditorSurface({ autoFocus = true, content, collaborationHist
12564
12649
  selection: selectionState,
12565
12650
  targetSheetId: toolbarSheetId,
12566
12651
  toolbarCell,
12567
- view: preview ? {
12568
- activateSheet: activateReadOnlySpreadsheetSheet
12652
+ view: preview || collaborationView ? {
12653
+ activateSheet: activateLocalSpreadsheetSheet
12569
12654
  } : null,
12570
12655
  workbook: workbookInstance
12571
12656
  }, spreadsheetExtensions);
@@ -2777,8 +2777,8 @@ function work_docx_table_sizing_import_textNodes(root) {
2777
2777
  }
2778
2778
  const work_docx_import_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
2779
2779
  const work_docx_import_XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
2780
- async function prepareDocxImport(buffer) {
2781
- const archive = await OoxmlPackage.load(buffer);
2780
+ async function prepareDocxImport(buffer, sourcePackage) {
2781
+ const archive = sourcePackage ?? await OoxmlPackage.load(buffer);
2782
2782
  const bibliography = (await readDocxBibliography(archive)).bibliography;
2783
2783
  const fallback = documentInitialSectionLayout({
2784
2784
  type: 'document',
@@ -1626,14 +1626,15 @@ async function diagnoseXlsxPivots(archive) {
1626
1626
  });
1627
1627
  return issues;
1628
1628
  }
1629
- async function analyzeDocxCompatibility(file, messages) {
1629
+ async function analyzeDocxCompatibility(file, messages, sourcePackage) {
1630
1630
  const issues = [
1631
1631
  work_office_diagnostics_issue('docx.page-layout', 'Page layout', 'Per-section paper size, orientation, exact native page margins, one-to-six equal or custom-width columns, section breaks, and explicit page breaks are preserved; exact pagination and line wrapping may normalize.'),
1632
1632
  work_office_diagnostics_issue('docx.package-state', 'OOXML package state', 'Source-backed export retains safe source-only parts byte-for-byte and reconnects their content types and relationships. Passive relationship-free extensions in settings XML, uniquely matched style or numbering identities, stable picture drawings, unchanged native-identity paragraphs, stable table, row, or cell scopes, and uniquely matched footnote, endnote, comment, or comment-thread records are preserved against the final package graph. Text-stable direct, supported-hyperlink-wrapped, and eligible static-content-control note and comment runs retain paragraph, table, wrapper, run, and run-property metadata; native note tables and DrawingML pictures remain editable, note image relationships and media targets are repaired and validated, bounded structured OMML equations remain atomically updateable in supported Word stories, uniquely matched table-bearing rich-text controls are reconstructed, safe web, mail, and internal note/comment links are rebound to collision-free final relationship IDs, and content-control ID collisions are rewritten. Native note, comment, and note-picture identities survive reorderings, comment durable IDs are rebound to regenerated paragraph IDs, and eligible source font-table metadata is retained. Generated Word semantics remain authoritative; source-only, changed-text or table structure, malformed, duplicate or cross-kind, active, unsupported wrappers, mixed-semantic, unsafe relationship-bound, legacy VML, non-picture drawings, or unsupported rich content may normalize.', 'info')
1633
1633
  ];
1634
1634
  for (const message of messages)issues.push(work_office_diagnostics_issue(`docx.converter.${message.type}`, 'Document conversion', message.message, 'error' === message.type ? 'error' : 'warning'));
1635
1635
  try {
1636
- const archive = await OoxmlPackage.load(await file.arrayBuffer());
1636
+ const archive = void 0 === sourcePackage ? await OoxmlPackage.load(await file.arrayBuffer()) : sourcePackage;
1637
+ if (!archive) throw new Error('The DOCX package could not be loaded.');
1637
1638
  const packagePaths = archive.paths('');
1638
1639
  const normalizedPackagePaths = packagePaths.map((path)=>path.toLowerCase());
1639
1640
  const document = archive.has('word/document.xml') ? await archive.xml('word/document.xml') : null;
@@ -1723,7 +1724,7 @@ async function analyzeDocxCompatibility(file, messages) {
1723
1724
  }
1724
1725
  return report(file, 'DOCX', issues);
1725
1726
  }
1726
- async function analyzeSpreadsheetCompatibility(file, extension, workbook) {
1727
+ async function analyzeSpreadsheetCompatibility(file, extension, workbook, sourcePackage) {
1727
1728
  if ('csv' === extension) return null;
1728
1729
  const sourceFormat = extension.toUpperCase();
1729
1730
  const issues = [];
@@ -1748,7 +1749,8 @@ async function analyzeSpreadsheetCompatibility(file, extension, workbook) {
1748
1749
  if ((workbook.Workbook?.Sheets?.[index]?.Hidden ?? 0) > 0) issues.push(work_office_diagnostics_issue('sheet.hidden', 'Hidden worksheets', 'Hidden worksheet state is preserved but can be changed by the editor.', 'info', name));
1749
1750
  }
1750
1751
  if ('xlsx' === extension) try {
1751
- const archive = await OoxmlPackage.load(await file.arrayBuffer());
1752
+ const archive = void 0 === sourcePackage ? await OoxmlPackage.load(await file.arrayBuffer()) : sourcePackage;
1753
+ if (!archive) throw new Error('The XLSX package could not be loaded.');
1752
1754
  await inspectXlsxPackage(archive, issues);
1753
1755
  } catch {
1754
1756
  issues.push(work_office_diagnostics_issue('xlsx.inspect', 'Package inspection', 'Some XLSX features could not be inspected before conversion.'));
@@ -478,8 +478,8 @@ const EMU_PER_INCH = 914400;
478
478
  const DEFAULT_SLIDE_WIDTH = 13.333;
479
479
  const DEFAULT_SLIDE_HEIGHT = 7.5;
480
480
  const MAX_EMBEDDED_IMAGE_BYTES = 11534336;
481
- async function importPptxPresentation(file) {
482
- const archive = await OoxmlPackage.load(await file.arrayBuffer());
481
+ async function importPptxPresentation(file, sourceBytes) {
482
+ const archive = await OoxmlPackage.load(sourceBytes ?? await file.arrayBuffer());
483
483
  if (!archive.has('ppt/presentation.xml')) throw new Error('The selected file is not a valid PPTX presentation.');
484
484
  const presentation = await archive.xml('ppt/presentation.xml');
485
485
  const presentationRelationships = await archive.relationships('ppt/presentation.xml');