ooxml-ui 1.1.0 → 1.3.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.
Files changed (203) hide show
  1. package/README.md +38 -36
  2. package/custom-elements.json +372 -4
  3. package/dist/chrome/ruler.d.ts +60 -2
  4. package/dist/chrome/ruler.d.ts.map +1 -1
  5. package/dist/chrome/status-bar.d.ts +3 -1
  6. package/dist/chrome/status-bar.d.ts.map +1 -1
  7. package/dist/{chunk-JFJXBYRD.js → chunk-2WLRFPMW.js} +9 -0
  8. package/dist/{chunk-X7RINDJQ.js → chunk-56A2JK63.js} +1 -1
  9. package/dist/{chunk-KVKKXRL7.js → chunk-5OFAJDMQ.js} +93 -14
  10. package/dist/{chunk-M5DTJSOV.js → chunk-723JC3OC.js} +461 -16
  11. package/dist/{chunk-USDLALB5.js → chunk-IY7QJLQG.js} +1 -1
  12. package/dist/{chunk-BOX43CZD.js → chunk-M6GF5ED2.js} +2 -1
  13. package/dist/{chunk-BEN3453I.js → chunk-NYRRH2MD.js} +1 -1
  14. package/dist/{chunk-3FPW7M6U.js → chunk-TWQ45BFL.js} +1 -1
  15. package/dist/{chunk-ZU7GX4SY.js → chunk-V6P6KZCO.js} +7 -5
  16. package/dist/comments/comments-pane.d.ts +116 -0
  17. package/dist/comments/comments-pane.d.ts.map +1 -0
  18. package/dist/comments/types.d.ts +80 -0
  19. package/dist/comments/types.d.ts.map +1 -0
  20. package/dist/controls.d.ts +6 -2
  21. package/dist/controls.d.ts.map +1 -1
  22. package/dist/controls.js +5 -5
  23. package/dist/docx/comments-panel.d.ts +8 -1
  24. package/dist/docx/editor-chrome.d.ts +8 -1
  25. package/dist/docx/file-commands.d.ts +1 -1
  26. package/dist/docx/image-media.d.ts +1 -1
  27. package/dist/docx/locales/en-app.d.ts +1 -0
  28. package/dist/docx/locales/en-ribbon.d.ts +1 -0
  29. package/dist/docx/locales/en.d.ts +2 -0
  30. package/dist/docx/localization-strings.d.ts +2 -0
  31. package/dist/docx/localization.d.ts +7 -0
  32. package/dist/docx/print-layout.d.ts +2 -2
  33. package/dist/docx/print-picture.d.ts +12 -0
  34. package/dist/docx/review-controller.d.ts +2 -0
  35. package/dist/docx/ribbon-actions.d.ts +22 -0
  36. package/dist/docx/ruler.d.ts +7 -10
  37. package/dist/docx/title-bar.d.ts +3 -5
  38. package/dist/docx.js +342 -362
  39. package/dist/form/gradient-preset-gallery.d.ts +2 -2
  40. package/dist/icons.d.ts.map +1 -1
  41. package/dist/icons.js +1 -1
  42. package/dist/index.d.ts +1 -1
  43. package/dist/index.js +9 -9
  44. package/dist/pptx/{chart-3d-view-scene-2KRQ5667.mjs → chart-3d-view-scene-OYJ4CDCO.mjs} +4 -5
  45. package/dist/pptx/{chart-3d-view-scene-36DMZWZ3.cjs → chart-3d-view-scene-SE2Y6QRE.cjs} +38 -39
  46. package/dist/pptx/{chunk-YOGDIDPB.mjs → chunk-CIVA3MLN.mjs} +8 -2
  47. package/dist/pptx/{chunk-KPZWYF44.cjs → chunk-FPE5PZMI.cjs} +789 -491
  48. package/dist/pptx/{chunk-AIL3BNZS.cjs → chunk-IURZJAWP.cjs} +529 -387
  49. package/dist/pptx/{chunk-YHA6XAB3.cjs → chunk-KRMMJUCO.cjs} +83 -1
  50. package/dist/pptx/{chunk-Y2W4SZER.mjs → chunk-MWHGUEIJ.mjs} +637 -348
  51. package/dist/pptx/{chunk-YARCML7O.mjs → chunk-STLXPKJJ.mjs} +519 -388
  52. package/dist/pptx/{chunk-ADOC5JLI.mjs → chunk-TC7HCF5M.mjs} +76 -2
  53. package/dist/pptx/{chunk-CJU4P5FT.cjs → chunk-WKLHJEPD.cjs} +18 -12
  54. package/dist/pptx/dom/element-styles.d.ts.map +1 -1
  55. package/dist/pptx/dom/elements/chart.d.ts +2 -3
  56. package/dist/pptx/dom/elements/chart.d.ts.map +1 -1
  57. package/dist/pptx/dom/elements/connector.d.ts.map +1 -1
  58. package/dist/pptx/dom/elements/shape-filter-defs.d.ts.map +1 -1
  59. package/dist/pptx/dom/elements/table.d.ts.map +1 -1
  60. package/dist/pptx/dom/index.cjs +272 -261
  61. package/dist/pptx/dom/index.mjs +46 -35
  62. package/dist/pptx/dom/slide-stage.d.ts.map +1 -1
  63. package/dist/pptx/export/html2canvas-clone.cjs +2 -2
  64. package/dist/pptx/export/html2canvas-clone.mjs +1 -1
  65. package/dist/pptx/index.cjs +3065 -2993
  66. package/dist/pptx/index.mjs +4 -4
  67. package/dist/pptx/render/chart-3d-oblique-layout.d.ts.map +1 -1
  68. package/dist/pptx/render/chart-3d-persp-bars.d.ts.map +1 -1
  69. package/dist/pptx/render/chart-3d-persp-layout.d.ts.map +1 -1
  70. package/dist/pptx/render/chart-axis-range.d.ts +7 -0
  71. package/dist/pptx/render/chart-axis-range.d.ts.map +1 -1
  72. package/dist/pptx/render/chart-axis-render.d.ts +3 -1
  73. package/dist/pptx/render/chart-axis-render.d.ts.map +1 -1
  74. package/dist/pptx/render/chart-axis-style.d.ts +5 -0
  75. package/dist/pptx/render/chart-axis-style.d.ts.map +1 -1
  76. package/dist/pptx/render/chart-axis.d.ts +1 -1
  77. package/dist/pptx/render/chart-axis.d.ts.map +1 -1
  78. package/dist/pptx/render/chart-bar-cluster-geometry.d.ts +1 -1
  79. package/dist/pptx/render/chart-bar-cluster-geometry.d.ts.map +1 -1
  80. package/dist/pptx/render/chart-cartesian-area.d.ts.map +1 -1
  81. package/dist/pptx/render/chart-cartesian-axes.d.ts.map +1 -1
  82. package/dist/pptx/render/chart-cartesian-bars.d.ts +3 -3
  83. package/dist/pptx/render/chart-cartesian-line-area.d.ts.map +1 -1
  84. package/dist/pptx/render/chart-cartesian-percent-stacked.d.ts.map +1 -1
  85. package/dist/pptx/render/chart-cartesian-stacked-labels.d.ts +11 -7
  86. package/dist/pptx/render/chart-cartesian-stacked-labels.d.ts.map +1 -1
  87. package/dist/pptx/render/chart-cartesian.d.ts.map +1 -1
  88. package/dist/pptx/render/chart-combo-stock.cjs +3 -3
  89. package/dist/pptx/render/chart-combo-stock.mjs +1 -1
  90. package/dist/pptx/render/chart-combo.d.ts.map +1 -1
  91. package/dist/pptx/render/chart-data-label-anchor.d.ts +4 -1
  92. package/dist/pptx/render/chart-data-label-anchor.d.ts.map +1 -1
  93. package/dist/pptx/render/chart-gridlines-toggle.d.ts +13 -1
  94. package/dist/pptx/render/chart-gridlines-toggle.d.ts.map +1 -1
  95. package/dist/pptx/render/chart-helpers.d.ts +13 -13
  96. package/dist/pptx/render/chart-helpers.d.ts.map +1 -1
  97. package/dist/pptx/render/chart-horizontal-bars-helpers.d.ts +7 -3
  98. package/dist/pptx/render/chart-horizontal-bars-helpers.d.ts.map +1 -1
  99. package/dist/pptx/render/chart-horizontal-bars.d.ts.map +1 -1
  100. package/dist/pptx/render/chart-interaction-stacked.d.ts.map +1 -1
  101. package/dist/pptx/render/chart-line-path.d.ts +9 -0
  102. package/dist/pptx/render/chart-line-path.d.ts.map +1 -1
  103. package/dist/pptx/render/chart-overlays.cjs +7 -7
  104. package/dist/pptx/render/chart-overlays.mjs +1 -1
  105. package/dist/pptx/render/chart-palette.d.ts +5 -14
  106. package/dist/pptx/render/chart-palette.d.ts.map +1 -1
  107. package/dist/pptx/render/chart-plot-clip.d.ts +37 -0
  108. package/dist/pptx/render/chart-plot-clip.d.ts.map +1 -0
  109. package/dist/pptx/render/chart-quick-action-styles.d.ts +5 -3
  110. package/dist/pptx/render/chart-quick-action-styles.d.ts.map +1 -1
  111. package/dist/pptx/render/chart-scatter-x-axis.d.ts.map +1 -1
  112. package/dist/pptx/render/chart-style-palette.d.ts +52 -0
  113. package/dist/pptx/render/chart-style-palette.d.ts.map +1 -0
  114. package/dist/pptx/render/chart-surface-treemap.cjs +4 -4
  115. package/dist/pptx/render/chart-surface-treemap.mjs +1 -1
  116. package/dist/pptx/render/chart-view-model-bars.d.ts +32 -1
  117. package/dist/pptx/render/chart-view-model-bars.d.ts.map +1 -1
  118. package/dist/pptx/render/chart-view-model-build.d.ts.map +1 -1
  119. package/dist/pptx/render/chart-view-model.cjs +53 -41
  120. package/dist/pptx/render/chart-view-model.mjs +1 -1
  121. package/dist/pptx/render/chart-waterfall-map.cjs +6 -6
  122. package/dist/pptx/render/chart-waterfall-map.mjs +1 -1
  123. package/dist/pptx/render/device-pixel-stroke.d.ts +101 -0
  124. package/dist/pptx/render/device-pixel-stroke.d.ts.map +1 -0
  125. package/dist/pptx/render/export-clone.d.ts +4 -0
  126. package/dist/pptx/render/export-clone.d.ts.map +1 -1
  127. package/dist/pptx/render/foreign-object-svg-document.d.ts +12 -0
  128. package/dist/pptx/render/foreign-object-svg-document.d.ts.map +1 -1
  129. package/dist/pptx/render/index.d.ts +3 -0
  130. package/dist/pptx/render/index.d.ts.map +1 -1
  131. package/dist/pptx/render/stroke-outline.d.ts +6 -0
  132. package/dist/pptx/render/stroke-outline.d.ts.map +1 -1
  133. package/dist/pptx/render/stroke-style.d.ts +8 -2
  134. package/dist/pptx/render/stroke-style.d.ts.map +1 -1
  135. package/dist/pptx/render/tab-row-actions.d.ts +40 -0
  136. package/dist/pptx/render/tab-row-actions.d.ts.map +1 -0
  137. package/dist/pptx/render/table-cell-css.d.ts +3 -2
  138. package/dist/pptx/render/table-cell-css.d.ts.map +1 -1
  139. package/dist/pptx/render/table-cell-edit.d.ts +1 -1
  140. package/dist/pptx/render/table-cell-edit.d.ts.map +1 -1
  141. package/dist/pptx/render/table-cell-paragraphs.d.ts +29 -0
  142. package/dist/pptx/render/table-cell-paragraphs.d.ts.map +1 -0
  143. package/dist/pptx/render/table-style-borders.d.ts.map +1 -1
  144. package/dist/pptx/render/table-style.d.ts +2 -0
  145. package/dist/pptx/render/table-style.d.ts.map +1 -1
  146. package/dist/pptx/render/title-bar.d.ts +5 -0
  147. package/dist/pptx/render/title-bar.d.ts.map +1 -1
  148. package/dist/pptx/render/viewer-root-options.d.ts +11 -3
  149. package/dist/pptx/render/viewer-root-options.d.ts.map +1 -1
  150. package/dist/pptx/web-components/index.d.ts.map +1 -1
  151. package/dist/pptx/web-components/office-token-bridge.d.ts +1 -1
  152. package/dist/pptx/web-components/office-token-bridge.d.ts.map +1 -1
  153. package/dist/pptx/web-components/ribbon-actions.d.ts +8 -0
  154. package/dist/pptx/web-components/ribbon-actions.d.ts.map +1 -0
  155. package/dist/presence.js +3 -3
  156. package/dist/ribbon/ribbon-actions.d.ts +119 -0
  157. package/dist/ribbon/ribbon-actions.d.ts.map +1 -0
  158. package/dist/ribbon/ribbon-tabs.d.ts +2 -1
  159. package/dist/ribbon/ribbon-tabs.d.ts.map +1 -1
  160. package/dist/smartart/smartart-svg.d.ts +3 -2
  161. package/dist/smartart.js +3 -3
  162. package/dist/suite/attachment-store.d.ts +13 -0
  163. package/dist/suite/document-store.d.ts +31 -0
  164. package/dist/suite/index.d.ts +2 -0
  165. package/dist/suite.js +132 -1
  166. package/dist/teams/app/channel-tab.d.ts +5 -0
  167. package/dist/teams/chat-composer.d.ts +3 -0
  168. package/dist/teams.js +7 -7
  169. package/dist/theme.js +2 -2
  170. package/dist/tokens.d.ts +9 -0
  171. package/dist/tokens.d.ts.map +1 -1
  172. package/dist/visio/styles/index.d.ts +2 -0
  173. package/dist/visio/styles/theme.d.ts +11 -0
  174. package/dist/visio/styles.d.ts +2 -2
  175. package/dist/visio.js +66 -21
  176. package/dist/xlsx/backstage/index.d.ts +4 -1
  177. package/dist/xlsx/backstage/pages-share.d.ts +19 -0
  178. package/dist/xlsx/backstage/parts.d.ts +3 -0
  179. package/dist/xlsx/calc-preparation.d.ts +44 -0
  180. package/dist/xlsx/chart-fill-binding.d.ts +2 -2
  181. package/dist/xlsx/chart-series-gradient-type.d.ts +3 -2
  182. package/dist/xlsx/collaboration-session.d.ts +9 -0
  183. package/dist/xlsx/collaboration-types.d.ts +41 -0
  184. package/dist/xlsx/collaboration.d.ts +56 -0
  185. package/dist/xlsx/component.d.ts +8 -0
  186. package/dist/xlsx/dialogs/comments-list.d.ts +4 -0
  187. package/dist/xlsx/dialogs/help-dialogs.d.ts +1 -1
  188. package/dist/xlsx/editor-core.d.ts +6 -0
  189. package/dist/xlsx/element-api.d.ts +19 -1
  190. package/dist/xlsx/events.d.ts +4 -1
  191. package/dist/xlsx/grid/remote-layer.d.ts +3 -0
  192. package/dist/xlsx/grid/sparkline.d.ts +3 -0
  193. package/dist/xlsx/grid/styles.d.ts +1 -1
  194. package/dist/xlsx/index.d.ts +1 -0
  195. package/dist/xlsx/ribbon-actions.d.ts +22 -0
  196. package/dist/xlsx/shell-commands.d.ts +1 -1
  197. package/dist/xlsx/title-bar.d.ts +4 -2
  198. package/dist/xlsx.js +801 -92
  199. package/package.json +2 -2
  200. package/dist/pptx/web-components/status-bar-styles.d.ts +0 -3
  201. package/dist/pptx/web-components/status-bar-styles.d.ts.map +0 -1
  202. package/dist/pptx/web-components/title-bar-styles.d.ts +0 -7
  203. package/dist/pptx/web-components/title-bar-styles.d.ts.map +0 -1
package/dist/xlsx.js CHANGED
@@ -1,27 +1,24 @@
1
- import './chunk-ZU7GX4SY.js';
2
- import { defineSmartArt } from './chunk-BEN3453I.js';
3
- import { withExtension, saveExtension, downloadBytes, emit, emitEvent, blobFor, announceFileCommand, pickFile } from './chunk-KVKKXRL7.js';
4
- export { XLSX_EDITOR_EVENTS } from './chunk-KVKKXRL7.js';
1
+ import './chunk-V6P6KZCO.js';
2
+ import { defineSmartArt } from './chunk-NYRRH2MD.js';
3
+ import { withExtension, saveExtension, downloadBytes, emit, emitEvent, blobFor, announceFileCommand, pickFile } from './chunk-5OFAJDMQ.js';
4
+ export { XLSX_EDITOR_EVENTS } from './chunk-5OFAJDMQ.js';
5
5
  import './chunk-SNV6YKPC.js';
6
- import './chunk-X7RINDJQ.js';
7
- import { range_control_default, createRibbonOverflow, defineBackstage, defineRibbon, defineTitleBar, defineStatusBar, defineZoomSlider, rangeControl, defineDialog, runKeyTips, assignKeyTips, defineCheckbox, defineGallery, defineContextMenu, parseSvgPreview } from './chunk-M5DTJSOV.js';
8
- import { getIcon, registerIcon } from './chunk-BOX43CZD.js';
9
- import './chunk-3FPW7M6U.js';
10
- import { withTokens } from './chunk-USDLALB5.js';
11
- import './chunk-JFJXBYRD.js';
12
- import { SPECIAL_CODES, FRACTION_CODES, AUTOSUM_FUNCTIONS, themeToCssVars, lightTheme, darkTheme, ICON_ACCENTS, ICON_PATHS, normalizeThemeMode, applyThemeColors, parseSelectionRef, selectionRef, normalizeEditorLocale, DirtyState, createCommandRegistry, createDialogRegistry, activeRef, createSelectionModel, SheetSelections, translator, initialSelection, sheetBaseName, onRibbonTabsChange, activeChart, editChart, ribbonTabs, isTabHidden, selectTab, setTabHidden, tabButton, searchCommands, formatNumber, TEMPLATES, createTemplateWorkbook, isMenuSeparator, registerRibbonTabs, installFormatPainter, SHEET_ICONS, clipboardCommands, fontCommands, alignmentCommands, numberCommands, cellCommands, editingCommands, pageLayoutCommands, formulaCommands, dataCommands, contextualCommands, reviewTab, viewTab, helpTab, editBridge, CellViewCache, QUADRANTS, columnHeaderHeight, rowHeaderWidth, GridGeometry, cellRange, scrollToReveal, buildItems, fitNumber, cssFont, clampAddress, selectCell, movingEnd, extendSelection, moveWithinSelection, rowsRange, colsRange, ALL_RANGE, tabMenu, editing, icon, DATA_BAR_COLORS, dataBarRule, COLOR_SCALES, colorScaleRule, iconSetRule, target, tableAt, viewing, activeTable, numberFormatOptions, FONT_NAMES, FONT_SIZES, BORDER_PRESETS, ORIENTATIONS, ACCOUNTING_FORMATS, themePaletteItems, MARGIN_PRESETS, PAPER_SIZES, FUNCTION_MENUS, MORE_FUNCTION_CATEGORIES, functionsIn, isSingleCell, spanRange, selectionCellCount, addRange, activeStyle, selectSpecial, insertFunctionCall, guessHeader, regionOf, HIGHLIGHT_STYLES, ICON_SETS, TABLE_STYLES, tableStylePreview, resolveNameBox, absoluteReference, nameForRange, fillPaint, iconSvg, colSelected, rowSelected, rowHeaderMenu, columnHeaderMenu, cellMenu, recentFunctions, evaluateRef, buildChart, detectCategory, CATEGORIES, TIME_CODES, DATE_CODES, DEFAULT_OPTIONS, cellStyleItems, textDecoration, justify, alignItems, textAlign, hasBorders, edgeCss, diagonalSvg, SYMBOLS, buildCode, UNSET, dxfCss, negativeCodes, iconCount, iconThresholds, rowsOf, colsOf } from 'ooxml-core/xlsx/ui';
6
+ import { shadcnBridge } from './chunk-56A2JK63.js';
7
+ import { range_control_default, createRibbonOverflow, defineBackstage, defineRibbon, defineRibbonActions, defineTitleBar, defineStatusBar, defineZoomSlider, rangeControl, defineDialog, runKeyTips, assignKeyTips, defineCheckbox, defineGallery, defineContextMenu, defineCommentsPane, parseSvgPreview } from './chunk-723JC3OC.js';
8
+ import { getIcon, registerIcon } from './chunk-M6GF5ED2.js';
9
+ import { definePresence } from './chunk-TWQ45BFL.js';
10
+ import { withTokens } from './chunk-IY7QJLQG.js';
11
+ import './chunk-2WLRFPMW.js';
12
+ import { SPECIAL_CODES, FRACTION_CODES, AUTOSUM_FUNCTIONS, themeToCssVars, lightTheme, darkTheme, ICON_ACCENTS, ICON_PATHS, normalizeThemeMode, applyThemeColors, parseSelectionRef, selectionRef, normalizeEditorLocale, DirtyState, createCommandRegistry, createDialogRegistry, activeRef, createSelectionModel, SheetSelections, translator, initialSelection, sheetBaseName, historyOf, onRibbonTabsChange, activeChart, editChart, ribbonTabs, isTabHidden, selectTab, setTabHidden, tabButton, searchCommands, formatNumber, TEMPLATES, createTemplateWorkbook, isMenuSeparator, registerRibbonTabs, installFormatPainter, SHEET_ICONS, clipboardCommands, fontCommands, alignmentCommands, numberCommands, cellCommands, editingCommands, pageLayoutCommands, formulaCommands, dataCommands, contextualCommands, reviewTab, viewTab, helpTab, editBridge, CellViewCache, QUADRANTS, columnHeaderHeight, rowHeaderWidth, GridGeometry, cellRange, scrollToReveal, buildItems, fitNumber, cssFont, clampAddress, selectCell, movingEnd, extendSelection, moveWithinSelection, rowsRange, colsRange, ALL_RANGE, tabMenu, editing, icon, DATA_BAR_COLORS, dataBarRule, COLOR_SCALES, colorScaleRule, iconSetRule, target, tableAt, viewing, activeTable, numberFormatOptions, FONT_NAMES, FONT_SIZES, BORDER_PRESETS, ORIENTATIONS, ACCOUNTING_FORMATS, themePaletteItems, MARGIN_PRESETS, PAPER_SIZES, FUNCTION_MENUS, MORE_FUNCTION_CATEGORIES, functionsIn, isSingleCell, spanRange, selectionCellCount, addRange, activeStyle, selectSpecial, insertFunctionCall, guessHeader, regionOf, HIGHLIGHT_STYLES, ICON_SETS, TABLE_STYLES, tableStylePreview, resolveNameBox, absoluteReference, nameForRange, fillPaint, iconSvg, colSelected, rowSelected, rowHeaderMenu, columnHeaderMenu, cellMenu, recentFunctions, evaluateRef, buildChart, detectCategory, CATEGORIES, TIME_CODES, DATE_CODES, DEFAULT_OPTIONS, cellStyleItems, textDecoration, justify, alignItems, textAlign, hasBorders, edgeCss, diagonalSvg, SYMBOLS, buildCode, UNSET, dxfCss, negativeCodes, iconCount, iconThresholds, rowsOf, colsOf } from 'ooxml-core/xlsx/ui';
13
13
  export { EDITOR_LOCALES, THEME_KEYS, normalizeEditorLocale, registerRibbonTabs, themeToCssVars, darkTheme as xlsxDarkTheme, lightTheme as xlsxLightTheme } from 'ooxml-core/xlsx/ui';
14
- import { createWorksheet, createEditSession, approximateMeasure, createWorkbook, MAX_ROW, MAX_COL, chartView, createRefEvaluator, chartBarSpacing, CHART_FILL_PARTS, selectionStats, formatRange, chartSeriesTransparencyPatch, chartSeriesTransparency, chartSeriesGradientPatch, chartSeriesSolidFillPatch, chartSeriesFillPatch, chartWithElementFills, chartElementTransparencyPatch, chartElementTransparency, chartElementGradientPatch, chartElementSolidFillPatch, chartElementFillPatch, chartElementFill, usedRange, chartGradientStopTransparency, createGridMetrics, mergeView, cellView, DEFAULT_THEME, resolveColor, nextSheetName, FUNCTION_CATALOG, mergeAt, visibleCells, getCell, cellInputText, closeParens, isFormulaText, parseCellInput, referenceTarget, formatAddress, navigate, anchorToPixelBox, renderChartSvg, pixelBoxToAnchor, listValidationOptions, validationAt, getValue, resolvePasteOptions, DEFAULT_COL_WIDTH, nameAtCaret, functionMatches, callAtCaret, splice, acceptsReference, translateFormula, isCellError, parseHtmlTable, toggleAbsolute, rangeContains, formatValue, isDateFormat, pictureAnchorAt, validateDefinedName, parseAddress, styleAt, parseRange, rangesIntersect, chartColorScheme, coloredReferences, syntaxParams, activeParam, columnLabel, THEME_SLOTS, normalizeRange, currentRegion, autoFitColumnWidth, validateSheetName, dateToSerial, IMAGE_EXTENSIONS, verifySheetPassword, verifyWorkbookPassword, sheetByName, quoteSheetName, serialToDate, BUILTIN_NUMBER_FORMATS, pixelsToColumnWidth, validateTableName } from 'ooxml-core/xlsx';
14
+ import { createWorksheet, createEditSession, approximateMeasure, createWorkbook, MAX_ROW, MAX_COL, chartView, createRefEvaluator, chartBarSpacing, CHART_FILL_PARTS, selectionStats, formatSelectionStat, formatRange, chartSeriesTransparencyPatch, chartSeriesTransparency, chartSeriesGradientPatch, chartSeriesSolidFillPatch, chartSeriesFillPatch, chartWithElementFills, chartElementTransparencyPatch, chartElementTransparency, chartElementGradientPatch, chartElementSolidFillPatch, chartElementFillPatch, chartElementFill, usedRange, chartGradientStopTransparency, createGridMetrics, mergeView, cellView, DEFAULT_THEME, resolveColor, nextSheetName, FUNCTION_CATALOG, mergeAt, visibleCells, getCell, cellInputText, closeParens, isFormulaText, parseCellInput, referenceTarget, formatAddress, navigate, anchorToPixelBox, renderChartSvg, pixelBoxToAnchor, listValidationOptions, validationAt, getValue, resolvePasteOptions, DEFAULT_COL_WIDTH, nameAtCaret, functionMatches, callAtCaret, splice, acceptsReference, translateFormula, isCellError, layoutRemoteSelections, parseHtmlTable, toggleAbsolute, rangeContains, formatValue, isDateFormat, pictureAnchorAt, validateDefinedName, parseAddress, styleAt, parseRange, rangesIntersect, chartColorScheme, coloredReferences, syntaxParams, activeParam, columnLabel, THEME_SLOTS, normalizeRange, currentRegion, autoFitColumnWidth, validateSheetName, dateToSerial, IMAGE_EXTENSIONS, verifySheetPassword, verifyWorkbookPassword, sheetByName, quoteSheetName, serialToDate, BUILTIN_NUMBER_FORMATS, pixelsToColumnWidth, validateTableName } from 'ooxml-core/xlsx';
15
15
  import { hyperlinkPolicy } from 'ooxml-core/opc';
16
16
  import { resolveChartGradient, sortGradientStops, withChartGradientDirection, buildChartGradientDef, chartGradientMarkup, CHART_COLOR_PALETTES, chartPaletteColors } from 'ooxml-core/chart';
17
17
  import { render } from 'lit';
18
18
  import { RECT_GRADIENT_DIRECTIONS, OFFICE_GRADIENT_PRESETS, officeGradientPresetId, officeGradientPresetFill, resolveDrawingColor, drawingColorBrightness, drawingColorCss, rectGradientDirection } from 'ooxml-core/diagram';
19
19
 
20
- // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/xlsx/theme/office-bridge.css?raw
21
- var office_bridge_default = "/*\n * The shared ooxml-ui elements read only --office-* tokens. This feeds them from the editor's\n * --xve-* theme, declared on the host so a theme, a dark scheme or a host override of --xve-*\n * keeps working (the same approach as pptx-viewer's office-token-bridge and docx-viewer's).\n */\n:host {\n --office-font: 'Segoe UI', system-ui, -apple-system, sans-serif;\n --office-foreground: var(--xve-foreground);\n --office-muted-foreground: var(--xve-muted-foreground);\n --office-background: var(--xve-background);\n --office-surface: var(--xve-card);\n --office-selected: var(--xve-accent);\n --office-border: var(--xve-border);\n --office-accent: var(--xve-primary);\n --office-accent-foreground: var(--xve-primary-foreground);\n --office-ring: var(--xve-ring);\n --office-popover: var(--xve-popover);\n --office-popover-foreground: var(--xve-popover-foreground, var(--xve-foreground));\n --office-danger: var(--xve-destructive);\n --office-danger-foreground: var(--xve-destructive-foreground);\n}\n";
22
-
23
20
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/xlsx/theme/shell.css?raw
24
- var shell_default = "/* Frame, title bar and status bar. Mirrors docx-viewer's chrome (styles/base.css,\n chrome-titlebar.css, status-dialogs.css) with --xve-* tokens. */\n:host {\n --xl: var(--xve-primary);\n --xl-strong: color-mix(in srgb, var(--xve-primary) 70%, #000);\n --xl-soft: color-mix(in srgb, var(--xve-primary) 12%, transparent);\n --accent-soft: var(--xl-soft);\n --ink: var(--xve-foreground);\n --muted: var(--xve-muted-foreground);\n --line: var(--xve-border);\n --surface: var(--xve-card);\n --chrome: var(--xve-background);\n display: block;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n color: var(--ink);\n font:\n 13px/1.35 'Segoe UI',\n Arial,\n sans-serif;\n}\n.xve-frame {\n position: relative;\n display: flex;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n min-width: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--surface);\n}\n:focus-visible {\n outline: 2px solid var(--xve-ring);\n outline-offset: 1px;\n}\n[hidden],\n[data-xve-hidden] {\n display: none !important;\n}\n.xve-icon {\n flex: none;\n}\n.xve-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 28px;\n padding: 0 6px;\n border: 0;\n border-radius: 4px;\n background: none;\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n.xve-icon-button:hover:not(:disabled),\n.xve-icon-button[aria-pressed='true'] {\n background: var(--xl-soft);\n}\n.xve-icon-button:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* Workspace: formula bar, grid and sheet tabs between the ribbon and the status bar. */\n.xve-workspace {\n position: relative;\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n flex-direction: column;\n background: var(--xve-sheet-bg);\n}\n.xve-formula-row {\n flex: none;\n min-width: 0;\n border-bottom: 1px solid var(--line);\n background: var(--surface);\n}\n.xve-grid-host {\n position: relative;\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n background: var(--xve-sheet-bg);\n color-scheme: light;\n}\n.xve-grid-host > * {\n flex: 1;\n min-width: 0;\n}\n.xve-tabs-row {\n flex: none;\n min-width: 0;\n border-top: 1px solid var(--line);\n background: var(--chrome);\n}\n.xve-empty {\n position: absolute;\n z-index: 5;\n inset: 0;\n display: grid;\n place-items: center;\n color: var(--muted);\n background: var(--chrome);\n}\n:host([show-formula-bar='false']) .xve-formula-row,\n:host([show-toolbar='false']) .xve-ribbon {\n display: none;\n}\n\n/* Title bar: drawn by the shared office-ui-title-bar; the mark, background and borders come from its tokens. */\n.xve-titlebar {\n display: block;\n flex: none;\n --office-title-bar-background: var(--chrome);\n --office-title-bar-foreground: var(--ink);\n --office-title-bar-border: var(--line);\n --office-title-bar-mark-background: var(--xl);\n --office-title-bar-mark-foreground: var(--xve-primary-foreground);\n}\n.xve-title-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n.xve-mode-select {\n height: 28px;\n margin-left: 4px;\n padding: 0 6px;\n border: 1px solid var(--line);\n border-radius: 4px;\n background: var(--surface);\n color: var(--ink);\n font: inherit;\n}\n\n/* Status bar: drawn by the shared office-ui-status-bar; this places it and styles the slotted stats. */\n.xve-status {\n display: block;\n flex: none;\n min-width: 0;\n border-top: 1px solid var(--line);\n}\n.xve-status-stats {\n margin-inline-end: 10px;\n overflow: hidden;\n color: var(--ink);\n font-size: 11px;\n white-space: nowrap;\n}\n\n@media (max-width: 760px) {\n .xve-status-stats {\n display: none;\n }\n}\n";
21
+ var shell_default = "/* Frame, title bar and status bar. Mirrors docx-viewer's chrome (styles/base.css,\n chrome-titlebar.css, status-dialogs.css) with --xve-* tokens. */\n:host {\n --xl: var(--xve-primary);\n --xl-strong: color-mix(in srgb, var(--xve-primary) 70%, #000);\n --xl-soft: color-mix(in srgb, var(--xve-primary) 12%, transparent);\n --accent-soft: var(--xl-soft);\n --ink: var(--xve-foreground);\n --muted: var(--xve-muted-foreground);\n --line: var(--xve-border);\n --surface: var(--xve-card);\n --chrome: var(--xve-background);\n display: block;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n color: var(--ink);\n font:\n 13px/1.35 'Segoe UI',\n Arial,\n sans-serif;\n}\n.xve-frame {\n position: relative;\n display: flex;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n min-width: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--surface);\n}\n:focus-visible {\n outline: 2px solid var(--xve-ring);\n outline-offset: 1px;\n}\n[hidden],\n[data-xve-hidden] {\n display: none !important;\n}\n.xve-icon {\n flex: none;\n}\n.xve-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 28px;\n padding: 0 6px;\n border: 0;\n border-radius: 4px;\n background: none;\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n.xve-icon-button:hover:not(:disabled),\n.xve-icon-button[aria-pressed='true'] {\n background: var(--xl-soft);\n}\n.xve-icon-button:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* Workspace: formula bar, grid and sheet tabs between the ribbon and the status bar. */\n.xve-workspace {\n position: relative;\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n flex-direction: column;\n background: var(--xve-sheet-bg);\n}\n.xve-formula-row {\n flex: none;\n min-width: 0;\n border-bottom: 1px solid var(--line);\n background: var(--surface);\n}\n.xve-grid-host {\n position: relative;\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n background: var(--xve-sheet-bg);\n color-scheme: light;\n}\n.xve-grid-host > * {\n flex: 1;\n min-width: 0;\n}\n.xve-tabs-row {\n flex: none;\n min-width: 0;\n border-top: 1px solid var(--line);\n background: var(--chrome);\n}\n.xve-empty {\n position: absolute;\n z-index: 5;\n inset: 0;\n display: grid;\n place-items: center;\n color: var(--muted);\n background: var(--chrome);\n}\n:host([show-formula-bar='false']) .xve-formula-row,\n:host([show-toolbar='false']) .xve-ribbon {\n display: none;\n}\n\n/* Title bar: drawn by the shared office-ui-title-bar; the mark, background and borders come from its tokens. */\n.xve-titlebar {\n display: block;\n flex: none;\n --office-title-bar-background: var(--chrome);\n --office-title-bar-foreground: var(--ink);\n --office-title-bar-border: var(--line);\n --office-title-bar-mark-background: var(--xl);\n --office-title-bar-mark-foreground: var(--xve-primary-foreground);\n}\n\n/* Status bar: drawn by the shared office-ui-status-bar; this places it and styles the slotted stats. */\n.xve-status {\n display: block;\n flex: none;\n min-width: 0;\n border-top: 1px solid var(--line);\n}\n.xve-status-stats {\n margin-inline-end: 10px;\n overflow: hidden;\n color: var(--ink);\n font-size: 11px;\n white-space: nowrap;\n}\n\n@media (max-width: 760px) {\n .xve-status-stats {\n display: none;\n }\n}\n";
25
22
 
26
23
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/xlsx/theme/ribbon.css?raw
27
24
  var ribbon_default = "/* Ribbon: tabs, panels, groups and controls. Mirrors docx-viewer's styles/ribbon*.css. */\n.xve-ribbon-gallery[data-office-gallery] .trigger {\n flex-direction: column;\n justify-content: center;\n min-width: 56px;\n height: 78px;\n white-space: normal;\n text-align: center;\n}\n.xve-ribbon-gallery[data-office-gallery] .trigger > svg {\n width: 28px;\n height: 28px;\n}\n.xve-ribbon-gallery[data-office-gallery] .trigger > span:not([aria-hidden]) {\n max-width: 56px;\n}\n.xve-ribbon {\n position: relative;\n display: flex;\n flex: none;\n min-width: 0;\n flex-direction: column;\n background: var(--surface);\n border-bottom: 1px solid var(--line);\n}\n/* The tab row, File button and collapse button are the shared office-ui-ribbon; these colour it. */\n.xve-ribbon {\n --office-ribbon-contextual: #0f7b6c;\n}\n.xve-ribbon::part(file) {\n font-weight: 600;\n}\n.ribbon-panel {\n display: flex;\n flex: none;\n box-sizing: border-box;\n height: 104px;\n min-width: 0;\n align-items: stretch;\n gap: 4px;\n padding: 6px 10px 4px;\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: thin;\n}\n.ribbon-group {\n position: relative;\n display: flex;\n flex: 0 0 auto;\n align-items: flex-start;\n min-width: 0;\n gap: 2px;\n padding: 2px 10px 18px 2px;\n border-right: 1px solid var(--line);\n}\n.ribbon-group:last-child {\n border: 0;\n}\n.ribbon-group::after {\n position: absolute;\n right: 0;\n bottom: 2px;\n left: 0;\n color: var(--muted);\n font-size: 10px;\n line-height: 1.1;\n text-align: center;\n white-space: nowrap;\n content: attr(data-caption);\n}\n.ribbon-caption-sizer {\n position: absolute;\n visibility: hidden;\n font-size: 10px;\n white-space: nowrap;\n}\n.ribbon-stack {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n}\n.ribbon-stack > .ribbon-stack {\n flex-direction: row;\n align-items: center;\n}\n.ribbon-separator {\n align-self: stretch;\n width: 1px;\n margin: 2px 3px;\n background: var(--line);\n}\n.ribbon-stack > .ribbon-separator {\n width: auto;\n height: 1px;\n}\n.ribbon-stack > .ribbon-stack > .ribbon-separator {\n width: 1px;\n height: 20px;\n}\n\n/* Buttons */\n.ribbon-group button {\n flex: none;\n min-width: 28px;\n height: 28px;\n max-width: 100%;\n padding: 0 4px;\n overflow: hidden;\n border: 1px solid transparent;\n border-radius: 4px;\n background: transparent;\n color: var(--ink);\n font:\n 400 12px 'Segoe UI',\n Arial,\n sans-serif;\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n}\n.ribbon-group button:hover:not(:disabled) {\n border-color: var(--line);\n background: var(--accent-soft);\n}\n.ribbon-group button[aria-pressed='true'],\n.ribbon-group button[aria-expanded='true'] {\n border-color: color-mix(in srgb, var(--xl) 45%, transparent);\n background: color-mix(in srgb, var(--xl) 20%, transparent);\n}\n.ribbon-group button:active:not(:disabled) {\n background: color-mix(in srgb, var(--xl) 28%, transparent);\n}\n.ribbon-group button:focus-visible {\n outline: 2px solid var(--xl);\n outline-offset: -2px;\n}\n.ribbon-group button:disabled,\n.ribbon-group input:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.ribbon-group button.ribbon-small {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 24px;\n padding: 0 3px;\n}\n.ribbon-group button.ribbon-small.ribbon-labelled {\n justify-content: flex-start;\n padding: 0 6px 0 4px;\n}\n.ribbon-group button.ribbon-large {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-start;\n gap: 2px;\n width: auto;\n min-width: 48px;\n height: 76px;\n padding: 3px 3px 2px;\n font-size: 11px;\n line-height: 1.15;\n white-space: normal;\n text-align: center;\n}\n.ribbon-large > .ribbon-icon:last-child:not(:first-child) {\n margin-top: auto;\n}\n.ribbon-large > span,\n.ribbon-caption {\n /* Two balanced lines (see ribbon/caption.ts); the button grows to the widest. */\n white-space: pre;\n}\n.ribbon-icon {\n flex: none;\n}\n.ribbon-icon[data-icon$='-caret'],\n.ribbon-icon[data-icon='caret'] {\n opacity: 0.8;\n}\n\n/* Split buttons: a main half and a caret that opens a menu or the colour grid. */\n.ribbon-split {\n display: inline-flex;\n align-items: stretch;\n}\n.ribbon-split.ribbon-split-large {\n flex-direction: column;\n}\n.ribbon-group .ribbon-split > button:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.ribbon-group .ribbon-split > .ribbon-caret {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n min-width: 14px;\n padding: 0;\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n.ribbon-group .ribbon-split-large > button:first-child {\n height: 46px;\n border-radius: 4px 4px 0 0;\n}\n.ribbon-group .ribbon-split-large > .ribbon-caret {\n width: auto;\n height: 26px;\n border-radius: 0 0 4px 4px;\n font-size: 11px;\n flex-direction: column;\n gap: 0;\n}\n.ribbon-color {\n position: relative;\n}\n.ribbon-group button.ribbon-color::after {\n position: absolute;\n right: 4px;\n bottom: 2px;\n left: 4px;\n height: 3px;\n background: var(--bar, transparent);\n outline: 1px solid var(--line);\n content: '';\n}\n\n/* Editable combo boxes (font name and size) and plain selects. */\n.ribbon-combo {\n display: inline-flex;\n align-items: stretch;\n height: 24px;\n border: 1px solid var(--line);\n border-radius: 3px;\n background: var(--surface);\n}\n.ribbon-combo:focus-within {\n border-color: var(--xl);\n outline: 1px solid var(--xl);\n}\n.ribbon-combo input {\n flex: 1;\n min-width: 0;\n padding: 0 6px;\n border: 0;\n background: transparent;\n color: var(--ink);\n font:\n 12px 'Segoe UI',\n Arial,\n sans-serif;\n outline: none;\n}\n.ribbon-group .ribbon-combo .ribbon-caret {\n width: 18px;\n min-width: 18px;\n height: 100%;\n padding: 0;\n border: 0;\n border-radius: 0;\n}\n\n/* Plain drop-downs (Number Format) and gallery previews. */\n.ribbon-group select.ribbon-select {\n height: 24px;\n padding: 0 6px;\n border: 1px solid var(--line);\n border-radius: 3px;\n background: var(--surface);\n color: var(--ink);\n font:\n 12px 'Segoe UI',\n Arial,\n sans-serif;\n text-overflow: ellipsis;\n}\n.ribbon-group select.ribbon-select:focus-visible {\n outline: 2px solid var(--xl);\n outline-offset: -1px;\n}\n.ribbon-gallery-preview {\n display: block;\n height: 36px;\n margin-bottom: 3px;\n border: 1px solid var(--line);\n border-radius: 2px;\n background: #fff;\n color: #1f1f1f;\n font-size: 11px;\n line-height: 36px;\n text-align: center;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.ribbon-gallery-preview > svg {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n/* Dialog launcher: the corner arrow of a group. */\n.ribbon-group button.ribbon-launcher {\n position: absolute;\n right: 2px;\n bottom: 0;\n z-index: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n min-width: 14px;\n height: 14px;\n padding: 0;\n color: var(--muted);\n}\n\n/* Collapsed groups: a narrow ribbon folds its rightmost groups into one dropdown each. */\n.ribbon-group > .ribbon-overflow-button {\n display: none;\n}\n.ribbon-group[data-collapsed] {\n padding-right: 4px;\n}\n.ribbon-group[data-collapsed] > :not(.ribbon-overflow-button) {\n display: none !important;\n}\n.ribbon-group[data-collapsed] > button.ribbon-overflow-button {\n display: flex;\n}\n.ribbon-group[data-collapsed]::after {\n display: none;\n}\n.ribbon-overflow-panel .ribbon-group {\n height: 84px;\n padding-right: 2px;\n border: 0;\n}\n\n/* KeyTips */\n.xve-keytip {\n position: absolute;\n bottom: -2px;\n left: 50%;\n z-index: 70;\n min-width: 16px;\n padding: 1px 4px;\n border: 1px solid var(--line);\n border-radius: 3px;\n background: #1f1f1f;\n color: #fff;\n font:\n 600 11px/14px 'Segoe UI',\n Arial,\n sans-serif;\n text-align: center;\n pointer-events: none;\n transform: translateX(-50%);\n}\n.xve-keytip-command {\n position: fixed;\n bottom: auto;\n}\n\n/* Accent hues on command icons, as in Office. */\n:host {\n --ribbon-accent-blue: #2b7cd3;\n --ribbon-accent-green: #2f9e5f;\n --ribbon-accent-red: #d13438;\n --ribbon-accent-orange: #d9822b;\n}\n@media (prefers-color-scheme: dark) {\n :host(:not([theme='light'])) {\n --ribbon-accent-blue: #5ea5f0;\n --ribbon-accent-green: #5cc98a;\n --ribbon-accent-red: #f07a7d;\n --ribbon-accent-orange: #f0a35e;\n }\n}\n:host([theme='dark']) {\n --ribbon-accent-blue: #5ea5f0;\n --ribbon-accent-green: #5cc98a;\n --ribbon-accent-red: #f07a7d;\n --ribbon-accent-orange: #f0a35e;\n}\n.ribbon-icon[data-accent='blue'] {\n color: var(--ribbon-accent-blue);\n}\n.ribbon-icon[data-accent='green'] {\n color: var(--ribbon-accent-green);\n}\n.ribbon-icon[data-accent='red'] {\n color: var(--ribbon-accent-red);\n}\n.ribbon-icon[data-accent='orange'] {\n color: var(--ribbon-accent-orange);\n}\n";
@@ -43,7 +40,10 @@ var themeTokenText = `:host{${declarations(lightTheme, "light")}}
43
40
  :host([theme="light"]){${declarations(lightTheme, "light")}}`;
44
41
  var editorStyleText = [
45
42
  themeTokenText,
46
- office_bridge_default,
43
+ // The shared elements read --office-*; feed them from the editor's --xve-* theme.
44
+ shadcnBridge(":host", "--xve-", {
45
+ extra: { "--office-font": "'Segoe UI', system-ui, -apple-system, sans-serif" }
46
+ }),
47
47
  shell_default,
48
48
  ribbon_default,
49
49
  popups_default,
@@ -363,7 +363,7 @@ function renderInfo(page) {
363
363
  paragraph(
364
364
  page,
365
365
  t(
366
- "Charts are shown but not edited; pivot tables, sparklines, macros and other unsupported parts are kept in the file but not shown."
366
+ "Charts and sparklines are shown but not edited; pivot tables, macros and other unsupported parts are kept in the file but not shown."
367
367
  ),
368
368
  "xve-backstage-muted"
369
369
  )
@@ -856,12 +856,105 @@ function renderCustomize(page) {
856
856
  ...sections
857
857
  );
858
858
  }
859
+
860
+ // src/xlsx/backstage/pages-share.ts
861
+ var STATUS = {
862
+ off: "Not sharing.",
863
+ connecting: "Connecting...",
864
+ connected: "Connected.",
865
+ disconnected: "Disconnected. Changes are kept and sent when the connection returns.",
866
+ error: "The connection failed. Changes are kept and sent when the connection returns."
867
+ };
868
+ var suggestRoom = () => `book-${Math.random().toString(36).slice(2, 8)}`;
869
+ function renderShare(page) {
870
+ const { t, doc } = page;
871
+ const share = page.host.share;
872
+ if (!share) {
873
+ page.content.replaceChildren(heading(page, t("Share")));
874
+ return;
875
+ }
876
+ const state = share.state();
877
+ const status = paragraph(page, t(STATUS[state.status]), "xve-share-status");
878
+ status.setAttribute("role", "status");
879
+ const say = (message) => {
880
+ status.textContent = message;
881
+ };
882
+ const children = [
883
+ heading(page, t("Share")),
884
+ paragraph(
885
+ page,
886
+ t(
887
+ "Edit this workbook together with other people. Everyone who joins the same session sees changes as they are made."
888
+ )
889
+ )
890
+ ];
891
+ if (state.active) {
892
+ children.push(paragraph(page, t("Session: {room}", { room: state.roomId ?? "" })));
893
+ if (state.status === "connected" && !state.synced) say(t("Joining the session..."));
894
+ } else {
895
+ const room = doc.createElement("input");
896
+ room.type = "text";
897
+ room.className = "xve-share-room";
898
+ room.maxLength = 128;
899
+ room.autocomplete = "off";
900
+ room.spellcheck = false;
901
+ room.value = suggestRoom();
902
+ children.push(labelled(page, t("Session name"), room));
903
+ const start = primary(page, t("Start sharing"), () => {
904
+ start.disabled = true;
905
+ say(t("Connecting..."));
906
+ share.start(room.value.trim()).catch((error) => {
907
+ start.disabled = false;
908
+ say(t(error instanceof Error ? error.message : String(error)));
909
+ });
910
+ });
911
+ start.dataset.share = "start";
912
+ children.push(start);
913
+ }
914
+ children.push(status);
915
+ if (state.active) {
916
+ definePresence();
917
+ const people = doc.createElement("office-ui-presence");
918
+ people.className = "xve-share-people";
919
+ people.setAttribute("label", t("People in this session"));
920
+ people.participants = participants(state);
921
+ const stop = primary(page, t("Stop sharing"), () => share.stop());
922
+ stop.dataset.share = "stop";
923
+ children.push(people, stop);
924
+ }
925
+ children.push(
926
+ paragraph(
927
+ page,
928
+ t(
929
+ "Cell values, formulas, formats, sheets, rows, columns, merges and names are shared. Charts, pictures, comments, conditional formats, data validation, tables and page setup stay in this window."
930
+ ),
931
+ "xve-backstage-muted"
932
+ ),
933
+ paragraph(
934
+ page,
935
+ t(
936
+ "Without a server set by the page, a session connects only windows and tabs of this browser. Nothing is uploaded."
937
+ ),
938
+ "xve-backstage-muted"
939
+ )
940
+ );
941
+ page.content.replaceChildren(...children);
942
+ }
943
+ function participants(state) {
944
+ return state.people.map((person) => ({
945
+ id: String(person.clientId),
946
+ name: person.name,
947
+ color: person.color,
948
+ ...person.self ? { self: true } : {}
949
+ }));
950
+ }
859
951
  var PAGES = [
860
952
  ["info", "Info"],
861
953
  ["new", "New"],
862
954
  ["open", "Open"],
863
955
  ["saveAs", "Save As"],
864
956
  ["print", "Print"],
957
+ ["share", "Share"],
865
958
  ["export", "Export"],
866
959
  ["options", "Options"],
867
960
  ["customize", "Customize Ribbon"]
@@ -872,6 +965,7 @@ var RENDERERS = {
872
965
  open: renderOpen,
873
966
  saveAs: renderSaveAs,
874
967
  print: renderPrint,
968
+ share: renderShare,
875
969
  export: renderExport,
876
970
  options: renderOptions,
877
971
  customize: renderCustomize
@@ -934,6 +1028,7 @@ function createBackstage(host) {
934
1028
  item("save", "Save"),
935
1029
  page("saveAs"),
936
1030
  page("print"),
1031
+ page("share"),
937
1032
  page("export"),
938
1033
  page("options", "footer"),
939
1034
  page("customize", "footer")
@@ -953,7 +1048,10 @@ function createBackstage(host) {
953
1048
  get isOpen() {
954
1049
  return element.open;
955
1050
  },
956
- relocalize
1051
+ relocalize,
1052
+ refresh(page) {
1053
+ if (element.open && current === page) show(page);
1054
+ }
957
1055
  };
958
1056
  }
959
1057
 
@@ -3083,46 +3181,61 @@ function openGoToSpecial(ctx) {
3083
3181
  }
3084
3182
 
3085
3183
  // src/xlsx/dialogs/comments-list.ts
3184
+ function excelCommentThreads(comments) {
3185
+ return [...comments].sort((a, b) => a.address.row - b.address.row || a.address.col - b.address.col).map((comment) => {
3186
+ const id = formatAddress(comment.address);
3187
+ return {
3188
+ id,
3189
+ anchorLabel: id,
3190
+ resolved: false,
3191
+ comments: [
3192
+ { id: `${id}:0`, author: comment.author, text: comment.text },
3193
+ ...(comment.replies ?? []).map((reply, index) => ({
3194
+ id: `${id}:${index + 1}`,
3195
+ author: reply.author,
3196
+ text: reply.text,
3197
+ ...reply.date ? { created: reply.date } : {}
3198
+ }))
3199
+ ]
3200
+ };
3201
+ });
3202
+ }
3086
3203
  function openCommentsList(ctx) {
3087
3204
  const ws = ctx.workbook()?.sheets[ctx.activeSheet()];
3088
- const comments = [...ws?.comments ?? []].sort(
3089
- (a, b) => a.address.row - b.address.row || a.address.col - b.address.col
3090
- );
3205
+ const comments = ws?.comments ?? [];
3206
+ const threads = excelCommentThreads(comments);
3091
3207
  let close;
3092
3208
  let chosen;
3093
- const activate = (value) => {
3094
- const comment = comments[Number(value)];
3209
+ defineCommentsPane(ctx.host.ownerDocument.defaultView?.customElements);
3210
+ const pane = ctx.host.ownerDocument.createElement(
3211
+ "office-ui-comments-pane"
3212
+ );
3213
+ pane.className = "xve-comments-list";
3214
+ pane.listOnly = true;
3215
+ pane.labels = {
3216
+ heading: "",
3217
+ list: ctx.t("Comments"),
3218
+ empty: ctx.t("There are no comments on this sheet.")
3219
+ };
3220
+ pane.threads = threads;
3221
+ pane.activeThreadId = threads[0]?.id ?? null;
3222
+ pane.addEventListener("thread-select", (event) => {
3223
+ const id = event.detail.threadId;
3224
+ const comment = comments.find((item) => formatAddress(item.address) === id);
3095
3225
  if (!comment) return;
3096
3226
  const at = { ...comment.address };
3097
- chosen = formatAddress(at);
3227
+ chosen = id;
3098
3228
  selectOn(ctx, ctx.activeSheet(), { start: at, end: at });
3099
3229
  close?.();
3100
- };
3101
- const list = listBox(ctx, "Comments", void 0, activate);
3102
- list.setItems(
3103
- comments.map(
3104
- (c, i) => [
3105
- String(i),
3106
- `${formatAddress(c.address)} ${c.author}: ${c.text.replace(/\s+/g, " ")}`
3107
- ]
3108
- )
3109
- );
3110
- for (const option of list.element.querySelectorAll('[role="option"]'))
3111
- option.addEventListener(
3112
- "click",
3113
- () => option.dataset.value !== void 0 && activate(option.dataset.value)
3114
- );
3115
- const first = comments.length ? "0" : void 0;
3116
- if (first) list.select(first);
3117
- const body = comments.length ? [list.element] : [text(ctx, "There are no comments on this sheet.")];
3230
+ });
3118
3231
  return showDialog(
3119
3232
  ctx,
3120
3233
  {
3121
3234
  name: "comments-list",
3122
3235
  heading: "Comments",
3123
3236
  okLabel: null,
3124
- body,
3125
- opened: () => list.element.focus()
3237
+ body: [pane],
3238
+ opened: () => pane.focus()
3126
3239
  },
3127
3240
  (open) => {
3128
3241
  close = open.close;
@@ -4067,7 +4180,8 @@ var SUPPORTED_FEATURES = [
4067
4180
  "Freeze panes"
4068
4181
  ];
4069
4182
  var UNSUPPORTED_FEATURES = [
4070
- "Pivot tables and sparklines (kept in the file but not shown)",
4183
+ "Pivot tables (kept in the file but not shown)",
4184
+ "Creating or editing sparklines (existing ones are shown)",
4071
4185
  "Show Formulas",
4072
4186
  "Manual calculation",
4073
4187
  "Outline (grouping) for columns",
@@ -11092,10 +11206,10 @@ function gridCommands(host) {
11092
11206
  icon: "undo",
11093
11207
  shortcut: "Ctrl+Z",
11094
11208
  editing: true,
11095
- enabled: (ctx) => Boolean(ctx.session()?.canUndo()) || host.isEditing(),
11209
+ enabled: (ctx) => Boolean(historyOf(ctx)?.canUndo()) || host.isEditing(),
11096
11210
  run: (ctx) => {
11097
11211
  if (host.isEditing()) return host.cancelEdit();
11098
- ctx.session()?.undo();
11212
+ historyOf(ctx)?.undo();
11099
11213
  }
11100
11214
  },
11101
11215
  {
@@ -11104,8 +11218,8 @@ function gridCommands(host) {
11104
11218
  icon: "redo",
11105
11219
  shortcut: "Ctrl+Y",
11106
11220
  editing: true,
11107
- enabled: (ctx) => Boolean(ctx.session()?.canRedo()),
11108
- run: (ctx) => void ctx.session()?.redo()
11221
+ enabled: (ctx) => Boolean(historyOf(ctx)?.canRedo()),
11222
+ run: (ctx) => void historyOf(ctx)?.redo()
11109
11223
  },
11110
11224
  {
11111
11225
  id: "edit.select-all",
@@ -11880,6 +11994,82 @@ var GridSelection = class {
11880
11994
  return g.fCols > 0 ? 0 : this.#view.metrics.colAt(g.scrollLeft);
11881
11995
  }
11882
11996
  };
11997
+
11998
+ // src/xlsx/grid/sparkline.ts
11999
+ var SVG = "http://www.w3.org/2000/svg";
12000
+ var fixed = (n) => String(Math.round(n * 100) / 100);
12001
+ function svgElement(doc, tag, attrs) {
12002
+ const node = doc.createElementNS(SVG, tag);
12003
+ for (const [name, value] of Object.entries(attrs)) node.setAttribute(name, value);
12004
+ return node;
12005
+ }
12006
+ function sparklineSvg(doc, view, w, h2, zoom) {
12007
+ const scale = zoom / 100;
12008
+ const stroke = Math.max(0.5, view.lineWeightPt * (96 / 72) * scale);
12009
+ const radius = view.markers.length ? Math.max(1.5 * scale, stroke * 1.25) : 0;
12010
+ const padX = 2 * scale + (view.type === "line" ? Math.max(radius, stroke / 2) : 0);
12011
+ const padY = 2 * scale + (view.type === "line" ? Math.max(radius, stroke / 2) : 0);
12012
+ const innerW = Math.max(0, w - 2 * padX);
12013
+ const innerH = Math.max(0, h2 - 2 * padY);
12014
+ const px = (x) => padX + x * innerW;
12015
+ const py = (y) => padY + y * innerH;
12016
+ const svg = svgElement(doc, "svg", {
12017
+ class: "xg-spark",
12018
+ width: fixed(w),
12019
+ height: fixed(h2),
12020
+ "aria-hidden": "true"
12021
+ });
12022
+ if (view.axis)
12023
+ svg.append(
12024
+ svgElement(doc, "line", {
12025
+ x1: fixed(padX),
12026
+ x2: fixed(padX + innerW),
12027
+ y1: fixed(py(view.axis.y)),
12028
+ y2: fixed(py(view.axis.y)),
12029
+ stroke: view.axis.color,
12030
+ "stroke-width": fixed(Math.max(1, scale)),
12031
+ "shape-rendering": "crispEdges"
12032
+ })
12033
+ );
12034
+ for (const column of view.columns) {
12035
+ const height = Math.max(1, column.h * innerH);
12036
+ const top = column.h * innerH < 1 && column.y >= 0.5 ? py(column.y) - 1 : py(column.y);
12037
+ svg.append(
12038
+ svgElement(doc, "rect", {
12039
+ x: fixed(px(column.x)),
12040
+ y: fixed(top),
12041
+ width: fixed(Math.max(1, column.w * innerW)),
12042
+ height: fixed(height),
12043
+ fill: column.color
12044
+ })
12045
+ );
12046
+ }
12047
+ for (const line of view.lines) {
12048
+ if (line.length === 1) continue;
12049
+ svg.append(
12050
+ svgElement(doc, "polyline", {
12051
+ points: line.map((p) => `${fixed(px(p.x))},${fixed(py(p.y))}`).join(" "),
12052
+ fill: "none",
12053
+ stroke: view.color,
12054
+ "stroke-width": fixed(stroke),
12055
+ "stroke-linejoin": "round",
12056
+ "stroke-linecap": "round"
12057
+ })
12058
+ );
12059
+ }
12060
+ for (const marker of view.markers)
12061
+ svg.append(
12062
+ svgElement(doc, "circle", {
12063
+ cx: fixed(px(marker.x)),
12064
+ cy: fixed(py(marker.y)),
12065
+ r: fixed(radius),
12066
+ fill: marker.color
12067
+ })
12068
+ );
12069
+ return svg;
12070
+ }
12071
+
12072
+ // src/xlsx/grid/cell-layer.ts
11883
12073
  var viewKeys = /* @__PURE__ */ new WeakMap();
11884
12074
  var viewKey = (view) => {
11885
12075
  let key = viewKeys.get(view);
@@ -12006,6 +12196,7 @@ function paintCell(doc, node, item, zoom) {
12006
12196
  style.backgroundImage = fill?.image ?? "";
12007
12197
  node.className = `xg-c${item.overflowing ? " xg-c-over" : ""}${view.hasHyperlink ? " xg-link" : ""}${item.merged ? " xg-merged" : ""}`;
12008
12198
  node.replaceChildren();
12199
+ if (view.sparkline) node.append(sparklineSvg(doc, view.sparkline, item.w, item.h, zoom));
12009
12200
  if (view.dataBar) {
12010
12201
  const bar = h(doc, "div", "xg-db");
12011
12202
  const width = Math.max(0, Math.min(1, view.dataBar.fraction)) * (item.w - 4);
@@ -12764,6 +12955,7 @@ var GRID_CSS = `
12764
12955
  .xg-link{cursor:pointer}
12765
12956
  .xg-b{position:absolute;left:-1px;top:-1px;box-sizing:border-box;pointer-events:none}
12766
12957
  .xg-diag{position:absolute;left:0;top:0;pointer-events:none}
12958
+ .xg-spark{position:absolute;left:0;top:0;pointer-events:none;overflow:hidden}
12767
12959
  .xg-db{position:absolute;left:2px;top:2px;bottom:2px;border:1px solid;box-sizing:border-box}
12768
12960
  .xg-ic{position:absolute;left:2px;top:50%;transform:translateY(-50%)}
12769
12961
  .xg-cm{position:absolute;right:0;top:0;width:0;height:0;border-left:6px solid transparent;border-top:6px solid #d13438;z-index:2}
@@ -12781,6 +12973,12 @@ var GRID_CSS = `
12781
12973
  @media (prefers-reduced-motion:reduce){.xg-ants{animation:none}}
12782
12974
  .xg-fill-preview{position:absolute;box-sizing:border-box;border:1px dashed #666}
12783
12975
  .xg-refbox{position:absolute;box-sizing:border-box;border:2px solid}
12976
+ .xg-remote-layer{position:absolute;left:0;top:0;pointer-events:none;z-index:3}
12977
+ .xg-remote{position:absolute;box-sizing:border-box;border:2px solid}
12978
+ .xg-remote[hidden]{display:none}
12979
+ .xg-remote-tag{position:absolute;left:-2px;bottom:100%;max-width:160px;padding:0 4px;height:16px;line-height:16px;
12980
+ font:600 11px/16px var(--xve-font,system-ui,sans-serif);color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-radius:2px 2px 2px 0}
12981
+ .xg-remote[data-tag="inside"] .xg-remote-tag{bottom:auto;top:0;left:0;border-radius:0 0 2px 0}
12784
12982
  .xg-hbox{background:var(--xve-header-bg,#f3f3f3);z-index:4}
12785
12983
  .xg-hdr-plane{position:absolute;left:0;top:0;will-change:transform}
12786
12984
  .xg-hd{position:absolute;box-sizing:border-box;display:flex;align-items:center;justify-content:center;
@@ -12986,6 +13184,66 @@ var ValidationList = class {
12986
13184
  if (refocus) this.#onDone();
12987
13185
  }
12988
13186
  };
13187
+ var TAG_HEIGHT = 16;
13188
+ var RemoteLayer = class {
13189
+ constructor(doc) {
13190
+ this.doc = doc;
13191
+ this.element = h(doc, "div", "xg-remote-layer");
13192
+ }
13193
+ doc;
13194
+ element;
13195
+ #nodes = [];
13196
+ render(boxes) {
13197
+ boxes.forEach((box, index) => {
13198
+ let node = this.#nodes[index];
13199
+ if (!node) {
13200
+ node = h(this.doc, "div", "xg-remote");
13201
+ node.append(h(this.doc, "span", "xg-remote-tag"));
13202
+ this.#nodes.push(node);
13203
+ this.element.append(node);
13204
+ }
13205
+ node.hidden = false;
13206
+ place(node, box.x - 1, box.y - 1, box.w + 1, box.h + 1);
13207
+ node.style.borderColor = box.color;
13208
+ node.dataset.clientId = String(box.clientId);
13209
+ node.dataset.tag = box.tag;
13210
+ node.title = box.name;
13211
+ const tag = node.firstElementChild;
13212
+ tag.textContent = box.label;
13213
+ tag.style.backgroundColor = box.color;
13214
+ });
13215
+ for (let i = boxes.length; i < this.#nodes.length; i++) {
13216
+ const node = this.#nodes[i];
13217
+ if (node) node.hidden = true;
13218
+ }
13219
+ }
13220
+ };
13221
+ function installRemoteSelections(view) {
13222
+ const ctx = view.ctx;
13223
+ const layers = QUADRANTS.map((q) => {
13224
+ const layer = new RemoteLayer(view.doc);
13225
+ view.quads[q].plane.append(layer.element);
13226
+ return layer;
13227
+ });
13228
+ const hook = () => {
13229
+ const peers = ctx.remoteSelections?.() ?? [];
13230
+ const boxes = peers.length ? layoutRemoteSelections(view.metrics, peers, {
13231
+ sheet: view.sheetIndex(),
13232
+ maxRow: view.extentRow,
13233
+ maxCol: view.extentCol,
13234
+ tagHeight: TAG_HEIGHT
13235
+ }) : [];
13236
+ for (const layer of layers) layer.render(boxes);
13237
+ };
13238
+ view.hooks.add(hook);
13239
+ const stop = ctx.onRemoteSelectionsChange?.(() => view.schedule());
13240
+ hook();
13241
+ return () => {
13242
+ stop?.();
13243
+ view.hooks.delete(hook);
13244
+ for (const layer of layers) layer.element.remove();
13245
+ };
13246
+ }
12989
13247
  var structuralKinds = /* @__PURE__ */ new Set(["structure", "sheets", "view", "undo", "redo", "batch"]);
12990
13248
  function mountGrid(ctx, container) {
12991
13249
  ensureStyle(ctx.root, "xg", GRID_CSS);
@@ -12998,6 +13256,7 @@ function mountGrid(ctx, container) {
12998
13256
  const drawings = new DrawingLayer(view);
12999
13257
  const list = new ValidationList(view, () => editor.focus());
13000
13258
  const a11y = new GridA11y(view, sink);
13259
+ const removeRemote = installRemoteSelections(view);
13001
13260
  let marqueeSheet = -1;
13002
13261
  const clipboard = createGridClipboard({
13003
13262
  ctx,
@@ -13141,6 +13400,7 @@ function mountGrid(ctx, container) {
13141
13400
  drawings.destroy();
13142
13401
  editor.destroy();
13143
13402
  a11y.destroy();
13403
+ removeRemote();
13144
13404
  view.destroy();
13145
13405
  };
13146
13406
  }
@@ -14341,6 +14601,52 @@ function createRibbon(ctx, handlers) {
14341
14601
  }
14342
14602
  };
14343
14603
  }
14604
+
14605
+ // src/xlsx/ribbon-actions.ts
14606
+ function createRibbonActions(ctx, handlers) {
14607
+ const doc = ctx.host.ownerDocument;
14608
+ defineRibbonActions(doc.defaultView?.customElements);
14609
+ const element = doc.createElement("office-ui-ribbon-actions");
14610
+ element.className = "xve-ribbon-actions";
14611
+ element.slot = "actions";
14612
+ element.addEventListener(
14613
+ "office-ribbon-mode",
14614
+ (event) => handlers.setReadOnly(event.detail.mode === "viewing")
14615
+ );
14616
+ element.addEventListener("office-ribbon-comments", () => {
14617
+ void ctx.commands.run("review.show-comments");
14618
+ });
14619
+ element.addEventListener("office-ribbon-share", () => void ctx.commands.run("file.share"));
14620
+ const relocalize = () => {
14621
+ element.modeLabel = ctx.t("Editing mode");
14622
+ element.modes = [
14623
+ { value: "editing", label: ctx.t("Editing") },
14624
+ { value: "viewing", label: ctx.t("Viewing") }
14625
+ ];
14626
+ element.commentsLabel = ctx.t("Comments");
14627
+ element.commentsTitle = ctx.t("Show comments");
14628
+ element.shareLabel = ctx.t("Share");
14629
+ element.shareTitle = ctx.t("Share");
14630
+ refresh();
14631
+ };
14632
+ const refresh = () => {
14633
+ element.mode = ctx.readOnly() ? "viewing" : "editing";
14634
+ element.noShare = !ctx.commands.get("file.share") || handlers.isHidden("file.share");
14635
+ element.sharePressed = Boolean(handlers.collaboration?.().active);
14636
+ const showComments = ctx.commands.get("review.show-comments");
14637
+ element.noComments = !showComments;
14638
+ if (!showComments) return;
14639
+ let pressed = false;
14640
+ try {
14641
+ pressed = showComments.checked?.(ctx) ?? false;
14642
+ } catch {
14643
+ pressed = false;
14644
+ }
14645
+ element.commentsPressed = pressed;
14646
+ };
14647
+ relocalize();
14648
+ return { element, refresh, relocalize };
14649
+ }
14344
14650
  var hasWorkbook = (ctx) => Boolean(ctx.workbook());
14345
14651
  function adjacentSheet(ctx, step2) {
14346
14652
  const sheets = ctx.workbook()?.sheets ?? [];
@@ -14372,6 +14678,12 @@ function shellCommands(handlers) {
14372
14678
  run: () => handlers.openBackstage("saveAs")
14373
14679
  },
14374
14680
  { id: "file.info", label: "Info", icon: "info", run: () => handlers.openBackstage("info") },
14681
+ {
14682
+ id: "file.share",
14683
+ label: "Share",
14684
+ icon: "share",
14685
+ run: () => handlers.openBackstage("share")
14686
+ },
14375
14687
  {
14376
14688
  id: "file.options",
14377
14689
  label: "Options",
@@ -14384,8 +14696,8 @@ function shellCommands(handlers) {
14384
14696
  icon: "undo",
14385
14697
  shortcut: "Ctrl+Z",
14386
14698
  editing: true,
14387
- enabled: (ctx) => ctx.session()?.canUndo() ?? false,
14388
- run: (ctx) => void ctx.session()?.undo()
14699
+ enabled: (ctx) => historyOf(ctx)?.canUndo() ?? false,
14700
+ run: (ctx) => void historyOf(ctx)?.undo()
14389
14701
  },
14390
14702
  {
14391
14703
  id: "edit.redo",
@@ -14393,8 +14705,8 @@ function shellCommands(handlers) {
14393
14705
  icon: "redo",
14394
14706
  shortcut: "Ctrl+Y",
14395
14707
  editing: true,
14396
- enabled: (ctx) => ctx.session()?.canRedo() ?? false,
14397
- run: (ctx) => void ctx.session()?.redo()
14708
+ enabled: (ctx) => historyOf(ctx)?.canRedo() ?? false,
14709
+ run: (ctx) => void historyOf(ctx)?.redo()
14398
14710
  },
14399
14711
  {
14400
14712
  id: "sheet.next",
@@ -14503,7 +14815,8 @@ function statisticsText(ctx, shown2) {
14503
14815
  if (!shown2.has(key)) continue;
14504
14816
  const value = key === "count" ? stats.count : key === "numericCount" ? stats.numericCount : key === "sum" ? stats.numericCount ? stats.sum : void 0 : stats[key];
14505
14817
  if (value === void 0) continue;
14506
- parts.push(`${ctx.t(STAT_LABELS[key])}: ${formatNumber(locale, value)}`);
14818
+ const text3 = key === "count" || key === "numericCount" ? formatNumber(locale, value) : formatSelectionStat(workbook, stats, value);
14819
+ parts.push(`${ctx.t(STAT_LABELS[key])}: ${text3}`);
14507
14820
  }
14508
14821
  return parts.join(" ");
14509
14822
  }
@@ -14517,7 +14830,7 @@ function createStatusBar(ctx, handlers) {
14517
14830
  element.setAttribute("part", "status-bar");
14518
14831
  element.setAttribute("role", "status");
14519
14832
  const stats = el2(doc, "span", "xve-status-stats");
14520
- stats.slot = "end";
14833
+ stats.slot = "summary";
14521
14834
  const shown2 = /* @__PURE__ */ new Set(["average", "count", "sum"]);
14522
14835
  stats.addEventListener("contextmenu", (event) => {
14523
14836
  event.preventDefault();
@@ -14603,22 +14916,17 @@ function createTitleBar(ctx, handlers) {
14603
14916
  const element = doc.createElement("office-ui-title-bar");
14604
14917
  element.className = "xve-titlebar";
14605
14918
  element.setAttribute("part", "title-bar");
14606
- const actions = el2(doc, "div", "xve-title-actions");
14607
- actions.slot = "actions";
14608
- const comments = el2(doc, "button", "xve-icon-button");
14609
- comments.type = "button";
14610
- comments.append(ribbonIcon(doc, "comments", 16));
14611
- comments.addEventListener("click", () => void ctx.commands.run("review.show-comments"));
14612
- const mode = el2(doc, "select", "xve-mode-select");
14613
- mode.append(new Option("", "editing"), new Option("", "viewing"));
14614
- mode.addEventListener("change", () => handlers.setReadOnly(mode.value === "viewing"));
14615
- actions.append(comments, mode);
14616
- element.append(actions);
14919
+ definePresence();
14920
+ const people = doc.createElement("office-ui-presence");
14921
+ people.className = "xve-title-people";
14922
+ people.slot = "collaboration";
14923
+ people.setAttribute("max", "3");
14924
+ people.hidden = true;
14925
+ element.append(people);
14617
14926
  let fileName = "";
14618
14927
  let saveState = "saved";
14619
14928
  const render3 = () => {
14620
- const session = ctx.session();
14621
- const undoLabel = session?.undoLabel();
14929
+ const undoLabel = historyOf(ctx)?.undoLabel();
14622
14930
  const entries = [
14623
14931
  { id: "save", icon: "save", label: ctx.t("Save"), title: `${ctx.t("Save")} (Ctrl+S)` },
14624
14932
  {
@@ -14669,31 +14977,15 @@ function createTitleBar(ctx, handlers) {
14669
14977
  ctx.grid()?.focus();
14670
14978
  void ctx.commands.run(command.id);
14671
14979
  });
14672
- const label = (control, text3) => {
14673
- const translated = ctx.t(text3);
14674
- control.setAttribute("aria-label", translated);
14675
- control.title = translated;
14676
- };
14677
14980
  const relocalize = () => {
14678
- label(comments, "Show comments");
14679
- mode.setAttribute("aria-label", ctx.t("Editing mode"));
14680
- mode.options[0].textContent = ctx.t("Editing");
14681
- mode.options[1].textContent = ctx.t("Viewing");
14981
+ people.setAttribute("label", ctx.t("People in this session"));
14682
14982
  render3();
14683
14983
  };
14684
14984
  const refresh = () => {
14685
- mode.value = ctx.readOnly() ? "viewing" : "editing";
14686
- const showComments = ctx.commands.get("review.show-comments");
14687
- comments.hidden = !showComments;
14688
- if (showComments) {
14689
- let pressed = false;
14690
- try {
14691
- pressed = showComments.checked?.(ctx) ?? false;
14692
- } catch {
14693
- pressed = false;
14694
- }
14695
- comments.setAttribute("aria-pressed", String(pressed));
14696
- }
14985
+ const sharing = handlers.collaboration?.();
14986
+ people.hidden = !sharing?.active;
14987
+ const next = sharing?.active ? participants(sharing) : [];
14988
+ if (JSON.stringify(next) !== JSON.stringify(people.participants)) people.participants = next;
14697
14989
  render3();
14698
14990
  };
14699
14991
  relocalize();
@@ -14822,6 +15114,11 @@ function buildShell(core) {
14822
15114
  core.dirty.set(true);
14823
15115
  backstage.relocalize();
14824
15116
  },
15117
+ share: {
15118
+ state: () => core.collab.state(),
15119
+ start: (roomId) => core.collab.start({ roomId }),
15120
+ stop: () => core.collab.stop()
15121
+ },
14825
15122
  hiddenActions: () => core.hiddenActions,
14826
15123
  setHiddenActions(ids) {
14827
15124
  element.hiddenActions = ids;
@@ -14841,10 +15138,16 @@ function buildShell(core) {
14841
15138
  );
14842
15139
  safeMount("commands", () => installCommands(ctx));
14843
15140
  const ribbon = createRibbon(ctx, { openBackstage: () => backstage.open("info"), isHidden });
15141
+ const ribbonActions = createRibbonActions(ctx, {
15142
+ isHidden,
15143
+ setReadOnly: (readOnly) => element.readOnly = readOnly,
15144
+ collaboration: () => core.collab.state()
15145
+ });
15146
+ ribbon.element.append(ribbonActions.element);
14844
15147
  const titleBar = createTitleBar(ctx, {
14845
15148
  isHidden,
14846
15149
  save: () => void fileCommand("save"),
14847
- setReadOnly: (readOnly) => element.readOnly = readOnly,
15150
+ collaboration: () => core.collab.state(),
14848
15151
  revealControl(id) {
14849
15152
  const control = ribbon.element.querySelector(
14850
15153
  `.ribbon-panel [data-command="${id.replace(/["\\]/g, "")}"]`
@@ -14935,10 +15238,12 @@ function buildShell(core) {
14935
15238
  focusRegion(next);
14936
15239
  };
14937
15240
  installKeyboard(ctx, actions);
15241
+ element.addEventListener("collaboration-change", () => backstage.refresh("share"));
14938
15242
  core.shell = {
14939
15243
  render() {
14940
15244
  ribbon.refresh();
14941
15245
  titleBar.refresh();
15246
+ ribbonActions.refresh();
14942
15247
  statusBar.refresh();
14943
15248
  chartSeriesPane.refresh();
14944
15249
  chartAreaPane.refresh();
@@ -14954,6 +15259,7 @@ function buildShell(core) {
14954
15259
  emptyText.textContent = ctx.t("Open a workbook or create a new one from the File tab.");
14955
15260
  ribbon.rebuild();
14956
15261
  titleBar.relocalize();
15262
+ ribbonActions.relocalize();
14957
15263
  statusBar.relocalize();
14958
15264
  backstage.relocalize();
14959
15265
  chartSeriesPane.relocalize();
@@ -14972,6 +15278,357 @@ function buildShell(core) {
14972
15278
  fileCommand
14973
15279
  };
14974
15280
  }
15281
+
15282
+ // src/xlsx/calc-preparation.ts
15283
+ var SLICE_MS = 8;
15284
+ var QUIET_MS = 250;
15285
+ var ACTIVITY = ["keydown", "wheel", "pointerdown", "touchmove"];
15286
+ function windowScheduler(view) {
15287
+ const clock = () => (view?.performance ?? performance).now();
15288
+ const later = (run, ms) => {
15289
+ const id = setTimeout(run, ms);
15290
+ return () => clearTimeout(id);
15291
+ };
15292
+ return {
15293
+ now: clock,
15294
+ later,
15295
+ afterPaint(run) {
15296
+ if (!view || typeof view.requestAnimationFrame !== "function") return later(run, 0);
15297
+ let cancelTimer;
15298
+ const id = view.requestAnimationFrame(() => {
15299
+ cancelTimer = later(run, 0);
15300
+ });
15301
+ return () => {
15302
+ view.cancelAnimationFrame(id);
15303
+ cancelTimer?.();
15304
+ };
15305
+ },
15306
+ idle(run) {
15307
+ if (view && typeof view.requestIdleCallback === "function") {
15308
+ const id = view.requestIdleCallback(run);
15309
+ return () => view.cancelIdleCallback(id);
15310
+ }
15311
+ const start = clock();
15312
+ return later(() => run({ timeRemaining: () => SLICE_MS - (clock() - start) }), 1);
15313
+ }
15314
+ };
15315
+ }
15316
+ var CalcPreparation = class {
15317
+ constructor(host, busy, scheduler = windowScheduler(host.ownerDocument.defaultView)) {
15318
+ this.busy = busy;
15319
+ this.scheduler = scheduler;
15320
+ const active2 = () => {
15321
+ this.lastActivity = this.scheduler.now();
15322
+ };
15323
+ for (const type of ACTIVITY) host.addEventListener(type, active2, { passive: true });
15324
+ }
15325
+ busy;
15326
+ scheduler;
15327
+ session;
15328
+ cancel;
15329
+ lastActivity = Number.NEGATIVE_INFINITY;
15330
+ /** Prepares `session` (the workbook just opened) after the next paint; stops earlier work. */
15331
+ start(session) {
15332
+ this.stop();
15333
+ if (!session) return;
15334
+ this.session = session;
15335
+ this.cancel = this.scheduler.afterPaint(() => this.next());
15336
+ }
15337
+ stop() {
15338
+ this.cancel?.();
15339
+ this.cancel = void 0;
15340
+ this.session = void 0;
15341
+ }
15342
+ /** Whether work is still waiting (tests and diagnostics). */
15343
+ get pending() {
15344
+ return this.session !== void 0;
15345
+ }
15346
+ next() {
15347
+ this.cancel = this.scheduler.idle((deadline) => this.slice(deadline));
15348
+ }
15349
+ slice(deadline) {
15350
+ this.cancel = void 0;
15351
+ const session = this.session;
15352
+ if (!session) return;
15353
+ const quietFor = this.scheduler.now() - this.lastActivity;
15354
+ if (this.busy() || quietFor < QUIET_MS) {
15355
+ const wait = Math.max(QUIET_MS - quietFor, 50);
15356
+ this.cancel = this.scheduler.later(() => this.next(), wait);
15357
+ return;
15358
+ }
15359
+ const end = this.scheduler.now() + Math.min(SLICE_MS, Math.max(deadline.timeRemaining(), 1));
15360
+ let done = true;
15361
+ try {
15362
+ done = session.prepareCalculation({ timeRemaining: () => end - this.scheduler.now() });
15363
+ } catch {
15364
+ }
15365
+ if (done) this.session = void 0;
15366
+ else this.next();
15367
+ }
15368
+ };
15369
+
15370
+ // src/xlsx/collaboration-session.ts
15371
+ function createRoomSession(collab, xlsx, options, defaultName) {
15372
+ const roomId = options.roomId ?? "";
15373
+ if (!collab.isValidRoomId(roomId))
15374
+ throw new Error("Use 1 to 128 letters, digits, hyphens or underscores for the room name.");
15375
+ let provider = options.provider;
15376
+ if (!provider && options.serverUrl)
15377
+ provider = collab.transportProvider({
15378
+ transport: collab.createWebSocketTransport({
15379
+ url: collab.roomUrl(options.serverUrl, roomId)
15380
+ })
15381
+ });
15382
+ if (!provider) {
15383
+ if (!collab.canBroadcast())
15384
+ throw new Error("This browser cannot share between windows; pass a server URL.");
15385
+ provider = collab.transportProvider({
15386
+ transport: collab.createBroadcastTransport({ roomId, prefix: "ooxml-xlsx:" })
15387
+ });
15388
+ }
15389
+ const user = options.user ?? {};
15390
+ return collab.createCollabSession({
15391
+ roomId,
15392
+ provider,
15393
+ user: {
15394
+ name: user.name || defaultName,
15395
+ ...user.color ? { color: user.color } : {},
15396
+ ...user.role ? { role: user.role } : {}
15397
+ },
15398
+ initialPresence: {},
15399
+ sanitizePayload: xlsx.sanitizeXlsxPresence,
15400
+ teardown: true
15401
+ });
15402
+ }
15403
+
15404
+ // src/xlsx/collaboration-types.ts
15405
+ var OFF_STATE = Object.freeze({
15406
+ active: false,
15407
+ roomId: void 0,
15408
+ status: "off",
15409
+ synced: false,
15410
+ people: []
15411
+ });
15412
+
15413
+ // src/xlsx/collaboration.ts
15414
+ var loadRuntime = () => Promise.all([import('ooxml-core/collab'), import('ooxml-core/xlsx/collab')]);
15415
+ function presenceRange(selection) {
15416
+ const { row: row2, col } = selection.active;
15417
+ const holding = selection.ranges.find(
15418
+ (r) => row2 >= r.start.row && row2 <= r.end.row && col >= r.start.col && col <= r.end.col
15419
+ );
15420
+ return holding ?? selection.ranges[selection.ranges.length - 1] ?? {
15421
+ start: selection.active,
15422
+ end: selection.active
15423
+ };
15424
+ }
15425
+ var XlsxCollaboration = class {
15426
+ constructor(host, load = loadRuntime) {
15427
+ this.host = host;
15428
+ this.load = load;
15429
+ }
15430
+ host;
15431
+ load;
15432
+ #active;
15433
+ #starting;
15434
+ /** Bumped by every stop, so a join still loading the runtime is abandoned. */
15435
+ #epoch = 0;
15436
+ #wanted;
15437
+ #lost = false;
15438
+ #connected = false;
15439
+ #listeners = /* @__PURE__ */ new Set();
15440
+ get active() {
15441
+ return this.#active !== void 0;
15442
+ }
15443
+ /** Joined or joining. */
15444
+ get busy() {
15445
+ return this.#active !== void 0 || this.#starting !== void 0;
15446
+ }
15447
+ /** The options of the declarative `collaboration` property, if set. */
15448
+ get wanted() {
15449
+ return this.#wanted;
15450
+ }
15451
+ /**
15452
+ * Declarative sharing: while set, the open workbook is shared in that room, and a workbook
15453
+ * opened later joins it too (the room's content wins when it already has one). Undefined stops.
15454
+ */
15455
+ want(options) {
15456
+ if (options === this.#wanted) return;
15457
+ this.#wanted = options;
15458
+ this.#leave();
15459
+ this.resume();
15460
+ }
15461
+ /** Leaves the room before the workbook is replaced; true when it was shared. */
15462
+ suspend() {
15463
+ const was = this.busy;
15464
+ this.#leave();
15465
+ return was;
15466
+ }
15467
+ /** Joins the wanted room with the workbook now open. */
15468
+ resume() {
15469
+ const wanted = this.#wanted;
15470
+ if (!wanted || this.busy || !this.host.edit()) return;
15471
+ this.start(wanted).catch((error) => {
15472
+ const failure = asError(error);
15473
+ this.host.toast(this.host.t(failure.message), "error");
15474
+ this.host.error(failure);
15475
+ });
15476
+ }
15477
+ /** Joins a room. Rejects when one is active, no workbook is open or the room is invalid. */
15478
+ start(options) {
15479
+ if (this.#active || this.#starting)
15480
+ return Promise.reject(new Error("Stop the current collaboration session first."));
15481
+ const starting = this.#start(options).finally(() => {
15482
+ if (this.#starting === starting) this.#starting = void 0;
15483
+ });
15484
+ this.#starting = starting;
15485
+ return starting;
15486
+ }
15487
+ async #start(options) {
15488
+ if (!this.host.edit()) throw new Error("Open or create a workbook before sharing it.");
15489
+ const epoch = this.#epoch;
15490
+ const [collab, xlsx] = await this.load();
15491
+ if (epoch !== this.#epoch) return;
15492
+ const edit = this.host.edit();
15493
+ if (!edit) throw new Error("Open or create a workbook before sharing it.");
15494
+ const session = options.session ?? createRoomSession(collab, xlsx, options, this.host.userName());
15495
+ const binding = xlsx.bindWorkbookSession(session, edit, {
15496
+ onError: (error) => this.host.error(asError(error))
15497
+ });
15498
+ const active2 = { session, binding, owned: !options.session, stop: [] };
15499
+ this.#active = active2;
15500
+ this.#lost = false;
15501
+ this.#connected = session.status === "connected";
15502
+ const publish = (selection) => binding.setSelection(selection.sheet, presenceRange(selection));
15503
+ active2.stop.push(
15504
+ this.host.selection.onChange(publish),
15505
+ session.on("peers", () => this.#changed()),
15506
+ session.on("synced", () => this.#changed()),
15507
+ session.on("status", (status) => this.#status(status)),
15508
+ session.on("error", (error) => this.host.error(error)),
15509
+ edit.onChange((change) => {
15510
+ if (change.external) this.#notify();
15511
+ })
15512
+ );
15513
+ if (session.role === "viewer") this.host.setReadOnly(true);
15514
+ publish(this.host.selection.get());
15515
+ this.#changed();
15516
+ }
15517
+ /**
15518
+ * Leaves the room and forgets the `collaboration` property. A session the host passed in stays
15519
+ * alive; one the editor made is destroyed.
15520
+ */
15521
+ stop() {
15522
+ this.#wanted = void 0;
15523
+ this.#leave();
15524
+ }
15525
+ #leave() {
15526
+ this.#epoch++;
15527
+ this.#starting = void 0;
15528
+ const active2 = this.#active;
15529
+ if (!active2) return;
15530
+ this.#active = void 0;
15531
+ for (const stop of active2.stop) stop();
15532
+ active2.binding.dispose();
15533
+ if (active2.owned) active2.session.destroy();
15534
+ this.#changed();
15535
+ }
15536
+ /** Restarts the connection, keeping the shared workbook and queued changes. */
15537
+ reconnect() {
15538
+ this.#active?.session.reconnect();
15539
+ }
15540
+ /** The binding's history while shared (only this window's edits), else undefined. */
15541
+ history() {
15542
+ const binding = this.#active?.binding;
15543
+ if (!binding) return void 0;
15544
+ return {
15545
+ undo: () => binding.undo(),
15546
+ redo: () => binding.redo(),
15547
+ canUndo: () => binding.canUndo(),
15548
+ canRedo: () => binding.canRedo(),
15549
+ undoLabel: () => void 0,
15550
+ redoLabel: () => void 0
15551
+ };
15552
+ }
15553
+ remoteSelections() {
15554
+ return this.#active?.binding.remoteSelections() ?? [];
15555
+ }
15556
+ onRemoteChange(listener) {
15557
+ this.#listeners.add(listener);
15558
+ return () => {
15559
+ this.#listeners.delete(listener);
15560
+ };
15561
+ }
15562
+ state() {
15563
+ const active2 = this.#active;
15564
+ if (!active2) return OFF_STATE;
15565
+ const { session } = active2;
15566
+ return {
15567
+ active: true,
15568
+ roomId: session.roomId,
15569
+ status: session.status,
15570
+ synced: session.synced,
15571
+ people: [
15572
+ {
15573
+ clientId: session.clientId,
15574
+ name: session.identity.userName,
15575
+ color: session.identity.userColor,
15576
+ self: true
15577
+ },
15578
+ ...session.peers().map((peer) => ({
15579
+ clientId: peer.clientId,
15580
+ name: peer.userName,
15581
+ color: peer.userColor,
15582
+ self: false
15583
+ }))
15584
+ ]
15585
+ };
15586
+ }
15587
+ #status(status) {
15588
+ const { t } = this.host;
15589
+ if (status === "connected") {
15590
+ if (this.#lost)
15591
+ this.host.toast(t("Reconnected. Changes made while offline were sent."), "info");
15592
+ this.#lost = false;
15593
+ this.#connected = true;
15594
+ } else if ((status === "disconnected" || status === "error") && this.#connected && !this.#lost) {
15595
+ this.#lost = true;
15596
+ this.host.toast(
15597
+ t("Connection lost. Keep working; your changes are sent when it returns."),
15598
+ "warning"
15599
+ );
15600
+ }
15601
+ this.#changed();
15602
+ }
15603
+ #notify() {
15604
+ for (const listener of [...this.#listeners]) listener();
15605
+ }
15606
+ #changed() {
15607
+ this.#notify();
15608
+ this.host.changed(this.state());
15609
+ }
15610
+ };
15611
+ function collaborationFor(core) {
15612
+ const { host } = core;
15613
+ return new XlsxCollaboration({
15614
+ edit: () => core.session,
15615
+ selection: core.selection,
15616
+ userName: () => core.authorName,
15617
+ t: core.ctx.t,
15618
+ toast: (message, kind) => core.shell?.toast(message, kind),
15619
+ error: (error) => emit(host, "workbook-error", { error, message: error.message }),
15620
+ changed: (state) => {
15621
+ emit(host, "collaboration-change", state);
15622
+ core.requestRender();
15623
+ },
15624
+ setReadOnly: (readOnly) => core.setReadOnly(readOnly)
15625
+ });
15626
+ }
15627
+ function asError(error) {
15628
+ return error instanceof Error ? error : new Error(String(error));
15629
+ }
15630
+
15631
+ // src/xlsx/editor-core.ts
14975
15632
  var EditorCore = class {
14976
15633
  constructor(host) {
14977
15634
  this.host = host;
@@ -15008,6 +15665,8 @@ var EditorCore = class {
15008
15665
  this.requestRender();
15009
15666
  });
15010
15667
  this.ctx = this.createContext();
15668
+ this.collab = collaborationFor(this);
15669
+ this.calcPreparation = new CalcPreparation(host, () => !!this.gridController?.isEditing());
15011
15670
  }
15012
15671
  host;
15013
15672
  workbook;
@@ -15031,6 +15690,10 @@ var EditorCore = class {
15031
15690
  dialogs;
15032
15691
  dirty;
15033
15692
  ctx;
15693
+ /** Co-editing over `ooxml-core/collab`; undo and redo go through it while shared. */
15694
+ collab;
15695
+ /** Builds the formula graph in idle time after a workbook opens. */
15696
+ calcPreparation;
15034
15697
  gridController;
15035
15698
  modelListeners = /* @__PURE__ */ new Set();
15036
15699
  stopSession;
@@ -15081,7 +15744,10 @@ var EditorCore = class {
15081
15744
  return emitEvent(core.host, type, detail);
15082
15745
  },
15083
15746
  authorName: () => core.authorName,
15084
- locale: () => core.locale
15747
+ locale: () => core.locale,
15748
+ history: () => core.collab.history() ?? core.session,
15749
+ remoteSelections: () => core.collab.remoteSelections(),
15750
+ onRemoteSelectionsChange: (listener) => core.collab.onRemoteChange(listener)
15085
15751
  };
15086
15752
  }
15087
15753
  /** Debounced refresh of the ribbon, title bar and status bar. */
@@ -15109,6 +15775,8 @@ var EditorCore = class {
15109
15775
  }
15110
15776
  /** Replaces the workbook (load, new, template, property), with a fresh edit session. */
15111
15777
  setWorkbook(workbook) {
15778
+ if (this.collab.suspend() && !this.collab.wanted)
15779
+ this.shell?.toast(this.ctx.t("Sharing stopped because another workbook was opened."));
15112
15780
  this.savePassword = void 0;
15113
15781
  this.stopSession?.();
15114
15782
  this.stopSession = void 0;
@@ -15122,6 +15790,7 @@ var EditorCore = class {
15122
15790
  }
15123
15791
  }
15124
15792
  this.stopSession = this.session?.onChange((change) => this.onSessionChange(change));
15793
+ this.calcPreparation.start(this.session);
15125
15794
  const sheets = workbook?.sheets.length ?? 0;
15126
15795
  const preferred = workbook?.activeSheet ?? 0;
15127
15796
  this.activeSheet = Math.min(Math.max(0, preferred), Math.max(0, sheets - 1));
@@ -15134,6 +15803,7 @@ var EditorCore = class {
15134
15803
  this.selection.set(initialSelection(this.activeSheet, workbook));
15135
15804
  this.dirty.set(false);
15136
15805
  this.notifyModel({ kind: "load" });
15806
+ this.collab.resume();
15137
15807
  }
15138
15808
  createSession(workbook) {
15139
15809
  try {
@@ -15247,6 +15917,8 @@ var XlsxEditorApi = class extends HTMLElementBase {
15247
15917
  core = new EditorCore(this);
15248
15918
  /** Set by the shell once connected. */
15249
15919
  chrome;
15920
+ /** Set by the shell once connected: opens File > Share. */
15921
+ openShare;
15250
15922
  /** Opens .xlsx, .xlsm, .xltx, .xls or .csv bytes; `fileName` helps detection and names the file. */
15251
15923
  async load(bytes, fileName) {
15252
15924
  await loadInto(this.core, bytes, fileName, this.chrome);
@@ -15300,6 +15972,31 @@ var XlsxEditorApi = class extends HTMLElementBase {
15300
15972
  setActiveSheet(index) {
15301
15973
  this.core.setActiveSheet(index);
15302
15974
  }
15975
+ /**
15976
+ * Shares the open workbook in a room (see `XlsxCollaborationOptions`). Resolves once joined;
15977
+ * the room's workbook replaces this one when the room already has one. Rejects when sharing is
15978
+ * already active or no workbook is open.
15979
+ */
15980
+ startCollaboration(options) {
15981
+ return this.core.collab.start(options);
15982
+ }
15983
+ /** Leaves the room (and clears `collaboration`). The workbook stays as it is. */
15984
+ stopCollaboration() {
15985
+ this.core.collab.stop();
15986
+ }
15987
+ /** Restarts the connection, keeping the shared workbook and changes made offline. */
15988
+ reconnectCollaboration() {
15989
+ this.core.collab.reconnect();
15990
+ }
15991
+ /** Who is in the room and how the connection is. */
15992
+ get collaborationState() {
15993
+ return this.core.collab.state();
15994
+ }
15995
+ /** Opens File > Share, where the user starts or stops sharing and sees who is in the room. */
15996
+ share() {
15997
+ this.openShare?.();
15998
+ }
15999
+ /** Undo the last edit; while shared, only this window's edits are undone. */
15303
16000
  undo() {
15304
16001
  void this.core.commands.run("edit.undo");
15305
16002
  }
@@ -15323,6 +16020,7 @@ var XlsxEditorElement = class extends XlsxEditorApi {
15323
16020
  if (this.shell) return;
15324
16021
  this.shell = buildShell(this.core);
15325
16022
  this.chrome = this.shell.chrome;
16023
+ this.openShare = () => this.shell?.backstage.open("share");
15326
16024
  this.core.requestRender();
15327
16025
  }
15328
16026
  attributeChangedCallback(name, _old, value) {
@@ -15417,6 +16115,17 @@ var XlsxEditorElement = class extends XlsxEditorApi {
15417
16115
  get dirty() {
15418
16116
  return this.core.dirty.dirty;
15419
16117
  }
16118
+ /**
16119
+ * Shares the open workbook in a room while set: a workbook opened later joins the same room
16120
+ * (the room's content wins when it already has a workbook); `null` leaves it. A window joining
16121
+ * an existing room still needs a workbook open (`newWorkbook()` is enough).
16122
+ */
16123
+ get collaboration() {
16124
+ return this.core.collab.wanted ?? null;
16125
+ }
16126
+ set collaboration(value) {
16127
+ this.core.collab.want(value ?? void 0);
16128
+ }
15420
16129
  get activeSheet() {
15421
16130
  return this.core.activeSheet;
15422
16131
  }