ooxml-ui 1.11.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/chunk-GSXOZCAN.js +75 -0
  2. package/dist/{chunk-QVPO35YI.js → chunk-N3BQ3IIJ.js} +1 -1
  3. package/dist/docx/context-menu-actions.d.ts +1 -1
  4. package/dist/docx/decoration-cache.d.ts +16 -0
  5. package/dist/docx/editor-core.d.ts +3 -0
  6. package/dist/docx/locales/en-ribbon.d.ts +34 -0
  7. package/dist/docx/locales/en.d.ts +34 -0
  8. package/dist/docx/localization-strings.d.ts +34 -0
  9. package/dist/docx/model-adapter.d.ts +1 -1
  10. package/dist/docx/page-controller.d.ts +1 -1
  11. package/dist/docx/page-navigator.d.ts +1 -0
  12. package/dist/docx/paragraph-decoration-cache.d.ts +28 -0
  13. package/dist/docx/print-block-pages.d.ts +3 -0
  14. package/dist/docx/print-editing-index.d.ts +1 -0
  15. package/dist/docx/print-editing.d.ts +1 -1
  16. package/dist/docx/print-layout-view.d.ts +2 -0
  17. package/dist/docx/print-layout.d.ts +5 -0
  18. package/dist/docx/print-page-window.d.ts +16 -0
  19. package/dist/docx/ribbon-action.d.ts +6 -0
  20. package/dist/docx/ribbon-home-menus.d.ts +3 -0
  21. package/dist/docx/ribbon-icons.d.ts +3 -0
  22. package/dist/docx/ribbon-native-tabs.d.ts +2 -0
  23. package/dist/docx/ribbon-parts.d.ts +2 -2
  24. package/dist/docx.js +815 -154
  25. package/dist/index.js +2 -2
  26. package/dist/pointer-gesture.d.ts +10 -0
  27. package/dist/pptx/{chunk-J3T77ZMS.mjs → chunk-GD2XJ32O.mjs} +16 -85
  28. package/dist/pptx/{chunk-BPQVJLHW.cjs → chunk-WP7PQCJ5.cjs} +59 -107
  29. package/dist/pptx/dom/index.cjs +202 -202
  30. package/dist/pptx/dom/index.mjs +2 -2
  31. package/dist/pptx/index.cjs +2754 -2754
  32. package/dist/pptx/index.mjs +1 -1
  33. package/dist/pptx/render/editor-gestures.d.ts.map +1 -1
  34. package/dist/pptx/render/element-interaction.d.ts +3 -87
  35. package/dist/pptx/render/element-interaction.d.ts.map +1 -1
  36. package/dist/pptx/render/responsive/touch-target.d.ts +1 -2
  37. package/dist/pptx/render/responsive/touch-target.d.ts.map +1 -1
  38. package/dist/teams.js +1 -1
  39. package/dist/touch-targets.d.ts +4 -0
  40. package/dist/touch-targets.d.ts.map +1 -0
  41. package/dist/visio.js +33 -3
  42. package/dist/xlsx/formula-bar/styles.d.ts +1 -1
  43. package/dist/xlsx/grid/styles.d.ts +1 -1
  44. package/dist/xlsx/ribbon/cell-style-gallery.d.ts +10 -0
  45. package/dist/xlsx/sheet-tabs/styles.d.ts +1 -1
  46. package/dist/xlsx.js +193 -73
  47. package/package.json +2 -2
  48. /package/dist/{chunk-JZP5L2AZ.js → chunk-B7NCMLSK.js} +0 -0
package/dist/docx.js CHANGED
@@ -8,16 +8,16 @@ import './chunk-L7JV5FVE.js';
8
8
  import './chunk-OGNNNTKI.js';
9
9
  import './chunk-SPNWHKSR.js';
10
10
  import { normalizeLocale, SUPPORTED_OFFICE_LOCALES, interpolate } from 'ooxml-core/i18n';
11
- import { createDocument, saveDocx, sectionsFromLayout, sectionLayoutJson, sectionsOf, pageSizeOf, columnPreset, applyPageSetup, withSection, computeListLabels, numberNotesInOrder, tocEntries, tableOfFiguresInstruction, findTableOfContents, ensureListDefinition, resolveNumberingLevel, equalColumnDraft, readColumnDraft, PAGE_SIZE_OPTIONS, PAGE_SIZES, readPageSetup, isRunHiddenForReview, resolveTableStyleFormatting, resolveParagraphFormatting, displayListLabel, setTitlePage, setLineNumbering, setVerticalAlign, setPageNumbering, ST_NumberFormat, unequalColumnPreset, setColumns, setOrientation, setPageSize, setMargins, lineSpacingLabel, formatNoteNumber, reviewDocumentFormatting, reviewFormattingWarnings, listRevisions, plainText, tocHyperlinks, DEFAULT_TOC_INSTRUCTION, tocBookmarks, buildTableOfContents, tocCaptionLabel, isFieldLocked, twips as twips$1, signedTwips, formatListNumber, columnTextWidth, validColumnDraft, validatePageSetup, presetOf, LIGATURE_VALUES, isLigatures, reviewRunFormatting, resolveRunFormatting, reviewParagraphFormatting, resolveThemeColorReference, lineSpacingValue, isStHighlightColor, transformCase, DEFAULT_TABLE_BORDERS, parseLineSpacingValue, twipsAttr, twipsFromPixels, headingLevel, isValidLanguageTag, WORD, isHeadingListKind, fieldName, lineSpacingOptions, halfPointsFromPoints, changeColumnWidth, changeColumnGap, createListDefinition, linkStylesToList, headingListLevels, documentStats } from 'ooxml-core/docx';
11
+ import { createDocument, saveDocx, sectionsFromLayout, sectionLayoutJson, sectionsOf, pageSizeOf, columnPreset, applyPageSetup, withSection, computeListLabels, numberNotesInOrder, tocEntries, tableOfFiguresInstruction, findTableOfContents, ensureListDefinition, resolveNumberingLevel, equalColumnDraft, readColumnDraft, PAGE_SIZE_OPTIONS, PAGE_SIZES, readPageSetup, isRunHiddenForReview, resolveTableStyleFormatting, resolveParagraphFormatting, displayListLabel, setTitlePage, setLineNumbering, setVerticalAlign, setPageNumbering, ST_NumberFormat, unequalColumnPreset, setColumns, setOrientation, setPageSize, setMargins, lineSpacingLabel, formatNoteNumber, reviewDocumentFormatting, reviewFormattingWarnings, listRevisions, plainText, tocHyperlinks, DEFAULT_TOC_INSTRUCTION, tocBookmarks, buildTableOfContents, tocCaptionLabel, isFieldLocked, twips as twips$1, signedTwips, formatListNumber, columnTextWidth, validColumnDraft, validatePageSetup, presetOf, LIGATURE_VALUES, isLigatures, reviewRunFormatting, resolveRunFormatting, resolveThemeColorReference, lineSpacingValue, isStHighlightColor, transformCase, DEFAULT_TABLE_BORDERS, parseLineSpacingValue, twipsAttr, twipsFromPixels, headingLevel, reviewParagraphFormatting, isValidLanguageTag, WORD, isHeadingListKind, lineSpacingOptions, halfPointsFromPoints, changeColumnWidth, changeColumnGap, createListDefinition, linkStylesToList, headingListLevels, documentStats, fieldName } from 'ooxml-core/docx';
12
12
  import { Schema, DOMSerializer, Fragment } from 'prosemirror-model';
13
- import { markSpecs, imageNodeSpec, fieldMarkerNodeSpec, noteReferenceNodeSpec, pageBreakNodeSpec, hardBreakNodeSpec, commentHistoryAttributes, tableCellStyle, tableStyle, createToggleFormat, expectDefined, paragraphBoxCss, themeToCssVars, lightTheme, darkTheme, normalizeThemeMode, applyThemeColors, refreshWordYjsPresence, normalizeRibbonActions, DirtyState, isWordYjsRemoteTransaction, REMOTE_TRANSACTION_META, repairCollaborativeDocumentIds, HEADER_FOOTER_INPUT, restoreSectionParts, commentThreadsFromDoc, restoreNoteParts, CollaborationClient, createCollaborationIdGenerator, WordYjsCollaboration, sectionPartsJson, NotePartsStep, notePartsJson, SectionPartsStep, fitZoomPercent, hasChangeAtCursor, hasAnyChange, commentIdsAtSelection, commentAnchors, countWords, fieldGuardPlugin, tableStructureGuardPlugin, trackChangesPlugin, wordYjsPresencePlugin, chartNodeSpec, placementClass, removeCommentThread, removeCommentAnchor, deleteComment, updateCommentThreads, addComment, toggleTrackChanges, goToNextChange, goToPreviousChange, rejectAllChanges, acceptAllChanges, rejectChangeAtCursor, acceptChangeAtCursor, goToComment, appendInlineNode, sameJson, sameRuns, PARAGRAPH_KEEP_KEYS, paragraphFromAttrs, parseBordersJson, isNavigableHref, wordYjsPluginKey, setReviewRecordingPreferences, nodeNumbering, paragraphFormattingRevision, inlineNodeRun, formattingRevision, insertWordHardBreak, paragraphAttrs, resolveCellVisuals, themeDrawing, ribbonActionIdForLabel, removeWordLink, fieldResultRanges, wordLinkRangeAt, ligatureCss as ligatureCss$1, wordYjsRedo, wordYjsUndo, runToInlineNodes as runToInlineNodes$1, backstageFocus, applyRunFormattingPatch, clearDirectRunFormatting, applyWordLink, ligatureStyle, wordHighlightColors, swatchColor, cssBorderSide, deleteFieldSelection, resolveComment, replyToComment, CollaborationAuthority } from 'ooxml-core/docx/ui';
13
+ import { markSpecs, imageNodeSpec, fieldMarkerNodeSpec, noteReferenceNodeSpec, pageBreakNodeSpec, hardBreakNodeSpec, commentHistoryAttributes, tableCellStyle, tableStyle, createToggleFormat, expectDefined, paragraphBoxCss, themeToCssVars, lightTheme, darkTheme, normalizeThemeMode, applyThemeColors, refreshWordYjsPresence, normalizeRibbonActions, DirtyState, isWordYjsRemoteTransaction, REMOTE_TRANSACTION_META, repairCollaborativeDocumentIds, HEADER_FOOTER_INPUT, restoreSectionParts, commentThreadsFromDoc, restoreNoteParts, CollaborationClient, createCollaborationIdGenerator, WordYjsCollaboration, sectionPartsJson, NotePartsStep, notePartsJson, SectionPartsStep, fitZoomPercent, hasChangeAtCursor, hasAnyChange, commentIdsAtSelection, commentAnchors, documentWordCount, fieldGuardPlugin, tableStructureGuardPlugin, trackChangesPlugin, wordYjsPresencePlugin, chartNodeSpec, placementClass, removeCommentThread, removeCommentAnchor, deleteComment, updateCommentThreads, addComment, toggleTrackChanges, goToNextChange, goToPreviousChange, rejectAllChanges, acceptAllChanges, rejectChangeAtCursor, acceptChangeAtCursor, goToComment, appendInlineNode, sameJson, sameRuns, PARAGRAPH_KEEP_KEYS, paragraphFromAttrs, parseBordersJson, isNavigableHref, wordYjsPluginKey, setReviewRecordingPreferences, nodeNumbering, paragraphFormattingRevision, inlineNodeRun, formattingRevision, insertWordHardBreak, paragraphAttrs, resolveCellVisuals, themeDrawing, ribbonActionIdForLabel, removeWordLink, fieldResultRanges, wordLinkRangeAt, ligatureCss as ligatureCss$1, wordYjsRedo, wordYjsUndo, runToInlineNodes as runToInlineNodes$1, backstageFocus, applyRunFormattingPatch, clearDirectRunFormatting, applyWordLink, ligatureStyle, wordHighlightColors, swatchColor, cssBorderSide, deleteFieldSelection, countWords, resolveComment, replyToComment, CollaborationAuthority } from 'ooxml-core/docx/ui';
14
14
  export { CollaborationAuthority, CollaborationClient, RIBBON_ACTION_IDS, RIBBON_ACTION_LABELS, createCollaborationIdGenerator, darkTheme, isRibbonActionId, lightTheme, normalizeRibbonActions, repairCollaborativeDocumentIds, themeToCssVars } from 'ooxml-core/docx/ui';
15
15
  import { parseOmml, convertOmmlToLatex, convertOmmlToMathMl } from 'ooxml-core/math';
16
16
  import { PluginKey, EditorState, Plugin, NodeSelection, TextSelection, Selection } from 'prosemirror-state';
17
17
  import { EditorView, Decoration, DecorationSet } from 'prosemirror-view';
18
18
  import { loadDocument } from 'ooxml-core/docx/load';
19
19
  import { closeHistory, undo, redo, history } from 'prosemirror-history';
20
- import { layoutDocumentModel, pageNumbers, createFakeMeasurer, ligatureCss, cssFontStack, pageNumberValues, sectionPageCounts, headerFooterForPage, tokenizeRun, paragraphFloats, floatPosition, placeTab, cellVerticalOffset, DEFAULT_FONT_SIZE_PT, fieldDisplayText } from 'ooxml-core/docx/layout';
20
+ import { createLayoutSession, layoutDocumentModel, pageNumbers, createFakeMeasurer, ligatureCss, cssFontStack, tokenizeRun, placeTab, cellVerticalOffset, DEFAULT_FONT_SIZE_PT, pageNumberValues, sectionPageCounts, headerFooterForPage, paragraphFloats, floatPosition, fieldDisplayText } from 'ooxml-core/docx/layout';
21
21
  import { sendableSteps, getVersion } from 'prosemirror-collab';
22
22
  import { sanitizeColor, sanitizeUserName, SequenceTracker, isValidId, classifyVersion, validateDisplayName } from 'ooxml-core/collab';
23
23
  import { keymap } from 'prosemirror-keymap';
@@ -1111,6 +1111,10 @@ var deRibbon = {
1111
1111
  "Capitalize Each Word": "Jedes Wort Gro\xDF",
1112
1112
  "tOGGLE cASE": "gROSS-/kLEINSCHREIBUNG",
1113
1113
  Paste: "Einf\xFCgen",
1114
+ "Paste options": "Einf\xFCgeoptionen",
1115
+ "Keep text only": "Nur den Text \xFCbernehmen",
1116
+ "Bullets options": "Aufz\xE4hlungsoptionen",
1117
+ "Numbering options": "Nummerierungsoptionen",
1114
1118
  Cut: "Ausschneiden",
1115
1119
  Copy: "Kopieren",
1116
1120
  "Grow font": "Schrift vergr\xF6\xDFern",
@@ -1295,7 +1299,37 @@ var deRibbon = {
1295
1299
  "dyn.atLeastRule": "Mindestens (ohne Wert)",
1296
1300
  "dyn.exact": "Genau {n} Pt.",
1297
1301
  "dyn.atLeast": "Mindestens {n} Pt.",
1298
- "Enter a BCP 47 language tag, such as en-US or ar-SA.": "Geben Sie ein BCP-47-Sprach-Tag ein, zum Beispiel de-DE oder ar-SA."
1302
+ "Enter a BCP 47 language tag, such as en-US or ar-SA.": "Geben Sie ein BCP-47-Sprach-Tag ein, zum Beispiel de-DE oder ar-SA.",
1303
+ "tab.design": "Entwurf",
1304
+ "tab.mailings": "Sendungen",
1305
+ "tab.help": "Hilfe",
1306
+ "Document Formatting": "Dokumentformatierung",
1307
+ Themes: "Designs",
1308
+ Colors: "Farben",
1309
+ Fonts: "Schriftarten",
1310
+ "Paragraph spacing": "Absatzabstand",
1311
+ "Set as Default": "Als Standard festlegen",
1312
+ Create: "Erstellen",
1313
+ Envelopes: "Umschl\xE4ge",
1314
+ Labels: "Etiketten",
1315
+ "Start Mail Merge": "Seriendruck starten",
1316
+ "Select Recipients": "Empf\xE4nger ausw\xE4hlen",
1317
+ "Edit Recipient List": "Empf\xE4ngerliste bearbeiten",
1318
+ "Write & Insert Fields": "Felder schreiben und einf\xFCgen",
1319
+ "Highlight Merge Fields": "Seriendruckfelder hervorheben",
1320
+ "Address Block": "Adressblock",
1321
+ "Greeting Line": "Gru\xDFzeile",
1322
+ "Insert Merge Field": "Seriendruckfeld einf\xFCgen",
1323
+ Rules: "Regeln",
1324
+ "Match Fields": "Felder zuordnen",
1325
+ "Update Labels": "Etiketten aktualisieren",
1326
+ "Preview Results": "Vorschau Ergebnisse",
1327
+ "Find Recipient": "Empf\xE4nger suchen",
1328
+ "Check for Errors": "Auf Fehler \xFCberpr\xFCfen",
1329
+ Finish: "Fertig stellen",
1330
+ "Finish & Merge": "Fertig stellen und zusammenf\xFChren",
1331
+ Help: "Hilfe",
1332
+ "Not available in this editor": "In diesem Editor nicht verf\xFCgbar"
1299
1333
  };
1300
1334
 
1301
1335
  // src/docx/locales/de.ts
@@ -1773,6 +1807,10 @@ var enRibbon = {
1773
1807
  "Capitalize Each Word": "Capitalize Each Word",
1774
1808
  "tOGGLE cASE": "tOGGLE cASE",
1775
1809
  Paste: "Paste",
1810
+ "Paste options": "Paste options",
1811
+ "Keep text only": "Keep text only",
1812
+ "Bullets options": "Bullets options",
1813
+ "Numbering options": "Numbering options",
1776
1814
  Cut: "Cut",
1777
1815
  Copy: "Copy",
1778
1816
  "Grow font": "Grow font",
@@ -1957,7 +1995,37 @@ var enRibbon = {
1957
1995
  "dyn.atLeastRule": "At least rule (no amount)",
1958
1996
  "dyn.exact": "Exact {n} pt",
1959
1997
  "dyn.atLeast": "At least {n} pt",
1960
- "Enter a BCP 47 language tag, such as en-US or ar-SA.": "Enter a BCP 47 language tag, such as en-US or ar-SA."
1998
+ "Enter a BCP 47 language tag, such as en-US or ar-SA.": "Enter a BCP 47 language tag, such as en-US or ar-SA.",
1999
+ "tab.design": "Design",
2000
+ "tab.mailings": "Mailings",
2001
+ "tab.help": "Help",
2002
+ "Document Formatting": "Document Formatting",
2003
+ Themes: "Themes",
2004
+ Colors: "Colors",
2005
+ Fonts: "Fonts",
2006
+ "Paragraph spacing": "Paragraph spacing",
2007
+ "Set as Default": "Set as Default",
2008
+ Create: "Create",
2009
+ Envelopes: "Envelopes",
2010
+ Labels: "Labels",
2011
+ "Start Mail Merge": "Start Mail Merge",
2012
+ "Select Recipients": "Select Recipients",
2013
+ "Edit Recipient List": "Edit Recipient List",
2014
+ "Write & Insert Fields": "Write & Insert Fields",
2015
+ "Highlight Merge Fields": "Highlight Merge Fields",
2016
+ "Address Block": "Address Block",
2017
+ "Greeting Line": "Greeting Line",
2018
+ "Insert Merge Field": "Insert Merge Field",
2019
+ Rules: "Rules",
2020
+ "Match Fields": "Match Fields",
2021
+ "Update Labels": "Update Labels",
2022
+ "Preview Results": "Preview Results",
2023
+ "Find Recipient": "Find Recipient",
2024
+ "Check for Errors": "Check for Errors",
2025
+ Finish: "Finish",
2026
+ "Finish & Merge": "Finish & Merge",
2027
+ Help: "Help",
2028
+ "Not available in this editor": "Not available in this editor"
1961
2029
  };
1962
2030
 
1963
2031
  // src/docx/locales/en.ts
@@ -2435,6 +2503,10 @@ var esRibbon = {
2435
2503
  "Capitalize Each Word": "Poner en may\xFAscula cada palabra",
2436
2504
  "tOGGLE cASE": "iNVERTIR MAY\xDASCULAS Y MIN\xDASCULAS",
2437
2505
  Paste: "Pegar",
2506
+ "Paste options": "Opciones de pegado",
2507
+ "Keep text only": "Conservar solo texto",
2508
+ "Bullets options": "Opciones de vi\xF1etas",
2509
+ "Numbering options": "Opciones de numeraci\xF3n",
2438
2510
  Cut: "Cortar",
2439
2511
  Copy: "Copiar",
2440
2512
  "Grow font": "Aumentar tama\xF1o de fuente",
@@ -2446,7 +2518,7 @@ var esRibbon = {
2446
2518
  "tab.home": "Inicio",
2447
2519
  "tab.insert": "Insertar",
2448
2520
  "tab.references": "Referencias",
2449
- "tab.layout": "Dise\xF1o",
2521
+ "tab.layout": "Disposici\xF3n",
2450
2522
  "tab.review": "Revisar",
2451
2523
  "tab.view": "Vista",
2452
2524
  "tab.table": "Tabla",
@@ -2619,7 +2691,37 @@ var esRibbon = {
2619
2691
  "dyn.atLeastRule": "M\xEDnimo (sin valor)",
2620
2692
  "dyn.exact": "Exactamente {n} pt",
2621
2693
  "dyn.atLeast": "M\xEDnimo {n} pt",
2622
- "Enter a BCP 47 language tag, such as en-US or ar-SA.": "Escriba una etiqueta de idioma BCP 47, por ejemplo es-ES o ar-SA."
2694
+ "Enter a BCP 47 language tag, such as en-US or ar-SA.": "Escriba una etiqueta de idioma BCP 47, por ejemplo es-ES o ar-SA.",
2695
+ "tab.design": "Dise\xF1o",
2696
+ "tab.mailings": "Correspondencia",
2697
+ "tab.help": "Ayuda",
2698
+ "Document Formatting": "Formato del documento",
2699
+ Themes: "Temas",
2700
+ Colors: "Colores",
2701
+ Fonts: "Fuentes",
2702
+ "Paragraph spacing": "Espaciado entre p\xE1rrafos",
2703
+ "Set as Default": "Establecer como predeterminado",
2704
+ Create: "Crear",
2705
+ Envelopes: "Sobres",
2706
+ Labels: "Etiquetas",
2707
+ "Start Mail Merge": "Iniciar combinaci\xF3n de correspondencia",
2708
+ "Select Recipients": "Seleccionar destinatarios",
2709
+ "Edit Recipient List": "Editar lista de destinatarios",
2710
+ "Write & Insert Fields": "Escribir e insertar campos",
2711
+ "Highlight Merge Fields": "Resaltar campos de combinaci\xF3n",
2712
+ "Address Block": "Bloque de direcciones",
2713
+ "Greeting Line": "L\xEDnea de saludo",
2714
+ "Insert Merge Field": "Insertar campo combinado",
2715
+ Rules: "Reglas",
2716
+ "Match Fields": "Asignar campos",
2717
+ "Update Labels": "Actualizar etiquetas",
2718
+ "Preview Results": "Vista previa de resultados",
2719
+ "Find Recipient": "Buscar destinatario",
2720
+ "Check for Errors": "Comprobar errores",
2721
+ Finish: "Finalizar",
2722
+ "Finish & Merge": "Finalizar y combinar",
2723
+ Help: "Ayuda",
2724
+ "Not available in this editor": "No disponible en este editor"
2623
2725
  };
2624
2726
 
2625
2727
  // src/docx/locales/es.ts
@@ -3097,6 +3199,10 @@ var frRibbon = {
3097
3199
  "Capitalize Each Word": "Mettre en majuscule chaque mot",
3098
3200
  "tOGGLE cASE": "iNVERSER LA CASSE",
3099
3201
  Paste: "Coller",
3202
+ "Paste options": "Options de collage",
3203
+ "Keep text only": "Conserver uniquement le texte",
3204
+ "Bullets options": "Options des puces",
3205
+ "Numbering options": "Options de num\xE9rotation",
3100
3206
  Cut: "Couper",
3101
3207
  Copy: "Copier",
3102
3208
  "Grow font": "Agrandir la police",
@@ -3281,7 +3387,37 @@ var frRibbon = {
3281
3387
  "dyn.atLeastRule": "R\xE8gle minimale (sans valeur)",
3282
3388
  "dyn.exact": "Exactement {n} pt",
3283
3389
  "dyn.atLeast": "Au moins {n} pt",
3284
- "Enter a BCP 47 language tag, such as en-US or ar-SA.": "Saisissez une balise de langue BCP 47, par exemple fr-FR ou ar-SA."
3390
+ "Enter a BCP 47 language tag, such as en-US or ar-SA.": "Saisissez une balise de langue BCP 47, par exemple fr-FR ou ar-SA.",
3391
+ "tab.design": "Cr\xE9ation",
3392
+ "tab.mailings": "Publipostage",
3393
+ "tab.help": "Aide",
3394
+ "Document Formatting": "Mise en forme du document",
3395
+ Themes: "Th\xE8mes",
3396
+ Colors: "Couleurs",
3397
+ Fonts: "Polices",
3398
+ "Paragraph spacing": "Espacement des paragraphes",
3399
+ "Set as Default": "D\xE9finir par d\xE9faut",
3400
+ Create: "Cr\xE9er",
3401
+ Envelopes: "Enveloppes",
3402
+ Labels: "\xC9tiquettes",
3403
+ "Start Mail Merge": "D\xE9marrer la fusion et le publipostage",
3404
+ "Select Recipients": "S\xE9lectionner les destinataires",
3405
+ "Edit Recipient List": "Modifier la liste des destinataires",
3406
+ "Write & Insert Fields": "Champs de fusion",
3407
+ "Highlight Merge Fields": "Mettre les champs de fusion en surbrillance",
3408
+ "Address Block": "Bloc adresse",
3409
+ "Greeting Line": "Formule de salutation",
3410
+ "Insert Merge Field": "Ins\xE9rer un champ de fusion",
3411
+ Rules: "R\xE8gles",
3412
+ "Match Fields": "Faire correspondre les champs",
3413
+ "Update Labels": "Mettre \xE0 jour les \xE9tiquettes",
3414
+ "Preview Results": "Aper\xE7u des r\xE9sultats",
3415
+ "Find Recipient": "Rechercher un destinataire",
3416
+ "Check for Errors": "Rechercher les erreurs",
3417
+ Finish: "Terminer",
3418
+ "Finish & Merge": "Terminer et fusionner",
3419
+ Help: "Aide",
3420
+ "Not available in this editor": "Indisponible dans cet \xE9diteur"
3285
3421
  };
3286
3422
 
3287
3423
  // src/docx/locales/fr.ts
@@ -3759,6 +3895,10 @@ var zhCNRibbon = {
3759
3895
  "Capitalize Each Word": "\u6BCF\u4E2A\u5355\u8BCD\u9996\u5B57\u6BCD\u5927\u5199",
3760
3896
  "tOGGLE cASE": "\u5207\u6362\u5927\u5C0F\u5199",
3761
3897
  Paste: "\u7C98\u8D34",
3898
+ "Paste options": "\u7C98\u8D34\u9009\u9879",
3899
+ "Keep text only": "\u53EA\u4FDD\u7559\u6587\u672C",
3900
+ "Bullets options": "\u9879\u76EE\u7B26\u53F7\u9009\u9879",
3901
+ "Numbering options": "\u7F16\u53F7\u9009\u9879",
3762
3902
  Cut: "\u526A\u5207",
3763
3903
  Copy: "\u590D\u5236",
3764
3904
  "Grow font": "\u589E\u5927\u5B57\u53F7",
@@ -3943,7 +4083,37 @@ var zhCNRibbon = {
3943
4083
  "dyn.atLeastRule": "\u6700\u5C0F\u503C\uFF08\u65E0\u6570\u503C\uFF09",
3944
4084
  "dyn.exact": "\u56FA\u5B9A\u503C {n} \u78C5",
3945
4085
  "dyn.atLeast": "\u6700\u5C0F\u503C {n} \u78C5",
3946
- "Enter a BCP 47 language tag, such as en-US or ar-SA.": "\u8BF7\u8F93\u5165 BCP 47 \u8BED\u8A00\u6807\u8BB0\uFF0C\u4F8B\u5982 zh-CN \u6216 ar-SA\u3002"
4086
+ "Enter a BCP 47 language tag, such as en-US or ar-SA.": "\u8BF7\u8F93\u5165 BCP 47 \u8BED\u8A00\u6807\u8BB0\uFF0C\u4F8B\u5982 zh-CN \u6216 ar-SA\u3002",
4087
+ "tab.design": "\u8BBE\u8BA1",
4088
+ "tab.mailings": "\u90AE\u4EF6",
4089
+ "tab.help": "\u5E2E\u52A9",
4090
+ "Document Formatting": "\u6587\u6863\u683C\u5F0F",
4091
+ Themes: "\u4E3B\u9898",
4092
+ Colors: "\u989C\u8272",
4093
+ Fonts: "\u5B57\u4F53",
4094
+ "Paragraph spacing": "\u6BB5\u843D\u95F4\u8DDD",
4095
+ "Set as Default": "\u8BBE\u4E3A\u9ED8\u8BA4\u503C",
4096
+ Create: "\u521B\u5EFA",
4097
+ Envelopes: "\u4FE1\u5C01",
4098
+ Labels: "\u6807\u7B7E",
4099
+ "Start Mail Merge": "\u5F00\u59CB\u90AE\u4EF6\u5408\u5E76",
4100
+ "Select Recipients": "\u9009\u62E9\u6536\u4EF6\u4EBA",
4101
+ "Edit Recipient List": "\u7F16\u8F91\u6536\u4EF6\u4EBA\u5217\u8868",
4102
+ "Write & Insert Fields": "\u7F16\u5199\u548C\u63D2\u5165\u57DF",
4103
+ "Highlight Merge Fields": "\u7A81\u51FA\u663E\u793A\u5408\u5E76\u57DF",
4104
+ "Address Block": "\u5730\u5740\u5757",
4105
+ "Greeting Line": "\u95EE\u5019\u8BED",
4106
+ "Insert Merge Field": "\u63D2\u5165\u5408\u5E76\u57DF",
4107
+ Rules: "\u89C4\u5219",
4108
+ "Match Fields": "\u5339\u914D\u57DF",
4109
+ "Update Labels": "\u66F4\u65B0\u6807\u7B7E",
4110
+ "Preview Results": "\u9884\u89C8\u7ED3\u679C",
4111
+ "Find Recipient": "\u67E5\u627E\u6536\u4EF6\u4EBA",
4112
+ "Check for Errors": "\u68C0\u67E5\u9519\u8BEF",
4113
+ Finish: "\u5B8C\u6210",
4114
+ "Finish & Merge": "\u5B8C\u6210\u5E76\u5408\u5E76",
4115
+ Help: "\u5E2E\u52A9",
4116
+ "Not available in this editor": "\u6B64\u7F16\u8F91\u5668\u5C1A\u4E0D\u652F\u6301"
3947
4117
  };
3948
4118
 
3949
4119
  // src/docx/locales/zh-CN.ts
@@ -4679,19 +4849,41 @@ function keepHeaderFooterSections(prior, blocks, doc) {
4679
4849
  ]
4680
4850
  };
4681
4851
  }
4682
- function docToModel(doc, prior) {
4852
+ function docToModel(doc, prior, previousDoc) {
4683
4853
  let nextId = 0;
4684
4854
  const previousParagraphs = /* @__PURE__ */ new Map();
4685
4855
  const priorTables = /* @__PURE__ */ new Map();
4686
- const remember = (paragraph2) => previousParagraphs.set(paragraph2.id, paragraph2);
4856
+ const duplicateIds = /* @__PURE__ */ new Set();
4857
+ const remember = (paragraph2) => {
4858
+ if (previousParagraphs.has(paragraph2.id)) duplicateIds.add(paragraph2.id);
4859
+ previousParagraphs.set(paragraph2.id, paragraph2);
4860
+ };
4687
4861
  for (const block of prior.blocks) {
4688
4862
  if (block.type === "paragraph") remember(block);
4689
4863
  else {
4864
+ if (priorTables.has(block.id)) duplicateIds.add(block.id);
4690
4865
  priorTables.set(block.id, block);
4691
4866
  for (const row3 of block.rows) for (const cell of row3) cell.paragraphs.forEach(remember);
4692
4867
  }
4693
4868
  }
4869
+ const unchanged = /* @__PURE__ */ new WeakMap();
4870
+ const unchangedTables = /* @__PURE__ */ new WeakMap();
4871
+ previousDoc?.descendants((node) => {
4872
+ if (duplicateIds.has(String(node.attrs.id))) return true;
4873
+ if (node.type.name === "paragraph") {
4874
+ const paragraph2 = previousParagraphs.get(String(node.attrs.id));
4875
+ if (paragraph2) unchanged.set(node, paragraph2);
4876
+ return false;
4877
+ }
4878
+ if (node.type.name === "table") {
4879
+ const table = priorTables.get(String(node.attrs.id));
4880
+ if (table) unchangedTables.set(node, table);
4881
+ }
4882
+ return true;
4883
+ });
4694
4884
  const convertParagraph = (node) => {
4885
+ const cached = unchanged.get(node);
4886
+ if (cached) return cached;
4695
4887
  const runs = [];
4696
4888
  node.forEach((child) => appendInlineNode(runs, child));
4697
4889
  if (!runs.length) runs.push({ text: "" });
@@ -4705,6 +4897,11 @@ function docToModel(doc, prior) {
4705
4897
  doc.forEach((node) => {
4706
4898
  if (node.type.name === "paragraph") blocks.push(convertParagraph(node));
4707
4899
  if (node.type.name === "table") {
4900
+ const cached = unchangedTables.get(node);
4901
+ if (cached) {
4902
+ blocks.push(cached);
4903
+ return;
4904
+ }
4708
4905
  const id = String(node.attrs.id || `t-edit-${++nextId}`);
4709
4906
  const prior2 = priorTables.get(id);
4710
4907
  const asParagraph = (n) => convertParagraph(n);
@@ -5097,6 +5294,9 @@ function openListPopover(anchor, items, current2, preview, choose) {
5097
5294
 
5098
5295
  // src/docx/ribbon-icons.ts
5099
5296
  var paths = {
5297
+ envelope: "M3 5h18v14H3z M3 5l9 7 9-7",
5298
+ field: "M4 3h16v18H4z M10 8l-3 4 3 4 M14 8l3 4-3 4",
5299
+ help: "M9 8a3 3 0 1 1 4 2.8c-1 .5-1 1.2-1 2.2 M12 17h.01 M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20",
5100
5300
  close: "M6 6l12 12 M18 6L6 18",
5101
5301
  paste: "M8 4h8v3H8z M6 5.5H5v15h14v-15h-1 M9 12h6 M9 15.5h6",
5102
5302
  cut: "M6 3l9 12 M18 3l-9 12 M7 15.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z M17 15.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z",
@@ -5429,7 +5629,11 @@ var button = (label, text, action, className = "") => {
5429
5629
  el.textContent = text;
5430
5630
  el.setAttribute("aria-label", label);
5431
5631
  el.addEventListener("mousedown", (event) => event.preventDefault());
5432
- el.dataset.action = JSON.stringify(action);
5632
+ if (action) el.dataset.action = JSON.stringify(action);
5633
+ else {
5634
+ el.disabled = true;
5635
+ el.dataset.unavailable = "";
5636
+ }
5433
5637
  if (className) el.className = className;
5434
5638
  return el;
5435
5639
  };
@@ -5529,7 +5733,7 @@ var row = (...children) => {
5529
5733
  };
5530
5734
  var tool = (label, icon2, action, options = {}) => {
5531
5735
  const el = button(label, "", action, options.className);
5532
- el.title = label;
5736
+ el.title = action ? label : "Not available in this editor";
5533
5737
  el.classList.add(
5534
5738
  options.large ? "ribbon-large" : options.inline ? "ribbon-inline" : "ribbon-small"
5535
5739
  );
@@ -5804,6 +6008,54 @@ function openGallery(anchor, control, kind, commands) {
5804
6008
  pop.querySelector('[aria-checked="true"]')?.focus({ preventScroll: true });
5805
6009
  }
5806
6010
 
6011
+ // src/docx/ribbon-home-menus.ts
6012
+ function split(main, label, choices, action, large = false) {
6013
+ const control = splitInline(main, label, choices, action);
6014
+ control.classList.add("ribbon-command-split");
6015
+ if (large) control.classList.add("ribbon-command-split-large");
6016
+ return control;
6017
+ }
6018
+ function pasteSplit() {
6019
+ return split(
6020
+ tool("Paste", "paste", { type: "clipboard", key: "paste" }, { large: true }),
6021
+ "Paste options",
6022
+ [
6023
+ ["paste", "Paste"],
6024
+ ["text", "Keep text only"]
6025
+ ],
6026
+ (value) => ({ type: "clipboard", key: "paste", plainText: value === "text" }),
6027
+ true
6028
+ );
6029
+ }
6030
+ function underlineSplit() {
6031
+ return split(
6032
+ tool("Underline", "underline", { type: "format", key: "underline" }),
6033
+ "Underline style",
6034
+ [
6035
+ ["single", "Single"],
6036
+ ["double", "Double"],
6037
+ ["dotted", "Dotted"],
6038
+ ["dash", "Dashed"],
6039
+ ["wave", "Wave"],
6040
+ ["none", "(none)"]
6041
+ ],
6042
+ (value) => ({ type: "underline", value })
6043
+ );
6044
+ }
6045
+ function listSplit(key2) {
6046
+ const label = key2 === "bullet" ? "Bulleted list" : "Numbered list";
6047
+ const icon2 = key2 === "bullet" ? "bullets" : "numbering";
6048
+ return split(
6049
+ tool(label, icon2, { type: "list", key: key2 }),
6050
+ key2 === "bullet" ? "Bullets options" : "Numbering options",
6051
+ [
6052
+ [key2, label],
6053
+ ["remove", "Remove list"]
6054
+ ],
6055
+ (value) => ({ type: "list", key: value })
6056
+ );
6057
+ }
6058
+
5807
6059
  // src/docx/ribbon-home.ts
5808
6060
  var SHADINGS = [
5809
6061
  ["none", "No color"],
@@ -5863,7 +6115,7 @@ function buildHomePanel(panels) {
5863
6115
  home.append(
5864
6116
  group(
5865
6117
  "Clipboard",
5866
- tool("Paste", "paste", { type: "clipboard", key: "paste" }, { large: true }),
6118
+ pasteSplit(),
5867
6119
  stack(
5868
6120
  tool("Cut", "cut", { type: "clipboard", key: "cut" }, { inline: true }),
5869
6121
  tool("Copy", "copy", { type: "clipboard", key: "copy" }, { inline: true }),
@@ -5910,7 +6162,7 @@ function buildHomePanel(panels) {
5910
6162
  row(
5911
6163
  tool("Bold", "bold", { type: "format", key: "bold" }),
5912
6164
  tool("Italic", "italic", { type: "format", key: "italic" }),
5913
- tool("Underline", "underline", { type: "format", key: "underline" }),
6165
+ underlineSplit(),
5914
6166
  tool("Strikethrough", "strike", { type: "format", key: "strike" }),
5915
6167
  tool("Subscript", "subscript", { type: "format", key: "subscript" }),
5916
6168
  tool("Superscript", "superscript", { type: "format", key: "superscript" }),
@@ -5987,8 +6239,8 @@ function buildHomePanel(panels) {
5987
6239
  group(
5988
6240
  "Paragraph",
5989
6241
  row(
5990
- tool("Bulleted list", "bullets", { type: "list", key: "bullet" }),
5991
- tool("Numbered list", "numbering", { type: "list", key: "number" }),
6242
+ listSplit("bullet"),
6243
+ listSplit("number"),
5992
6244
  menuSelect(
5993
6245
  "Multilevel list",
5994
6246
  "multilevel",
@@ -6139,6 +6391,57 @@ function spacingGroup() {
6139
6391
  );
6140
6392
  }
6141
6393
 
6394
+ // src/docx/ribbon-native-tabs.ts
6395
+ var unavailable = (label, icon2, inline = false) => tool(label, icon2, null, inline ? { inline: true } : { large: true });
6396
+ function buildNativeTabs(panels) {
6397
+ const design = panels.get("Design");
6398
+ design.append(
6399
+ group(
6400
+ "Document Formatting",
6401
+ unavailable("Themes", "pageColor"),
6402
+ unavailable("Colors", "fontColor"),
6403
+ unavailable("Fonts", "changeCase"),
6404
+ unavailable("Paragraph spacing", "lineSpacing"),
6405
+ unavailable("Effects", "formatPainter"),
6406
+ unavailable("Set as Default", "accept")
6407
+ )
6408
+ );
6409
+ const background = panels.get("Layout").querySelector('[data-label="Page background"]');
6410
+ if (background) design.append(background);
6411
+ panels.get("Mailings").append(
6412
+ group("Create", unavailable("Envelopes", "envelope"), unavailable("Labels", "table")),
6413
+ group(
6414
+ "Start Mail Merge",
6415
+ unavailable("Start Mail Merge", "envelope"),
6416
+ unavailable("Select Recipients", "select"),
6417
+ unavailable("Edit Recipient List", "table")
6418
+ ),
6419
+ group(
6420
+ "Write & Insert Fields",
6421
+ unavailable("Highlight Merge Fields", "highlight"),
6422
+ unavailable("Address Block", "textBox"),
6423
+ unavailable("Greeting Line", "textBox"),
6424
+ unavailable("Insert Merge Field", "field"),
6425
+ stack(
6426
+ unavailable("Rules", "field", true),
6427
+ unavailable("Match Fields", "field", true),
6428
+ unavailable("Update Labels", "updateTable", true)
6429
+ )
6430
+ ),
6431
+ group(
6432
+ "Preview Results",
6433
+ unavailable("Preview Results", "find"),
6434
+ stack(
6435
+ unavailable("Find Recipient", "find", true),
6436
+ unavailable("Check for Errors", "spelling", true)
6437
+ )
6438
+ ),
6439
+ group("Finish", unavailable("Finish & Merge", "envelope"))
6440
+ );
6441
+ const help = tool("Help", "help", { type: "help" }, { large: true });
6442
+ panels.get("Help").append(group("Help", help));
6443
+ }
6444
+
6142
6445
  // src/docx/ribbon-keytips.ts
6143
6446
  var WORD_KEYS = {
6144
6447
  Paste: "V",
@@ -6265,10 +6568,13 @@ function fileButton(ribbon) {
6265
6568
  var KEY_TIPS = {
6266
6569
  home: "H",
6267
6570
  insert: "N",
6571
+ design: "G",
6268
6572
  layout: "P",
6269
6573
  references: "S",
6574
+ mailings: "M",
6270
6575
  review: "R",
6271
6576
  view: "W",
6577
+ help: "Y",
6272
6578
  table: "T",
6273
6579
  "header-footer": "J"
6274
6580
  };
@@ -7354,10 +7660,13 @@ function createRibbon(locale = "en") {
7354
7660
  for (const name of [
7355
7661
  "Home",
7356
7662
  "Insert",
7663
+ "Design",
7357
7664
  "Layout",
7358
7665
  "References",
7666
+ "Mailings",
7359
7667
  "Review",
7360
7668
  "View",
7669
+ "Help",
7361
7670
  "Table",
7362
7671
  "Header & Footer"
7363
7672
  ]) {
@@ -7374,6 +7683,7 @@ function createRibbon(locale = "en") {
7374
7683
  }
7375
7684
  buildHomePanel(panels);
7376
7685
  buildOtherPanels(panels);
7686
+ buildNativeTabs(panels);
7377
7687
  buildHeaderFooterPanel(panels.get("Header & Footer"));
7378
7688
  for (const panel of panels.values()) root.append(panel);
7379
7689
  for (const key2 of ["table", "header-footer"]) setTabHidden(root, key2, "contextual", true);
@@ -7403,6 +7713,7 @@ function syncSelectTitles(root) {
7403
7713
  }
7404
7714
  function syncButtonTitles(root) {
7405
7715
  for (const button3 of root.querySelectorAll("button")) {
7716
+ if (button3.hasAttribute("data-unavailable")) continue;
7406
7717
  const label = button3.getAttribute("aria-label");
7407
7718
  if (!label) continue;
7408
7719
  const hint = shortcutHint(button3.dataset.localearialabel ?? label);
@@ -7500,6 +7811,7 @@ function setTriState(input, value) {
7500
7811
  function createCanvasMeasurer() {
7501
7812
  let context2;
7502
7813
  const widthCache = /* @__PURE__ */ new Map();
7814
+ const metricCache = /* @__PURE__ */ new Map();
7503
7815
  const fallback = createFakeMeasurer();
7504
7816
  const ctx = () => {
7505
7817
  if (context2 !== void 0) return context2;
@@ -7511,6 +7823,20 @@ function createCanvasMeasurer() {
7511
7823
  return context2;
7512
7824
  };
7513
7825
  const fontString = (font) => `${font.italic ? "italic " : ""}${font.bold ? "bold " : ""}${font.sizePx}px ${cssFontStack(font.family)}`;
7826
+ const metricsOf = (c, font) => {
7827
+ const key2 = fontString(font);
7828
+ const cached = metricCache.get(key2);
7829
+ if (cached) return cached;
7830
+ c.font = key2;
7831
+ const measured = c.measureText("Mg");
7832
+ const metrics = {
7833
+ ascent: measured.fontBoundingBoxAscent ?? font.sizePx * 0.95,
7834
+ height: measured.fontBoundingBoxAscent !== void 0 && measured.fontBoundingBoxDescent !== void 0 ? measured.fontBoundingBoxAscent + measured.fontBoundingBoxDescent : font.sizePx * 1.22
7835
+ };
7836
+ if (metricCache.size >= 256) metricCache.delete(metricCache.keys().next().value);
7837
+ metricCache.set(key2, metrics);
7838
+ return metrics;
7839
+ };
7514
7840
  return {
7515
7841
  widthOf(text, font) {
7516
7842
  const c = ctx();
@@ -7537,24 +7863,19 @@ function createCanvasMeasurer() {
7537
7863
  span.remove();
7538
7864
  }
7539
7865
  }
7540
- if (widthCache.size > 2e4) widthCache.clear();
7866
+ if (widthCache.size >= 2e4) widthCache.delete(widthCache.keys().next().value);
7541
7867
  widthCache.set(key2, width);
7542
7868
  return width;
7543
7869
  },
7544
7870
  ascentOf(font) {
7545
7871
  const c = ctx();
7546
7872
  if (!c) return fallback.lineHeightOf(font) * 0.8;
7547
- c.font = fontString(font);
7548
- return c.measureText("Mg").fontBoundingBoxAscent ?? font.sizePx * 0.95;
7873
+ return metricsOf(c, font).ascent;
7549
7874
  },
7550
7875
  lineHeightOf(font) {
7551
7876
  const c = ctx();
7552
7877
  if (!c) return fallback.lineHeightOf(font);
7553
- c.font = fontString(font);
7554
- const metrics = c.measureText("Mg");
7555
- const ascent = metrics.fontBoundingBoxAscent;
7556
- const descent = metrics.fontBoundingBoxDescent;
7557
- return ascent !== void 0 && descent !== void 0 ? ascent + descent : font.sizePx * 1.22;
7878
+ return metricsOf(c, font).height;
7558
7879
  }
7559
7880
  };
7560
7881
  }
@@ -7654,6 +7975,66 @@ function reviewRunCss(format) {
7654
7975
  return css.join(";");
7655
7976
  }
7656
7977
 
7978
+ // src/docx/decoration-cache.ts
7979
+ var DecorationCache = class {
7980
+ doc;
7981
+ inputs = [];
7982
+ value;
7983
+ get(doc, inputs) {
7984
+ return doc === this.doc && inputs.length === this.inputs.length && inputs.every((input, index) => input === this.inputs[index]) ? this.value : void 0;
7985
+ }
7986
+ set(doc, inputs, value) {
7987
+ this.doc = doc;
7988
+ this.inputs = inputs;
7989
+ this.value = value;
7990
+ return value;
7991
+ }
7992
+ clear() {
7993
+ this.doc = void 0;
7994
+ this.value = void 0;
7995
+ this.inputs = [];
7996
+ }
7997
+ };
7998
+ var AttributeCache = class {
7999
+ entries = /* @__PURE__ */ new WeakMap();
8000
+ resolve(attributes, inputs, create) {
8001
+ const cached = this.entries.get(attributes);
8002
+ if (cached && inputs.length === cached.inputs.length && inputs.every((input, index) => input === cached.inputs[index]))
8003
+ return cached.value;
8004
+ const value = create();
8005
+ this.entries.set(attributes, { inputs, value });
8006
+ return value;
8007
+ }
8008
+ };
8009
+ var ParagraphDecorationBuilder = class {
8010
+ descriptors = [];
8011
+ inline(from2, to, attrs, spec) {
8012
+ this.descriptors.push({ from: from2, to, attrs, spec, kind: "inline" });
8013
+ return Decoration.inline(from2, to, attrs, spec);
8014
+ }
8015
+ node(from2, to, attrs, spec) {
8016
+ this.descriptors.push({ from: from2, to, attrs, spec, kind: "node" });
8017
+ return Decoration.node(from2, to, attrs, spec);
8018
+ }
8019
+ };
8020
+ var ParagraphDecorationCache = class {
8021
+ entries = /* @__PURE__ */ new WeakMap();
8022
+ get(node, inputs) {
8023
+ const cached = this.entries.get(node);
8024
+ return cached && inputs.length === cached.inputs.length && inputs.every((input, index) => input === cached.inputs[index]) ? cached : void 0;
8025
+ }
8026
+ set(node, inputs, builder, hasScale) {
8027
+ const entry = { inputs, descriptors: builder.descriptors, hasScale };
8028
+ this.entries.set(node, entry);
8029
+ return entry;
8030
+ }
8031
+ place(value, start) {
8032
+ return value.descriptors.map(
8033
+ (entry) => Decoration[entry.kind](start + entry.from - 1, start + entry.to - 1, entry.attrs, entry.spec)
8034
+ );
8035
+ }
8036
+ };
8037
+
7657
8038
  // src/docx/run-styles.ts
7658
8039
  function themeFontOf(formatting, theme) {
7659
8040
  const role = formatting.fontTheme?.ascii ?? formatting.fontTheme?.hAnsi;
@@ -7713,6 +8094,8 @@ function styleModelOf(state) {
7713
8094
  }
7714
8095
  function runStylesPlugin(getModel, getMode = () => "all") {
7715
8096
  let measurer = scaleMeasurer();
8097
+ const cache = new DecorationCache();
8098
+ const paragraphs = new ParagraphDecorationCache();
7716
8099
  return new Plugin({
7717
8100
  key: runStylesKey,
7718
8101
  getModel,
@@ -7720,6 +8103,7 @@ function runStylesPlugin(getModel, getMode = () => "all") {
7720
8103
  const fonts = view.dom.ownerDocument.fonts;
7721
8104
  const refresh = () => {
7722
8105
  measurer = scaleMeasurer();
8106
+ cache.clear();
7723
8107
  view.dispatch(view.state.tr.setMeta("addToHistory", false).setMeta(runStylesKey, "fonts"));
7724
8108
  };
7725
8109
  fonts?.addEventListener?.("loadingdone", refresh);
@@ -7729,10 +8113,37 @@ function runStylesPlugin(getModel, getMode = () => "all") {
7729
8113
  decorations(state) {
7730
8114
  const model = getModel();
7731
8115
  const mode = getMode();
8116
+ const inputs = [
8117
+ model,
8118
+ model.characterStyles,
8119
+ model.paragraphStyles,
8120
+ model.theme,
8121
+ model.tableStyles,
8122
+ mode
8123
+ ];
8124
+ const cached = cache.get(state.doc, inputs);
8125
+ if (cached) return cached;
7732
8126
  const tables = /* @__PURE__ */ new Map();
7733
8127
  for (const block of model.blocks) if (block.type === "table") tables.set(block.id, block);
7734
8128
  const decorations2 = [];
7735
- const visitParagraph = (paragraph2, start, tableStyleRun) => {
8129
+ const visitParagraph = (paragraph2, position, tableStyleRun) => {
8130
+ const inputs2 = [
8131
+ model.characterStyles,
8132
+ model.paragraphStyles,
8133
+ model.theme,
8134
+ mode,
8135
+ measurer,
8136
+ JSON.stringify(tableStyleRun)
8137
+ ];
8138
+ const cached2 = paragraphs.get(paragraph2, inputs2);
8139
+ if (cached2) {
8140
+ decorations2.push(...paragraphs.place(cached2, position));
8141
+ if (cached2.hasScale) decorations2.push(...scaledParagraphBreaks(paragraph2, position));
8142
+ return;
8143
+ }
8144
+ const start = 1;
8145
+ const localDecorations = [];
8146
+ const builder = new ParagraphDecorationBuilder();
7736
8147
  const paragraphStyleId = displayParagraph(paragraph2, mode).value.style;
7737
8148
  let hasScale = false;
7738
8149
  paragraph2.forEach((child, offset) => {
@@ -7755,8 +8166,8 @@ function runStylesPlugin(getModel, getMode = () => "all") {
7755
8166
  } : {};
7756
8167
  const hidden = resolved.vanish === true;
7757
8168
  if (!child.isText) {
7758
- decorations2.push(
7759
- Decoration.node(start + offset, start + offset + child.nodeSize, {
8169
+ localDecorations.push(
8170
+ builder.node(start + offset, start + offset + child.nodeSize, {
7760
8171
  style: `${runFormattingCss(resolved, model.theme)};${reviewRunCss(resolved)}`,
7761
8172
  ...run.language ? { lang: run.language } : {},
7762
8173
  ...run.rtl !== void 0 ? { dir: run.rtl ? "rtl" : "ltr" } : {},
@@ -7775,8 +8186,8 @@ function runStylesPlugin(getModel, getMode = () => "all") {
7775
8186
  if (segments.length) {
7776
8187
  hasScale = true;
7777
8188
  for (const segment of segments)
7778
- decorations2.push(
7779
- Decoration.inline(
8189
+ localDecorations.push(
8190
+ builder.inline(
7780
8191
  start + offset + segment.from,
7781
8192
  start + offset + segment.to,
7782
8193
  {
@@ -7790,15 +8201,17 @@ function runStylesPlugin(getModel, getMode = () => "all") {
7790
8201
  return;
7791
8202
  }
7792
8203
  if (style || hidden)
7793
- decorations2.push(
7794
- Decoration.inline(start + offset, start + offset + child.nodeSize, {
8204
+ localDecorations.push(
8205
+ builder.inline(start + offset, start + offset + child.nodeSize, {
7795
8206
  ...originalAttrs,
7796
8207
  ...style ? { style } : {},
7797
8208
  ...hidden ? { class: "dve-hidden-text" } : {}
7798
8209
  })
7799
8210
  );
7800
8211
  });
7801
- if (hasScale) decorations2.push(...scaledParagraphBreaks(paragraph2, start));
8212
+ if (hasScale) decorations2.push(...scaledParagraphBreaks(paragraph2, position));
8213
+ const value = paragraphs.set(paragraph2, inputs2, builder, hasScale);
8214
+ decorations2.push(...paragraphs.place(value, position));
7802
8215
  };
7803
8216
  state.doc.forEach((block, blockOffset) => {
7804
8217
  if (block.type.name === "paragraph") return visitParagraph(block, blockOffset + 1);
@@ -7835,7 +8248,7 @@ function runStylesPlugin(getModel, getMode = () => "all") {
7835
8248
  });
7836
8249
  });
7837
8250
  });
7838
- return DecorationSet.create(state.doc, decorations2);
8251
+ return cache.set(state.doc, inputs, DecorationSet.create(state.doc, decorations2));
7839
8252
  }
7840
8253
  }
7841
8254
  });
@@ -11064,7 +11477,7 @@ function createTablePropertiesDialog(viewOf) {
11064
11477
  ["atLeast", "At least"],
11065
11478
  ["exact", "Exactly"]
11066
11479
  ]);
11067
- const split = checkbox("Allow row to break across pages");
11480
+ const split2 = checkbox("Allow row to break across pages");
11068
11481
  const header = checkbox("Repeat as header row at the top of each page");
11069
11482
  const sides2 = ["top", "bottom", "left", "right"];
11070
11483
  const margins = Object.fromEntries(
@@ -11084,7 +11497,7 @@ function createTablePropertiesDialog(viewOf) {
11084
11497
  "Row",
11085
11498
  specified.wrapper,
11086
11499
  row2(labelled("Height (inches)", height), labelled("Row height is", rule)),
11087
- split.wrapper,
11500
+ split2.wrapper,
11088
11501
  header.wrapper
11089
11502
  ),
11090
11503
  fieldset(
@@ -11104,7 +11517,7 @@ function createTablePropertiesDialog(viewOf) {
11104
11517
  specified: specified.input,
11105
11518
  height,
11106
11519
  rule,
11107
- split: split.input,
11520
+ split: split2.input,
11108
11521
  header: header.input,
11109
11522
  ...margins
11110
11523
  };
@@ -11154,7 +11567,7 @@ function createTablePropertiesDialog(viewOf) {
11154
11567
  patch.heightRule = rule.value === "exact" ? "exact" : "atLeast";
11155
11568
  }
11156
11569
  }
11157
- if (changed.has("split")) patch.cantSplit = !split.input.checked;
11570
+ if (changed.has("split")) patch.cantSplit = !split2.input.checked;
11158
11571
  if (changed.has("header")) patch.header = header.input.checked;
11159
11572
  const marginPatch = {};
11160
11573
  for (const side of sides2)
@@ -11200,7 +11613,7 @@ function createTablePropertiesDialog(viewOf) {
11200
11613
  height.value = heightTwips == null ? "" : String(heightTwips / 1440);
11201
11614
  rule.value = common((p) => p.heightRule ?? "atLeast") ?? "";
11202
11615
  setTriState(
11203
- split.input,
11616
+ split2.input,
11204
11617
  common((p) => !p.cantSplit)
11205
11618
  );
11206
11619
  setTriState(
@@ -12906,10 +13319,15 @@ function resetStylePicker(toolbar) {
12906
13319
  // src/docx/paragraph-styles.ts
12907
13320
  function paragraphStylesPlugin(getModel, getMode = () => "all") {
12908
13321
  let measurer = scaleMeasurer();
13322
+ const cache = new DecorationCache();
13323
+ const projections = new AttributeCache();
13324
+ const attributes = new AttributeCache();
13325
+ const project = (node, mode) => projections.resolve(node.attrs, [mode], () => displayParagraph(node, mode));
12909
13326
  return new Plugin({
12910
13327
  view(view) {
12911
13328
  const reload = () => {
12912
13329
  measurer = scaleMeasurer();
13330
+ cache.clear();
12913
13331
  view.dispatch(
12914
13332
  view.state.tr.setMeta("list-marker-fonts", true).setMeta("addToHistory", false)
12915
13333
  );
@@ -12925,34 +13343,53 @@ function paragraphStylesPlugin(getModel, getMode = () => "all") {
12925
13343
  decorations(state) {
12926
13344
  const model = getModel();
12927
13345
  const mode = getMode();
13346
+ const inputs = [
13347
+ model,
13348
+ model.paragraphStyles,
13349
+ model.numberingCatalog,
13350
+ model.theme,
13351
+ model.characterStyles,
13352
+ mode
13353
+ ];
13354
+ const cached = cache.get(state.doc, inputs);
13355
+ if (cached) return cached;
12928
13356
  const catalog = model.paragraphStyles;
12929
13357
  const paragraphs = [];
12930
13358
  state.doc.descendants((node) => {
12931
- if (node.type.name === "paragraph") paragraphs.push(displayParagraph(node, mode).value);
13359
+ if (node.type.name === "paragraph") paragraphs.push(project(node, mode).value);
12932
13360
  });
12933
13361
  const labels3 = computeListLabels({ ...model, blocks: paragraphs });
12934
13362
  const decorations2 = [];
12935
13363
  state.doc.descendants((node, pos) => {
12936
13364
  if (node.type.name !== "paragraph") return;
12937
- const projected = displayParagraph(node, mode);
12938
- const paragraph2 = projected.value;
12939
- const reset = mode === "original" && node.attrs.formatRevision && !projected.error ? `${ORIGINAL_PARAGRAPH_RESET};` : "";
12940
- const effective = catalog ? resolveParagraphFormatting(paragraph2, catalog) : paragraph2;
12941
13365
  const label = labels3.get(String(node.attrs.id));
12942
- const marker = listMarkerDisplay(
12943
- node,
12944
- { ...paragraph2, ...effective },
12945
- label,
12946
- model,
12947
- measurer
12948
- );
12949
- const list = label ? {
12950
- listIndentLeftTwips: label.indentLeftTwips ?? null,
12951
- listHangingTwips: label.hangingTwips ?? null,
12952
- listFirstLineTwips: label.firstLineTwips ?? null
12953
- } : { listIndentLeftTwips: null, listHangingTwips: null, listFirstLineTwips: null };
12954
- decorations2.push(
12955
- Decoration.node(pos, pos + node.nodeSize, {
13366
+ const dependencies = [
13367
+ catalog,
13368
+ model.characterStyles,
13369
+ model.theme,
13370
+ mode,
13371
+ measurer,
13372
+ JSON.stringify(label),
13373
+ label ? node.firstChild : void 0
13374
+ ];
13375
+ const rendered = attributes.resolve(node.attrs, dependencies, () => {
13376
+ const projected = project(node, mode);
13377
+ const paragraph2 = projected.value;
13378
+ const reset = mode === "original" && node.attrs.formatRevision && !projected.error ? `${ORIGINAL_PARAGRAPH_RESET};` : "";
13379
+ const effective = catalog ? resolveParagraphFormatting(paragraph2, catalog) : paragraph2;
13380
+ const marker = listMarkerDisplay(
13381
+ node,
13382
+ { ...paragraph2, ...effective },
13383
+ label,
13384
+ model,
13385
+ measurer
13386
+ );
13387
+ const list = label ? {
13388
+ listIndentLeftTwips: label.indentLeftTwips ?? null,
13389
+ listHangingTwips: label.hangingTwips ?? null,
13390
+ listFirstLineTwips: label.firstLineTwips ?? null
13391
+ } : { listIndentLeftTwips: null, listHangingTwips: null, listFirstLineTwips: null };
13392
+ return {
12956
13393
  style: `${reset}${paragraphStyle({ ...effective, ...list })};${marker.css}`,
12957
13394
  ...projected.error ? { "data-review-format-error": projected.error } : {},
12958
13395
  ...marker.attributes,
@@ -12960,10 +13397,11 @@ function paragraphStylesPlugin(getModel, getMode = () => "all") {
12960
13397
  "data-list-label": label ? `${displayListLabel(label.text)}${label.suffix === "space" ? " " : label.suffix === "none" ? "" : " "}` : ""
12961
13398
  } : {},
12962
13399
  ...effective.direction || reset ? { dir: effective.direction ?? "ltr" } : {}
12963
- })
12964
- );
13400
+ };
13401
+ });
13402
+ decorations2.push(Decoration.node(pos, pos + node.nodeSize, rendered));
12965
13403
  });
12966
- return DecorationSet.create(state.doc, decorations2);
13404
+ return cache.set(state.doc, inputs, DecorationSet.create(state.doc, decorations2));
12967
13405
  }
12968
13406
  }
12969
13407
  });
@@ -13332,7 +13770,7 @@ function refreshEditorControls(toolbar, view, model, readOnly, collaboration, lo
13332
13770
  ".ribbon-group button, .ribbon-group input, .ribbon-group select"
13333
13771
  ).forEach((control) => {
13334
13772
  const label = control.dataset.localearialabel ?? control.getAttribute("aria-label");
13335
- control.disabled = readOnly && ![
13773
+ control.disabled = control.hasAttribute("data-unavailable") || readOnly && ![
13336
13774
  "Find and replace",
13337
13775
  "Zoom",
13338
13776
  "Show hidden text",
@@ -13341,6 +13779,7 @@ function refreshEditorControls(toolbar, view, model, readOnly, collaboration, lo
13341
13779
  "Layout view",
13342
13780
  "Print",
13343
13781
  "Copy",
13782
+ "Help",
13344
13783
  "Select all",
13345
13784
  "Show paragraph marks",
13346
13785
  "Zoom to 100%",
@@ -13401,8 +13840,7 @@ function refreshEditorControls(toolbar, view, model, readOnly, collaboration, lo
13401
13840
  if (control) control.disabled = readOnly || !enabled;
13402
13841
  }
13403
13842
  }
13404
- const content = state.doc.textBetween(0, state.doc.content.size, " ").trim();
13405
- const words = countWords(content, language || void 0);
13843
+ const words = documentWordCount(state.doc, language || void 0);
13406
13844
  const pageText = printPageStatus ? formatPageStatus(locale, printPageStatus.current, printPageStatus.total) : formatPageStatus(locale, 1, 1);
13407
13845
  return { pageText, wordText: formatWordCount(locale, words) };
13408
13846
  }
@@ -15553,7 +15991,7 @@ var PageController = class {
15553
15991
  this.host.statusBar()?.setViewMode(mode);
15554
15992
  this.host.refreshControls();
15555
15993
  }
15556
- /** Re-lays out Print Layout after a model change (debounced by the layout view). */
15994
+ /** Re-lays out Print Layout after a model change (coalesced by the layout view). */
15557
15995
  relayout() {
15558
15996
  if (this.viewMode === "print") this.host.printLayout()?.scheduleRelayout(this.host.model());
15559
15997
  }
@@ -16159,8 +16597,14 @@ function applyRibbonVisibility(toolbar, showToolbar, hiddenActions) {
16159
16597
  menu.toggleAttribute(HIDDEN, Boolean(menu.querySelector(`select[${HIDDEN}]`)));
16160
16598
  for (const combo of toolbar.querySelectorAll(".ribbon-combo"))
16161
16599
  combo.toggleAttribute(HIDDEN, Boolean(combo.querySelector(`input[${HIDDEN}]`)));
16162
- for (const split of toolbar.querySelectorAll(".ribbon-split"))
16163
- split.querySelector("[data-split-caret]")?.toggleAttribute(HIDDEN, Boolean(split.querySelector(`button[${HIDDEN}]`)));
16600
+ for (const split2 of toolbar.querySelectorAll(".ribbon-split"))
16601
+ split2.querySelector("[data-split-caret]")?.toggleAttribute(HIDDEN, Boolean(split2.querySelector(`button[${HIDDEN}]`)));
16602
+ for (const split2 of toolbar.querySelectorAll(".ribbon-command-split")) {
16603
+ const isHidden = Boolean(split2.querySelector(`button[${HIDDEN}]`));
16604
+ split2.toggleAttribute(HIDDEN, isHidden);
16605
+ const menu = split2.querySelector("select");
16606
+ menu?.toggleAttribute(HIDDEN, isHidden || menu.hasAttribute(HIDDEN));
16607
+ }
16164
16608
  for (const group3 of toolbar.querySelectorAll(".ribbon-group"))
16165
16609
  group3.toggleAttribute(HIDDEN, allHidden(group3));
16166
16610
  for (const panel of panelsOf(toolbar))
@@ -16305,6 +16749,7 @@ var EditorCore = class {
16305
16749
  viewOptions = new ViewOptions();
16306
16750
  dirtyState;
16307
16751
  pageTracker;
16752
+ wordText = "";
16308
16753
  contentWidth() {
16309
16754
  return this.model.page.width - this.model.page.marginLeft - this.model.page.marginRight;
16310
16755
  }
@@ -16340,6 +16785,7 @@ var EditorCore = class {
16340
16785
  return;
16341
16786
  }
16342
16787
  if (remote) transaction.setMeta(REMOTE_TRANSACTION_META, true);
16788
+ const previousDoc = view.state.doc;
16343
16789
  const previousParts = view.state.doc.attrs.sectionParts;
16344
16790
  const previousNotes = view.state.doc.attrs.noteParts;
16345
16791
  const previousComments = view.state.doc.attrs.commentThreads;
@@ -16360,7 +16806,7 @@ var EditorCore = class {
16360
16806
  ) : assignMissingParagraphIds(applied);
16361
16807
  view.updateState(repaired ? applied.apply(repaired) : applied);
16362
16808
  if (docChanged) {
16363
- this.model = docToModel(view.state.doc, this.model);
16809
+ this.model = docToModel(view.state.doc, this.model, previousDoc);
16364
16810
  if (previousComments !== view.state.doc.attrs.commentThreads)
16365
16811
  this.shell.review?.commentsPanel.refresh();
16366
16812
  this.pages.refreshPageStyles();
@@ -16401,7 +16847,10 @@ var EditorCore = class {
16401
16847
  Boolean(review?.commentsOpen),
16402
16848
  this.canEditBody() && (!this.collab.active || Boolean(this.collab.yjs?.sharedComments))
16403
16849
  );
16404
- if (status) chrome?.refresh(status.pageText, status.wordText);
16850
+ if (status) {
16851
+ this.wordText = status.wordText;
16852
+ chrome?.refresh(status.pageText, status.wordText);
16853
+ }
16405
16854
  this.pages.syncControls();
16406
16855
  if (toolbar)
16407
16856
  syncHeaderFooterRibbon(
@@ -16413,6 +16862,19 @@ var EditorCore = class {
16413
16862
  syncPageState(this);
16414
16863
  chrome?.ribbonActions.setCommentsOpen(Boolean(review?.commentsOpen));
16415
16864
  }
16865
+ /** Scrolling only changes page state, never formatting controls or document statistics. */
16866
+ refreshPageStatus() {
16867
+ const { printLayout, printEditing, chrome } = this.shell;
16868
+ printLayout?.refreshCurrentPage();
16869
+ printEditing?.refresh();
16870
+ const status = printLayout?.pageStatus();
16871
+ if (status)
16872
+ chrome?.statusBar.setPageAndWords(
16873
+ formatPageStatus(this.locale, status.current, status.total),
16874
+ this.wordText
16875
+ );
16876
+ syncPageState(this);
16877
+ }
16416
16878
  };
16417
16879
 
16418
16880
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/docx/styles/aliases.css?raw
@@ -16428,7 +16890,7 @@ var ribbon_default = ".dve-ribbon {\n display: flex;\n flex: none;\n min-width:
16428
16890
  var ribbon_buttons_default = "/* Word-style icon commands: small icon buttons, large stacked buttons and split colour buttons. */\n.ribbon-group button.ribbon-small {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n min-width: 28px;\n height: 28px;\n padding: 0;\n}\n.ribbon-group button.ribbon-large {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-start;\n gap: 3px;\n width: 62px;\n height: 66px;\n padding: 4px 2px;\n font-size: 11px;\n line-height: 1.15;\n white-space: normal;\n text-align: center;\n}\n.ribbon-large > span {\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ribbon-stack {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.ribbon-split {\n display: inline-flex;\n align-items: center;\n}\n.ribbon-group .ribbon-split button.ribbon-color {\n position: relative;\n width: 28px;\n min-width: 28px;\n border-radius: 4px 0 0 4px;\n}\n.ribbon-color::after {\n position: absolute;\n right: 6px;\n bottom: 3px;\n left: 6px;\n height: 3px;\n background: var(--bar, transparent);\n outline: 1px solid var(--line);\n content: '';\n}\n.ribbon-group .ribbon-split .ribbon-caret {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n min-width: 14px;\n height: 28px;\n padding: 0;\n border-radius: 0 4px 4px 0;\n}\n.ribbon-popover {\n position: fixed;\n z-index: 1000;\n display: grid;\n grid-template-columns: repeat(6, 22px);\n gap: 4px;\n padding: 8px;\n border: 1px solid var(--line);\n border-radius: 6px;\n background: var(--surface);\n box-shadow: 0 6px 20px rgb(0 0 0 / 0.18);\n}\n.ribbon-popover .swatch {\n width: 22px;\n height: 22px;\n padding: 0;\n border: 1px solid var(--line);\n border-radius: 3px;\n background: var(--swatch);\n cursor: pointer;\n}\n.ribbon-popover .swatch:hover,\n.ribbon-popover .swatch:focus-visible {\n outline: 2px solid var(--blue);\n outline-offset: 1px;\n}\n.ribbon-popover .swatch-none {\n background: linear-gradient(135deg, transparent 46%, #c00000 46% 54%, transparent 54%);\n}\n.ribbon-group button.ribbon-inline {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 28px;\n padding: 0 8px 0 6px;\n font-size: 12px;\n}\n.ribbon-group button.ribbon-large {\n min-height: 66px;\n overflow: hidden;\n}\n.ribbon-group:has(.ribbon-large) {\n max-width: none;\n flex-wrap: wrap;\n}\n/* A row of stacked dropdown commands (Layout > Page Setup) stays on one line at the panel height. */\n.ribbon-group:has(> .ribbon-menu:not(.ribbon-menu-compact)) {\n max-width: none;\n flex-wrap: nowrap;\n}\n.ribbon-group button.ribbon-inline {\n font-weight: 400;\n}\n.ribbon-group button.ribbon-large {\n width: 70px;\n}\n.ribbon-panel[data-panel='Table'] .ribbon-group button.ribbon-large {\n width: 80px;\n}\n\n/* Word-style dropdown command: the native select is transparent and covers the whole control. */\n.ribbon-menu {\n box-sizing: border-box;\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 3px;\n width: 70px;\n height: 66px;\n padding: 4px 2px;\n border: 1px solid transparent;\n border-radius: 4px;\n color: var(--ink);\n font-size: 11px;\n line-height: 1.15;\n text-align: center;\n}\n.ribbon-menu:hover {\n border-color: var(--line);\n background: var(--accent-soft);\n}\n.ribbon-menu:focus-within {\n outline: 2px solid var(--blue);\n outline-offset: -1px;\n}\n.ribbon-menu > span {\n max-width: 100%;\n}\n.ribbon-menu > .ribbon-icon:last-of-type {\n margin-top: -2px;\n}\n.ribbon-menu-compact {\n flex-direction: row;\n width: 44px;\n height: 28px;\n padding: 0 4px;\n justify-content: center;\n gap: 2px;\n}\n.ribbon-menu-compact > .ribbon-icon:last-of-type {\n margin-top: 0;\n}\n.ribbon-group .ribbon-menu select.ribbon-menu-input {\n position: absolute;\n inset: 0;\n width: 100%;\n max-width: none;\n height: 100%;\n opacity: 0;\n cursor: pointer;\n border: 0;\n}\n.ribbon-menu[data-dve-hidden] {\n display: none;\n}\n.ribbon-stack .ribbon-small,\n.ribbon-stack .ribbon-inline {\n height: 22px;\n}\n.ribbon-stack .ribbon-inline {\n justify-content: flex-start;\n}\n.ribbon-panel[data-panel='Header & Footer'] .ribbon-stack button.ribbon-inline {\n height: 22px;\n}\n.ribbon-panel[data-panel='Header & Footer'] button.ribbon-large {\n width: 112px;\n}\n.ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Editing'] {\n width: auto;\n min-width: 110px;\n}\n.dve-painting,\n.dve-painting * {\n cursor: copy;\n}\n";
16429
16891
 
16430
16892
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/docx/styles/ribbon-gallery.css?raw
16431
- var ribbon_gallery_default = "/* Styles gallery: a scrolling strip of tiles with a preview of each style. */\n.ribbon-gallery {\n display: flex;\n gap: 3px;\n width: 216px;\n height: 66px;\n padding: 2px;\n overflow-x: auto;\n overflow-y: hidden;\n scroll-snap-type: x proximity;\n scrollbar-width: none;\n border: 1px solid var(--line);\n border-radius: 4px;\n background: var(--surface);\n}\n.ribbon-group .ribbon-gallery .style-tile {\n flex: 0 0 78px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: space-between;\n height: 58px;\n min-width: 0;\n padding: 3px 2px;\n border: 1px solid transparent;\n scroll-snap-align: start;\n}\n.style-sample {\n max-width: 100%;\n overflow: hidden;\n line-height: 1.1;\n white-space: nowrap;\n}\n.style-name {\n max-width: 100%;\n overflow: hidden;\n color: var(--muted);\n font-size: 10px;\n text-overflow: ellipsis;\n}\n.ribbon-group .ribbon-gallery .style-tile[aria-pressed='true'] {\n border-color: var(--blue);\n}\n.ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Styles'] {\n width: 270px;\n max-width: none;\n flex-wrap: nowrap;\n}\n.ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Styles'] .ribbon-stack {\n gap: 2px;\n}\n\n.ribbon-group[data-label='Section breaks'] {\n width: 210px;\n}\n.ribbon-group[data-label='Page numbers'] {\n width: 400px;\n}\n\n/* Language: labelled controls flow in short columns instead of one tall stack. */\n.ribbon-group[data-label='Language'] {\n flex: 0 0 auto;\n flex-direction: column;\n flex-wrap: wrap;\n align-content: flex-start;\n max-height: 86px;\n max-width: none;\n column-gap: 12px;\n overflow: hidden;\n}\n";
16893
+ var ribbon_gallery_default = "/* Styles gallery: a scrolling strip of tiles with a preview of each style. */\n.ribbon-gallery {\n display: flex;\n gap: 3px;\n width: 216px;\n height: 66px;\n padding: 2px;\n overflow-x: auto;\n overflow-y: hidden;\n scroll-snap-type: x proximity;\n scrollbar-width: none;\n border: 1px solid var(--line);\n border-radius: 4px;\n background: var(--surface);\n}\n.ribbon-group .ribbon-gallery .style-tile {\n flex: 0 0 78px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: space-between;\n height: 58px;\n min-width: 0;\n padding: 3px 2px;\n border: 1px solid transparent;\n scroll-snap-align: start;\n}\n.style-sample {\n max-width: 100%;\n overflow: hidden;\n line-height: 1.1;\n white-space: nowrap;\n}\n.style-name {\n max-width: 100%;\n overflow: hidden;\n color: var(--muted);\n font-size: 10px;\n text-overflow: ellipsis;\n}\n.ribbon-group .ribbon-gallery .style-tile[aria-pressed='true'] {\n border-color: var(--blue);\n}\n.ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Styles'] {\n width: 270px;\n max-width: none;\n flex-wrap: nowrap;\n}\n.ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Styles']:not([data-collapsed]) {\n flex: 1 0 270px;\n min-width: 270px;\n}\n.ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Styles'] .ribbon-gallery {\n flex: 1;\n min-width: 0;\n width: auto;\n}\n.ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Styles'] .ribbon-stack {\n gap: 2px;\n}\n\n.ribbon-group[data-label='Section breaks'] {\n width: 210px;\n}\n.ribbon-group[data-label='Page numbers'] {\n width: 400px;\n}\n\n/* Language: labelled controls flow in short columns instead of one tall stack. */\n.ribbon-group[data-label='Language'] {\n flex: 0 0 auto;\n flex-direction: column;\n flex-wrap: wrap;\n align-content: flex-start;\n max-height: 86px;\n max-width: none;\n column-gap: 12px;\n overflow: hidden;\n}\n";
16432
16894
 
16433
16895
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/docx/styles/ribbon-widgets.css?raw
16434
16896
  var ribbon_widgets_default = ".dve-keytip {\n position: absolute;\n bottom: -2px;\n left: 50%;\n z-index: 70;\n transform: translateX(-50%);\n min-width: 16px;\n padding: 1px 4px;\n border: 1px solid var(--line);\n border-radius: 3px;\n background: #1f1f1f;\n color: #fff;\n font:\n 600 11px/14px 'Segoe UI',\n Arial,\n sans-serif;\n text-align: center;\n pointer-events: none;\n}\n\n.ribbon-popover.dve-word-count {\n grid-template-columns: auto auto;\n gap: 4px 24px;\n padding: 10px 14px;\n color: var(--ink);\n font:\n 12px 'Segoe UI',\n Arial,\n sans-serif;\n}\n.dve-word-count strong {\n text-align: right;\n}\n\n.ribbon-spinner {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n width: 170px;\n font-size: 12px;\n white-space: nowrap;\n}\n.ribbon-panel[data-panel='Header & Footer'] .ribbon-group[data-label='Position'] {\n max-width: none;\n}\n.ribbon-panel[data-panel='Header & Footer'] .ribbon-group[data-label='Position'] .ribbon-spinner {\n width: auto;\n min-width: 280px;\n}\n.ribbon-panel[data-panel='Header & Footer'] .ribbon-spinner input {\n flex: none;\n}\n.ribbon-group .ribbon-spinner input {\n width: 64px;\n height: 24px;\n border: 1px solid var(--line);\n border-radius: 3px;\n background: var(--surface);\n color: var(--ink);\n font:\n 12px 'Segoe UI',\n Arial,\n sans-serif;\n}\n\n/* Editable combo boxes (font family and size). */\n.ribbon-combo {\n display: inline-flex;\n align-items: stretch;\n height: 26px;\n border: 1px solid var(--line);\n border-radius: 3px;\n background: var(--surface);\n}\n.ribbon-combo:focus-within {\n border-color: var(--blue);\n outline: 1px solid var(--blue);\n}\n.ribbon-combo.font-family-combo {\n width: 132px;\n}\n.ribbon-combo.font-size-combo {\n width: 58px;\n}\n.ribbon-combo input {\n flex: 1;\n min-width: 0;\n padding: 0 6px;\n border: 0;\n background: transparent;\n color: var(--ink);\n font:\n 12px 'Segoe UI',\n Arial,\n sans-serif;\n outline: none;\n}\n.ribbon-group .ribbon-combo .ribbon-combo-caret {\n width: 18px;\n min-width: 18px;\n height: 100%;\n border-radius: 0;\n}\n.ribbon-popover.ribbon-list {\n display: flex;\n flex-direction: column;\n gap: 0;\n min-width: 160px;\n max-height: 280px;\n overflow-y: auto;\n padding: 4px 0;\n}\n.ribbon-list button[role='option'] {\n padding: 4px 12px;\n border: 0;\n background: none;\n color: var(--ink);\n font:\n 14px 'Segoe UI',\n Arial,\n sans-serif;\n text-align: left;\n cursor: pointer;\n}\n.ribbon-list button[role='option']:hover,\n.ribbon-list button[role='option']:focus-visible {\n background: var(--accent-soft);\n outline: none;\n}\n.ribbon-list button[aria-selected='true'] {\n color: var(--blue);\n font-weight: 600;\n}\n\n.ribbon-combo[data-dve-hidden] {\n display: none;\n}\n\n/* Tiles preview a style on paper, so a style's own colours stay legible in dark themes too. */\n.ribbon-group .ribbon-gallery .style-tile {\n background: #fff;\n color: #1f1f1f;\n}\n.ribbon-group .ribbon-gallery .style-tile:hover:not(:disabled) {\n background: #f3f7fc;\n border-color: #a8c4e6;\n}\n.ribbon-group .ribbon-gallery .style-tile[aria-pressed='true'] {\n border-color: var(--blue);\n background: #e8f0fa;\n}\n.ribbon-gallery .style-name {\n color: #555;\n}\n.ribbon-group .ribbon-gallery .style-tile:disabled {\n opacity: 0.6;\n}\n\n/* Disabled controls look disabled, and keyboard focus is always visible. */\n.ribbon-group button:disabled,\n.ribbon-group select:disabled,\n.ribbon-group input:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.ribbon-menu:has(select:disabled),\n.ribbon-combo:has(input:disabled),\n.ribbon-spinner:has(input:disabled),\n.ribbon-split:has(button:disabled) {\n opacity: 0.4;\n}\n.ribbon-menu:has(select:disabled):hover {\n border-color: transparent;\n background: none;\n}\n.ribbon-group button:focus-visible {\n outline: 2px solid var(--blue);\n outline-offset: -2px;\n}\n.ribbon-menu:has(select:disabled) select,\n.ribbon-split:has(button:disabled) button:disabled {\n opacity: 1;\n}\n\n/* Dialog launcher: the small corner button of a group, as in Word. */\n.ribbon-group {\n position: relative;\n}\n.ribbon-group .ribbon-launcher {\n position: absolute;\n right: 2px;\n bottom: 0;\n z-index: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n min-width: 14px;\n height: 14px;\n padding: 0;\n color: var(--muted);\n}\n.ribbon-group .ribbon-launcher:hover:not(:disabled) {\n color: var(--ink);\n}\n";
@@ -16442,9 +16904,35 @@ var ribbon_popups_default = "/* Editing: a row-height command with a caret menu,
16442
16904
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/docx/styles/ribbon-polish.css?raw
16443
16905
  var ribbon_polish_default = "/* Ribbon polish: distinct pressed state and Word's accent hues on command icons. Loaded last. */\n:host {\n --ribbon-accent-blue: #2b7cd3;\n --ribbon-accent-green: #2f9e5f;\n --ribbon-accent-red: #d13438;\n --ribbon-accent-orange: #d9822b;\n}\n@media (prefers-color-scheme: dark) {\n :host(:not([theme='light'])) {\n --ribbon-accent-blue: #5ea5f0;\n --ribbon-accent-green: #5cc98a;\n --ribbon-accent-red: #f07a7d;\n --ribbon-accent-orange: #f0a35e;\n }\n}\n:host([theme='dark']) {\n --ribbon-accent-blue: #5ea5f0;\n --ribbon-accent-green: #5cc98a;\n --ribbon-accent-red: #f07a7d;\n --ribbon-accent-orange: #f0a35e;\n}\n\n/* Pressed reads darker than hover, and pressing a control gives immediate feedback. */\n.ribbon-group button[aria-pressed='true'] {\n border-color: color-mix(in srgb, var(--blue) 45%, transparent);\n background: color-mix(in srgb, var(--blue) 20%, transparent);\n}\n.ribbon-group button[aria-pressed='true']:hover:not(:disabled) {\n background: color-mix(in srgb, var(--blue) 30%, transparent);\n}\n.ribbon-group button:active:not(:disabled),\n.ribbon-menu:active:not(:has(select:disabled)) {\n background: color-mix(in srgb, var(--blue) 28%, transparent);\n}\n\n/* Accent hues (buttons only; a disabled control keeps its 0.4 opacity). */\n.ribbon-icon[data-icon='table'],\n.ribbon-icon[data-icon='find'],\n.ribbon-icon[data-icon='replace'],\n.ribbon-icon[data-icon='link'],\n.ribbon-icon[data-icon='crossReference'],\n.ribbon-icon[data-icon='toc'],\n.ribbon-icon[data-icon='updateTable'],\n.ribbon-icon[data-icon='header'],\n.ribbon-icon[data-icon='footer'],\n.ribbon-icon[data-icon='pageNumber'],\n.ribbon-icon[data-icon='pageNumberIcon'],\n.ribbon-icon[data-icon='margins'],\n.ribbon-icon[data-icon='orientation'],\n.ribbon-icon[data-icon='pageSize'],\n.ribbon-icon[data-icon='columns'],\n.ribbon-icon[data-icon='select'],\n.ribbon-icon[data-icon='paste'],\n.ribbon-icon[data-icon='borders'],\n.ribbon-icon[data-icon='rowAbove'],\n.ribbon-icon[data-icon='rowBelow'],\n.ribbon-icon[data-icon='columnLeft'],\n.ribbon-icon[data-icon='columnRight'] {\n color: var(--ribbon-accent-blue);\n}\n.ribbon-icon[data-icon='picture'],\n.ribbon-icon[data-icon='formatPicture'],\n.ribbon-icon[data-icon='formatPainter'],\n.ribbon-icon[data-icon='accept'],\n.ribbon-icon[data-icon='acceptAll'],\n.ribbon-icon[data-icon='spelling'],\n.ribbon-icon[data-icon='readAloud'] {\n color: var(--ribbon-accent-green);\n}\n.ribbon-icon[data-icon='reject'],\n.ribbon-icon[data-icon='rejectAll'],\n.ribbon-icon[data-icon='deleteRow'],\n.ribbon-icon[data-icon='deleteColumn'],\n.ribbon-icon[data-icon='deleteTable'],\n.ribbon-icon[data-icon='deleteComment'],\n.ribbon-icon[data-icon='clear'] {\n color: var(--ribbon-accent-red);\n}\n.ribbon-icon[data-icon='comment'],\n.ribbon-icon[data-icon='comments'],\n.ribbon-icon[data-icon='bookmark'],\n.ribbon-icon[data-icon='caption'],\n.ribbon-icon[data-icon='footnote'],\n.ribbon-icon[data-icon='endnote'],\n.ribbon-icon[data-icon='coverPage'] {\n color: var(--ribbon-accent-orange);\n}\n\n/* Icons never shrink to make room for a two-line caption, so every large icon is the same size. */\n.ribbon-menu > .ribbon-icon,\n.ribbon-large > .ribbon-icon {\n flex: none;\n}\n\n/* A two-line caption plus the caret must still fit the dropdown command's box. */\n.ribbon-menu:not(.ribbon-menu-compact):not(.ribbon-menu-caret) {\n gap: 1px;\n height: 72px;\n line-height: 1.1;\n}\n.ribbon-menu:not(.ribbon-menu-compact):not(.ribbon-menu-caret) > .ribbon-icon:last-of-type {\n margin-top: -3px;\n}\n";
16444
16906
 
16907
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/docx/styles/ribbon-home-menus.css?raw
16908
+ var ribbon_home_menus_default = ".ribbon-command-split {\n flex: none;\n height: 28px;\n}\n.ribbon-command-split .ribbon-menu.ribbon-menu-caret {\n width: 14px;\n height: 28px;\n}\n.ribbon-command-split-large {\n flex-direction: column;\n height: 74px;\n}\n.ribbon-group .ribbon-command-split-large .ribbon-large {\n height: 56px;\n min-height: 56px;\n}\n.ribbon-command-split-large .ribbon-menu.ribbon-menu-caret {\n width: 100%;\n height: 18px;\n}\n.ribbon-command-split[data-dve-hidden] {\n display: none;\n}\n";
16909
+
16445
16910
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/docx/styles/document.css?raw
16446
16911
  var document_default = ".dve-revision-insert {\n color: var(--dve-revision-color, inherit);\n text-decoration: underline;\n text-decoration-color: var(--dve-revision-color, currentColor);\n}\n.dve-revision-delete {\n color: var(--dve-revision-color, inherit);\n text-decoration: line-through;\n text-decoration-color: var(--dve-revision-color, currentColor);\n}\n/* Moved text, like Word: green, double underline at the destination, double strike at the source. */\n.dve-revision-move {\n --dve-revision-color: #2e7d32;\n text-decoration-style: double;\n}\n.ProseMirror .dve-revision-hidden {\n /* Review visibility takes precedence over picture/equation node-view display rules. */\n display: none !important;\n}\n[data-review-display]:not([data-review-display='all']) .dve-revision-insert,\n[data-review-display]:not([data-review-display='all']) .dve-revision-delete {\n color: inherit;\n text-decoration: none;\n}\n[data-review-display]:not([data-review-display='all']) .dve-comment-range {\n background: transparent;\n}\n.dve-revision-format-markup {\n outline: 1px dotted currentColor;\n outline-offset: 2px;\n}\n.dve-comment-range {\n background: rgba(255, 212, 0, 0.28);\n}\n.dve-paper {\n box-sizing: border-box;\n position: relative;\n flex: none;\n margin: 0 auto;\n background: var(--dve-page-color, #fff);\n color: #202124;\n box-shadow:\n 0 1px 3px #20212430,\n 0 4px 12px #20212420;\n outline: none;\n /* Single spacing and no paragraph gap unless the document's styles set them, as in Word. */\n font:\n 11pt/1.2 Calibri,\n Carlito,\n Arial,\n sans-serif;\n zoom: var(--dve-zoom, 1);\n}\n.dve-paper p {\n min-height: 1.2em;\n margin: 0;\n white-space: pre-wrap;\n}\n.dve-paper p:last-child {\n margin-bottom: 0;\n}\n/* The caret marks focus in the document, as in Word; no ring around the editable area. */\n.dve-paper .ProseMirror:focus,\n.dve-paper .ProseMirror:focus-visible {\n outline: none;\n}\n.dve-paper:focus {\n box-shadow:\n 0 1px 3px #20212430,\n 0 4px 12px #20212420,\n 0 0 0 1px var(--blue);\n}\n/* Columns follow the table's grid and cell widths (equal when it has none), as Word lays them out;\n tables add no space around themselves. */\n.dve-paper table {\n width: 100%;\n table-layout: fixed;\n border-collapse: collapse;\n margin: 0;\n}\n.dve-paper td {\n min-width: 44px;\n border: var(--dve-cell-border, 1px dashed #c8c6c4);\n vertical-align: top;\n}\n.dve-paper td p {\n margin: 0;\n}\n.dve-paper p[data-list-label]::before {\n content: attr(data-list-label);\n white-space: pre;\n user-select: none;\n}\n.dve-paper p[data-list-marker]:not([data-list-marker=''])::before {\n content: attr(data-list-marker);\n position: absolute;\n left: var(--dve-marker-left);\n text-indent: 0;\n font-family: var(--dve-marker-family);\n font-size: var(--dve-marker-size);\n font-weight: var(--dve-marker-weight);\n font-style: var(--dve-marker-style);\n color: var(--dve-marker-color);\n}\n.multilingual-controls {\n flex: 1 1 100%;\n align-items: flex-start;\n gap: 9px 14px;\n}\n.multilingual-language-control {\n display: grid;\n grid-template-columns: minmax(145px, 180px);\n gap: 4px;\n color: var(--muted);\n font-size: 11px;\n}\n.multilingual-language-control select,\n.multilingual-language-control input {\n box-sizing: border-box;\n width: 180px;\n}\n.multilingual-language-control input {\n height: 26px;\n border: 1px solid var(--line);\n border-radius: 3px;\n background: var(--surface);\n padding: 0 7px;\n color: var(--ink);\n font:\n 11px 'Segoe UI',\n Arial,\n sans-serif;\n}\n.multilingual-note {\n align-self: end;\n max-width: 240px;\n color: var(--muted);\n font-size: 11px;\n}\n@media (max-width: 650px) {\n .ribbon-panel {\n min-height: 96px;\n padding: 6px;\n gap: 4px;\n }\n .ribbon-group {\n padding-right: 7px;\n }\n .ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Font'] {\n width: 340px;\n }\n .ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Paragraph'] {\n width: 280px;\n }\n .ribbon-panel[data-panel='Home'] .ribbon-group[data-label='Editing'] {\n width: auto;\n }\n .ribbon-group button {\n min-width: 34px;\n min-height: 34px;\n }\n .ribbon-group select {\n max-width: min(150px, 42vw);\n }\n .dve-canvas {\n padding: 18px 8px 38px;\n align-items: flex-start;\n }\n .ribbon-group select.font-family-select {\n width: 120px;\n max-width: 38vw;\n }\n .ribbon-group select.font-size-select {\n width: 56px;\n }\n .ribbon-group select.spacing-select {\n width: min(128px, 36vw);\n }\n .ribbon-group select.line-spacing-select {\n width: min(112px, 32vw);\n }\n}\n.dve-break-marker {\n display: block;\n margin: 0.4em 0;\n padding: 2px 0;\n border-top: 1px dashed var(--muted);\n border-bottom: 1px dashed var(--muted);\n color: var(--muted);\n font-size: 10px;\n text-align: center;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n user-select: none;\n}\n.dve-note-reference {\n color: var(--blue);\n cursor: default;\n}\n.dve-header,\n.dve-footer {\n box-sizing: border-box;\n width: 816px;\n max-width: 100%;\n margin: 0 auto;\n padding: 10px clamp(16px, 5vw, 70px);\n background: var(--surface);\n color: var(--muted);\n font:\n 10pt/1.35 Calibri,\n Arial,\n sans-serif;\n}\n.dve-header {\n border-bottom: 1px dashed var(--line);\n}\n.dve-footer {\n border-top: 1px dashed var(--line);\n}\n.dve-header-footer-label {\n margin-bottom: 2px;\n color: var(--muted);\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n.dve-notes-container {\n box-sizing: border-box;\n width: 816px;\n max-width: 100%;\n margin: 12px auto 0;\n}\n.dve-notes {\n box-sizing: border-box;\n padding: 10px clamp(16px, 5vw, 70px);\n background: var(--surface);\n border-top: 1px solid var(--line);\n font:\n 10pt/1.35 Calibri,\n Arial,\n sans-serif;\n}\n.dve-notes-heading {\n margin: 0 0 6px;\n color: var(--ink);\n font-size: 12px;\n}\n.dve-note-list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.dve-note-list li {\n display: flex;\n gap: 4px;\n align-items: baseline;\n margin-bottom: 4px;\n}\n.dve-note-marker {\n flex: none;\n color: var(--muted);\n}\n@media (prefers-reduced-motion: reduce) {\n * {\n scroll-behavior: auto !important;\n }\n}\n\n/* Print Layout: a read-only, paginated render kept beside the continuous\n editing surface (see print-layout.ts / print-layout-view.ts). */\n";
16447
16912
 
16913
+ // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/docx/styles/underline.css?raw
16914
+ var underline_default = `/* Styled runs paint their own underline. Do not superimpose the semantic mark's single line. */
16915
+ .ProseMirror u:has([data-run-props*='"underlineStyle"']) {
16916
+ text-decoration-line: none;
16917
+ }
16918
+ .ProseMirror [data-run-props*='"underlineStyle":"double"'] {
16919
+ --dve-underline-style: double;
16920
+ }
16921
+ .ProseMirror [data-run-props*='"underlineStyle":"dotted"'] {
16922
+ --dve-underline-style: dotted;
16923
+ }
16924
+ .ProseMirror [data-run-props*='"underlineStyle":"dash"'] {
16925
+ --dve-underline-style: dashed;
16926
+ }
16927
+ .ProseMirror [data-run-props*='"underlineStyle":"wave"'] {
16928
+ --dve-underline-style: wavy;
16929
+ }
16930
+ /* Inherited decorations sit inside direct run properties, including ranges sharing one u mark. */
16931
+ .ProseMirror [data-run-props*='"underlineStyle"'] span[style*='text-decoration-line'] {
16932
+ text-decoration-style: var(--dve-underline-style, solid);
16933
+ }
16934
+ `;
16935
+
16448
16936
  // raw-css:/home/runner/work/ooxml/ooxml/src/ui/src/docx/styles/print.css?raw
16449
16937
  var print_default = ".dve-print-pages {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 24px;\n margin: 0 auto;\n zoom: var(--dve-zoom, 1);\n}\n.dve-print-pages[hidden] {\n display: none;\n}\n.dve-print-page {\n position: relative;\n flex: none;\n background: var(--dve-page-color, #fff);\n color: #202124;\n box-shadow:\n 0 1px 3px #20212430,\n 0 4px 12px #20212420;\n overflow: hidden;\n}\n.dve-print-column {\n position: absolute;\n z-index: 1;\n}\n/* Floating pictures sit in front of text unless anchored behind it (wp:anchor/@behindDoc). */\n.dve-print-float {\n z-index: 2;\n}\n.dve-print-float-behind {\n z-index: 0;\n}\n.dve-print-picture-missing {\n box-sizing: border-box;\n border: 1px dashed #8a8886;\n background: #f3f2f1;\n}\n.dve-print-chart {\n overflow: hidden;\n background: #fff;\n}\n.dve-print-chart > svg {\n display: block;\n width: 100%;\n height: 100%;\n}\n.dve-print-chart-frame {\n display: grid;\n place-items: center;\n color: #605e5c;\n font-size: 11px;\n}\n.dve-print-block {\n position: absolute;\n left: 0;\n width: 100%;\n}\n.dve-print-line {\n position: absolute;\n left: 0;\n width: 100%;\n font:\n 11pt/1.2 Calibri,\n Arial,\n sans-serif;\n cursor: text;\n}\n.dve-print-line span {\n position: absolute;\n top: 0;\n}\n.dve-print-row {\n position: absolute;\n left: 0;\n width: 100%;\n}\n/* Cells sit at their grid positions; borders come from the document (Word prints no gridlines). */\n.dve-print-cell {\n position: absolute;\n top: 0;\n box-sizing: border-box;\n}\n.dve-print-cell-content {\n position: absolute;\n bottom: 0;\n}\n@media print {\n .dve-ribbon,\n .dve-status,\n .multilingual-controls {\n display: none !important;\n }\n .dve-canvas {\n overflow: visible !important;\n padding: 0 !important;\n }\n .dve-paper {\n display: none !important;\n }\n .dve-print-pages {\n gap: 0 !important;\n zoom: 1 !important;\n }\n .dve-print-page {\n /* Like Word, page colours are not printed unless asked for. */\n background: #fff !important;\n box-shadow: none !important;\n break-after: page;\n }\n}\n.dve-print-header,\n.dve-print-footer {\n position: absolute;\n color: var(--ink);\n font-size: 11pt;\n pointer-events: none;\n}\n.dve-print-header p,\n.dve-print-footer p {\n margin: 0;\n}\n.dve-print-header table,\n.dve-print-footer table {\n width: 100%;\n border-collapse: collapse;\n}\n\n/* Complex field begin/code/separate/end markers: kept in the document, never shown. */\n.dve-field-marker {\n display: inline;\n font-size: 0;\n}\n\n/* TOC entries link to hidden _Toc bookmarks; Word draws them in the entry's style, not as links. */\na.dve-link[data-anchor^='_Toc'] {\n color: inherit;\n text-decoration: none;\n}\n\n/* Tabs sized to Word's tab stops by tab-stops-view.ts; leaders fill the tab's width. */\n.dve-tab {\n display: inline-block;\n overflow: hidden;\n white-space: pre;\n vertical-align: baseline;\n text-indent: 0;\n}\n.dve-tab-leader-dot,\n.dve-tab-leader-middleDot {\n background-image: radial-gradient(circle, currentColor 0.8px, transparent 1.1px);\n background-size: 4px 4px;\n background-repeat: repeat-x;\n background-position: right 0 bottom 0.25em;\n}\n.dve-tab-leader-hyphen {\n background-image: linear-gradient(to right, currentColor 50%, transparent 50%);\n background-size: 6px 1px;\n background-repeat: repeat-x;\n background-position: right 0 bottom 0.4em;\n}\n.dve-tab-leader-underscore,\n.dve-tab-leader-heavy {\n border-bottom: 1px solid currentColor;\n}\n.dve-tab-leader-heavy {\n border-bottom-width: 2px;\n}\n\n/* Footnotes above the bottom margin; the separator is Word's 2-inch rule. */\n.dve-print-footnotes {\n position: absolute;\n z-index: 1;\n}\n.dve-print-footnote-separator {\n position: absolute;\n top: -7px;\n left: 0;\n width: 192px;\n border-top: 1px solid currentColor;\n}\n.dve-print-footnote-continuation-separator {\n width: 100%;\n}\n.dve-print-footnote {\n position: absolute;\n left: 0;\n width: 100%;\n}\n\n[data-print-view] .dve-notes-container {\n display: none;\n}\n\n/* Word draws superscript and subscript at about two thirds size without growing the line. */\n.dve-paper sup,\n.dve-paper sub {\n font-size: 0.65em;\n line-height: 0;\n}\n/* Exact-height rows clip content that does not fit, as Word does. */\n.dve-print-cell {\n overflow: hidden;\n}\n.dve-print-paragraph-frame {\n position: absolute;\n top: 0;\n box-sizing: border-box;\n}\n\n/* View > Multiple Pages: Print Layout's sheets sit side by side and wrap, instead of one column. */\n.dve-canvas[data-multipage] .dve-print-pages {\n flex-direction: row;\n flex-wrap: wrap;\n align-items: flex-start;\n justify-content: center;\n}\n\n/* Layout > Line Numbers: numbers in the left margin, one per printed line. */\n.dve-print-line-number {\n position: absolute;\n width: 40px;\n text-align: right;\n font:\n 10px 'Segoe UI',\n Arial,\n sans-serif;\n color: #444;\n pointer-events: none;\n}\n\n/* Native input stays mounted while page geometry supplies the visible selection. */\n.dve-paper.dve-print-input {\n position: fixed;\n top: 0;\n left: 0;\n margin: 0;\n opacity: 0;\n max-height: 1px;\n overflow: hidden;\n pointer-events: none;\n z-index: -1;\n}\n.dve-print-pages {\n position: relative;\n}\n.dve-print-selection-layer {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 3;\n}\n.dve-print-caret,\n.dve-print-selection {\n position: absolute;\n pointer-events: none;\n}\n.dve-print-caret {\n background: currentColor;\n animation: dve-print-caret-blink 1s step-end infinite;\n}\n.dve-print-selection {\n background: Highlight;\n opacity: 0.3;\n}\n@keyframes dve-print-caret-blink {\n 50% {\n opacity: 0;\n }\n}\n@media print {\n .dve-print-input,\n .dve-print-selection-layer {\n display: none !important;\n }\n}\n";
16450
16938
 
@@ -16488,7 +16976,9 @@ var editorStyleText = [
16488
16976
  ribbon_styles_group_default,
16489
16977
  ribbon_popups_default,
16490
16978
  ribbon_polish_default,
16979
+ ribbon_home_menus_default,
16491
16980
  document_default,
16981
+ underline_default,
16492
16982
  print_default,
16493
16983
  chrome_titlebar_default,
16494
16984
  backstage_default,
@@ -16976,6 +17466,58 @@ function renderTable(box2, columnWidthPx, renderParagraph) {
16976
17466
  return table;
16977
17467
  }
16978
17468
 
17469
+ // src/docx/print-page-window.ts
17470
+ var PrintPageWindow = class {
17471
+ constructor(root, virtual) {
17472
+ this.root = root;
17473
+ this.virtual = virtual;
17474
+ }
17475
+ root;
17476
+ virtual;
17477
+ pages = [];
17478
+ mounted = /* @__PURE__ */ new Map();
17479
+ revision = 0;
17480
+ add(sheet, draw) {
17481
+ this.pages.push({ sheet, draw });
17482
+ this.root.append(sheet);
17483
+ }
17484
+ mount(index) {
17485
+ if (this.mounted.has(index)) return;
17486
+ const page2 = this.pages[index];
17487
+ if (!page2) return;
17488
+ this.mounted.set(index, page2.draw());
17489
+ this.changed();
17490
+ }
17491
+ unmount(index) {
17492
+ this.mounted.get(index)?.();
17493
+ this.mounted.delete(index);
17494
+ this.pages[index]?.sheet.replaceChildren();
17495
+ this.changed();
17496
+ }
17497
+ changed() {
17498
+ this.root.dataset.renderVersion = String(++this.revision);
17499
+ }
17500
+ show(center, pinned) {
17501
+ if (!this.virtual) return this.all();
17502
+ const first = Math.max(0, center - 2);
17503
+ const last = Math.min(this.pages.length - 1, center + 2);
17504
+ for (const index of this.mounted.keys())
17505
+ if ((index < first || index > last) && index !== pinned) this.unmount(index);
17506
+ for (let index = first; index <= last; index++) this.mount(index);
17507
+ if (pinned !== void 0) this.mount(pinned);
17508
+ }
17509
+ all() {
17510
+ for (let index = 0; index < this.pages.length; index++) this.mount(index);
17511
+ }
17512
+ snapshot(index) {
17513
+ const existed = this.mounted.has(index);
17514
+ this.mount(index);
17515
+ const clone = this.pages[index]?.sheet.cloneNode(true);
17516
+ if (!existed) this.unmount(index);
17517
+ return clone;
17518
+ }
17519
+ };
17520
+
16979
17521
  // src/docx/print-line-numbers.ts
16980
17522
  function lineNumberLabels(page2, settings, counter3, suppressed) {
16981
17523
  const newSection = counter3.section !== page2.sectionIndex;
@@ -17348,7 +17890,8 @@ function footnoteArea(page2, hitboxes, pictureUrl) {
17348
17890
  function renderPrintLayout(result, pictureUrl, options = {}) {
17349
17891
  const container = document.createElement("div");
17350
17892
  container.className = "dve-print-pages";
17351
- const hitboxes = [];
17893
+ const hits = /* @__PURE__ */ new Map();
17894
+ const window2 = new PrintPageWindow(container, options.virtual ?? false);
17352
17895
  const counter3 = { next: 1, section: -1 };
17353
17896
  result.pages.forEach((page2, pageIndex) => {
17354
17897
  const sheet = document.createElement("div");
@@ -17356,33 +17899,7 @@ function renderPrintLayout(result, pictureUrl, options = {}) {
17356
17899
  sheet.dataset.pageIndex = String(pageIndex);
17357
17900
  sheet.style.width = `${page2.widthPx}px`;
17358
17901
  sheet.style.height = `${page2.heightPx}px`;
17359
- const borders = options.pageBorders?.[page2.sectionIndex];
17360
- const borderBox = borders ? pageBorderBox(page2, borders) : null;
17361
- if (borderBox) sheet.append(borderBox);
17362
- for (const column of page2.columns) {
17363
- const previous = page2.columns[page2.columns.indexOf(column) - 1];
17364
- if ((column.separator ?? page2.columnSeparator) && previous && (previous.sectionIndex ?? page2.sectionIndex) === (column.sectionIndex ?? page2.sectionIndex)) {
17365
- const rule = document.createElement("div");
17366
- rule.className = "dve-print-column-rule";
17367
- Object.assign(rule.style, {
17368
- position: "absolute",
17369
- left: `${page2.marginLeftPx + (previous.xPx + previous.widthPx + column.xPx) / 2}px`,
17370
- top: `${page2.marginTopPx + (column.startYPx ?? 0)}px`,
17371
- height: `${(column.endYPx ?? page2.heightPx - page2.marginTopPx - page2.marginBottomPx) - (column.startYPx ?? 0)}px`,
17372
- borderLeft: "1px solid currentColor"
17373
- });
17374
- sheet.append(rule);
17375
- }
17376
- const columnEl = document.createElement("div");
17377
- columnEl.className = "dve-print-column";
17378
- columnEl.style.top = `${page2.marginTopPx}px`;
17379
- columnEl.style.left = `${page2.marginLeftPx + column.xPx}px`;
17380
- columnEl.style.width = `${column.widthPx}px`;
17381
- columnEl.style.height = `${page2.heightPx - page2.marginTopPx - page2.marginBottomPx}px`;
17382
- for (const block of column.blocks)
17383
- columnEl.append(renderBlock(block, hitboxes, pictureUrl, column.widthPx));
17384
- sheet.append(columnEl);
17385
- }
17902
+ const labels3 = [];
17386
17903
  const bands = /* @__PURE__ */ new Map();
17387
17904
  for (const column of page2.columns) {
17388
17905
  const index = column.sectionIndex ?? page2.sectionIndex;
@@ -17393,7 +17910,7 @@ function renderPrintLayout(result, pictureUrl, options = {}) {
17393
17910
  for (const [sectionIndex, columns] of bands) {
17394
17911
  const numbering = options.lineNumbers?.[sectionIndex];
17395
17912
  if (numbering)
17396
- sheet.append(
17913
+ labels3.push(
17397
17914
  ...lineNumberLabels(
17398
17915
  { ...page2, sectionIndex, columns },
17399
17916
  numbering,
@@ -17402,22 +17919,62 @@ function renderPrintLayout(result, pictureUrl, options = {}) {
17402
17919
  )
17403
17920
  );
17404
17921
  }
17405
- if (page2.footnotes?.length) sheet.append(footnoteArea(page2, hitboxes, pictureUrl));
17406
- for (const float of page2.floats ?? []) {
17407
- const picture = pictureElement(float, pictureUrl);
17408
- picture.classList.add("dve-print-float");
17409
- if (float.behindText) picture.classList.add("dve-print-float-behind");
17410
- picture.style.left = `${float.xPx}px`;
17411
- picture.style.top = `${float.yPx}px`;
17412
- sheet.append(picture);
17413
- }
17414
- container.append(sheet);
17922
+ window2.add(sheet, () => {
17923
+ const hitboxes = [];
17924
+ const borders = options.pageBorders?.[page2.sectionIndex];
17925
+ const borderBox = borders ? pageBorderBox(page2, borders) : null;
17926
+ if (borderBox) sheet.append(borderBox);
17927
+ for (const column of page2.columns) {
17928
+ const previous = page2.columns[page2.columns.indexOf(column) - 1];
17929
+ if ((column.separator ?? page2.columnSeparator) && previous && (previous.sectionIndex ?? page2.sectionIndex) === (column.sectionIndex ?? page2.sectionIndex)) {
17930
+ const rule = document.createElement("div");
17931
+ rule.className = "dve-print-column-rule";
17932
+ Object.assign(rule.style, {
17933
+ position: "absolute",
17934
+ left: `${page2.marginLeftPx + (previous.xPx + previous.widthPx + column.xPx) / 2}px`,
17935
+ top: `${page2.marginTopPx + (column.startYPx ?? 0)}px`,
17936
+ height: `${(column.endYPx ?? page2.heightPx - page2.marginTopPx - page2.marginBottomPx) - (column.startYPx ?? 0)}px`,
17937
+ borderLeft: "1px solid currentColor"
17938
+ });
17939
+ sheet.append(rule);
17940
+ }
17941
+ const columnEl = document.createElement("div");
17942
+ columnEl.className = "dve-print-column";
17943
+ columnEl.style.top = `${page2.marginTopPx}px`;
17944
+ columnEl.style.left = `${page2.marginLeftPx + column.xPx}px`;
17945
+ columnEl.style.width = `${column.widthPx}px`;
17946
+ columnEl.style.height = `${page2.heightPx - page2.marginTopPx - page2.marginBottomPx}px`;
17947
+ for (const block of column.blocks)
17948
+ columnEl.append(renderBlock(block, hitboxes, pictureUrl, column.widthPx));
17949
+ sheet.append(columnEl);
17950
+ }
17951
+ if (page2.footnotes?.length) sheet.append(footnoteArea(page2, hitboxes, pictureUrl));
17952
+ for (const float of page2.floats ?? []) {
17953
+ const picture = pictureElement(float, pictureUrl);
17954
+ picture.classList.add("dve-print-float");
17955
+ if (float.behindText) picture.classList.add("dve-print-float-behind");
17956
+ picture.style.left = `${float.xPx}px`;
17957
+ picture.style.top = `${float.yPx}px`;
17958
+ sheet.append(picture);
17959
+ }
17960
+ sheet.append(...labels3);
17961
+ options.onPageRender?.(sheet, pageIndex);
17962
+ for (const hit of hitboxes) hits.set(hit.element, hit);
17963
+ return () => {
17964
+ for (const hit of hitboxes) hits.delete(hit.element);
17965
+ };
17966
+ });
17415
17967
  });
17968
+ window2.show(0);
17416
17969
  return {
17417
17970
  element: container,
17418
17971
  pageCount: result.pages.length,
17972
+ showPages: (center, pinned) => window2.show(center, pinned),
17973
+ renderAll: () => window2.all(),
17974
+ pageSnapshot: (index) => window2.snapshot(index),
17419
17975
  resolveClick(target, clientX) {
17420
- const hit = hitboxes.find((box2) => box2.element === target || box2.element.contains(target));
17976
+ const line = target.closest(".dve-print-line");
17977
+ const hit = line ? hits.get(line) : void 0;
17421
17978
  if (!hit) return null;
17422
17979
  const precise = resolveFragmentClick(hit.fragments, clientX);
17423
17980
  if (precise !== void 0) return { blockId: hit.blockId, offset: precise };
@@ -17430,15 +17987,50 @@ function renderPrintLayout(result, pictureUrl, options = {}) {
17430
17987
  };
17431
17988
  }
17432
17989
 
17990
+ // src/docx/print-block-pages.ts
17991
+ function printBlockPages(result) {
17992
+ const ranges = /* @__PURE__ */ new Map();
17993
+ function visit(block, page2) {
17994
+ if (block.kind === "table") {
17995
+ for (const row3 of block.rows)
17996
+ for (const cell of row3.cells) for (const paragraph2 of cell) visit(paragraph2, page2);
17997
+ return;
17998
+ }
17999
+ const first = block.lines[0];
18000
+ const last = block.lines.at(-1);
18001
+ if (!first || !last) return;
18002
+ const list = ranges.get(block.blockId) ?? [];
18003
+ list.push({ page: page2, from: first.sourceStart, to: last.sourceEnd });
18004
+ ranges.set(block.blockId, list);
18005
+ }
18006
+ result.pages.forEach((page2, index) => {
18007
+ for (const column of page2.columns) for (const block of column.blocks) visit(block, index);
18008
+ });
18009
+ return (id, offset) => {
18010
+ const list = ranges.get(id);
18011
+ for (let index = (list?.length ?? 0) - 1; index >= 0; index--) {
18012
+ const range = list[index];
18013
+ if (offset >= range.from && offset <= range.to) return range.page;
18014
+ }
18015
+ return void 0;
18016
+ };
18017
+ }
18018
+
17433
18019
  // src/docx/print-layout-view.ts
17434
- var RELAYOUT_DEBOUNCE_MS = 150;
18020
+ var RELAYOUT_DELAY_MS = 32;
17435
18021
  function createPrintLayoutController(scrollContainer, onRequestCursor, pictureUrl, onLayout, getReviewMode = () => "all", isEditing = () => false) {
17436
- let measurer = createCanvasMeasurer();
18022
+ let session = createLayoutSession(createCanvasMeasurer());
17437
18023
  let lastModel = null;
17438
18024
  const fonts = typeof document === "undefined" ? void 0 : document.fonts;
17439
18025
  const onFontsLoaded = () => {
17440
- measurer = createCanvasMeasurer();
17441
- if (lastModel && !element.hidden) relayout(lastModel);
18026
+ session = createLayoutSession(createCanvasMeasurer());
18027
+ const model = pendingModel ?? lastModel;
18028
+ if (model && !element.hidden) {
18029
+ if (timer) clearTimeout(timer);
18030
+ timer = void 0;
18031
+ pendingModel = null;
18032
+ relayout(model);
18033
+ }
17442
18034
  };
17443
18035
  fonts?.addEventListener?.("loadingdone", onFontsLoaded);
17444
18036
  const element = document.createElement("div");
@@ -17449,13 +18041,25 @@ function createPrintLayoutController(scrollContainer, onRequestCursor, pictureUr
17449
18041
  let currentPage = 1;
17450
18042
  let version = 0;
17451
18043
  let timer;
18044
+ let pendingModel = null;
17452
18045
  let projectionWarnings = [];
18046
+ let pageSheets = [];
18047
+ let pageForBlock;
18048
+ let pinnedPage;
18049
+ const beforePrint = () => {
18050
+ if (element.isConnected && !element.hidden) handle?.renderAll();
18051
+ };
18052
+ const afterPrint = () => {
18053
+ if (element.isConnected && !element.hidden) handle?.showPages(currentPage - 1, pinnedPage);
18054
+ };
18055
+ window.addEventListener("beforeprint", beforePrint);
18056
+ window.addEventListener("afterprint", afterPrint);
17453
18057
  function relayout(model) {
17454
18058
  lastModel = model;
17455
18059
  const mode = getReviewMode();
17456
18060
  const projected = reviewDocumentFormatting(model, mode);
17457
18061
  model = projected.model;
17458
- result = layoutDocumentModel(model, measurer, { reviewDisplayMode: mode });
18062
+ result = session.layoutDocumentModel(model, { reviewDisplayMode: mode });
17459
18063
  projectionWarnings = reviewFormattingWarnings(projected.diagnostics);
17460
18064
  if (mode !== "all" && listRevisions(model).some(
17461
18065
  (revision) => revision.runIndex === void 0 && revision.kind !== "paragraphChange"
@@ -17464,7 +18068,16 @@ function createPrintLayoutController(scrollContainer, onRequestCursor, pictureUr
17464
18068
  "Print Layout retains revised paragraph marks; merging them for review display is not yet supported."
17465
18069
  );
17466
18070
  result.approximations = [.../* @__PURE__ */ new Set([...result.approximations, ...projectionWarnings])];
18071
+ const pages = result.pages;
18072
+ pageForBlock = printBlockPages(result);
18073
+ pinnedPage = void 0;
17467
18074
  handle = renderPrintLayout(result, pictureUrl, {
18075
+ virtual: pages.length > 10,
18076
+ onPageRender(sheet, index) {
18077
+ const sheets = [];
18078
+ sheets[index] = sheet;
18079
+ decoratePages(model, pages, sheets, /* @__PURE__ */ new Date(), pictureUrl, mode);
18080
+ },
17468
18081
  lineNumbers: (model.sections ?? []).map((section3) => section3.lineNumberSettings),
17469
18082
  pageBorders: (model.sections ?? []).map((section3) => section3.pageBorders),
17470
18083
  suppressedLineNumberParagraphs: new Set(
@@ -17475,15 +18088,8 @@ function createPrintLayoutController(scrollContainer, onRequestCursor, pictureUr
17475
18088
  });
17476
18089
  if (model.pageColor)
17477
18090
  handle.element.style.setProperty("--dve-page-color", `#${model.pageColor}`);
17478
- decoratePages(
17479
- model,
17480
- result.pages,
17481
- [...handle.element.querySelectorAll(".dve-print-page")],
17482
- /* @__PURE__ */ new Date(),
17483
- pictureUrl,
17484
- mode
17485
- );
17486
18091
  element.replaceChildren(handle.element);
18092
+ pageSheets = [...handle.element.querySelectorAll(".dve-print-page")];
17487
18093
  version++;
17488
18094
  refreshCurrentPage();
17489
18095
  onLayout?.();
@@ -17495,43 +18101,61 @@ function createPrintLayoutController(scrollContainer, onRequestCursor, pictureUr
17495
18101
  }
17496
18102
  element.addEventListener("click", onClick);
17497
18103
  function refreshCurrentPage() {
17498
- const pages = element.querySelectorAll(".dve-print-page");
18104
+ const pages = pageSheets;
17499
18105
  if (!pages.length) {
17500
18106
  currentPage = 1;
17501
18107
  return;
17502
18108
  }
17503
18109
  const anchor = scrollContainer.getBoundingClientRect().top + scrollContainer.clientHeight / 3;
17504
- let closest = 0;
17505
- let closestDistance = Number.POSITIVE_INFINITY;
17506
- pages.forEach((page2, index) => {
17507
- const distance = Math.abs(page2.getBoundingClientRect().top - anchor);
17508
- if (distance < closestDistance) {
17509
- closestDistance = distance;
17510
- closest = index;
17511
- }
17512
- });
17513
- currentPage = closest + 1;
18110
+ let low = 0;
18111
+ let high = pages.length;
18112
+ while (low < high) {
18113
+ const mid = low + high >>> 1;
18114
+ if (pages[mid].getBoundingClientRect().top < anchor) low = mid + 1;
18115
+ else high = mid;
18116
+ }
18117
+ const after = Math.min(low, pages.length - 1);
18118
+ const before = Math.max(0, after - 1);
18119
+ currentPage = (Math.abs(pages[before].getBoundingClientRect().top - anchor) <= Math.abs(pages[after].getBoundingClientRect().top - anchor) ? before : after) + 1;
18120
+ handle?.showPages(currentPage - 1, pinnedPage);
17514
18121
  }
17515
18122
  return {
17516
18123
  element,
17517
18124
  resolveClick: (target, clientX) => handle?.resolveClick(target, clientX) ?? null,
17518
18125
  setActive(active) {
17519
18126
  element.hidden = !active;
18127
+ if (!active) {
18128
+ if (timer) clearTimeout(timer);
18129
+ timer = void 0;
18130
+ pendingModel = null;
18131
+ }
17520
18132
  },
17521
18133
  scheduleRelayout(model) {
17522
- if (timer) clearTimeout(timer);
17523
- timer = setTimeout(() => relayout(model), RELAYOUT_DEBOUNCE_MS);
18134
+ pendingModel = model;
18135
+ if (timer !== void 0) return;
18136
+ timer = setTimeout(() => {
18137
+ timer = void 0;
18138
+ const latest = pendingModel;
18139
+ pendingModel = null;
18140
+ if (latest) relayout(latest);
18141
+ }, RELAYOUT_DELAY_MS);
17524
18142
  },
17525
18143
  refreshCurrentPage,
17526
18144
  pageStatus() {
17527
18145
  return result ? { current: currentPage, total: result.pages.length } : null;
17528
18146
  },
17529
- pageElements: () => [...element.querySelectorAll(".dve-print-page")],
18147
+ pageElements: () => [...pageSheets],
18148
+ pageSnapshot: (index) => handle?.pageSnapshot(index),
18149
+ ensureBlock(id, offset) {
18150
+ pinnedPage = pageForBlock?.(id, offset);
18151
+ handle?.showPages(currentPage - 1, pinnedPage);
18152
+ },
17530
18153
  layoutVersion: () => version,
17531
18154
  scrollToPage(page2) {
17532
- const target = element.querySelectorAll(".dve-print-page")[page2 - 1];
18155
+ const target = pageSheets[page2 - 1];
17533
18156
  if (!target) return;
17534
18157
  currentPage = page2;
18158
+ handle?.showPages(page2 - 1, pinnedPage);
17535
18159
  target.scrollIntoView?.({ block: "start" });
17536
18160
  },
17537
18161
  approximations() {
@@ -17539,15 +18163,25 @@ function createPrintLayoutController(scrollContainer, onRequestCursor, pictureUr
17539
18163
  },
17540
18164
  print(model, note) {
17541
18165
  if (timer) clearTimeout(timer);
18166
+ timer = void 0;
18167
+ pendingModel = null;
17542
18168
  relayout(model);
17543
18169
  for (const warning of projectionWarnings) note(warning);
17544
- printLayoutResult(result, note);
18170
+ handle?.renderAll();
18171
+ try {
18172
+ printLayoutResult(result, note);
18173
+ } finally {
18174
+ handle?.showPages(currentPage - 1, pinnedPage);
18175
+ }
17545
18176
  },
17546
18177
  destroy() {
17547
18178
  if (timer) clearTimeout(timer);
17548
18179
  element.removeEventListener("click", onClick);
17549
18180
  fonts?.removeEventListener?.("loadingdone", onFontsLoaded);
18181
+ window.removeEventListener("beforeprint", beforePrint);
18182
+ window.removeEventListener("afterprint", afterPrint);
17550
18183
  lastModel = null;
18184
+ pendingModel = null;
17551
18185
  }
17552
18186
  };
17553
18187
  }
@@ -17745,6 +18379,7 @@ var PrintEditingIndex = class {
17745
18379
  pages;
17746
18380
  doc;
17747
18381
  root;
18382
+ renderVersion;
17748
18383
  paragraphs = [];
17749
18384
  positions = /* @__PURE__ */ new Map();
17750
18385
  byBlock = /* @__PURE__ */ new Map();
@@ -17771,12 +18406,15 @@ var PrintEditingIndex = class {
17771
18406
  });
17772
18407
  }
17773
18408
  const root = this.pages.firstElementChild;
17774
- if (!documentChanged && root === this.root) return;
17775
- if (root !== this.root)
18409
+ const renderVersion = root?.dataset.renderVersion;
18410
+ const renderChanged = root !== this.root || renderVersion !== this.renderVersion;
18411
+ if (!documentChanged && !renderChanged) return;
18412
+ if (renderChanged)
17776
18413
  this.renderedLines = [
17777
18414
  ...this.pages.querySelectorAll(".dve-print-line[data-block-id]")
17778
18415
  ];
17779
18416
  this.root = root;
18417
+ this.renderVersion = renderVersion;
17780
18418
  this.byBlock.clear();
17781
18419
  this.pageLines.clear();
17782
18420
  this.linePositions.clear();
@@ -17866,7 +18504,7 @@ var PrintEditingIndex = class {
17866
18504
  this.linePositions.clear();
17867
18505
  }
17868
18506
  };
17869
- function createPrintEditingBridge(pages, paper, getView, resolveHit, scrollContainer = pages.parentElement ?? pages) {
18507
+ function createPrintEditingBridge(pages, paper, getView, resolveHit, scrollContainer = pages.parentElement ?? pages, ensureBlock) {
17870
18508
  let active = false;
17871
18509
  let destroyed = false;
17872
18510
  const listeners = new AbortController();
@@ -17914,6 +18552,9 @@ function createPrintEditingBridge(pages, paper, getView, resolveHit, scrollConta
17914
18552
  view.dom.dataset.printTabstop = "true";
17915
18553
  }
17916
18554
  const { from: from2, to, head, empty } = view.state.selection;
18555
+ const { $head } = view.state.selection;
18556
+ if ($head.parent.type.name === "paragraph")
18557
+ ensureBlock?.(String($head.parent.attrs.id), $head.parentOffset);
17917
18558
  let caret;
17918
18559
  index.update(view.state.doc);
17919
18560
  for (const { id, start, end } of index.between(from2, to)) {
@@ -19401,12 +20042,12 @@ async function clipboardReadAllowed() {
19401
20042
  return true;
19402
20043
  }
19403
20044
  }
19404
- async function pasteClipboard(view) {
20045
+ async function pasteClipboard(view, plainText2 = false) {
19405
20046
  if (!view.editable) return false;
19406
20047
  try {
19407
20048
  if (navigator.clipboard.read) {
19408
20049
  const items = await navigator.clipboard.read();
19409
- for (const item of items) {
20050
+ for (const item of plainText2 ? [] : items) {
19410
20051
  if (!item.types.includes("text/html")) continue;
19411
20052
  const html = await (await item.getType("text/html")).text();
19412
20053
  if (!view.editable || view.isDestroyed) return false;
@@ -19847,17 +20488,28 @@ function withHeaderFooterDistance(model, index, kind, inches3) {
19847
20488
  }
19848
20489
  function routeRibbonAction(core, action) {
19849
20490
  const { inserts, pages, parts, shell } = core;
20491
+ if (action.type === "help") {
20492
+ core.element.ownerDocument.defaultView?.open(
20493
+ "https://christophervr.github.io/ooxml/docx/",
20494
+ "_blank",
20495
+ "noopener,noreferrer"
20496
+ );
20497
+ return;
20498
+ }
19850
20499
  if (inserts.handle(action)) return;
19851
20500
  const target = core.targetView();
19852
20501
  if (action.type === "history" && parts.usesBodyHistory()) {
19853
20502
  parts.runHistory(action.key);
19854
20503
  } else if (action.type === "clipboard") {
19855
20504
  if (!target || action.key !== "copy" && !target.editable) return;
19856
- const done = action.key === "paste" ? pasteClipboard(target) : copyOrCut(target, action.key);
20505
+ const done = action.key === "paste" ? pasteClipboard(target, action.plainText) : copyOrCut(target, action.key);
19857
20506
  void done.then((ok) => {
19858
20507
  if (!ok)
19859
20508
  emit(core.element, "document-warning", translate(core.locale, "menu.clipboardDenied"));
19860
20509
  });
20510
+ } else if (action.type === "underline" && target?.editable && !core.readOnly) {
20511
+ applyFontFormat(target, { underline: action.value });
20512
+ focusView(target);
19861
20513
  } else if (action.type === "search") shell.searchPanel?.open(action.focus);
19862
20514
  else if (action.type === "selectAll" && target) {
19863
20515
  selectAll(target.state, target.dispatch);
@@ -20136,7 +20788,7 @@ var PageNavigator = class {
20136
20788
  }
20137
20789
  draw({ sheet, source, scale }) {
20138
20790
  if (sheet.firstChild) return;
20139
- const clone = source.cloneNode(true);
20791
+ const clone = this.host.pageSnapshot?.(Number(source.dataset.pageIndex)) ?? source.cloneNode(true);
20140
20792
  clone.removeAttribute("id");
20141
20793
  for (const node of clone.querySelectorAll("[id]")) node.removeAttribute("id");
20142
20794
  clone.inert = true;
@@ -20769,7 +21421,7 @@ function createChrome(core, api) {
20769
21421
  ribbon: () => shell.toolbar,
20770
21422
  wordCount: () => {
20771
21423
  const doc = core.view?.state.doc;
20772
- return doc ? countWords(doc.textBetween(0, doc.content.size, " ").trim(), element.lang || void 0) : 0;
21424
+ return doc ? documentWordCount(doc, element.lang || void 0) : 0;
20773
21425
  },
20774
21426
  locale: () => core.locale,
20775
21427
  readOnly: () => core.readOnly,
@@ -20854,15 +21506,23 @@ function buildShell(core, api) {
20854
21506
  () => core.pages.viewMode === "print"
20855
21507
  );
20856
21508
  canvas.append(printLayout.element);
20857
- canvas.addEventListener("scroll", () => {
20858
- if (core.pages.viewMode === "print") {
20859
- printLayout.refreshCurrentPage();
20860
- core.refreshControls();
20861
- }
20862
- });
21509
+ let scrollFrame;
21510
+ canvas.addEventListener(
21511
+ "scroll",
21512
+ () => {
21513
+ if (core.pages.viewMode !== "print" || scrollFrame !== void 0) return;
21514
+ scrollFrame = requestAnimationFrame(() => {
21515
+ scrollFrame = void 0;
21516
+ if (!element.isConnected || core.pages.viewMode !== "print") return;
21517
+ core.refreshPageStatus();
21518
+ });
21519
+ },
21520
+ { passive: true }
21521
+ );
20863
21522
  shell.printLayout = printLayout;
20864
21523
  shell.navigator = new PageNavigator({
20865
21524
  pages: () => printLayout.pageElements(),
21525
+ pageSnapshot: (index) => printLayout.pageSnapshot(index),
20866
21526
  version: () => printLayout.layoutVersion(),
20867
21527
  goTo: (page2) => {
20868
21528
  printLayout.scrollToPage(page2);
@@ -20950,7 +21610,8 @@ function attachPrintEditing(core) {
20950
21610
  shell.paper,
20951
21611
  () => core.view,
20952
21612
  shell.printLayout.resolveClick,
20953
- shell.canvas
21613
+ shell.canvas,
21614
+ (id, offset) => shell.printLayout?.ensureBlock(id, offset)
20954
21615
  );
20955
21616
  shell.printEditing.setActive(core.pages.viewMode === "print");
20956
21617
  }