@a3s-lab/office 0.1.0 → 0.2.1

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 (69) hide show
  1. package/README.md +98 -24
  2. package/dist/0~2582.js +485 -0
  3. package/dist/0~4595.js +14 -7
  4. package/dist/0~4750.js +939 -0
  5. package/dist/{0~work-document-paragraph-formatting.js → 0~5861.js} +174 -3
  6. package/dist/0~7048.js +8 -6
  7. package/dist/0~9512.js +7 -4
  8. package/dist/0~document-editor.js +3147 -531
  9. package/dist/0~markdown-editor.js +26 -1
  10. package/dist/0~presentation-editor.js +68 -12
  11. package/dist/0~spreadsheet-editor.js +37 -17
  12. package/dist/0~work-document-extensions.js +588 -13
  13. package/dist/0~work-document-model-codec.js +1 -1
  14. package/dist/0~work-docx-bibliography.js +1 -1
  15. package/dist/0~work-docx-export.js +116 -55
  16. package/dist/0~work-docx-import.js +690 -99
  17. package/dist/0~work-office-diagnostics.js +2 -2
  18. package/dist/{4704.js → 2881.js} +162 -71
  19. package/dist/4104.js +2 -62
  20. package/dist/internal/features/work/editors/document-comment-window.d.ts +37 -0
  21. package/dist/internal/features/work/editors/document-navigation-panel.d.ts +3 -1
  22. package/dist/internal/features/work/editors/document-navigation-window.d.ts +64 -0
  23. package/dist/internal/features/work/editors/document-outline-navigation.d.ts +11 -0
  24. package/dist/internal/features/work/editors/document-page-navigation.d.ts +11 -1
  25. package/dist/internal/features/work/editors/document-page-thumbnail.d.ts +31 -0
  26. package/dist/internal/features/work/editors/document-picture-properties-dialog-model.d.ts +28 -0
  27. package/dist/internal/features/work/editors/document-picture-properties-dialog.d.ts +4 -0
  28. package/dist/internal/features/work/editors/document-search-navigation.d.ts +12 -0
  29. package/dist/internal/features/work/editors/document-table-margins-popover.d.ts +4 -0
  30. package/dist/internal/features/work/editors/document-table-properties-dialog-model.d.ts +51 -0
  31. package/dist/internal/features/work/editors/document-table-properties-dialog-sections.d.ts +19 -0
  32. package/dist/internal/features/work/editors/document-table-properties-dialog.d.ts +8 -0
  33. package/dist/internal/features/work/editors/office-task-pane.d.ts +1 -1
  34. package/dist/internal/features/work/editors/presentation-chart-panel.d.ts +2 -1
  35. package/dist/internal/features/work/editors/presentation-comments-panel.d.ts +2 -1
  36. package/dist/internal/features/work/editors/spreadsheet-workbook-panel.d.ts +2 -1
  37. package/dist/internal/features/work/editors/use-document-pagination.d.ts +6 -0
  38. package/dist/internal/features/work/editors/use-presentation-review-commands.d.ts +1 -1
  39. package/dist/internal/features/work/work-document-image-layout.d.ts +11 -0
  40. package/dist/internal/features/work/work-document-page-capture.d.ts +15 -0
  41. package/dist/internal/features/work/work-document-paragraph-formatting.d.ts +4 -0
  42. package/dist/internal/features/work/work-document-section.d.ts +3 -1
  43. package/dist/internal/features/work/work-document-table-borders.d.ts +20 -0
  44. package/dist/internal/features/work/work-document-table-cell-formatting.d.ts +8 -5
  45. package/dist/internal/features/work/work-document-table-geometry.d.ts +41 -0
  46. package/dist/internal/features/work/work-document-table-property-changes.d.ts +25 -0
  47. package/dist/internal/features/work/work-document-table-sizing.d.ts +15 -2
  48. package/dist/internal/features/work/work-document-word-line-metrics.d.ts +11 -0
  49. package/dist/internal/features/work/work-docx-document-layout.d.ts +2 -0
  50. package/dist/internal/features/work/work-docx-paragraph-alignment-import.d.ts +2 -1
  51. package/dist/internal/features/work/work-docx-paragraph-direction-import.d.ts +2 -1
  52. package/dist/internal/features/work/work-docx-paragraph-indent-import.d.ts +2 -1
  53. package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +2 -1
  54. package/dist/internal/features/work/work-docx-paragraph-spacing-import.d.ts +3 -2
  55. package/dist/internal/features/work/work-docx-paragraph-styles.d.ts +2 -2
  56. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +8 -1
  57. package/dist/internal/features/work/work-docx-tab-stop-import.d.ts +2 -1
  58. package/dist/internal/features/work/work-docx-table-cell-export.d.ts +1 -1
  59. package/dist/internal/features/work/work-docx-table-cell-import.d.ts +14 -4
  60. package/dist/internal/features/work/work-docx-table-sizing-export.d.ts +1 -1
  61. package/dist/internal/features/work/work-docx-table-sizing-import.d.ts +4 -3
  62. package/dist/internal/features/work/work-docx-table-styles.d.ts +22 -0
  63. package/dist/internal/features/work/work-types.d.ts +6 -0
  64. package/dist/office-kernel.wasm +0 -0
  65. package/dist/styles.css +1288 -233
  66. package/docs/{browser-editor-architecture.md → latest/en/browser-editor-architecture.md} +144 -35
  67. package/package.json +9 -5
  68. package/dist/0~6552.js +0 -787
  69. package/dist/0~8155.js +0 -299
package/README.md CHANGED
@@ -91,8 +91,12 @@ The images below are committed visual-regression baselines from the real
91
91
  - **Product-native UI** — Complete Office-style surfaces with no required
92
92
  backend, account system, or storage model.
93
93
  - **Accessible responsive shell** — Compact sidebars, slide navigation, AI
94
- panes, dialogs, menus, and popovers share bounded keyboard navigation,
95
- background isolation, topmost Escape handling, and focus restoration.
94
+ panes, responsive chart inspectors, dialogs, menus, and popovers share
95
+ bounded keyboard navigation, background isolation, topmost Escape handling,
96
+ and focus restoration.
97
+ Persistent desktop navigation and the temporary phone drawer keep separate
98
+ open states, so live breakpoint changes never turn a desktop sidebar into an
99
+ unexpected blocking modal or steal focus from the workspace.
96
100
  Shared color palettes expand to an eight-column touch layout on phones while
97
101
  preserving spatial arrow-key navigation.
98
102
  - **Editor-scoped zoom** — Status controls and Ctrl/Cmd + mouse-wheel gestures
@@ -246,20 +250,76 @@ interaction model.
246
250
 
247
251
  - **Document** — Pagination, sections, focused page-setup tabs, clean page
248
252
  margins with on-demand header/footer editing, outline navigation with
249
- contextual full-text results, styles,
253
+ contextual full-text results, keyboard-operated live physical-page raster
254
+ thumbnails with viewport-bounded capture, and shared virtualized
255
+ long-document windows for page buttons, heading rows, full-text results, and
256
+ tracked-revision and anchored-comment review that preserve native scroll
257
+ distance, sparse current/selection/draft pins, and Home/End reachability;
258
+ paragraph styles and phone-sized paragraph-spacing and pagination controls
259
+ with touch targets and exact invoker-focus restoration; stable compact-ribbon
260
+ edge paging and viewport-bounded list galleries with touch-sized numbering
261
+ actions, selection-preserving bullet and numbering commands, and explicit
262
+ Escape-to-invoker focus restoration;
250
263
  grouped bundled, common system, monospace, imported, and host-provided font
251
- choices with live typeface previews, precision table sizing and autofit,
252
- viewport-safe comment drafting,
253
- focus-preserving comment review and citation drafts, tracked changes, notes,
264
+ choices with live typeface previews, independent table layout algorithms,
265
+ auto, percentage, or pixel preferred widths, left/center/right placement,
266
+ indentation, table-level cell margins, cell-level margin overrides, and
267
+ rendered column measurements for autofit tables. One Table Properties dialog
268
+ presents table, row, column, and cell tabs. It combines preferred width,
269
+ placement, and indent with selected-row height and pagination, current-column
270
+ width, and selected-cell vertical alignment and margins. The complete draft
271
+ commits in one TipTap transaction and one undo record while untouched imported
272
+ measurements and partial margin inheritance retain their exact source values.
273
+ Cancel and Escape discard the draft and restore the exact ribbon invoker;
274
+ compact layouts keep tabs, numeric steppers, and footer actions touch-sized. A
275
+ contextual Picture Properties workflow combines centimeter width and height,
276
+ a per-image aspect-ratio lock, inline/square/top-and-bottom wrapping,
277
+ alignment, text distance, and alternative text. It commits only changed
278
+ fields in one TipTap history entry, preserves untouched imported dimensions
279
+ exactly, and retains the image selection and invoker focus after apply,
280
+ cancel, or Escape on desktop and phone layouts. A
281
+ reusable table-border pen with all, outside, inside, and individual-edge
282
+ targets, per-edge DOCX border preservation, and bounded `basedOn` table-style
283
+ inheritance with whole-table, banded row or column, first/last row or column,
284
+ and corner-cell formatting. Conditional fills, borders, bold, italic,
285
+ underline, strikethrough, fonts, and text colors are materialized before
286
+ direct table, cell, paragraph, and run formatting. Conditional paragraph
287
+ alignment, direction, indents, spacing and line rules, pagination rules, and
288
+ tab stops use the same precedence chain. Theme tint and shade values resolve
289
+ for table borders and cell fills across edit, preview, and RGB-stable export.
290
+ Splittable table rows that exceed a full physical page
291
+ continue at paragraph boundaries with repeated heading rows on every page,
292
+ viewport-safe comment drafting and focus-preserving citation drafts, tracked
293
+ changes, notes,
254
294
  pointer- and Shift+F10-accessible host-defined selection menus, and shared
255
295
  edit/preview/PDF typography and page-chrome placement. Editing, read-only
256
- preview, and browser PDF export share one live pagination result.
296
+ preview, and browser PDF export share one live pagination result. Tail edits
297
+ reuse the stable physical-page prefix, including resolved first/odd/even
298
+ headers, footers, page numbers, and navigation descriptors.
299
+ Real DOCX fixtures provide deterministic A3S Test evidence for inherited
300
+ table styling across edit and preview, a centered 62.5% table with table and
301
+ cell margin overrides, all four Table Properties tabs at 390 px, exact
302
+ preservation of untouched cell-margin edges, a complete 390 px Picture
303
+ Properties flow, and a 120-paragraph row across three physical pages through
304
+ the final table content and following paragraph.
305
+ A direct-formatting A4 fixture also runs through a real WPS Writer PDF export
306
+ and a page-only browser comparison. Its automatic line spacing, paragraph
307
+ gaps, centered fixed table, row heights, borders, fills, and cell margins
308
+ stay within the checked one-pixel layout-landmark budget while preserving
309
+ the original OOXML line multiples for DOCX export.
310
+ Separate 30-row common-font, 36-row CJK-font, and 18-row document-grid
311
+ matrices now gate text-band positions against WPS. The browser keeps the
312
+ measured per-font automatic-line advance, while DOCX round trips retain the
313
+ section `docGrid` type and line pitch and each run's `snapToGrid` override.
257
314
  _DOCX import/export; PDF export._
258
315
  - **Markdown** — GFM source, visual editing, synchronized and resizable split
259
316
  preview, source-native undo/redo with typing coalescing and selection restore,
260
317
  source-aware ribbon formatting and shortcuts, empty-source guidance, task
261
- lists, tables, links, images, code, and keyboard-accessible host-defined
262
- selection menus across both editing surfaces. _Markdown import/export._
318
+ lists, tables, links, images, code, a flat reading surface with a comfortable
319
+ line length, and keyboard-accessible host-defined selection menus across both
320
+ editing surfaces. At phone widths, Source and Preview each use the complete
321
+ workspace instead of being compressed into two stacked panes. _Markdown
322
+ import/export._
263
323
  - **Spreadsheet** — Multiple sheets, formulas, dependency-aware recalculation,
264
324
  live selection statistics, an A3S-owned worksheet bar with lifecycle and
265
325
  color controls, safe deletion confirmation, in-place rename validation, a
@@ -269,11 +329,15 @@ interaction model.
269
329
  PageUp/PageDown, row, column, and all-cells selection shortcuts, Cmd/Ctrl
270
330
  formatting through root-scoped capture before vendor grid listeners, an
271
331
  editor-owned Cmd/Ctrl+F search bar that finds displayed values, raw values,
272
- formulas, and sparse cells without opening browser Find,
332
+ formulas, and sparse cells without opening browser Find, with a viewport-safe
333
+ phone layout, touch-sized controls, and exact grid-focus restoration,
334
+ desktop workbook task panes that become focus-contained phone dialogs with
335
+ an inert workbook background and exact ribbon-invoker restoration,
273
336
  Shift+F11 creation, Ctrl/Cmd+PageUp/PageDown switching in edit and read-only
274
337
  preview,
275
338
  one shared cell/worksheet context-menu surface with executable shortcut
276
- hints, keyboard-operated cells and sheet tabs, direct type-to-edit,
339
+ hints that becomes a touch-sized, scrollable bottom action sheet on phones,
340
+ keyboard-operated cells and sheet tabs, direct type-to-edit,
277
341
  permission-resilient multi-cell cut/copy/paste, clear, F2 editing, and
278
342
  focus-safe Escape behavior, direct
279
343
  font-family, vertical-alignment, text-wrap, number/percent, and decimal
@@ -283,9 +347,13 @@ interaction model.
283
347
  transforms, keyboard-accessible table-size insertion, Shift+F10-accessible
284
348
  native slide and object context actions, guides, comments, transitions,
285
349
  editor-scoped formatting and clipboard shortcuts that restore the selected
286
- object, a dismissible phone slide navigator, a top-aligned phone canvas with
287
- a priority-aware status bar, one-step slideshow from the beginning or current
288
- slide, keyboard-complete playback, and a responsive presenter view with one
350
+ object, a docked desktop chart inspector that becomes a focus-contained modal
351
+ surface whenever it overlays the canvas, docked desktop comment review that
352
+ becomes a readable, touch-sized, focus-contained full-editor modal on phones
353
+ with dirty-draft cancellation and exact invoker restoration, a dismissible
354
+ phone slide navigator, a top-aligned phone canvas with a priority-aware status
355
+ bar, one-step slideshow from the beginning or current slide,
356
+ keyboard-complete playback, and a responsive presenter view with one
289
357
  navigation strip, current/next context, speaker notes, and a session timer.
290
358
  _PPTX import/export; PDF export._
291
359
  - **PDF** — PDFium rendering, navigation, search, form filling, annotations,
@@ -389,8 +457,8 @@ CLI, MCP, the typed Rust API, and the packaged Office Skill share the same
389
457
  bounded contracts. They inspect and modify files without launching desktop
390
458
  Office or scraping editor UI.
391
459
 
392
- Read the [native engine design](docs/native-office-engine.md), the complete
393
- [CLI reference](docs/cli-reference.md), or the published
460
+ Read the [native engine design](docs/latest/en/native-office-engine.md), the
461
+ complete [CLI reference](docs/latest/en/cli-reference.md), or the published
394
462
  [CLI and Skill guide](https://a3s-lab.github.io/Office/docs/automation/).
395
463
 
396
464
  ## Architecture
@@ -408,7 +476,8 @@ PDFium provider for read-only PDF page evidence. Both planes expose typed
408
476
  contracts; neither requires an A3S backend.
409
477
 
410
478
  For engine ownership, Worker/WASM boundaries, delivery stages, and performance
411
- gates, see [Browser editor architecture](docs/browser-editor-architecture.md).
479
+ gates, see
480
+ [Browser editor architecture](docs/latest/en/browser-editor-architecture.md).
412
481
 
413
482
  ## Project status
414
483
 
@@ -420,7 +489,7 @@ The project targets predictable browser editing and file preservation, not
420
489
  pixel parity with every Microsoft Office or WPS feature. Unsupported OOXML
421
490
  semantics and fidelity gaps remain explicit compatibility boundaries instead
422
491
  of being silently approximated. Track product depth and release evidence in
423
- the [editor quality roadmap](docs/editor-quality-roadmap.md).
492
+ the [editor quality roadmap](docs/latest/en/editor-quality-roadmap.md).
424
493
 
425
494
  ## Development
426
495
 
@@ -442,18 +511,23 @@ committed visual contracts; see [CONTRIBUTING.md](CONTRIBUTING.md).
442
511
  ## Documentation
443
512
 
444
513
  The published website keeps the interactive Playground at its root and builds
445
- the searchable Rspress documentation center under `/docs/`. Both surfaces use
446
- the same deployment base, so preview, Pages, and fork deployments keep working
514
+ the searchable Rspress documentation center under `/docs/`. Simplified Chinese
515
+ is the stable default, English remains available from the language menu, and
516
+ the version menu switches between `latest` and frozen release documentation.
517
+ Search is scoped to the active language and version. Both surfaces use the
518
+ same deployment base, so preview, Pages, and fork deployments keep working
447
519
  without hard-coded return URLs.
448
520
 
449
521
  - [Live Playground](https://a3s-lab.github.io/Office/)
450
522
  - [Documentation center](https://a3s-lab.github.io/Office/docs/)
523
+ - [A3S Office 0.2.0 documentation](https://a3s-lab.github.io/Office/docs/0.2.0/)
524
+ - [A3S Office 0.1.0 documentation](https://a3s-lab.github.io/Office/docs/0.1.0/)
451
525
  - [React, Vue, and Web Component integration](https://a3s-lab.github.io/Office/docs/components/)
452
526
  - [Office CLI and coding-agent Skill](https://a3s-lab.github.io/Office/docs/automation/)
453
- - [Browser editor architecture](docs/browser-editor-architecture.md)
454
- - [Native Office engine](docs/native-office-engine.md)
455
- - [CLI reference](docs/cli-reference.md)
456
- - [Editor quality roadmap](docs/editor-quality-roadmap.md)
527
+ - [Browser editor architecture](docs/latest/en/browser-editor-architecture.md)
528
+ - [Native Office engine](docs/latest/en/native-office-engine.md)
529
+ - [CLI reference](docs/latest/en/cli-reference.md)
530
+ - [Editor quality roadmap](docs/latest/en/editor-quality-roadmap.md)
457
531
  - [Changelog](CHANGELOG.md)
458
532
 
459
533
  ## Community and security
package/dist/0~2582.js ADDED
@@ -0,0 +1,485 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { TableCell, TableHeader, TableRow } from "@tiptap/extension-table";
3
+ import { NodeSelection } from "@tiptap/pm/state";
4
+ import { CellSelection } from "@tiptap/pm/tables";
5
+ import { renderDocumentTableBorders, setSelectedDocumentTableBorders, normalizeTableColor, uniformDocumentTableBorder, documentTableBordersFromElement, normalizeDocumentTableBorderWidth, normalizeDocumentTableBorderStyle, documentTableBordersFromAttributes, normalizeDocumentTableCellMarginOverrides, renderDocumentTableCellMarginOverrides, documentTableCellMarginOverridesFromElement, uniformDocumentTableBorders } from "./0~4750.js";
6
+ const DOCUMENT_TABLE_STYLE_OPTIONS = [
7
+ {
8
+ id: 'grid',
9
+ label: '网格',
10
+ headerColor: '#f1f4f9',
11
+ bodyColor: '#ffffff',
12
+ alternateColor: '#ffffff',
13
+ borderColor: '#cfd5df',
14
+ borderStyle: 'solid',
15
+ borderWidth: 1
16
+ },
17
+ {
18
+ id: 'blueStripe',
19
+ label: '蓝色条纹',
20
+ headerColor: '#d9eaf7',
21
+ bodyColor: '#ffffff',
22
+ alternateColor: '#f7fbff',
23
+ borderColor: '#9fbad0',
24
+ borderStyle: 'solid',
25
+ borderWidth: 1
26
+ },
27
+ {
28
+ id: 'greenStripe',
29
+ label: '绿色条纹',
30
+ headerColor: '#ddeee7',
31
+ bodyColor: '#ffffff',
32
+ alternateColor: '#f4faf7',
33
+ borderColor: '#abc8be',
34
+ borderStyle: 'solid',
35
+ borderWidth: 1
36
+ },
37
+ {
38
+ id: 'grayStripe',
39
+ label: '灰色条纹',
40
+ headerColor: '#e7e9ed',
41
+ bodyColor: '#ffffff',
42
+ alternateColor: '#f6f7f9',
43
+ borderColor: '#b9c0ca',
44
+ borderStyle: 'solid',
45
+ borderWidth: 1
46
+ },
47
+ {
48
+ id: 'clean',
49
+ label: '简洁',
50
+ headerColor: '#f1f4f9',
51
+ bodyColor: '#ffffff',
52
+ alternateColor: '#ffffff',
53
+ borderColor: '#cfd5df',
54
+ borderStyle: 'none',
55
+ borderWidth: 0
56
+ }
57
+ ];
58
+ const DEFAULT_DOCUMENT_TABLE_CELL_FORMAT = {
59
+ backgroundColor: '#ffffff',
60
+ verticalAlign: 'top',
61
+ borderColor: '#cfd5df',
62
+ borderStyle: 'solid',
63
+ borderWidth: 1,
64
+ borders: uniformDocumentTableBorders({
65
+ color: '#cfd5df',
66
+ style: 'solid',
67
+ width: 1
68
+ }),
69
+ margins: null
70
+ };
71
+ const DEFAULT_DOCUMENT_TABLE_HEADER_FORMAT = {
72
+ ...DEFAULT_DOCUMENT_TABLE_CELL_FORMAT,
73
+ backgroundColor: '#f1f4f9'
74
+ };
75
+ const DocumentTableCell = TableCell.extend({
76
+ addAttributes () {
77
+ return {
78
+ ...this.parent?.(),
79
+ ...documentTableCellAttributes(DEFAULT_DOCUMENT_TABLE_CELL_FORMAT)
80
+ };
81
+ }
82
+ });
83
+ const DocumentTableHeader = TableHeader.extend({
84
+ addAttributes () {
85
+ return {
86
+ ...this.parent?.(),
87
+ ...documentTableCellAttributes(DEFAULT_DOCUMENT_TABLE_HEADER_FORMAT)
88
+ };
89
+ }
90
+ });
91
+ const DocumentTableFormatting = Extension.create({
92
+ name: 'documentTableFormatting',
93
+ addCommands () {
94
+ return {
95
+ setDocumentTableCellFormat: (format)=>(props)=>setSelectedCellFormat(props, format),
96
+ setDocumentTableHorizontalAlignment: (alignment)=>(props)=>setSelectedCellAlignment(props, alignment),
97
+ applyDocumentTableStyle: (style)=>(props)=>applyTableStyle(props, style),
98
+ setDocumentTableBorders: (target, border)=>(props)=>setSelectedDocumentTableBorders(props, target, border)
99
+ };
100
+ }
101
+ });
102
+ function documentTableCellFormat(state) {
103
+ const position = selectedTableCellPositions(state.selection)[0];
104
+ const node = void 0 === position ? null : state.doc.nodeAt(position);
105
+ return node && isTableCell(node) ? formatFromAttributes(node.attrs) : null;
106
+ }
107
+ function documentTableHorizontalAlignment(state) {
108
+ const position = selectedTableCellPositions(state.selection)[0];
109
+ const cell = void 0 === position ? null : state.doc.nodeAt(position);
110
+ let alignment = 'left';
111
+ cell?.descendants((node)=>{
112
+ if ('paragraph' !== node.type.name && 'heading' !== node.type.name) return true;
113
+ if ('center' === node.attrs.textAlign || 'right' === node.attrs.textAlign) alignment = node.attrs.textAlign;
114
+ return false;
115
+ });
116
+ return alignment;
117
+ }
118
+ function activeDocumentTableStyle(state) {
119
+ const context = selectedTable(state.selection);
120
+ if (!context) return null;
121
+ for (const style of DOCUMENT_TABLE_STYLE_OPTIONS){
122
+ let matches = true;
123
+ context.node.forEach((row, _rowOffset, rowIndex)=>{
124
+ row.forEach((cell)=>{
125
+ const expected = tableStyleCellFormat(style, cell, rowIndex);
126
+ const actual = formatFromAttributes(cell.attrs);
127
+ const expectedBorder = {
128
+ color: style.borderColor,
129
+ style: style.borderStyle,
130
+ width: style.borderWidth
131
+ };
132
+ const actualBorder = uniformDocumentTableBorder(actual.borders);
133
+ if (expected.backgroundColor !== actual.backgroundColor || !actualBorder || actualBorder.color !== expectedBorder.color || actualBorder.style !== expectedBorder.style || actualBorder.width !== expectedBorder.width) matches = false;
134
+ });
135
+ });
136
+ if (matches) return style.id;
137
+ }
138
+ return null;
139
+ }
140
+ function documentTableCellAttributes(defaults) {
141
+ return {
142
+ backgroundColor: {
143
+ default: defaults.backgroundColor,
144
+ parseHTML: (element)=>normalizeTableColor(element.dataset.officeCellFill || element.style.backgroundColor) ?? defaults.backgroundColor,
145
+ renderHTML: (attributes)=>{
146
+ const color = normalizeTableColor(String(attributes.backgroundColor ?? '')) ?? defaults.backgroundColor;
147
+ return {
148
+ 'data-office-cell-fill': color,
149
+ style: `background-color: ${color}`
150
+ };
151
+ }
152
+ },
153
+ verticalAlign: {
154
+ default: defaults.verticalAlign,
155
+ parseHTML: (element)=>normalizeDocumentTableVerticalAlign(element.dataset.officeCellVerticalAlign || element.style.verticalAlign) ?? defaults.verticalAlign,
156
+ renderHTML: (attributes)=>{
157
+ const alignment = normalizeDocumentTableVerticalAlign(String(attributes.verticalAlign ?? '')) ?? defaults.verticalAlign;
158
+ return {
159
+ 'data-office-cell-vertical-align': alignment,
160
+ style: `vertical-align: ${alignment}`
161
+ };
162
+ }
163
+ },
164
+ borderColor: {
165
+ default: defaults.borderColor,
166
+ parseHTML: (element)=>normalizeTableColor(element.dataset.officeCellBorderColor || element.style.borderColor) ?? defaults.borderColor,
167
+ renderHTML: ()=>({})
168
+ },
169
+ borderStyle: {
170
+ default: defaults.borderStyle,
171
+ parseHTML: (element)=>normalizeDocumentTableBorderStyle(element.dataset.officeCellBorderStyle || element.style.borderStyle) ?? defaults.borderStyle,
172
+ renderHTML: ()=>({})
173
+ },
174
+ borderWidth: {
175
+ default: defaults.borderWidth,
176
+ parseHTML: (element)=>normalizeDocumentTableBorderWidth(element.dataset.officeCellBorderWidth || element.style.borderWidth) ?? defaults.borderWidth,
177
+ renderHTML: ()=>({})
178
+ },
179
+ borders: {
180
+ default: defaults.borders,
181
+ parseHTML: (element)=>documentTableBordersFromElement(element, {
182
+ color: defaults.borderColor,
183
+ style: defaults.borderStyle,
184
+ width: defaults.borderWidth
185
+ }),
186
+ renderHTML: (attributes)=>renderDocumentTableBorders(formatFromAttributes(attributes, defaults).borders)
187
+ },
188
+ margins: {
189
+ default: defaults.margins,
190
+ parseHTML: (element)=>documentTableCellMarginOverridesFromElement(element),
191
+ renderHTML: (attributes)=>renderDocumentTableCellMarginOverrides(attributes.margins)
192
+ }
193
+ };
194
+ }
195
+ function setSelectedCellFormat({ dispatch, state }, patch) {
196
+ const normalized = normalizeFormatPatch(patch);
197
+ if (!normalized) return false;
198
+ const positions = selectedTableCellPositions(state.selection);
199
+ if (!positions.length) return false;
200
+ const transaction = state.tr;
201
+ for (const position of positions){
202
+ const cell = transaction.doc.nodeAt(position);
203
+ if (!cell || !isTableCell(cell)) continue;
204
+ const current = formatFromAttributes(cell.attrs);
205
+ const changesBorder = void 0 !== normalized.borderColor || void 0 !== normalized.borderStyle || void 0 !== normalized.borderWidth;
206
+ const nextBorder = {
207
+ color: normalized.borderColor ?? current.borderColor,
208
+ style: normalized.borderStyle ?? current.borderStyle,
209
+ width: normalized.borderWidth ?? current.borderWidth
210
+ };
211
+ if ('none' === nextBorder.style) nextBorder.width = 0;
212
+ transaction.setNodeMarkup(position, void 0, {
213
+ ...cell.attrs,
214
+ ...normalized,
215
+ ...changesBorder ? {
216
+ borderColor: nextBorder.color,
217
+ borderStyle: nextBorder.style,
218
+ borderWidth: nextBorder.width,
219
+ borders: uniformDocumentTableBorders(nextBorder)
220
+ } : {}
221
+ });
222
+ }
223
+ if (dispatch && transaction.docChanged) dispatch(transaction.scrollIntoView());
224
+ return true;
225
+ }
226
+ function setSelectedCellAlignment({ dispatch, state }, alignment) {
227
+ if ('left' !== alignment && 'center' !== alignment && 'right' !== alignment) return false;
228
+ const positions = selectedTableCellPositions(state.selection);
229
+ if (!positions.length) return false;
230
+ const transaction = state.tr;
231
+ for (const position of positions){
232
+ const cell = transaction.doc.nodeAt(position);
233
+ if (cell && isTableCell(cell)) cell.descendants((node, offset)=>{
234
+ if ('paragraph' !== node.type.name && 'heading' !== node.type.name) return true;
235
+ transaction.setNodeMarkup(position + 1 + offset, void 0, {
236
+ ...node.attrs,
237
+ textAlign: alignment
238
+ });
239
+ return false;
240
+ });
241
+ }
242
+ if (dispatch && transaction.docChanged) dispatch(transaction.scrollIntoView());
243
+ return true;
244
+ }
245
+ function applyTableStyle({ dispatch, state }, styleId) {
246
+ const style = DOCUMENT_TABLE_STYLE_OPTIONS.find((candidate)=>candidate.id === styleId);
247
+ const context = selectedTable(state.selection);
248
+ if (!style || !context) return false;
249
+ const transaction = state.tr;
250
+ context.node.forEach((row, rowOffset, rowIndex)=>{
251
+ row.forEach((cell, cellOffset)=>{
252
+ const position = context.position + 2 + rowOffset + cellOffset;
253
+ const format = tableStyleCellFormat(style, cell, rowIndex);
254
+ const border = {
255
+ color: style.borderColor,
256
+ style: style.borderStyle,
257
+ width: style.borderWidth
258
+ };
259
+ transaction.setNodeMarkup(position, void 0, {
260
+ ...cell.attrs,
261
+ ...format,
262
+ borders: uniformDocumentTableBorders(border)
263
+ });
264
+ });
265
+ });
266
+ if (dispatch && transaction.docChanged) dispatch(transaction.scrollIntoView());
267
+ return true;
268
+ }
269
+ function selectedTableCellPositions(selection) {
270
+ if (selection instanceof CellSelection) {
271
+ const positions = [];
272
+ selection.forEachCell((_node, position)=>positions.push(position));
273
+ return positions;
274
+ }
275
+ if (selection instanceof NodeSelection && 'table' === selection.node.type.spec.tableRole) {
276
+ const positions = [];
277
+ selection.node.forEach((row, rowOffset)=>{
278
+ row.forEach((_cell, cellOffset)=>{
279
+ positions.push(selection.from + 2 + rowOffset + cellOffset);
280
+ });
281
+ });
282
+ return positions;
283
+ }
284
+ const position = ancestorPosition(selection.$from, isTableCell);
285
+ return null === position ? [] : [
286
+ position
287
+ ];
288
+ }
289
+ function selectedTable(selection) {
290
+ if (selection instanceof NodeSelection && 'table' === selection.node.type.spec.tableRole) return {
291
+ node: selection.node,
292
+ position: selection.from
293
+ };
294
+ for(let depth = selection.$from.depth; depth > 0; depth -= 1){
295
+ const node = selection.$from.node(depth);
296
+ if ('table' === node.type.spec.tableRole) return {
297
+ node,
298
+ position: selection.$from.before(depth)
299
+ };
300
+ }
301
+ return null;
302
+ }
303
+ function ancestorPosition(position, predicate) {
304
+ for(let depth = position.depth; depth > 0; depth -= 1)if (predicate(position.node(depth))) return position.before(depth);
305
+ return null;
306
+ }
307
+ function isTableCell(node) {
308
+ return 'cell' === node.type.spec.tableRole || 'header_cell' === node.type.spec.tableRole;
309
+ }
310
+ function tableStyleCellFormat(style, cell, rowIndex) {
311
+ return {
312
+ backgroundColor: 'header_cell' === cell.type.spec.tableRole ? style.headerColor : rowIndex % 2 === 1 ? style.alternateColor : style.bodyColor,
313
+ borderColor: style.borderColor,
314
+ borderStyle: style.borderStyle,
315
+ borderWidth: style.borderWidth
316
+ };
317
+ }
318
+ function normalizeFormatPatch(patch) {
319
+ const normalized = {};
320
+ if (void 0 !== patch.backgroundColor) {
321
+ const color = normalizeTableColor(patch.backgroundColor);
322
+ if (!color) return null;
323
+ normalized.backgroundColor = color;
324
+ }
325
+ if (void 0 !== patch.verticalAlign) {
326
+ const alignment = normalizeDocumentTableVerticalAlign(patch.verticalAlign);
327
+ if (!alignment) return null;
328
+ normalized.verticalAlign = alignment;
329
+ }
330
+ if (void 0 !== patch.borderColor) {
331
+ const color = normalizeTableColor(patch.borderColor);
332
+ if (!color) return null;
333
+ normalized.borderColor = color;
334
+ }
335
+ if (void 0 !== patch.borderStyle) {
336
+ const style = normalizeDocumentTableBorderStyle(patch.borderStyle);
337
+ if (!style) return null;
338
+ normalized.borderStyle = style;
339
+ }
340
+ if (void 0 !== patch.borderWidth) {
341
+ const width = normalizeDocumentTableBorderWidth(patch.borderWidth);
342
+ if (null === width) return null;
343
+ normalized.borderWidth = width;
344
+ }
345
+ if (void 0 !== patch.margins) if (null === patch.margins) normalized.margins = null;
346
+ else {
347
+ const margins = normalizeDocumentTableCellMarginOverrides(patch.margins);
348
+ if (!margins) return null;
349
+ normalized.margins = margins;
350
+ }
351
+ return normalized;
352
+ }
353
+ function formatFromAttributes(attributes, defaults = DEFAULT_DOCUMENT_TABLE_CELL_FORMAT) {
354
+ const fallbackBorder = {
355
+ color: normalizeTableColor(String(attributes.borderColor ?? '')) ?? defaults.borderColor,
356
+ style: normalizeDocumentTableBorderStyle(String(attributes.borderStyle ?? '')) ?? defaults.borderStyle,
357
+ width: normalizeDocumentTableBorderWidth(attributes.borderWidth) ?? defaults.borderWidth
358
+ };
359
+ if ('none' === fallbackBorder.style) fallbackBorder.width = 0;
360
+ const borders = documentTableBordersFromAttributes(attributes, fallbackBorder);
361
+ const representative = uniformDocumentTableBorder(borders) ?? borders.top;
362
+ return {
363
+ backgroundColor: normalizeTableColor(String(attributes.backgroundColor ?? '')) ?? defaults.backgroundColor,
364
+ verticalAlign: normalizeDocumentTableVerticalAlign(String(attributes.verticalAlign ?? '')) ?? defaults.verticalAlign,
365
+ borderColor: representative.color,
366
+ borderStyle: representative.style,
367
+ borderWidth: representative.width,
368
+ borders,
369
+ margins: normalizeDocumentTableCellMarginOverrides(attributes.margins)
370
+ };
371
+ }
372
+ function normalizeDocumentTableVerticalAlign(value) {
373
+ if ('top' === value || 'middle' === value || 'bottom' === value) return value;
374
+ if ('center' === value) return 'middle';
375
+ return null;
376
+ }
377
+ const DocumentTableRow = TableRow.extend({
378
+ addAttributes () {
379
+ return {
380
+ ...this.parent?.() ?? {},
381
+ cantSplit: booleanRowAttribute('cantSplit', 'officeCantSplit', 'data-office-cant-split'),
382
+ repeatHeader: booleanRowAttribute('repeatHeader', 'officeRepeatHeader', 'data-office-repeat-header'),
383
+ rowHeight: {
384
+ default: null,
385
+ parseHTML: (element)=>normalizeDocumentTableRowHeight(element.dataset.officeRowHeight || element.style.height),
386
+ renderHTML: (attributes)=>{
387
+ const height = normalizeDocumentTableRowHeight(attributes.rowHeight);
388
+ return null === height ? {} : {
389
+ 'data-office-row-height': String(height),
390
+ style: `height: ${height}px`
391
+ };
392
+ }
393
+ },
394
+ rowHeightRule: {
395
+ default: null,
396
+ parseHTML: (element)=>normalizeDocumentTableRowHeightRule(element.dataset.officeRowHeightRule),
397
+ renderHTML: (attributes)=>{
398
+ const rule = normalizeDocumentTableRowHeightRule(attributes.rowHeightRule);
399
+ return null === rule ? {} : {
400
+ 'data-office-row-height-rule': rule
401
+ };
402
+ }
403
+ }
404
+ };
405
+ },
406
+ addCommands () {
407
+ return {
408
+ ...this.parent?.() ?? {},
409
+ setDocumentTableRowOptions: (options, commandOptions = {})=>({ chain, editor })=>{
410
+ if (!editor.isActive('table')) return false;
411
+ let commandChain = chain();
412
+ if (false !== commandOptions.restoreFocus) commandChain = commandChain.focus();
413
+ return commandChain.updateAttributes('tableRow', {
414
+ cantSplit: Boolean(options.cantSplit),
415
+ repeatHeader: Boolean(options.repeatHeader)
416
+ }).run();
417
+ }
418
+ };
419
+ }
420
+ });
421
+ function documentTableRowOptions(editor) {
422
+ const attributes = editor.getAttributes('tableRow');
423
+ return {
424
+ cantSplit: directBoolean(attributes.cantSplit) ?? false,
425
+ repeatHeader: directBoolean(attributes.repeatHeader) ?? editor.isActive('tableHeader')
426
+ };
427
+ }
428
+ function canSetDocumentTableRowRepeatHeader(editor) {
429
+ const resolved = editor.state.selection.$from;
430
+ for(let depth = resolved.depth; depth > 0; depth -= 1){
431
+ if ('tableRow' !== resolved.node(depth).type.name) continue;
432
+ const tableDepth = depth - 1;
433
+ const table = resolved.node(tableDepth);
434
+ if ('table' !== table.type.name) break;
435
+ let leadingHeaderCount = 0;
436
+ for(let index = 0; index < table.childCount; index += 1){
437
+ if (!documentTableRowRepeats(table.child(index))) break;
438
+ leadingHeaderCount += 1;
439
+ }
440
+ return resolved.index(tableDepth) <= leadingHeaderCount;
441
+ }
442
+ return false;
443
+ }
444
+ function documentTableRowRepeats(node) {
445
+ const explicit = directBoolean(node.attrs.repeatHeader);
446
+ if (null !== explicit) return explicit;
447
+ if (0 === node.childCount) return false;
448
+ for(let index = 0; index < node.childCount; index += 1)if ('tableHeader' !== node.child(index).type.name) return false;
449
+ return true;
450
+ }
451
+ function documentTableRowCantSplit(node) {
452
+ return directBoolean(node.attrs.cantSplit) ?? false;
453
+ }
454
+ function documentTableRowHeight(attributes) {
455
+ const height = normalizeDocumentTableRowHeight(attributes.rowHeight);
456
+ return {
457
+ height,
458
+ rule: null === height ? null : normalizeDocumentTableRowHeightRule(attributes.rowHeightRule) ?? 'atLeast'
459
+ };
460
+ }
461
+ function normalizeDocumentTableRowHeight(value) {
462
+ const height = Number.parseFloat(String(value ?? '').replace(/px$/i, ''));
463
+ return Number.isFinite(height) && height > 0 ? Math.round(100 * height) / 100 : null;
464
+ }
465
+ function normalizeDocumentTableRowHeightRule(value) {
466
+ return 'exact' === value || 'atLeast' === value ? value : null;
467
+ }
468
+ function booleanRowAttribute(modelKey, datasetKey, htmlName) {
469
+ return {
470
+ default: null,
471
+ parseHTML: (element)=>directBoolean(element.dataset[datasetKey]),
472
+ renderHTML: (attributes)=>{
473
+ const value = directBoolean(attributes[modelKey]);
474
+ return null === value ? {} : {
475
+ [htmlName]: String(value)
476
+ };
477
+ }
478
+ };
479
+ }
480
+ function directBoolean(value) {
481
+ if (true === value || 'true' === value || '1' === value) return true;
482
+ if (false === value || 'false' === value || '0' === value) return false;
483
+ return null;
484
+ }
485
+ export { DEFAULT_DOCUMENT_TABLE_CELL_FORMAT, DOCUMENT_TABLE_STYLE_OPTIONS, DocumentTableCell, DocumentTableFormatting, DocumentTableHeader, DocumentTableRow, activeDocumentTableStyle, canSetDocumentTableRowRepeatHeader, documentTableCellFormat, documentTableHorizontalAlignment, documentTableRowCantSplit, documentTableRowHeight, documentTableRowOptions, documentTableRowRepeats, normalizeDocumentTableRowHeightRule, normalizeDocumentTableVerticalAlign };