@a3s-lab/office 0.28.0 → 0.30.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 (69) hide show
  1. package/README.md +126 -3
  2. package/dist/0~1403.js +1 -1
  3. package/dist/0~4980.js +1 -1
  4. package/dist/0~7048.js +1 -1
  5. package/dist/{0~7043.js → 0~7231.js} +400 -242
  6. package/dist/{0~5093.js → 0~7360.js} +293 -53
  7. package/dist/0~7614.js +1 -1
  8. package/dist/0~document-editor.js +1535 -102
  9. package/dist/0~presentation-editor.js +1 -1
  10. package/dist/0~spreadsheet-editor.js +184 -36
  11. package/dist/0~work-docx-export.js +437 -11
  12. package/dist/0~work-docx-import.js +32 -7
  13. package/dist/0~work-office-diagnostics.js +170 -7
  14. package/dist/0~work-pptx-export.js +1 -1
  15. package/dist/0~work-pptx-import.js +1 -1
  16. package/dist/0~work-presentation-charts.js +1 -1
  17. package/dist/1544.js +14 -2
  18. package/dist/4104.js +31 -8
  19. package/dist/4121.js +3448 -0
  20. package/dist/6282.js +558 -1688
  21. package/dist/8715.js +1 -1
  22. package/dist/core.js +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/internal/features/work/editors/document-command-catalog.d.ts +27 -0
  25. package/dist/internal/features/work/editors/document-font-dialog-model.d.ts +8 -2
  26. package/dist/internal/features/work/editors/document-font-dialog-run-border-model.d.ts +24 -0
  27. package/dist/internal/features/work/editors/document-font-dialog-run-border-section.d.ts +10 -0
  28. package/dist/internal/features/work/editors/document-font-dialog-run-shading-model.d.ts +23 -0
  29. package/dist/internal/features/work/editors/document-font-dialog-run-shading-section.d.ts +8 -0
  30. package/dist/internal/features/work/editors/document-page-chrome-ribbon.d.ts +2 -1
  31. package/dist/internal/features/work/editors/document-proofing-dialog-model.d.ts +22 -0
  32. package/dist/internal/features/work/editors/document-proofing-dialog.d.ts +7 -0
  33. package/dist/internal/features/work/editors/document-table-of-contents-dialog.d.ts +10 -0
  34. package/dist/internal/features/work/editors/document-toolbar.d.ts +3 -1
  35. package/dist/internal/features/work/editors/spreadsheet-data-validation.d.ts +7 -1
  36. package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +2 -0
  37. package/dist/internal/features/work/work-document-character-formatting.d.ts +12 -0
  38. package/dist/internal/features/work/work-document-format-changes.d.ts +7 -0
  39. package/dist/internal/features/work/work-document-highlight.d.ts +9 -0
  40. package/dist/internal/features/work/work-document-outline.d.ts +1 -0
  41. package/dist/internal/features/work/work-document-paragraph-shading.d.ts +6 -0
  42. package/dist/internal/features/work/work-document-proofing.d.ts +19 -0
  43. package/dist/internal/features/work/work-document-run-border.d.ts +11 -0
  44. package/dist/internal/features/work/work-document-run-shading.d.ts +12 -0
  45. package/dist/internal/features/work/work-document-table-of-contents-node.d.ts +18 -0
  46. package/dist/internal/features/work/work-document-table-of-contents.d.ts +51 -0
  47. package/dist/internal/features/work/work-document-word-line-metrics.d.ts +4 -0
  48. package/dist/internal/features/work/work-docx-field-instructions.d.ts +1 -1
  49. package/dist/internal/features/work/work-docx-import.d.ts +3 -1
  50. package/dist/internal/features/work/work-docx-proofing-diagnostics.d.ts +3 -0
  51. package/dist/internal/features/work/work-docx-proofing.d.ts +38 -0
  52. package/dist/internal/features/work/work-docx-run-border-diagnostics.d.ts +3 -0
  53. package/dist/internal/features/work/work-docx-run-border-export.d.ts +17 -0
  54. package/dist/internal/features/work/work-docx-run-border.d.ts +20 -0
  55. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +9 -0
  56. package/dist/internal/features/work/work-docx-run-shading-diagnostics.d.ts +3 -0
  57. package/dist/internal/features/work/work-docx-run-shading-export.d.ts +17 -0
  58. package/dist/internal/features/work/work-docx-run-shading.d.ts +20 -0
  59. package/dist/internal/features/work/work-docx-table-of-contents-export.d.ts +11 -0
  60. package/dist/internal/features/work/work-docx-table-of-contents-import.d.ts +15 -0
  61. package/dist/internal/features/work/work-spreadsheet-data-validation.d.ts +7 -0
  62. package/dist/internal/features/work/work-types.d.ts +9 -0
  63. package/dist/internal/kernel/office-kernel-protocol.d.ts +1 -0
  64. package/dist/office-kernel.wasm +0 -0
  65. package/dist/styles.css +339 -1
  66. package/docs/latest/en/browser-editor-architecture.md +72 -1
  67. package/package.json +15 -2
  68. package/dist/5184.js +0 -662
  69. package/dist/9424.js +0 -16
@@ -0,0 +1,17 @@
1
+ import type { IRunOptions } from 'docx';
2
+ import { type DocumentRunBorder } from './work-document-run-border';
3
+ export interface DocxRunBorderPatch {
4
+ marker: string;
5
+ border: DocumentRunBorder;
6
+ }
7
+ export declare class DocxRunBorderPatchCollector {
8
+ readonly patches: DocxRunBorderPatch[];
9
+ private nextMarker;
10
+ private readonly usedColors;
11
+ constructor(sourceHtml: string);
12
+ marker(source: unknown): string | null;
13
+ hasMarker(value: string | null | undefined): value is string;
14
+ }
15
+ export declare function documentRunBorderDocxOptions(source: unknown, collector: DocxRunBorderPatchCollector): Pick<IRunOptions, 'border'>;
16
+ export declare function patchDocxRunBorders(buffer: ArrayBuffer, patches: readonly DocxRunBorderPatch[]): Promise<ArrayBuffer>;
17
+ export declare function setDocxRunBorderAttributes(document: Document, element: Element, border: DocumentRunBorder): void;
@@ -0,0 +1,20 @@
1
+ import type { DocumentRunBorder } from './work-document-run-border';
2
+ import { type DocxThemeSource } from './work-docx-theme';
3
+ export type DocxRunBorderInspection = {
4
+ status: 'absent';
5
+ spoofedCount: number;
6
+ } | {
7
+ status: 'invalid';
8
+ spoofedCount: number;
9
+ } | {
10
+ status: 'valid';
11
+ value: DocumentRunBorder;
12
+ spoofedCount: number;
13
+ };
14
+ export interface ResolvedDocxRunBorder {
15
+ border: DocumentRunBorder | undefined;
16
+ invalidCount: number;
17
+ spoofedCount: number;
18
+ }
19
+ export declare function inspectDocxRunBorder(properties: Element | null | undefined, themeSource?: DocxThemeSource): DocxRunBorderInspection;
20
+ export declare function resolveDocxRunBorder(propertySources: readonly Element[], themeSource?: DocxThemeSource): ResolvedDocxRunBorder;
@@ -7,6 +7,10 @@ import { type WorkDocumentTextCase } from './work-document-text-case';
7
7
  import { type WorkDocumentUnderlineFormatting } from './work-document-underline';
8
8
  import { type WorkDocumentStrikeFormatting } from './work-document-strike';
9
9
  import { type WorkDocumentScriptFontSlot, type WorkDocumentScriptFonts } from './work-document-script-fonts';
10
+ import { type DocumentRunBorder } from './work-document-run-border';
11
+ import { type DocumentRunShading } from './work-document-run-shading';
12
+ import { type WorkDocumentHighlight } from './work-document-highlight';
13
+ import { type WorkDocumentProofingLanguages } from './work-document-proofing';
10
14
  export interface ImportedDocxRunFormatting {
11
15
  bold?: boolean;
12
16
  italic?: boolean;
@@ -32,9 +36,14 @@ export interface ImportedDocxRunFormatting {
32
36
  fontSize?: number;
33
37
  color?: string;
34
38
  backgroundColor?: string;
39
+ highlight?: WorkDocumentHighlight;
35
40
  themeColor?: DocxThemeColorReference;
36
41
  themeFill?: DocxThemeColorReference;
37
42
  textCase?: WorkDocumentTextCase;
43
+ runBorder?: DocumentRunBorder;
44
+ runShading?: DocumentRunShading;
45
+ proofingLanguages?: WorkDocumentProofingLanguages;
46
+ noProof?: boolean;
38
47
  }
39
48
  export interface ImportedDocxRunFormattingMarker {
40
49
  startMarker: string;
@@ -0,0 +1,3 @@
1
+ import { type OoxmlPackage } from './work-ooxml-package';
2
+ import type { WorkCompatibilityIssue } from './work-types';
3
+ export declare function diagnoseDocxRunShading(archive: OoxmlPackage, document: Document): Promise<WorkCompatibilityIssue[]>;
@@ -0,0 +1,17 @@
1
+ import type { IRunOptions } from 'docx';
2
+ import { type DocumentRunShading } from './work-document-run-shading';
3
+ export interface DocxRunShadingPatch {
4
+ marker: string;
5
+ shading: DocumentRunShading;
6
+ }
7
+ export declare class DocxRunShadingPatchCollector {
8
+ readonly patches: DocxRunShadingPatch[];
9
+ private nextMarker;
10
+ private readonly usedColors;
11
+ constructor(sourceHtml: string);
12
+ marker(source: unknown): string | null;
13
+ hasMarker(value: string | null | undefined): value is string;
14
+ }
15
+ export declare function documentRunShadingDocxOptions(source: unknown, collector: DocxRunShadingPatchCollector): Pick<IRunOptions, 'shading'>;
16
+ export declare function patchDocxRunShading(buffer: ArrayBuffer, patches: readonly DocxRunShadingPatch[]): Promise<ArrayBuffer>;
17
+ export declare function setDocxRunShadingAttributes(document: Document, element: Element, source: unknown): boolean;
@@ -0,0 +1,20 @@
1
+ import type { DocumentRunShading } from './work-document-run-shading';
2
+ import { type DocxThemeSource } from './work-docx-theme';
3
+ export type DocxRunShadingInspection = {
4
+ status: 'absent';
5
+ spoofedCount: number;
6
+ } | {
7
+ status: 'invalid';
8
+ spoofedCount: number;
9
+ } | {
10
+ status: 'valid';
11
+ value: DocumentRunShading;
12
+ spoofedCount: number;
13
+ };
14
+ export interface ResolvedDocxRunShading {
15
+ shading: DocumentRunShading | undefined;
16
+ invalidCount: number;
17
+ spoofedCount: number;
18
+ }
19
+ export declare function inspectDocxRunShading(properties: Element | null | undefined, themeSource?: DocxThemeSource): DocxRunShadingInspection;
20
+ export declare function resolveDocxRunShading(propertySources: readonly Element[], themeSource?: DocxThemeSource): ResolvedDocxRunShading;
@@ -0,0 +1,11 @@
1
+ import { type WorkDocumentTableOfContentsEntry, type WorkDocumentTableOfContentsOptions } from './work-document-table-of-contents';
2
+ export interface DocxTableOfContentsPatch {
3
+ options: WorkDocumentTableOfContentsOptions;
4
+ entries: WorkDocumentTableOfContentsEntry[];
5
+ }
6
+ export declare class DocxTableOfContentsPatchCollector {
7
+ readonly patches: DocxTableOfContentsPatch[];
8
+ add(element: HTMLElement): DocxTableOfContentsPatch;
9
+ }
10
+ export declare function docxTableOfContents(element: HTMLElement, docx: typeof import('docx'), collector: DocxTableOfContentsPatchCollector): InstanceType<typeof docx.TableOfContents>;
11
+ export declare function patchDocxTableOfContents(buffer: ArrayBuffer, patches: readonly DocxTableOfContentsPatch[]): Promise<ArrayBuffer>;
@@ -0,0 +1,15 @@
1
+ import { type WorkDocumentTableOfContentsEntry, type WorkDocumentTableOfContentsOptions } from './work-document-table-of-contents';
2
+ import { type DocxFieldOccurrence } from './work-docx-field-instructions';
3
+ export interface ImportedDocxTableOfContentsMarkers {
4
+ tables: ImportedDocxTableOfContentsMarker[];
5
+ }
6
+ export interface ImportedDocxTableOfContentsMarker {
7
+ marker: string;
8
+ id: string;
9
+ options: WorkDocumentTableOfContentsOptions;
10
+ entries: WorkDocumentTableOfContentsEntry[];
11
+ }
12
+ export declare function markDocxTablesOfContents(document: Document): ImportedDocxTableOfContentsMarkers;
13
+ export declare function applyImportedDocxTableOfContentsMarkers(document: Document, markers: ImportedDocxTableOfContentsMarkers): void;
14
+ export declare function hasImportedDocxTableOfContentsMarkers(markers: ImportedDocxTableOfContentsMarkers): boolean;
15
+ export declare function supportedDocxTableOfContentsField(field: DocxFieldOccurrence): boolean;
@@ -1,2 +1,9 @@
1
+ import type { WorkSpreadsheetDataValidationErrorStyle } from './work-types';
2
+ export declare const SPREADSHEET_DATA_VALIDATION_TITLE_LIMIT = 32;
3
+ export declare const SPREADSHEET_DATA_VALIDATION_HINT_LIMIT = 255;
4
+ export declare const SPREADSHEET_DATA_VALIDATION_ERROR_LIMIT = 225;
5
+ export declare const SPREADSHEET_DATA_VALIDATION_ERROR_STYLES: readonly ["stop", "warning", "information"];
6
+ export declare function normalizeSpreadsheetDataValidationErrorStyle(value: unknown): WorkSpreadsheetDataValidationErrorStyle;
7
+ export declare function boundedSpreadsheetDataValidationText(value: unknown, limit: number): string;
1
8
  export declare function normalizeSpreadsheetDateValidationBoundary(value: string, uses1904DateSystem?: boolean): string | null;
2
9
  export declare function spreadsheetDateValidationFormula(value: string): string;
@@ -196,6 +196,7 @@ export interface WorkSpreadsheetCellRange {
196
196
  row: [number, number];
197
197
  column: [number, number];
198
198
  }
199
+ export type WorkSpreadsheetDataValidationErrorStyle = 'information' | 'stop' | 'warning';
199
200
  export interface WorkSpreadsheetDataValidationItem {
200
201
  type: string;
201
202
  type2: string;
@@ -204,8 +205,16 @@ export interface WorkSpreadsheetDataValidationItem {
204
205
  value2: string;
205
206
  validity: string;
206
207
  remote: boolean;
208
+ /** Defaults to true for legacy A3S artifacts. */
209
+ allowBlank?: boolean;
210
+ /** Product semantic; OOXML stores the inverse in `showDropDown`. */
211
+ showDropdownArrow?: boolean;
207
212
  prohibitInput: boolean;
213
+ errorStyle?: WorkSpreadsheetDataValidationErrorStyle;
214
+ errorTitle?: string;
215
+ errorMessage?: string;
208
216
  hintShow: boolean;
217
+ hintTitle?: string;
209
218
  hintValue: string;
210
219
  checked?: boolean;
211
220
  }
@@ -140,6 +140,7 @@ export interface OfficeKernelTextLayoutRun {
140
140
  fallbackFontIds?: string[];
141
141
  fontSize: number;
142
142
  lineHeight: number;
143
+ language?: string;
143
144
  letterSpacing?: number;
144
145
  ligatures?: boolean;
145
146
  kerning?: boolean;
Binary file
package/dist/styles.css CHANGED
@@ -9718,6 +9718,37 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
9718
9718
  display: grid;
9719
9719
  }
9720
9720
 
9721
+ .work-document-font-dialog-run-border, .work-document-font-dialog-run-shading {
9722
+ border: 1px solid color-mix(in srgb, var(--a3s-line) 78%, transparent);
9723
+ background: var(--a3s-panel-soft);
9724
+ border-radius: 8px;
9725
+ grid-column: 1 / -1;
9726
+ grid-template-columns: repeat(3, minmax(0, 1fr));
9727
+ gap: 10px 14px;
9728
+ min-width: 0;
9729
+ margin: 0;
9730
+ padding: 10px 12px 12px;
9731
+ display: grid;
9732
+ }
9733
+
9734
+ .work-document-font-dialog-run-shading {
9735
+ grid-template-columns: repeat(4, minmax(0, 1fr));
9736
+ }
9737
+
9738
+ .work-document-font-dialog-run-border > legend, .work-document-font-dialog-run-shading > legend {
9739
+ color: var(--a3s-muted);
9740
+ padding: 0 4px;
9741
+ font-size: 11px;
9742
+ font-weight: 700;
9743
+ }
9744
+
9745
+ .work-document-font-dialog-run-border-effects {
9746
+ align-items: end;
9747
+ gap: 16px;
9748
+ min-height: 32px;
9749
+ display: flex;
9750
+ }
9751
+
9721
9752
  .work-document-font-dialog-field > .work-office-select, .work-document-font-dialog-field .work-office-number-field, .work-document-font-dialog-field button[role="combobox"] {
9722
9753
  width: 100%;
9723
9754
  }
@@ -9788,6 +9819,10 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
9788
9819
  grid-template-columns: repeat(2, minmax(0, 1fr));
9789
9820
  }
9790
9821
 
9822
+ .work-document-font-dialog-run-border, .work-document-font-dialog-run-shading {
9823
+ grid-template-columns: minmax(0, 1fr);
9824
+ }
9825
+
9791
9826
  .work-document-font-dialog-preview {
9792
9827
  min-height: 88px;
9793
9828
  }
@@ -9797,6 +9832,104 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
9797
9832
  }
9798
9833
  }
9799
9834
 
9835
+ .work-document-proofing-dialog {
9836
+ width: min(640px, 100%);
9837
+ }
9838
+
9839
+ .work-document-proofing-dialog form {
9840
+ gap: 16px;
9841
+ display: grid;
9842
+ }
9843
+
9844
+ .work-document-proofing-dialog fieldset {
9845
+ border: 1px solid var(--a3s-line);
9846
+ border-radius: 10px;
9847
+ grid-template-columns: repeat(3, minmax(0, 1fr));
9848
+ gap: 14px;
9849
+ margin: 0;
9850
+ padding: 15px 16px 17px;
9851
+ display: grid;
9852
+ }
9853
+
9854
+ .work-document-proofing-dialog legend {
9855
+ color: var(--a3s-ink);
9856
+ padding: 0 5px;
9857
+ font-size: 11px;
9858
+ font-weight: 700;
9859
+ }
9860
+
9861
+ .work-document-proofing-dialog-language {
9862
+ align-content: start;
9863
+ gap: 6px;
9864
+ min-width: 0;
9865
+ display: grid;
9866
+ }
9867
+
9868
+ .work-document-proofing-dialog-language > label, .work-document-proofing-dialog-state-label {
9869
+ color: var(--a3s-muted);
9870
+ font-size: 11px;
9871
+ font-weight: 650;
9872
+ }
9873
+
9874
+ .work-document-proofing-dialog-language > div {
9875
+ grid-template-columns: minmax(0, 1fr) auto;
9876
+ gap: 6px;
9877
+ display: grid;
9878
+ }
9879
+
9880
+ .work-document-proofing-dialog-language .work-office-text-field {
9881
+ width: 100%;
9882
+ min-width: 0;
9883
+ }
9884
+
9885
+ .work-document-proofing-dialog-language .ds-button {
9886
+ white-space: nowrap;
9887
+ padding-inline: 9px;
9888
+ }
9889
+
9890
+ .work-document-proofing-dialog-language > p, .work-document-proofing-dialog-state > p, .work-document-proofing-dialog-help, .work-document-proofing-dialog-error {
9891
+ margin: 0;
9892
+ font-size: 11px;
9893
+ line-height: 1.5;
9894
+ }
9895
+
9896
+ .work-document-proofing-dialog-language > p, .work-document-proofing-dialog-state > p, .work-document-proofing-dialog-help {
9897
+ color: var(--a3s-muted);
9898
+ }
9899
+
9900
+ .work-document-proofing-dialog-state {
9901
+ border: 1px solid var(--a3s-line);
9902
+ background: var(--a3s-panel-soft);
9903
+ border-radius: 10px;
9904
+ grid-template-columns: minmax(110px, auto) minmax(220px, 1fr);
9905
+ align-items: center;
9906
+ gap: 8px 14px;
9907
+ padding: 14px 16px;
9908
+ display: grid;
9909
+ }
9910
+
9911
+ .work-document-proofing-dialog-state > p {
9912
+ grid-column: 1 / -1;
9913
+ }
9914
+
9915
+ .work-document-proofing-dialog-state .work-office-select, .work-document-proofing-dialog-state button[role="combobox"] {
9916
+ width: 100%;
9917
+ }
9918
+
9919
+ .work-document-proofing-dialog-help code {
9920
+ color: var(--a3s-ink);
9921
+ }
9922
+
9923
+ .work-document-proofing-dialog-error {
9924
+ color: var(--a3s-red);
9925
+ }
9926
+
9927
+ @media (width <= 620px) {
9928
+ .work-document-proofing-dialog fieldset, .work-document-proofing-dialog-state {
9929
+ grid-template-columns: minmax(0, 1fr);
9930
+ }
9931
+ }
9932
+
9800
9933
  [data-office-character-position-half-points] {
9801
9934
  vertical-align: var(--work-document-character-position);
9802
9935
  }
@@ -12422,6 +12555,200 @@ sub [data-office-character-position-half-points], sup [data-office-character-pos
12422
12555
  padding: 0;
12423
12556
  }
12424
12557
 
12558
+ .work-document-table-of-contents {
12559
+ box-sizing: border-box;
12560
+ border: 1px solid color-mix(in srgb, var(--a3s-line) 82%, #8aa7d8);
12561
+ background: color-mix(in srgb, var(--a3s-panel) 96%, #edf4ff);
12562
+ width: 100%;
12563
+ color: var(--a3s-ink);
12564
+ cursor: default;
12565
+ border-radius: 6px;
12566
+ margin: 14px 0 20px;
12567
+ padding: 14px 16px 16px;
12568
+ }
12569
+
12570
+ .work-document-table-of-contents.ProseMirror-selectednode {
12571
+ border-color: var(--a3s-blue);
12572
+ outline: 2px solid color-mix(in srgb, var(--a3s-blue) 22%, transparent);
12573
+ outline-offset: 2px;
12574
+ }
12575
+
12576
+ .work-document-table-of-contents-header {
12577
+ border-bottom: 1px solid var(--a3s-line);
12578
+ justify-content: space-between;
12579
+ align-items: baseline;
12580
+ gap: 12px;
12581
+ margin-bottom: 10px;
12582
+ padding-bottom: 7px;
12583
+ display: flex;
12584
+ }
12585
+
12586
+ .work-document-table-of-contents-header strong {
12587
+ font-family: var(--a3s-font-display, inherit);
12588
+ letter-spacing: -.02em;
12589
+ font-size: 18px;
12590
+ font-weight: 680;
12591
+ }
12592
+
12593
+ .work-document-table-of-contents-header span {
12594
+ color: var(--a3s-muted);
12595
+ font-variant-numeric: tabular-nums;
12596
+ font-size: 9px;
12597
+ }
12598
+
12599
+ .work-document-table-of-contents-list {
12600
+ gap: 3px;
12601
+ margin: 0;
12602
+ padding: 0;
12603
+ list-style: none;
12604
+ display: grid;
12605
+ }
12606
+
12607
+ .work-document-table-of-contents-list > li {
12608
+ min-width: 0;
12609
+ padding-left: calc((var(--work-toc-level, 1) - 1) * 16px);
12610
+ align-items: baseline;
12611
+ gap: 5px;
12612
+ font-size: 11px;
12613
+ line-height: 1.6;
12614
+ display: flex;
12615
+ }
12616
+
12617
+ .work-document-table-of-contents-list a, .work-document-table-of-contents-list li > span:first-child {
12618
+ color: inherit;
12619
+ text-overflow: ellipsis;
12620
+ white-space: nowrap;
12621
+ text-decoration: none;
12622
+ overflow: hidden;
12623
+ }
12624
+
12625
+ .work-document-table-of-contents-list a:hover {
12626
+ color: var(--a3s-blue);
12627
+ text-decoration: underline;
12628
+ }
12629
+
12630
+ .work-document-table-of-contents-list i {
12631
+ border-bottom: 1px dotted
12632
+ color-mix(in srgb, var(--a3s-muted) 68%, transparent);
12633
+ flex: 24px;
12634
+ min-width: 12px;
12635
+ transform: translateY(-2px);
12636
+ }
12637
+
12638
+ .work-document-table-of-contents[data-toc-leader="dash"] .work-document-table-of-contents-list i {
12639
+ border-bottom-style: dashed;
12640
+ }
12641
+
12642
+ .work-document-table-of-contents[data-toc-leader="underline"] .work-document-table-of-contents-list i {
12643
+ border-bottom-style: solid;
12644
+ }
12645
+
12646
+ .work-document-table-of-contents[data-toc-leader="none"] .work-document-table-of-contents-list i, .work-document-table-of-contents[data-toc-right-align-page-numbers="false"] .work-document-table-of-contents-list i {
12647
+ border: 0;
12648
+ flex: 0 0 5px;
12649
+ min-width: 5px;
12650
+ }
12651
+
12652
+ .work-document-table-of-contents-page {
12653
+ font-variant-numeric: tabular-nums;
12654
+ flex: none;
12655
+ }
12656
+
12657
+ .work-document-table-of-contents-empty {
12658
+ color: var(--a3s-muted);
12659
+ font-style: italic;
12660
+ padding-left: 0 !important;
12661
+ }
12662
+
12663
+ .work-document-table-of-contents-dialog {
12664
+ width: min(520px, 100%);
12665
+ }
12666
+
12667
+ .work-document-table-of-contents-dialog-grid {
12668
+ grid-template-columns: repeat(2, minmax(0, 1fr));
12669
+ gap: 10px;
12670
+ display: grid;
12671
+ }
12672
+
12673
+ .work-document-table-of-contents-dialog-field, .work-document-table-of-contents-dialog-leader {
12674
+ min-width: 0;
12675
+ color: var(--a3s-muted);
12676
+ gap: 6px;
12677
+ font-size: 10px;
12678
+ display: grid;
12679
+ }
12680
+
12681
+ .work-document-table-of-contents-dialog-grid .work-office-select, .work-document-table-of-contents-dialog-leader .work-office-select {
12682
+ width: 100%;
12683
+ }
12684
+
12685
+ .work-document-table-of-contents-dialog-options {
12686
+ border: 1px solid var(--a3s-line);
12687
+ background: var(--a3s-panel-soft);
12688
+ border-radius: 7px;
12689
+ grid-template-columns: repeat(3, minmax(0, 1fr));
12690
+ gap: 8px;
12691
+ margin: 14px 0;
12692
+ padding: 10px;
12693
+ display: grid;
12694
+ }
12695
+
12696
+ .work-document-table-of-contents-dialog-preview {
12697
+ border: 1px solid var(--a3s-line);
12698
+ background: var(--a3s-bg);
12699
+ min-width: 0;
12700
+ color: var(--a3s-ink);
12701
+ border-radius: 7px;
12702
+ gap: 5px;
12703
+ margin-top: 14px;
12704
+ padding: 14px;
12705
+ font-size: 10px;
12706
+ display: grid;
12707
+ }
12708
+
12709
+ .work-document-table-of-contents-dialog-preview > legend {
12710
+ margin-bottom: 4px;
12711
+ font-size: 14px;
12712
+ }
12713
+
12714
+ .work-document-table-of-contents-dialog-preview > span {
12715
+ align-items: baseline;
12716
+ gap: 5px;
12717
+ display: flex;
12718
+ }
12719
+
12720
+ .work-document-table-of-contents-dialog-preview > span.nested {
12721
+ padding-left: 16px;
12722
+ }
12723
+
12724
+ .work-document-table-of-contents-dialog-preview i {
12725
+ border-bottom: 1px dotted var(--a3s-muted);
12726
+ flex: 1;
12727
+ }
12728
+
12729
+ .work-document-table-of-contents-dialog-preview[data-toc-leader="dash"] i {
12730
+ border-bottom-style: dashed;
12731
+ }
12732
+
12733
+ .work-document-table-of-contents-dialog-preview[data-toc-leader="underline"] i {
12734
+ border-bottom-style: solid;
12735
+ }
12736
+
12737
+ .work-document-table-of-contents-dialog-preview[data-toc-leader="none"] i, .work-document-table-of-contents-dialog-preview[data-toc-right-align-page-numbers="false"] i {
12738
+ border: 0;
12739
+ flex: 0 0 5px;
12740
+ }
12741
+
12742
+ @media (width <= 520px) {
12743
+ .work-document-table-of-contents {
12744
+ padding-inline: 12px;
12745
+ }
12746
+
12747
+ .work-document-table-of-contents-dialog-grid, .work-document-table-of-contents-dialog-options {
12748
+ grid-template-columns: 1fr;
12749
+ }
12750
+ }
12751
+
12425
12752
  .work-document-equation {
12426
12753
  vertical-align: -.12em;
12427
12754
  border-radius: .2em;
@@ -17049,6 +17376,17 @@ sub [data-office-character-position-half-points], sup [data-office-character-pos
17049
17376
  padding: 8px 10px;
17050
17377
  }
17051
17378
 
17379
+ .work-spreadsheet-data-validation-message-grid {
17380
+ grid-template-columns: minmax(0, .7fr) minmax(0, 1.3fr);
17381
+ align-items: start;
17382
+ gap: 10px;
17383
+ display: grid;
17384
+ }
17385
+
17386
+ .work-spreadsheet-data-validation-message-grid .ds-field.message {
17387
+ grid-column: 1 / -1;
17388
+ }
17389
+
17052
17390
  .work-spreadsheet-data-validation-dialog textarea {
17053
17391
  resize: vertical;
17054
17392
  min-height: 70px;
@@ -17065,7 +17403,7 @@ sub [data-office-character-position-half-points], sup [data-office-character-pos
17065
17403
  }
17066
17404
 
17067
17405
  @media (width <= 560px) {
17068
- .work-spreadsheet-data-validation-condition-grid, .work-spreadsheet-data-validation-values.two, .work-spreadsheet-data-validation-behavior {
17406
+ .work-spreadsheet-data-validation-condition-grid, .work-spreadsheet-data-validation-values.two, .work-spreadsheet-data-validation-behavior, .work-spreadsheet-data-validation-message-grid {
17069
17407
  grid-template-columns: minmax(0, 1fr);
17070
17408
  }
17071
17409
  }
@@ -94,7 +94,22 @@ model:
94
94
  instructionless structures keep their rendered result as text and emit a
95
95
  structural compatibility warning instead of being flattened into a false
96
96
  editable field. Native export writes live simple fields and requests a Word
97
- field update on open. Page Layout keeps direct margin, orientation, paper,
97
+ field update on open. Table of Contents is a separate block atom because its
98
+ native field spans cached paragraphs. It reads semantic headings and native
99
+ outline-level paragraphs only from the shared outline collector, reads page
100
+ numbers only from the pagination field resolver, and stores bounded options
101
+ plus at most 512 cached entries. Included outline items without a native
102
+ paragraph identity receive one in the insert/update transaction, so a block
103
+ insertion cannot invalidate its own targets. One
104
+ References dialog owns insert and selected-block customization; explicit
105
+ refresh updates every TOC in one transaction. DOCX export emits a real
106
+ `w:sdt`/`TOC` field, patches native leader and alignment semantics, and adds
107
+ stable heading bookmarks. Import accepts only the lossless common switch
108
+ subset and removes the complete content control before Mammoth conversion,
109
+ then restores one typed block after paragraph identities are applied. Each
110
+ imported TOC matches its cached rows independently, so repeated TOCs can
111
+ target the same heading.
112
+ Page Layout keeps direct margin, orientation, paper,
98
113
  and equal-column presets in the ribbon; custom margins and advanced columns
99
114
  open the corresponding controlled Page Setup tab instead of duplicating a
100
115
  second settings model. References separates footnotes, captions, citations
@@ -699,6 +714,62 @@ an empty nested container. The four effects change paint but not advances, so
699
714
  eligible paragraphs remain on Worker/WASM layout and the browser/PDF CSS
700
715
  projection stays an explicitly bounded approximation.
701
716
 
717
+ Native character borders are resolved as one validated TextStyle value before
718
+ layout eligibility, DOM serialization, and DOCX export. The closed model
719
+ accepts 25 visible `w:bdr` line styles plus `nil` and `none`, direct or resolved
720
+ theme colors with tint/shade identity, bounded eighth-point width, point
721
+ spacing, shadow, and frame fields. Duplicate, misplaced, art-style,
722
+ namespace-spoofed, child- or text-bearing, extra-attribute, out-of-range, and
723
+ unresolved-theme leaves normalize to an explicit `nil` reset and emit a
724
+ compatibility diagnostic instead of enabling untrusted paint or reviving
725
+ inherited formatting.
726
+
727
+ The canonical DOM mark stores semantic JSON in `data-office-run-border`; its
728
+ CSS projection uses four-edge border paint, padding, shadow, and
729
+ `box-decoration-break: clone`. A visible border changes inline geometry, so
730
+ the containing paragraph deliberately uses browser-authoritative measurement.
731
+ Explicit `nil` and `none` add no geometry and remain eligible for Worker/WASM
732
+ shaping. The DOCX generator allocates a collision-checked temporary border
733
+ color only inside generated runs, then patches the body, page chrome, and note
734
+ parts to exact `w:bdr` attributes before formatting-change snapshots are
735
+ written. This keeps direct/style inheritance and prior `w:rPrChange` values
736
+ exact while treating browser and PDF line-style paint as a bounded visual
737
+ approximation.
738
+
739
+ Native character shading follows a separate paint-only TextStyle path. The
740
+ closed `w:shd` model accepts every schema pattern, independent direct,
741
+ automatic, or resolved-theme foreground/background channels, tint and shade,
742
+ and explicit `nil`. Property sources are resolved from document defaults,
743
+ paragraph and character styles, conditional table styles, and direct run
744
+ properties in native precedence order. Invalid direct leaves normalize to
745
+ `nil`, so stale inherited paint cannot reappear.
746
+
747
+ The canonical mark stores bounded JSON in `data-office-run-shading`; CSS
748
+ projects solid, line, cross, and percentage masks with cloned fragments. These
749
+ declarations do not alter advances, so shading remains Worker/WASM eligible.
750
+ Native highlight is retained as an independent mark and paints above shading.
751
+ Export allocates a collision-checked temporary fill only on generated text
752
+ runs, patches it to the exact `w:shd` attributes across editable stories, and
753
+ then writes exact prior values into `w:rPrChange`.
754
+
755
+ Native proofing metadata follows a separate semantic TextStyle path. A closed
756
+ `w:lang` model retains Latin `w:val`, East Asian `w:eastAsia`, and bidi `w:bidi` slots
757
+ plus explicit `w:noProof` true or false state across style resolution and every
758
+ editable story. The canonical DOM mark uses bounded JSON in
759
+ `data-office-proofing-languages` and an explicit Boolean in
760
+ `data-office-no-proof`; derived `lang` and `spellcheck` attributes are display
761
+ and browser-proofing projections, not replacement source state. Duplicate,
762
+ misplaced, nested, text-bearing, extra-attribute, namespace-spoofed, invalid-tag,
763
+ or invalid on/off leaves fail closed and emit compatibility diagnostics.
764
+
765
+ The text-layout collector resolves the effective inherited `lang` for each DOM
766
+ run, validates the same bounded tag grammar, and includes it in the run-style
767
+ coalescing key. The optional protocol field is backward compatible: missing
768
+ language keeps existing shaping, while a valid value is validated again in
769
+ Rust and set on the RustyBuzz `UnicodeBuffer` before segment-property guessing
770
+ and shaping. This makes language-sensitive glyph selection deterministic
771
+ without coupling pagination to a browser dictionary or host grammar service.
772
+
702
773
  Tables, images, code blocks, and other complex content use explicit
703
774
  format-specific measurement. Top-level tables are row flows; eligible rows can
704
775
  additionally expose synchronized direct-cell block fragments. Ordered and bullet