@a3s-lab/office 0.9.2 → 0.11.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 (31) hide show
  1. package/COLLABORATION_ROADMAP.md +29 -5
  2. package/README.md +19 -6
  3. package/dist/{0~7754.js → 0~5093.js} +1449 -3
  4. package/dist/0~document-editor.js +8 -1
  5. package/dist/0~work-docx-export.js +482 -16
  6. package/dist/0~work-docx-import.js +31 -1142
  7. package/dist/0~work-office-diagnostics.js +9 -5
  8. package/dist/4104.js +1 -1
  9. package/dist/8928.js +763 -149
  10. package/dist/internal/features/work/work-document-format-change-tracking.d.ts +9 -0
  11. package/dist/internal/features/work/work-document-format-changes.d.ts +28 -0
  12. package/dist/internal/features/work/work-document-paragraph-format-change-tracking.d.ts +7 -0
  13. package/dist/internal/features/work/work-document-paragraph-format-changes.d.ts +8 -0
  14. package/dist/internal/features/work/work-docx-format-change-export.d.ts +17 -0
  15. package/dist/internal/features/work/work-docx-import.d.ts +3 -1
  16. package/dist/internal/features/work/work-docx-paragraph-alignment-import.d.ts +1 -0
  17. package/dist/internal/features/work/work-docx-paragraph-borders-import.d.ts +1 -0
  18. package/dist/internal/features/work/work-docx-paragraph-direction-import.d.ts +1 -0
  19. package/dist/internal/features/work/work-docx-paragraph-format-change-export.d.ts +13 -0
  20. package/dist/internal/features/work/work-docx-paragraph-format-change-import.d.ts +17 -0
  21. package/dist/internal/features/work/work-docx-paragraph-indent-import.d.ts +1 -0
  22. package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +1 -0
  23. package/dist/internal/features/work/work-docx-paragraph-shading-import.d.ts +1 -0
  24. package/dist/internal/features/work/work-docx-paragraph-spacing-import.d.ts +1 -0
  25. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +10 -0
  26. package/dist/internal/features/work/work-docx-tab-stop-import.d.ts +1 -0
  27. package/dist/internal/features/work/work-types.d.ts +1 -1
  28. package/dist/office-kernel.wasm +0 -0
  29. package/dist/styles.css +28 -0
  30. package/docs/latest/en/browser-editor-architecture.md +18 -0
  31. package/package.json +7 -3
@@ -0,0 +1,9 @@
1
+ import type { Mark as ProseMirrorMark } from '@tiptap/pm/model';
2
+ import type { EditorState, PluginKey, Transaction } from '@tiptap/pm/state';
3
+ import type { WorkDocumentChangeIdentity } from './work-document-changes';
4
+ interface DocumentFormattingChangeTrackingOptions {
5
+ isTracking: () => boolean;
6
+ createChange: (kind: 'formatting' | 'paragraph-formatting') => WorkDocumentChangeIdentity;
7
+ }
8
+ export declare function trackDocumentFormattingTransaction(transaction: Transaction, state: EditorState, type: ProseMirrorMark['type'], options: DocumentFormattingChangeTrackingOptions, pluginKey: PluginKey): void;
9
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { Mark, Schema } from '@tiptap/pm/model';
2
+ import type { Transaction } from '@tiptap/pm/state';
3
+ export declare const DOCUMENT_CHARACTER_FORMAT_MARKS: readonly ["bold", "italic", "underline", "strike", "subscript", "superscript", "textStyle", "highlight"];
4
+ export type DocumentCharacterFormatMarkName = (typeof DOCUMENT_CHARACTER_FORMAT_MARKS)[number];
5
+ export interface DocumentCharacterFormatMark {
6
+ type: DocumentCharacterFormatMarkName;
7
+ attrs?: Record<string, boolean | number | string>;
8
+ }
9
+ export declare function isDocumentCharacterFormatMark(value: string): boolean;
10
+ export declare function serializeDocumentCharacterFormatting(marks: readonly Mark[]): string;
11
+ export declare function parseDocumentCharacterFormatting(value: unknown): DocumentCharacterFormatMark[] | null;
12
+ export declare function restoreDocumentCharacterFormatting(transaction: Transaction, schema: Schema, from: number, to: number, serialized: unknown): boolean;
13
+ export declare function importedDocumentCharacterFormatting(formatting: {
14
+ bold?: boolean;
15
+ italic?: boolean;
16
+ underline?: boolean;
17
+ strike?: boolean;
18
+ subscript?: boolean;
19
+ superscript?: boolean;
20
+ fontFamily?: string;
21
+ wordLineHeightFactor?: number;
22
+ wordSnapToGrid?: boolean;
23
+ fontSize?: number;
24
+ color?: string;
25
+ backgroundColor?: string;
26
+ themeColor?: string;
27
+ themeFill?: string;
28
+ }): string;
@@ -0,0 +1,7 @@
1
+ import type { EditorState, Transaction } from '@tiptap/pm/state';
2
+ import type { WorkDocumentChangeIdentity } from './work-document-changes';
3
+ interface DocumentParagraphFormattingTrackingOptions {
4
+ createChange: () => WorkDocumentChangeIdentity;
5
+ }
6
+ export declare function trackDocumentParagraphFormattingTransaction(transaction: Transaction, state: EditorState, options: DocumentParagraphFormattingTrackingOptions): boolean;
7
+ export {};
@@ -0,0 +1,8 @@
1
+ export declare const DOCUMENT_PARAGRAPH_CHANGE_ATTRIBUTES: readonly ["paragraphChangeKind", "paragraphChangeId", "paragraphChangeActorId", "paragraphChangeAuthor", "paragraphChangeDate", "paragraphChangeBefore"];
2
+ export declare const DOCUMENT_PARAGRAPH_FORMAT_ATTRIBUTES: readonly ["textAlign", "paragraphDirection", "indentLevel", "rightIndent", "firstLineIndent", "spaceBefore", "spaceAfter", "lineHeight", "lineRule", "autoLineHeight", "keepLines", "keepWithNext", "pageBreakBefore", "widowControl", "contextualSpacing", "outlineLevel", "tabStops", "paragraphBorders", "paragraphShading", "defaultCollapsed"];
3
+ export type DocumentParagraphFormatAttribute = (typeof DOCUMENT_PARAGRAPH_FORMAT_ATTRIBUTES)[number];
4
+ export type DocumentParagraphFormattingSnapshot = Record<DocumentParagraphFormatAttribute, unknown>;
5
+ export declare function serializeDocumentParagraphFormatting(attributes: Record<string, unknown>): string;
6
+ export declare function parseDocumentParagraphFormatting(value: unknown): DocumentParagraphFormattingSnapshot | null;
7
+ export declare function restoredDocumentParagraphAttributes(attributes: Record<string, unknown>, serialized: unknown): Record<string, unknown> | null;
8
+ export declare function clearDocumentParagraphChangeAttributes(attributes: Record<string, unknown>): Record<string, unknown>;
@@ -0,0 +1,17 @@
1
+ interface DocxRunFormattingChangePatch {
2
+ start: string;
3
+ end: string;
4
+ id: number;
5
+ author: string;
6
+ date: string;
7
+ before: string;
8
+ }
9
+ export declare class DocxRunFormattingChangePatchCollector {
10
+ readonly patches: DocxRunFormattingChangePatch[];
11
+ register(element: HTMLElement, id: number): {
12
+ start: string;
13
+ end: string;
14
+ } | null;
15
+ }
16
+ export declare function patchDocxRunFormattingChanges(buffer: ArrayBuffer, patches: readonly DocxRunFormattingChangePatch[]): Promise<ArrayBuffer>;
17
+ export {};
@@ -10,6 +10,7 @@ import { type ImportedDocxListMarkers } from './work-docx-list-import';
10
10
  import { type ImportedDocxParagraphAlignmentMarkers } from './work-docx-paragraph-alignment-import';
11
11
  import { type ImportedDocxParagraphDirectionMarkers } from './work-docx-paragraph-direction-import';
12
12
  import { type ImportedDocxParagraphIdentityMarkers } from './work-docx-paragraph-identity-import';
13
+ import { type ImportedDocxParagraphFormattingChangeMarkers } from './work-docx-paragraph-format-change-import';
13
14
  import { type ImportedDocxParagraphIndentMarkers } from './work-docx-paragraph-indent-import';
14
15
  import { type ImportedDocxParagraphPaginationMarkers } from './work-docx-paragraph-pagination-import';
15
16
  import { type ImportedDocxParagraphBorderMarkers } from './work-docx-paragraph-borders-import';
@@ -39,6 +40,7 @@ export interface PreparedDocxImport {
39
40
  listMarkers: ImportedDocxListMarkers;
40
41
  imageLayoutMarkers: ImportedDocxImageLayoutMarkers;
41
42
  paragraphIdentityMarkers: ImportedDocxParagraphIdentityMarkers;
43
+ paragraphFormattingChangeMarkers: ImportedDocxParagraphFormattingChangeMarkers;
42
44
  paragraphAlignmentMarkers: ImportedDocxParagraphAlignmentMarkers;
43
45
  paragraphDirectionMarkers: ImportedDocxParagraphDirectionMarkers;
44
46
  paragraphIndentMarkers: ImportedDocxParagraphIndentMarkers;
@@ -55,6 +57,6 @@ export interface PreparedDocxImport {
55
57
  trackChanges: boolean;
56
58
  }
57
59
  export declare function prepareDocxImport(buffer: ArrayBuffer): Promise<PreparedDocxImport>;
58
- export declare function applyDocxSectionsToHtml(html: string, sections: PreparedDocxImport['sections'], captionMarkers?: ImportedDocxCaptionMarkers, bookmarkMarkers?: ImportedDocxBookmarkMarkers, changeMarkers?: ImportedDocxChangeMarkers, commentMarkers?: ImportedDocxCommentMarkers, fieldMarkers?: ImportedDocxFieldMarkers, equationMarkers?: ImportedDocxEquationMarkers, citationMarkers?: ImportedDocxCitationMarkers, listMarkers?: ImportedDocxListMarkers, imageLayoutMarkers?: ImportedDocxImageLayoutMarkers, paragraphIdentityMarkers?: ImportedDocxParagraphIdentityMarkers, paragraphAlignmentMarkers?: ImportedDocxParagraphAlignmentMarkers, runFormattingMarkers?: ImportedDocxRunFormattingMarkers, paragraphDirectionMarkers?: ImportedDocxParagraphDirectionMarkers, paragraphIndentMarkers?: ImportedDocxParagraphIndentMarkers, paragraphSpacingMarkers?: ImportedDocxParagraphSpacingMarkers, paragraphBorderMarkers?: ImportedDocxParagraphBorderMarkers, paragraphShadingMarkers?: ImportedDocxParagraphShadingMarkers, paragraphPaginationMarkers?: ImportedDocxParagraphPaginationMarkers, bibliography?: WorkDocumentContent['bibliography'], tabStopMarkers?: ImportedDocxParagraphTabStopMarkers, tableCellMarkers?: ImportedDocxTableCellMarkers, tableRowMarkers?: ImportedDocxTableRowMarkers, tableSizingMarkers?: ImportedDocxTableSizingMarkers): string;
60
+ export declare function applyDocxSectionsToHtml(html: string, sections: PreparedDocxImport['sections'], captionMarkers?: ImportedDocxCaptionMarkers, bookmarkMarkers?: ImportedDocxBookmarkMarkers, changeMarkers?: ImportedDocxChangeMarkers, commentMarkers?: ImportedDocxCommentMarkers, fieldMarkers?: ImportedDocxFieldMarkers, equationMarkers?: ImportedDocxEquationMarkers, citationMarkers?: ImportedDocxCitationMarkers, listMarkers?: ImportedDocxListMarkers, imageLayoutMarkers?: ImportedDocxImageLayoutMarkers, paragraphIdentityMarkers?: ImportedDocxParagraphIdentityMarkers, paragraphFormattingChangeMarkers?: ImportedDocxParagraphFormattingChangeMarkers, paragraphAlignmentMarkers?: ImportedDocxParagraphAlignmentMarkers, runFormattingMarkers?: ImportedDocxRunFormattingMarkers, paragraphDirectionMarkers?: ImportedDocxParagraphDirectionMarkers, paragraphIndentMarkers?: ImportedDocxParagraphIndentMarkers, paragraphSpacingMarkers?: ImportedDocxParagraphSpacingMarkers, paragraphBorderMarkers?: ImportedDocxParagraphBorderMarkers, paragraphShadingMarkers?: ImportedDocxParagraphShadingMarkers, paragraphPaginationMarkers?: ImportedDocxParagraphPaginationMarkers, bibliography?: WorkDocumentContent['bibliography'], tabStopMarkers?: ImportedDocxParagraphTabStopMarkers, tableCellMarkers?: ImportedDocxTableCellMarkers, tableRowMarkers?: ImportedDocxTableRowMarkers, tableSizingMarkers?: ImportedDocxTableSizingMarkers): string;
59
61
  export declare function readDocxLayout(buffer: ArrayBuffer): Promise<ImportedDocumentLayout>;
60
62
  export {};
@@ -11,3 +11,4 @@ export interface ImportedDocxParagraphAlignmentMarkers {
11
11
  export declare function markDocxParagraphAlignments(document: Document, styleSource?: DocxParagraphStyleSource, tableStyleSource?: DocxTableStyleSource): ImportedDocxParagraphAlignmentMarkers;
12
12
  export declare function applyImportedDocxParagraphAlignmentMarkers(document: Document, markers: ImportedDocxParagraphAlignmentMarkers): void;
13
13
  export declare function hasImportedDocxParagraphAlignmentMarkers(markers: ImportedDocxParagraphAlignmentMarkers): boolean;
14
+ export declare function resolveDocxParagraphAlignment(propertySources: readonly Element[]): ImportedDocxParagraphAlignment | null;
@@ -18,6 +18,7 @@ export interface ResolvedDocxParagraphBorders {
18
18
  }
19
19
  export declare function markDocxParagraphBorders(document: Document, styleSource?: DocxParagraphStyleSource, themeSource?: DocxThemeSource, tableStyleSource?: DocxTableStyleSource): ImportedDocxParagraphBorderMarkers;
20
20
  export declare function resolveDocxParagraphBordersForParagraph(paragraph: Element, styleSource?: DocxParagraphStyleSource, themeSource?: DocxThemeSource, tableStyleSource?: DocxTableStyleSource): ResolvedDocxParagraphBorders;
21
+ export declare function resolveDocxParagraphBordersFromSources(propertySources: readonly Element[], themeSource?: DocxThemeSource): ResolvedDocxParagraphBorders;
21
22
  export declare function applyImportedDocxParagraphBorderMarkers(document: Document, markers: ImportedDocxParagraphBorderMarkers): void;
22
23
  export declare function hasImportedDocxParagraphBorderMarkers(markers: ImportedDocxParagraphBorderMarkers): boolean;
23
24
  export declare function parseDirectDocxParagraphBorders(properties: Element, themeSource?: DocxThemeSource): DocumentParagraphBorders | null | undefined;
@@ -11,3 +11,4 @@ export interface ImportedDocxParagraphDirectionMarkers {
11
11
  export declare function markDocxParagraphDirections(document: Document, styleSource?: DocxParagraphStyleSource, tableStyleSource?: DocxTableStyleSource): ImportedDocxParagraphDirectionMarkers;
12
12
  export declare function applyImportedDocxParagraphDirectionMarkers(document: Document, markers: ImportedDocxParagraphDirectionMarkers): void;
13
13
  export declare function hasImportedDocxParagraphDirectionMarkers(markers: ImportedDocxParagraphDirectionMarkers): boolean;
14
+ export declare function resolveDocxParagraphDirection(propertySources: readonly Element[]): DocumentParagraphDirection | null;
@@ -0,0 +1,13 @@
1
+ interface DocxParagraphFormattingChangePatch {
2
+ marker: string;
3
+ id: number;
4
+ author: string;
5
+ date: string;
6
+ before: string;
7
+ }
8
+ export declare class DocxParagraphFormattingChangePatchCollector {
9
+ readonly patches: DocxParagraphFormattingChangePatch[];
10
+ register(element: HTMLElement, id: number): string | null;
11
+ }
12
+ export declare function patchDocxParagraphFormattingChanges(buffer: ArrayBuffer, patches: readonly DocxParagraphFormattingChangePatch[]): Promise<ArrayBuffer>;
13
+ export {};
@@ -0,0 +1,17 @@
1
+ import { type DocxParagraphStyleSource } from './work-docx-paragraph-styles';
2
+ import { type DocxTableStyleSource } from './work-docx-table-styles';
3
+ import { type DocxThemeSource } from './work-docx-theme';
4
+ export interface ImportedDocxParagraphFormattingChangeMarker {
5
+ marker: string;
6
+ id: string;
7
+ author: string;
8
+ date: string;
9
+ before: string;
10
+ }
11
+ export interface ImportedDocxParagraphFormattingChangeMarkers {
12
+ paragraphs: ImportedDocxParagraphFormattingChangeMarker[];
13
+ }
14
+ export declare function markDocxParagraphFormattingChanges(document: Document, styleSource?: DocxParagraphStyleSource, themeSource?: DocxThemeSource, tableStyleSource?: DocxTableStyleSource): ImportedDocxParagraphFormattingChangeMarkers;
15
+ export declare function applyImportedDocxParagraphFormattingChangeMarkers(document: Document, markers: ImportedDocxParagraphFormattingChangeMarkers): void;
16
+ export declare function hasImportedDocxParagraphFormattingChangeMarkers(markers: ImportedDocxParagraphFormattingChangeMarkers): boolean;
17
+ export declare function isSupportedDocxParagraphFormattingChange(change: Element): boolean;
@@ -11,3 +11,4 @@ export interface ImportedDocxParagraphIndentMarkers {
11
11
  export declare function markDocxParagraphIndents(document: Document, styleSource?: DocxParagraphStyleSource, tableStyleSource?: DocxTableStyleSource): ImportedDocxParagraphIndentMarkers;
12
12
  export declare function applyImportedDocxParagraphIndentMarkers(document: Document, markers: ImportedDocxParagraphIndentMarkers): void;
13
13
  export declare function hasImportedDocxParagraphIndentMarkers(markers: ImportedDocxParagraphIndentMarkers): boolean;
14
+ export declare function resolveDocxParagraphIndent(propertySources: readonly Element[]): DocumentParagraphIndent;
@@ -16,3 +16,4 @@ export interface ImportedDocxParagraphPaginationMarkers {
16
16
  export declare function markDocxParagraphPagination(document: Document, styleSource?: DocxParagraphStyleSource, tableStyleSource?: DocxTableStyleSource): ImportedDocxParagraphPaginationMarkers;
17
17
  export declare function applyImportedDocxParagraphPaginationMarkers(document: Document, markers: ImportedDocxParagraphPaginationMarkers): void;
18
18
  export declare function hasImportedDocxParagraphPaginationMarkers(markers: ImportedDocxParagraphPaginationMarkers): boolean;
19
+ export declare function resolveDocxParagraphPagination(sources: readonly Element[]): ImportedDocxParagraphPagination;
@@ -16,3 +16,4 @@ export declare function applyImportedDocxParagraphShadingMarkers(document: Docum
16
16
  export declare function hasImportedDocxParagraphShadingMarkers(markers: ImportedDocxParagraphShadingMarkers): boolean;
17
17
  export declare function parseDocxParagraphShadingElement(element: Element, themeSource?: DocxThemeSource): DocumentParagraphShading | null;
18
18
  export declare function parseDirectDocxParagraphShading(properties: Element, themeSource?: DocxThemeSource): DocumentParagraphShading | null | undefined;
19
+ export declare function resolveDocxParagraphShadingFromSources(propertySources: readonly Element[], themeSource?: DocxThemeSource): DocumentParagraphShading | null;
@@ -11,3 +11,4 @@ export interface ImportedDocxParagraphSpacingMarkers {
11
11
  export declare function markDocxParagraphSpacing(document: Document, styleSource?: DocxParagraphStyleSource, tableStyleSource?: DocxTableStyleSource): ImportedDocxParagraphSpacingMarkers;
12
12
  export declare function applyImportedDocxParagraphSpacingMarkers(document: Document, markers: ImportedDocxParagraphSpacingMarkers): void;
13
13
  export declare function hasImportedDocxParagraphSpacingMarkers(markers: ImportedDocxParagraphSpacingMarkers): boolean;
14
+ export declare function resolveDocxParagraphSpacing(propertySources: readonly Element[]): Partial<DocumentParagraphSpacing>;
@@ -7,6 +7,8 @@ export interface ImportedDocxRunFormatting {
7
7
  italic?: boolean;
8
8
  underline?: boolean;
9
9
  strike?: boolean;
10
+ subscript?: boolean;
11
+ superscript?: boolean;
10
12
  fontFamily?: string;
11
13
  wordLineHeightFactor?: number;
12
14
  wordSnapToGrid?: boolean;
@@ -20,6 +22,13 @@ export interface ImportedDocxRunFormattingMarker {
20
22
  startMarker: string;
21
23
  endMarker: string;
22
24
  formatting: ImportedDocxRunFormatting;
25
+ change?: ImportedDocxRunFormattingChange;
26
+ }
27
+ export interface ImportedDocxRunFormattingChange {
28
+ id: string;
29
+ author: string;
30
+ date: string;
31
+ before: string;
23
32
  }
24
33
  export interface ImportedDocxRunFormattingMarkers {
25
34
  runs: ImportedDocxRunFormattingMarker[];
@@ -27,3 +36,4 @@ export interface ImportedDocxRunFormattingMarkers {
27
36
  export declare function markDocxRunFormatting(document: Document, styleSource?: DocxParagraphStyleSource, themeSource?: DocxThemeSource, tableStyleSource?: DocxTableStyleSource): ImportedDocxRunFormattingMarkers;
28
37
  export declare function applyImportedDocxRunFormattingMarkers(document: Document, markers: ImportedDocxRunFormattingMarkers): void;
29
38
  export declare function hasImportedDocxRunFormattingMarkers(markers: ImportedDocxRunFormattingMarkers): boolean;
39
+ export declare function isSupportedDocxRunFormattingChange(change: Element): boolean;
@@ -12,3 +12,4 @@ export interface ImportedDocxParagraphTabStopMarkers {
12
12
  export declare function markDocxParagraphTabStops(document: Document, styleSource?: DocxParagraphStyleSource, tableStyleSource?: DocxTableStyleSource): ImportedDocxParagraphTabStopMarkers;
13
13
  export declare function applyImportedDocxParagraphTabStopMarkers(document: Document, markers: ImportedDocxParagraphTabStopMarkers): void;
14
14
  export declare function hasImportedDocxParagraphTabStopMarkers(markers: ImportedDocxParagraphTabStopMarkers): boolean;
15
+ export declare function resolveDocxParagraphTabStops(sources: readonly Element[]): DocumentTabStop[];
@@ -106,7 +106,7 @@ export interface WorkDocumentContent {
106
106
  comments?: WorkDocumentComment[];
107
107
  bibliography?: WorkDocumentBibliography;
108
108
  }
109
- export type WorkDocumentChangeKind = 'insertion' | 'deletion';
109
+ export type WorkDocumentChangeKind = 'insertion' | 'deletion' | 'formatting' | 'paragraph-formatting';
110
110
  export type WorkDocumentChangeDecisionAction = 'accept' | 'reject';
111
111
  /**
112
112
  * Immutable audit record created when an editor accepts or rejects one
Binary file
package/dist/styles.css CHANGED
@@ -8543,6 +8543,10 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
8543
8543
  border-left-color: var(--a3s-red);
8544
8544
  }
8545
8545
 
8546
+ .work-document-change-item.formatting {
8547
+ border-left-color: #6d5bd0;
8548
+ }
8549
+
8546
8550
  .work-document-change-summary {
8547
8551
  min-width: 0;
8548
8552
  color: var(--a3s-ink);
@@ -8569,6 +8573,11 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
8569
8573
  background: #fff0ee;
8570
8574
  }
8571
8575
 
8576
+ .work-document-change-list .work-document-change-item.formatting .work-document-change-summary > span {
8577
+ color: #5746b5;
8578
+ background: #f0edff;
8579
+ }
8580
+
8572
8581
  .work-document-change-summary strong {
8573
8582
  text-overflow: ellipsis;
8574
8583
  white-space: nowrap;
@@ -9066,6 +9075,17 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
9066
9075
  text-decoration-thickness: 1px;
9067
9076
  }
9068
9077
 
9078
+ .work-document-editor span[data-document-change][data-change-kind="formatting"], .work-pdf-export-page.document span[data-document-change][data-change-kind="formatting"] {
9079
+ color: inherit;
9080
+ background: #f0edff;
9081
+ border-bottom: 1px dashed #6d5bd0;
9082
+ text-decoration: none;
9083
+ }
9084
+
9085
+ .work-document-editor :is(p, h1, h2, h3, h4, h5, h6)[data-document-change][data-change-kind="paragraph-formatting"], .work-pdf-export-page.document :is(p, h1, h2, h3, h4, h5, h6)[data-document-change][data-change-kind="paragraph-formatting"] {
9086
+ box-shadow: inset 3px 0 #8b5cf6;
9087
+ }
9088
+
9069
9089
  .work-document-list-tools {
9070
9090
  align-items: center;
9071
9091
  gap: 2px;
@@ -12019,6 +12039,14 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
12019
12039
  color: var(--a3s-red);
12020
12040
  }
12021
12041
 
12042
+ .work-document-change-decisions li[data-document-change-kind="formatting"] {
12043
+ border-left: 3px solid #6d5bd0;
12044
+ }
12045
+
12046
+ .work-document-change-decisions li[data-document-change-kind="paragraph-formatting"] {
12047
+ border-left: 3px solid #8b5cf6;
12048
+ }
12049
+
12022
12050
  .work-document-change-decisions li > strong {
12023
12051
  text-overflow: ellipsis;
12024
12052
  white-space: nowrap;
@@ -222,6 +222,18 @@ rejecting every revision requires confirmation, and an empty revision pane does
222
222
  not retain disabled bulk controls. Deleting a comment confirms that its thread
223
223
  and any unsent reply will also be removed.
224
224
 
225
+ Paragraph formatting is a node-level review concern rather than an inline
226
+ mark. When tracking is active, one formatting transaction compares the complete
227
+ canonical paragraph-property set before and after each affected paragraph or
228
+ heading. A changed set receives one shared `paragraph-formatting` identity and
229
+ the first complete prior snapshot; later edits retain that original review
230
+ baseline. Accept clears only the revision attributes. Reject restores the
231
+ validated snapshot without changing child content. Both decisions are atomic
232
+ across every node with the same identity and have their own undo boundary.
233
+ DOCX import/export maps that model to strict or transitional `w:pPrChange`,
234
+ while Yjs stores the same node attributes and immutable decision audit without a
235
+ parallel transport model.
236
+
225
237
  Selected document text has a typed host-owned context-menu boundary.
226
238
  `getSelectionMenuItems` receives an immutable snapshot containing the selected
227
239
  plain text and structured fragment, bounded adjacent text, synchronized HTML,
@@ -350,6 +362,12 @@ real DOCX with 120 native OOXML insertions and proves bounded mounting,
350
362
  first/last focus, the 120-to-119 decision transition, spacer geometry, and zero
351
363
  console or page errors.
352
364
 
365
+ The public formatting fixture also exercises the non-windowed review boundary
366
+ with independent character- and paragraph-formatting cards. Focused A3S Test
367
+ suites reject each kind separately, verify that text and the other revision
368
+ remain intact, and prove full paragraph alignment, indentation, spacing, and
369
+ line-height restoration with clean browser diagnostics.
370
+
353
371
  Spreadsheet now uses a persistent browser Rust/WASM calculation session. The
354
372
  editor initializes it with a sparse workbook replacement, sends bounded cell
355
373
  patches from stable Fortune cell operations, and requests only the dirty
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a3s-lab/office",
3
- "version": "0.9.2",
3
+ "version": "0.11.0",
4
4
  "description": "Open-source collaborative browser editors for documents, Markdown, spreadsheets, presentations, and PDFs.",
5
5
  "keywords": [
6
6
  "office",
@@ -90,8 +90,8 @@
90
90
  "playground:visual:spreadsheet-ribbon": "playwright test visual-tests/spreadsheet-ribbon.functional.spec.ts --config playwright.config.ts",
91
91
  "playground:visual:update": "playwright test --config playwright.config.ts --update-snapshots",
92
92
  "test": "rstest",
93
- "test:e2e": "bun run test:e2e:fixtures && a3s-test run tests/e2e/word-page-color.acl --json && a3s-test run tests/e2e/word-page-setup-phone.acl --json && a3s-test run tests/e2e/word-paragraph-layout-phone.acl --json && a3s-test run tests/e2e/word-list-formatting-phone.acl --json && a3s-test run tests/e2e/word-font-picker-phone.acl --json && a3s-test run tests/e2e/word-comments-drawer.acl --json && a3s-test run tests/e2e/word-comment-windowing.acl --json && a3s-test run tests/e2e/word-citations-phone.acl --json && a3s-test run tests/e2e/word-navigation-search.acl --json && a3s-test run tests/e2e/word-navigation-windowing.acl --json && a3s-test run tests/e2e/word-find-replace-phone.acl --json && a3s-test run tests/e2e/word-page-navigation.acl --json && a3s-test run tests/e2e/word-page-windowing.acl --json && a3s-test run tests/e2e/word-ai-question.acl --json && a3s-test run tests/e2e/word-picture-insert.acl --json && a3s-test run tests/e2e/word-picture-alt-text-phone.acl --json && a3s-test run tests/e2e/word-track-changes-phone.acl --json && a3s-test run tests/e2e/word-review-conflict-phone.acl --json && a3s-test run tests/e2e/word-revision-windowing.acl --json && a3s-test run tests/e2e/word-table-phone.acl --json && a3s-test run tests/e2e/word-table-borders.acl --json && a3s-test run tests/e2e/word-theme-table.acl --json && a3s-test run tests/e2e/word-styled-table.acl --json && a3s-test run tests/e2e/word-multi-page-table.acl --json && a3s-test run tests/e2e/word-cross-reference-phone.acl --json && a3s-test run tests/e2e/word-bookmark-links-phone.acl --json && a3s-test run tests/e2e/word-notes-phone.acl --json && a3s-test run tests/e2e/word-fields-phone.acl --json && a3s-test run tests/e2e/spreadsheet-phone-rename.acl --json && a3s-test run tests/e2e/spreadsheet-phone-find.acl --json && a3s-test run tests/e2e/spreadsheet-phone-context-menu.acl --json && a3s-test run tests/e2e/spreadsheet-phone-task-pane.acl --json && a3s-test run tests/e2e/presentation-cut-paste-focus.acl --json && a3s-test run tests/e2e/presentation-presenter-view.acl --json && a3s-test run tests/e2e/presentation-phone-chart-pane.acl --json && a3s-test run tests/e2e/presentation-phone-comments.acl --json && a3s-test run tests/e2e/pdf-thumbnail-keyboard.acl --json && a3s-test run tests/e2e/pdf-page-drawer-phone.acl --json && a3s-test run tests/e2e/markdown-phone-modes.acl --json && a3s-test run tests/e2e/office-sidebar-breakpoint.acl --json && a3s-test run tests/e2e/office-docs-navigation.acl --json && a3s-test run tests/e2e/collaboration-playground-entry.acl --json && a3s-test run tests/e2e/collaboration-document-comments.acl --command-timeout-ms 120000 --json && a3s-test run tests/e2e/collaboration-document-suggestions.acl --command-timeout-ms 120000 --json && a3s-test run tests/e2e/collaboration-participants.acl --json && a3s-test run tests/e2e/collaboration-pdf-annotations.acl --json && a3s-test run tests/e2e/collaboration-spreadsheet-cells.acl --json && a3s-test run tests/e2e/collaboration-presentation-elements.acl --json",
94
- "test:e2e:check": "bun run test:e2e:fixtures && a3s-test check tests/e2e/word-page-color.acl --json && a3s-test check tests/e2e/word-page-setup-phone.acl --json && a3s-test check tests/e2e/word-paragraph-layout-phone.acl --json && a3s-test check tests/e2e/word-list-formatting-phone.acl --json && a3s-test check tests/e2e/word-font-picker-phone.acl --json && a3s-test check tests/e2e/word-comments-drawer.acl --json && a3s-test check tests/e2e/word-comment-windowing.acl --json && a3s-test check tests/e2e/word-citations-phone.acl --json && a3s-test check tests/e2e/word-navigation-search.acl --json && a3s-test check tests/e2e/word-navigation-windowing.acl --json && a3s-test check tests/e2e/word-find-replace-phone.acl --json && a3s-test check tests/e2e/word-page-navigation.acl --json && a3s-test check tests/e2e/word-page-windowing.acl --json && a3s-test check tests/e2e/word-ai-question.acl --json && a3s-test check tests/e2e/word-picture-insert.acl --json && a3s-test check tests/e2e/word-picture-alt-text-phone.acl --json && a3s-test check tests/e2e/word-track-changes-phone.acl --json && a3s-test check tests/e2e/word-review-conflict-phone.acl --json && a3s-test check tests/e2e/word-revision-windowing.acl --json && a3s-test check tests/e2e/word-table-phone.acl --json && a3s-test check tests/e2e/word-table-borders.acl --json && a3s-test check tests/e2e/word-theme-table.acl --json && a3s-test check tests/e2e/word-styled-table.acl --json && a3s-test check tests/e2e/word-multi-page-table.acl --json && a3s-test check tests/e2e/word-cross-reference-phone.acl --json && a3s-test check tests/e2e/word-bookmark-links-phone.acl --json && a3s-test check tests/e2e/word-notes-phone.acl --json && a3s-test check tests/e2e/word-fields-phone.acl --json && a3s-test check tests/e2e/spreadsheet-phone-rename.acl --json && a3s-test check tests/e2e/spreadsheet-phone-find.acl --json && a3s-test check tests/e2e/spreadsheet-phone-context-menu.acl --json && a3s-test check tests/e2e/spreadsheet-phone-task-pane.acl --json && a3s-test check tests/e2e/presentation-cut-paste-focus.acl --json && a3s-test check tests/e2e/presentation-presenter-view.acl --json && a3s-test check tests/e2e/presentation-phone-chart-pane.acl --json && a3s-test check tests/e2e/presentation-phone-comments.acl --json && a3s-test check tests/e2e/pdf-thumbnail-keyboard.acl --json && a3s-test check tests/e2e/pdf-page-drawer-phone.acl --json && a3s-test check tests/e2e/markdown-phone-modes.acl --json && a3s-test check tests/e2e/office-sidebar-breakpoint.acl --json && a3s-test check tests/e2e/office-docs-navigation.acl --json && a3s-test check tests/e2e/collaboration-playground-entry.acl --json && a3s-test check tests/e2e/collaboration-document-comments.acl --json && a3s-test check tests/e2e/collaboration-document-suggestions.acl --json && a3s-test check tests/e2e/collaboration-participants.acl --json && a3s-test check tests/e2e/collaboration-pdf-annotations.acl --json && a3s-test check tests/e2e/collaboration-spreadsheet-cells.acl --json && a3s-test check tests/e2e/collaboration-presentation-elements.acl --json",
93
+ "test:e2e": "bun run test:e2e:fixtures && a3s-test run tests/e2e/word-page-color.acl --json && a3s-test run tests/e2e/word-page-setup-phone.acl --json && a3s-test run tests/e2e/word-paragraph-layout-phone.acl --json && a3s-test run tests/e2e/word-list-formatting-phone.acl --json && a3s-test run tests/e2e/word-font-picker-phone.acl --json && a3s-test run tests/e2e/word-comments-drawer.acl --json && a3s-test run tests/e2e/word-comment-windowing.acl --json && a3s-test run tests/e2e/word-citations-phone.acl --json && a3s-test run tests/e2e/word-navigation-search.acl --json && a3s-test run tests/e2e/word-navigation-windowing.acl --json && a3s-test run tests/e2e/word-find-replace-phone.acl --json && a3s-test run tests/e2e/word-page-navigation.acl --json && a3s-test run tests/e2e/word-page-windowing.acl --json && a3s-test run tests/e2e/word-ai-question.acl --json && a3s-test run tests/e2e/word-picture-insert.acl --json && a3s-test run tests/e2e/word-picture-alt-text-phone.acl --json && a3s-test run tests/e2e/word-track-changes-phone.acl --json && a3s-test run tests/e2e/word-formatting-revision.acl --json && a3s-test run tests/e2e/word-paragraph-formatting-revision.acl --json && a3s-test run tests/e2e/word-review-conflict-phone.acl --json && a3s-test run tests/e2e/word-revision-windowing.acl --json && a3s-test run tests/e2e/word-table-phone.acl --json && a3s-test run tests/e2e/word-table-borders.acl --json && a3s-test run tests/e2e/word-theme-table.acl --json && a3s-test run tests/e2e/word-styled-table.acl --json && a3s-test run tests/e2e/word-multi-page-table.acl --json && a3s-test run tests/e2e/word-cross-reference-phone.acl --json && a3s-test run tests/e2e/word-bookmark-links-phone.acl --json && a3s-test run tests/e2e/word-notes-phone.acl --json && a3s-test run tests/e2e/word-fields-phone.acl --json && a3s-test run tests/e2e/spreadsheet-phone-rename.acl --json && a3s-test run tests/e2e/spreadsheet-phone-find.acl --json && a3s-test run tests/e2e/spreadsheet-phone-context-menu.acl --json && a3s-test run tests/e2e/spreadsheet-phone-task-pane.acl --json && a3s-test run tests/e2e/presentation-cut-paste-focus.acl --json && a3s-test run tests/e2e/presentation-presenter-view.acl --json && a3s-test run tests/e2e/presentation-phone-chart-pane.acl --json && a3s-test run tests/e2e/presentation-phone-comments.acl --json && a3s-test run tests/e2e/pdf-thumbnail-keyboard.acl --json && a3s-test run tests/e2e/pdf-page-drawer-phone.acl --json && a3s-test run tests/e2e/markdown-phone-modes.acl --json && a3s-test run tests/e2e/office-sidebar-breakpoint.acl --json && a3s-test run tests/e2e/office-docs-navigation.acl --json && a3s-test run tests/e2e/collaboration-playground-entry.acl --json && a3s-test run tests/e2e/collaboration-document-comments.acl --command-timeout-ms 120000 --json && a3s-test run tests/e2e/collaboration-document-suggestions.acl --command-timeout-ms 120000 --json && a3s-test run tests/e2e/collaboration-participants.acl --json && a3s-test run tests/e2e/collaboration-pdf-annotations.acl --json && a3s-test run tests/e2e/collaboration-spreadsheet-cells.acl --json && a3s-test run tests/e2e/collaboration-presentation-elements.acl --json",
94
+ "test:e2e:check": "bun run test:e2e:fixtures && a3s-test check tests/e2e/word-page-color.acl --json && a3s-test check tests/e2e/word-page-setup-phone.acl --json && a3s-test check tests/e2e/word-paragraph-layout-phone.acl --json && a3s-test check tests/e2e/word-list-formatting-phone.acl --json && a3s-test check tests/e2e/word-font-picker-phone.acl --json && a3s-test check tests/e2e/word-comments-drawer.acl --json && a3s-test check tests/e2e/word-comment-windowing.acl --json && a3s-test check tests/e2e/word-citations-phone.acl --json && a3s-test check tests/e2e/word-navigation-search.acl --json && a3s-test check tests/e2e/word-navigation-windowing.acl --json && a3s-test check tests/e2e/word-find-replace-phone.acl --json && a3s-test check tests/e2e/word-page-navigation.acl --json && a3s-test check tests/e2e/word-page-windowing.acl --json && a3s-test check tests/e2e/word-ai-question.acl --json && a3s-test check tests/e2e/word-picture-insert.acl --json && a3s-test check tests/e2e/word-picture-alt-text-phone.acl --json && a3s-test check tests/e2e/word-track-changes-phone.acl --json && a3s-test check tests/e2e/word-formatting-revision.acl --json && a3s-test check tests/e2e/word-paragraph-formatting-revision.acl --json && a3s-test check tests/e2e/word-review-conflict-phone.acl --json && a3s-test check tests/e2e/word-revision-windowing.acl --json && a3s-test check tests/e2e/word-table-phone.acl --json && a3s-test check tests/e2e/word-table-borders.acl --json && a3s-test check tests/e2e/word-theme-table.acl --json && a3s-test check tests/e2e/word-styled-table.acl --json && a3s-test check tests/e2e/word-multi-page-table.acl --json && a3s-test check tests/e2e/word-cross-reference-phone.acl --json && a3s-test check tests/e2e/word-bookmark-links-phone.acl --json && a3s-test check tests/e2e/word-notes-phone.acl --json && a3s-test check tests/e2e/word-fields-phone.acl --json && a3s-test check tests/e2e/spreadsheet-phone-rename.acl --json && a3s-test check tests/e2e/spreadsheet-phone-find.acl --json && a3s-test check tests/e2e/spreadsheet-phone-context-menu.acl --json && a3s-test check tests/e2e/spreadsheet-phone-task-pane.acl --json && a3s-test check tests/e2e/presentation-cut-paste-focus.acl --json && a3s-test check tests/e2e/presentation-presenter-view.acl --json && a3s-test check tests/e2e/presentation-phone-chart-pane.acl --json && a3s-test check tests/e2e/presentation-phone-comments.acl --json && a3s-test check tests/e2e/pdf-thumbnail-keyboard.acl --json && a3s-test check tests/e2e/pdf-page-drawer-phone.acl --json && a3s-test check tests/e2e/markdown-phone-modes.acl --json && a3s-test check tests/e2e/office-sidebar-breakpoint.acl --json && a3s-test check tests/e2e/office-docs-navigation.acl --json && a3s-test check tests/e2e/collaboration-playground-entry.acl --json && a3s-test check tests/e2e/collaboration-document-comments.acl --json && a3s-test check tests/e2e/collaboration-document-suggestions.acl --json && a3s-test check tests/e2e/collaboration-participants.acl --json && a3s-test check tests/e2e/collaboration-pdf-annotations.acl --json && a3s-test check tests/e2e/collaboration-spreadsheet-cells.acl --json && a3s-test check tests/e2e/collaboration-presentation-elements.acl --json",
95
95
  "test:e2e:fixtures": "bun scripts/create-e2e-fixtures.ts",
96
96
  "test:e2e:initial-focus": "a3s-test run tests/e2e/office-editor-initial-focus.acl --json",
97
97
  "test:e2e:initial-focus:check": "a3s-test check tests/e2e/office-editor-initial-focus.acl --json",
@@ -123,6 +123,10 @@
123
123
  "test:e2e:writer-layout:check": "a3s-test check tests/e2e/word-insert-page-layout.acl --json",
124
124
  "test:e2e:writer-review-view": "a3s-test run tests/e2e/word-review-view.acl --json",
125
125
  "test:e2e:writer-review-view:check": "a3s-test check tests/e2e/word-review-view.acl --json",
126
+ "test:e2e:writer-formatting-revision": "a3s-test run tests/e2e/word-formatting-revision.acl --json",
127
+ "test:e2e:writer-formatting-revision:check": "a3s-test check tests/e2e/word-formatting-revision.acl --json",
128
+ "test:e2e:writer-paragraph-formatting-revision": "a3s-test run tests/e2e/word-paragraph-formatting-revision.acl --json",
129
+ "test:e2e:writer-paragraph-formatting-revision:check": "a3s-test check tests/e2e/word-paragraph-formatting-revision.acl --json",
126
130
  "test:e2e:writer-status": "a3s-test run tests/e2e/word-status-bar.acl --json",
127
131
  "test:e2e:writer-status:check": "a3s-test check tests/e2e/word-status-bar.acl --json",
128
132
  "test:wps-layout": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/test-wps-layout-parity.ps1",