docxodus 10.0.0 → 12.0.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 (113) hide show
  1. package/README.md +7 -5
  2. package/dist/docxodus.worker.js +6 -38
  3. package/dist/docxodus.worker.js.map +1 -1
  4. package/dist/editor-comments.d.ts +129 -0
  5. package/dist/editor-comments.d.ts.map +1 -0
  6. package/dist/editor-comments.js +805 -0
  7. package/dist/editor-comments.js.map +1 -0
  8. package/dist/editor-headerfooter.d.ts +125 -85
  9. package/dist/editor-headerfooter.d.ts.map +1 -1
  10. package/dist/editor-headerfooter.js +572 -306
  11. package/dist/editor-headerfooter.js.map +1 -1
  12. package/dist/editor-image-patch.d.ts +38 -0
  13. package/dist/editor-image-patch.d.ts.map +1 -0
  14. package/dist/editor-image-patch.js +101 -0
  15. package/dist/editor-image-patch.js.map +1 -0
  16. package/dist/editor.bundle.js +3815 -778
  17. package/dist/editor.d.ts +237 -5
  18. package/dist/editor.d.ts.map +1 -1
  19. package/dist/editor.js +1063 -112
  20. package/dist/editor.js.map +1 -1
  21. package/dist/embed.bundle.js +3911 -849
  22. package/dist/embed.d.ts +1 -1
  23. package/dist/embed.d.ts.map +1 -1
  24. package/dist/embed.iife.js +3899 -837
  25. package/dist/embed.js +10 -1
  26. package/dist/embed.js.map +1 -1
  27. package/dist/export-assets.json +40 -46
  28. package/dist/export-browser.bundle.js +27 -3
  29. package/dist/index.d.ts +28 -64
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +68 -134
  32. package/dist/index.js.map +1 -1
  33. package/dist/pagination.bundle.js +25 -0
  34. package/dist/pagination.d.ts +6 -0
  35. package/dist/pagination.d.ts.map +1 -1
  36. package/dist/pagination.js +19 -0
  37. package/dist/pagination.js.map +1 -1
  38. package/dist/react.d.ts +4 -4
  39. package/dist/react.d.ts.map +1 -1
  40. package/dist/react.js.map +1 -1
  41. package/dist/ribbon-chrome.d.ts +20 -4
  42. package/dist/ribbon-chrome.d.ts.map +1 -1
  43. package/dist/ribbon-chrome.js +575 -184
  44. package/dist/ribbon-chrome.js.map +1 -1
  45. package/dist/ribbon.d.ts +5 -4
  46. package/dist/ribbon.d.ts.map +1 -1
  47. package/dist/ribbon.js +978 -129
  48. package/dist/ribbon.js.map +1 -1
  49. package/dist/session.bundle.js +100 -3
  50. package/dist/session.d.ts +60 -4
  51. package/dist/session.d.ts.map +1 -1
  52. package/dist/session.js +75 -3
  53. package/dist/session.js.map +1 -1
  54. package/dist/types.d.ts +194 -227
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/types.js +9 -75
  57. package/dist/types.js.map +1 -1
  58. package/dist/viewport.d.ts +7 -0
  59. package/dist/viewport.d.ts.map +1 -1
  60. package/dist/viewport.js +12 -0
  61. package/dist/viewport.js.map +1 -1
  62. package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm +0 -0
  63. package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm.br +0 -0
  64. package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm +0 -0
  65. package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm.br +0 -0
  66. package/dist/wasm/_framework/Docxodus.wasm +0 -0
  67. package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
  68. package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
  69. package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
  70. package/dist/wasm/_framework/System.Collections.Concurrent.wasm +0 -0
  71. package/dist/wasm/_framework/System.Collections.Concurrent.wasm.br +0 -0
  72. package/dist/wasm/_framework/System.ComponentModel.Primitives.wasm +0 -0
  73. package/dist/wasm/_framework/System.ComponentModel.Primitives.wasm.br +0 -0
  74. package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
  75. package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
  76. package/dist/wasm/_framework/System.IO.Packaging.wasm +0 -0
  77. package/dist/wasm/_framework/System.IO.Packaging.wasm.br +0 -0
  78. package/dist/wasm/_framework/System.IO.Pipelines.wasm +0 -0
  79. package/dist/wasm/_framework/System.IO.Pipelines.wasm.br +0 -0
  80. package/dist/wasm/_framework/System.Linq.wasm +0 -0
  81. package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
  82. package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
  83. package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
  84. package/dist/wasm/_framework/System.Private.Uri.wasm +0 -0
  85. package/dist/wasm/_framework/System.Private.Uri.wasm.br +0 -0
  86. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
  87. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
  88. package/dist/wasm/_framework/System.Private.Xml.wasm +0 -0
  89. package/dist/wasm/_framework/System.Private.Xml.wasm.br +0 -0
  90. package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm +0 -0
  91. package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm.br +0 -0
  92. package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
  93. package/dist/wasm/_framework/System.Security.Cryptography.wasm.br +0 -0
  94. package/dist/wasm/_framework/System.Text.Encodings.Web.wasm +0 -0
  95. package/dist/wasm/_framework/System.Text.Encodings.Web.wasm.br +0 -0
  96. package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
  97. package/dist/wasm/_framework/System.Text.Json.wasm.br +0 -0
  98. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
  99. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
  100. package/dist/wasm/_framework/dotnet.boot.js +31 -37
  101. package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
  102. package/dist/wasm/_framework/dotnet.native.js +39 -3
  103. package/dist/wasm/_framework/dotnet.native.js.br +0 -0
  104. package/dist/wasm/_framework/dotnet.native.wasm +0 -0
  105. package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
  106. package/dist/worker-proxy.bundle.js +2 -3
  107. package/dist/worker-proxy.d.ts +2 -2
  108. package/dist/worker-proxy.d.ts.map +1 -1
  109. package/dist/worker-proxy.js +1 -2
  110. package/dist/worker-proxy.js.map +1 -1
  111. package/package.json +3 -3
  112. package/dist/wasm/_framework/System.Diagnostics.Process.wasm +0 -0
  113. package/dist/wasm/_framework/System.Diagnostics.Process.wasm.br +0 -0
@@ -14,6 +14,12 @@
14
14
  * `data-dxr-list` does the same job for the `<input list>` → `<datalist id>`
15
15
  * link, which is by-id and cannot use a data attribute directly.
16
16
  *
17
+ * ── Shape ───────────────────────────────────────────────────────────────────
18
+ * Word's: a title bar, a tab strip (Home · Insert · Layout · References ·
19
+ * Review · View, plus the contextual Table and Header & Footer tabs), the panel
20
+ * for the active tab, the document with its comment gutter, and a status bar
21
+ * that carries the anchor rail, the page/word counts and the zoom control.
22
+ *
17
23
  * ── Responsiveness ──────────────────────────────────────────────────────────
18
24
  * Layout keys off `data-chrome` on the root ("full" | "compact"), which
19
25
  * `ribbon.ts` sets from a ResizeObserver on the ROOT rather than from viewport
@@ -22,8 +28,10 @@
22
28
  * `[data-chrome="full"]` adds back the desktop affordances.
23
29
  */
24
30
  /** Bumped whenever RIBBON_CSS changes, so a stale injected stylesheet is replaced. */
25
- export const RIBBON_STYLE_VERSION = "8";
31
+ export const RIBBON_STYLE_VERSION = "10";
26
32
  export const RIBBON_STYLE_ATTR = "data-docxodus-ribbon-styles";
33
+ /** Width of the comment gutter the surface reserves beside the sheet, in px. */
34
+ export const COMMENT_GUTTER_WIDTH = 264;
27
35
  /**
28
36
  * The ribbon stylesheet. Every rule is scoped under `.dxr` so injecting it is
29
37
  * safe on a host page we do not control — the same guarantee `embed.ts` gives
@@ -51,7 +59,7 @@ export const RIBBON_CSS = `
51
59
  --dxr-data: #0f766e;
52
60
  --dxr-danger: #dc2626;
53
61
  --dxr-danger-wash: #fef2f2;
54
- --dxr-desk: #f1f5f9;
62
+ --dxr-desk: #eef2f6;
55
63
  --dxr-ui: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
56
64
  --dxr-serif: Georgia, "Times New Roman", serif;
57
65
  --dxr-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;
@@ -60,7 +68,8 @@ export const RIBBON_CSS = `
60
68
  --dxr-shadow-lg: 0 4px 6px rgba(15, 23, 42, .05), 0 10px 15px rgba(15, 23, 42, .04);
61
69
  --dxr-shadow-xl: 0 8px 16px rgba(15, 23, 42, .06), 0 20px 25px rgba(15, 23, 42, .05);
62
70
  --dxr-ease: cubic-bezier(.4, 0, .2, 1);
63
- --dxr-tap: 30px;
71
+ --dxr-tap: 28px;
72
+ --dxr-gutter: ${COMMENT_GUTTER_WIDTH}px;
64
73
 
65
74
  position: relative;
66
75
  display: flex;
@@ -91,6 +100,27 @@ export const RIBBON_CSS = `
91
100
  /* Touch devices get bigger hit targets everywhere the token is used. */
92
101
  @media (pointer: coarse) { .dxr { --dxr-tap: 40px; } }
93
102
 
103
+ /* ── Scroll-edge affordances ──────────────────────────────────────────────────
104
+ The chrome's strips (title bar, tabs, panels, rail) scroll horizontally
105
+ rather than squashing or wrapping — the compact rule is "no command is
106
+ removed, the strip scrolls". But a hard clip mid-control reads as a broken
107
+ layout, especially on a phone, so ribbon.ts measures every strip and stamps
108
+ data-fade with the edges that hide more content ("l", "r", or both). The
109
+ mask dissolves content at exactly those edges — the horizontal twin of
110
+ .dxr-scroll's gradient veils — and lifts entirely once the strip fits, so
111
+ nothing is ever faded unless there is more behind it. */
112
+ .dxr-titlebar, .dxr-tabs, .dxr-panel, .dxr-rail, .dxr-findbar { --dxr-fade-l: 0px; --dxr-fade-r: 0px; }
113
+ .dxr [data-fade] {
114
+ -webkit-mask-image: linear-gradient(to right,
115
+ transparent, #000 var(--dxr-fade-l),
116
+ #000 calc(100% - var(--dxr-fade-r)), transparent);
117
+ mask-image: linear-gradient(to right,
118
+ transparent, #000 var(--dxr-fade-l),
119
+ #000 calc(100% - var(--dxr-fade-r)), transparent);
120
+ }
121
+ .dxr [data-fade~="l"] { --dxr-fade-l: 28px; }
122
+ .dxr [data-fade~="r"] { --dxr-fade-r: 28px; }
123
+
94
124
  /* ── Chrome shell ─────────────────────────────────────────────────────────── */
95
125
  .dxr-chrome {
96
126
  position: sticky;
@@ -101,12 +131,17 @@ export const RIBBON_CSS = `
101
131
  border-bottom: 1px solid var(--dxr-rule);
102
132
  }
103
133
 
134
+ /* Scrolls in EVERY density (compact just tightens it): the fade affordance
135
+ marks any overflowing strip as scrollable, so it must actually be. */
104
136
  .dxr-titlebar {
105
137
  display: flex;
106
138
  align-items: center;
107
139
  gap: 10px;
108
140
  padding: 6px 10px 0;
141
+ overflow-x: auto;
142
+ scrollbar-width: none;
109
143
  }
144
+ .dxr-titlebar::-webkit-scrollbar { display: none; }
110
145
  .dxr-brand {
111
146
  display: flex;
112
147
  align-items: baseline;
@@ -149,7 +184,7 @@ export const RIBBON_CSS = `
149
184
  .dxr-tabs {
150
185
  display: flex;
151
186
  gap: 1px;
152
- padding: 6px 10px 0;
187
+ padding: 4px 10px 0;
153
188
  overflow-x: auto;
154
189
  scrollbar-width: none;
155
190
  }
@@ -158,7 +193,7 @@ export const RIBBON_CSS = `
158
193
  tab is a 2px accent underline sitting on the panel's own hairline. */
159
194
  .dxr-tab {
160
195
  flex: 0 0 auto;
161
- padding: 7px 14px 8px;
196
+ padding: 6px 13px 7px;
162
197
  border: none;
163
198
  background: none;
164
199
  color: var(--dxr-muted);
@@ -174,8 +209,9 @@ export const RIBBON_CSS = `
174
209
  font-weight: 600;
175
210
  box-shadow: inset 0 -2px 0 var(--dxr-accent);
176
211
  }
177
- /* Contextual tab — present only while the caret is inside a table. */
212
+ /* Contextual tabs — present only while the caret is in a table / a header or footer story. */
178
213
  .dxr-tab[data-contextual] { color: var(--dxr-accent); }
214
+ .dxr-tab[data-contextual]::before { content: "\\2022"; margin-right: 6px; opacity: .6; }
179
215
  .dxr-tab[hidden] { display: none; }
180
216
 
181
217
  /* ── Ribbon panels ────────────────────────────────────────────────────────── */
@@ -184,7 +220,7 @@ export const RIBBON_CSS = `
184
220
  .dxr-panel {
185
221
  display: none;
186
222
  align-items: stretch;
187
- padding: 7px 10px 8px;
223
+ padding: 6px 10px 7px;
188
224
  overflow-x: auto;
189
225
  overscroll-behavior-x: contain;
190
226
  }
@@ -195,8 +231,8 @@ export const RIBBON_CSS = `
195
231
  flex: 0 0 auto;
196
232
  display: flex;
197
233
  flex-direction: column;
198
- gap: 5px;
199
- padding: 0 13px;
234
+ gap: 4px;
235
+ padding: 0 12px;
200
236
  border-right: 1px solid var(--dxr-rule);
201
237
  }
202
238
  .dxr-group:last-child { border-right: none; }
@@ -209,6 +245,7 @@ export const RIBBON_CSS = `
209
245
  text-transform: uppercase;
210
246
  }
211
247
  .dxr-row { display: flex; gap: 3px; align-items: center; }
248
+ .dxr-row + .dxr-row { margin-top: 1px; }
212
249
 
213
250
  /* ── Controls ─────────────────────────────────────────────────────────────── */
214
251
  /* One button family, house-style: every control is a ghost — no borders, no
@@ -217,13 +254,13 @@ export const RIBBON_CSS = `
217
254
  surface has exactly one accent-colored action. */
218
255
  .dxr button, .dxr label.dxr-btn {
219
256
  min-height: var(--dxr-tap);
220
- padding: 5px 9px;
257
+ padding: 4px 8px;
221
258
  border: none;
222
259
  border-radius: 6px;
223
260
  background: none;
224
261
  color: var(--dxr-ink);
225
262
  font: inherit;
226
- font-size: 13px;
263
+ font-size: 12.5px;
227
264
  font-weight: 500;
228
265
  line-height: 1.15;
229
266
  cursor: pointer;
@@ -234,7 +271,7 @@ export const RIBBON_CSS = `
234
271
  .dxr button:hover, .dxr label.dxr-btn:hover { background: #f1f5f9; }
235
272
  .dxr button:active { background: #e2e8f0; }
236
273
  .dxr button:disabled { opacity: .38; cursor: default; background: none; }
237
- .dxr button.dxr-on { color: var(--dxr-accent); background: var(--dxr-wash-on); }
274
+ .dxr button.dxr-on, .dxr button[aria-pressed="true"] { color: var(--dxr-accent); background: var(--dxr-wash-on); }
238
275
  .dxr-quick button, .dxr-quick label.dxr-btn { padding: 4px 10px; font-size: 12.5px; }
239
276
  /* Save — the house primary: flat accent, sm shadow, the 1px lift on hover. */
240
277
  .dxr-quick button[data-dxr="save"]:not(:disabled) {
@@ -254,7 +291,7 @@ export const RIBBON_CSS = `
254
291
  transform: translateY(0);
255
292
  }
256
293
  /* Icon controls sit quieter than text ones until touched (house IconButton). */
257
- .dxr button.dxr-icon { min-width: var(--dxr-tap); text-align: center; color: var(--dxr-muted); }
294
+ .dxr button.dxr-icon { min-width: var(--dxr-tap); padding: 4px 6px; text-align: center; color: var(--dxr-muted); }
258
295
  .dxr button.dxr-icon:hover:not(:disabled) { color: var(--dxr-ink); }
259
296
  .dxr button.dxr-icon.dxr-on { color: var(--dxr-accent); }
260
297
  /* Icons are inline SVG drawn from the same shapes the control acts on (text lines,
@@ -263,9 +300,9 @@ export const RIBBON_CSS = `
263
300
  .dxr button svg { display: block; margin: 0 auto; fill: currentColor; }
264
301
  .dxr button.dxr-wide { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
265
302
  .dxr button.dxr-danger:hover { color: var(--dxr-danger); background: var(--dxr-danger-wash); }
266
- .dxr select, .dxr input[type="number"], .dxr input[type="text"] {
303
+ .dxr select, .dxr input[type="number"], .dxr input[type="text"], .dxr input[type="search"], .dxr input[type="url"] {
267
304
  min-height: var(--dxr-tap);
268
- padding: 4px 6px;
305
+ padding: 3px 6px;
269
306
  border: 1px solid var(--dxr-rule);
270
307
  border-radius: 6px;
271
308
  background: var(--dxr-sheet);
@@ -279,29 +316,72 @@ export const RIBBON_CSS = `
279
316
  outline: 2px solid var(--dxr-accent);
280
317
  outline-offset: 1px;
281
318
  }
282
- .dxr [data-dxr="fontsize"] { width: 62px; }
283
- .dxr [data-dxr="fontfamily"] { max-width: 132px; }
284
- .dxr [data-dxr="pgstart"] { width: 64px; }
319
+ .dxr [data-dxr="fontsize"] { width: 58px; }
320
+ .dxr [data-dxr="fontfamily"] { width: 138px; }
321
+ .dxr [data-dxr="style"] { width: 148px; }
322
+ .dxr [data-dxr="pgstart"], .dxr [data-dxr="spacebefore"], .dxr [data-dxr="spaceafter"],
323
+ .dxr [data-dxr="specialindentval"] { width: 62px; }
324
+ .dxr [data-dxr="linespacing"], .dxr [data-dxr="listmenu"], .dxr [data-dxr="highlight"],
325
+ .dxr [data-dxr="markup"], .dxr [data-dxr="zoom"], .dxr [data-dxr="margins"],
326
+ .dxr [data-dxr="orientation"], .dxr [data-dxr="pagesize"], .dxr [data-dxr="specialindent"] { max-width: 150px; }
285
327
  .dxr-toggle {
286
328
  display: inline-flex;
287
329
  gap: 5px;
288
330
  align-items: center;
331
+ min-height: var(--dxr-tap);
289
332
  color: var(--dxr-ink);
290
333
  font-size: 12.5px;
291
334
  white-space: nowrap;
292
335
  cursor: pointer;
293
336
  }
337
+ .dxr-toggle input[type="checkbox"] { accent-color: var(--dxr-accent); margin: 0; }
294
338
  .dxr-note { color: var(--dxr-muted); font-size: 11.5px; }
339
+ .dxr-kbd { color: var(--dxr-faint); font-family: var(--dxr-mono); font-size: 10px; }
340
+
341
+ /* Colour swatch buttons: the button shows the current colour as a bar under its glyph and
342
+ opens the native picker that sits (visually hidden) inside it. */
343
+ .dxr-swatch { position: relative; display: inline-flex; flex-direction: column; align-items: center; gap: 2px; }
344
+ .dxr-swatch > button { padding-bottom: 6px; }
345
+ .dxr-swatch .dxr-swatch-bar {
346
+ position: absolute; left: 7px; right: 7px; bottom: 4px; height: 3px; border-radius: 2px;
347
+ background: var(--dxr-swatch, #1e293b); pointer-events: none;
348
+ }
349
+ .dxr-swatch input[type="color"] {
350
+ position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; border: 0; padding: 0;
351
+ }
352
+ .dxr-swatch [data-dxr="highlight"] {
353
+ min-height: var(--dxr-tap);
354
+ }
355
+ .dxr-hl-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 2px; vertical-align: -2px; margin-right: 4px; border: 1px solid rgba(15,23,42,.15); }
356
+
357
+ /* ── Find & replace bar ───────────────────────────────────────────────────── */
358
+ .dxr-findbar {
359
+ display: flex;
360
+ align-items: center;
361
+ gap: 6px;
362
+ padding: 5px 10px;
363
+ overflow-x: auto;
364
+ border-top: 1px solid var(--dxr-rule);
365
+ background: var(--dxr-chrome);
366
+ scrollbar-width: none;
367
+ }
368
+ .dxr-findbar[hidden] { display: none; }
369
+ .dxr-findbar > *, .dxr-findbar [data-dxr="replacegroup"] { flex: 0 0 auto; white-space: nowrap; }
370
+ .dxr-findbar [data-dxr="replacegroup"] { display: inline-flex; align-items: center; gap: 6px; }
371
+ .dxr-findbar input[type="search"], .dxr-findbar input[type="text"] { width: 180px; }
372
+ .dxr-findbar .dxr-findcount { min-width: 5ch; color: var(--dxr-muted); font-family: var(--dxr-mono); font-size: 11.5px; }
295
373
 
296
- /* ── Anchor rail ──────────────────────────────────────────────────────────────
374
+ /* ── Status bar (carries the anchor rail) ─────────────────────────────────────
297
375
  The addressing spine made permanent chrome. Every block in this editor is
298
376
  addressable as kind:scope:unid, and the model of record is a live WASM session —
299
377
  so the surface states both, live, instead of hiding them behind devtools. It also
300
- reports each command's real cost, which is what a smoke test needs to see. */
378
+ reports each command's real cost, which is what a smoke test needs to see. Word's
379
+ own status-bar cells (page, words, zoom) sit on the same strip. */
301
380
  .dxr-rail {
302
381
  display: flex;
303
382
  align-items: center;
304
- height: 25px;
383
+ flex: 0 0 auto;
384
+ height: 26px;
305
385
  padding: 0 10px;
306
386
  overflow-x: auto;
307
387
  background: #fafafa;
@@ -316,12 +396,12 @@ export const RIBBON_CSS = `
316
396
  display: flex;
317
397
  align-items: baseline;
318
398
  gap: 6px;
319
- padding: 0 13px;
399
+ padding: 0 12px;
320
400
  border-right: 1px solid var(--dxr-rule);
321
401
  white-space: nowrap;
322
402
  }
323
403
  .dxr-rail .dxr-cell:first-child { padding-left: 0; }
324
- .dxr-rail .dxr-cell:last-child { border-right: none; }
404
+ .dxr-rail .dxr-cell.dxr-cell-end { margin-left: auto; border-right: none; padding-right: 0; }
325
405
  .dxr-rail .dxr-k {
326
406
  color: var(--dxr-faint);
327
407
  font-family: var(--dxr-ui);
@@ -331,7 +411,11 @@ export const RIBBON_CSS = `
331
411
  }
332
412
  .dxr-rail .dxr-v { color: var(--dxr-data); }
333
413
  .dxr-rail .dxr-v.dxr-flash { animation: dxr-railflash .45s ease-out; }
414
+ .dxr-rail .dxr-v.dxr-plain { color: var(--dxr-ink); font-family: var(--dxr-ui); font-size: 11.5px; }
334
415
  @keyframes dxr-railflash { from { background: #ccfbf1; } to { background: transparent; } }
416
+ .dxr-zoom { display: inline-flex; align-items: center; gap: 2px; }
417
+ .dxr-zoom button { min-height: 22px; min-width: 22px; padding: 0 5px; font-size: 13px; color: var(--dxr-muted); }
418
+ .dxr-zoom select { min-height: 22px; padding: 0 4px; font-size: 11.5px; }
335
419
 
336
420
  /* ── Hint ─────────────────────────────────────────────────────────────────── */
337
421
  .dxr-hint {
@@ -383,7 +467,10 @@ export const RIBBON_CSS = `
383
467
  margin-top: -26px;
384
468
  background: linear-gradient(to top, var(--dxr-desk), transparent);
385
469
  }
386
- .dxr[data-chrome] .dxr-surface { margin: 26px auto; padding: 0 16px 96px; }
470
+ .dxr[data-chrome] .dxr-surface { position: relative; margin: 26px auto; padding: 0 16px 96px; }
471
+ /* The comment gutter sits to the right of the sheet; the surface reserves its width so the
472
+ page shifts left rather than the bubbles covering it — Word's markup area. */
473
+ .dxr[data-chrome="full"] .dxr-surface[data-comments="on"] { padding-right: calc(var(--dxr-gutter) + 8px); }
387
474
  .dxr-surface [contenteditable="true"]:focus {
388
475
  outline: 2px solid var(--dxr-accent);
389
476
  outline-offset: 2px;
@@ -391,77 +478,65 @@ export const RIBBON_CSS = `
391
478
  }
392
479
  @media (hover: hover) { .dxr-surface [contenteditable="true"]:hover { background: var(--dxr-wash); } }
393
480
 
394
- /* Header/footer bands: stories live in their own OOXML parts outside the body,
395
- so they dock as their own regions. Styled from the same tokens as the ribbon
396
- so the surface reads as one instrument rather than two apps. */
481
+ /* Header/footer bands (continuous view): the top and bottom margin of the sheet, drawn the way
482
+ Word draws a header that is being edited — a dashed rule with a small tag in the margin. */
397
483
  .dxr-surface .docx-hf-band {
398
484
  /* Docked outside the zoomed sheet, so it takes the page's on-screen width from the
399
485
  custom property the viewport publishes rather than stretching to the whole surface. */
486
+ position: relative;
400
487
  width: min(100%, var(--docx-sheet-width, 100%));
401
- margin: 0 auto 14px;
402
- padding: 9px 72px 13px;
403
- border: 1px solid var(--dxr-rule);
404
- border-left: 2px solid #5eead4;
405
- border-radius: 6px;
406
- background: var(--dxr-sheet);
407
- box-shadow: var(--dxr-shadow-sm);
408
- }
409
- .dxr-surface .docx-hf-band + .docx-body-flow { margin-top: 0; }
410
- .dxr-surface .docx-hf-band[data-hf-band="footer"] { margin: 14px auto 0; }
411
- .dxr-surface .docx-hf-chrome {
412
- display: flex;
413
- gap: 8px;
414
- align-items: center;
415
- flex-wrap: wrap;
416
- margin: 0 0 7px -60px;
417
- color: var(--dxr-muted);
418
- font-size: 9.5px;
419
- font-weight: 600;
420
- letter-spacing: .11em;
421
- text-transform: uppercase;
422
- }
423
- .dxr-surface .docx-hf-chrome select, .dxr-surface .docx-hf-chrome input {
424
- padding: 3px 5px;
425
- border: 1px solid var(--dxr-rule);
426
- border-radius: 6px;
488
+ margin: 0 auto;
489
+ padding: 34px 72px 12px;
427
490
  background: var(--dxr-sheet);
428
- color: var(--dxr-ink);
429
- font: inherit;
430
- font-family: var(--dxr-ui);
431
- font-size: 12.5px;
432
- font-weight: 400;
433
- letter-spacing: normal;
434
- text-transform: none;
435
- }
436
- .dxr-surface .docx-hf-chrome input[data-hf-pagestart] { width: 64px; }
437
- .dxr-surface .docx-hf-label { font-weight: 600; }
438
- .dxr-surface .docx-hf-warning {
439
- margin: 0 0 8px -60px;
440
- padding: 7px 9px;
441
- border: 1px solid #fde68a;
442
- border-left: 2px solid #d97706;
443
- border-radius: 6px;
444
- background: #fffbeb;
445
- color: #92400e;
446
- font-size: 12px;
447
- line-height: 1.45;
448
- }
449
- .dxr-surface .docx-hf-warning button {
450
- margin-left: 6px;
451
- padding: 2px 8px;
452
- border-color: #fde68a;
453
- background: #fff;
454
- font-size: 12px;
455
- }
456
- .dxr-surface .docx-hf-placeholder { color: var(--dxr-faint); font-style: italic; }
457
- .dxr-surface .docx-hf-inherited {
458
- color: var(--dxr-muted);
459
- font-style: italic;
460
- font-weight: 400;
461
- letter-spacing: normal;
462
- text-transform: none;
491
+ border-radius: 3px 3px 0 0;
492
+ box-shadow: 0 1px 3px rgba(15, 23, 42, .08), 0 12px 24px rgba(15, 23, 42, .06);
463
493
  }
464
- .dxr-surface .docx-hf-band[data-hf-inherited] { border-style: dashed; }
494
+ .dxr-surface .docx-hf-band[data-hf-band="footer"] { padding: 12px 72px 34px; border-radius: 0 0 3px 3px; }
495
+ /* The band and the sheet are one piece of paper: kill the sheet's own top/bottom corners and
496
+ let the dashed rule separate the stories from the body. */
497
+ .dxr-surface .docx-hf-band + .docx-body-flow { margin-top: 0; border-radius: 0; box-shadow: none; }
498
+ .dxr-surface .docx-hf-band[data-hf-band="footer"] { margin-top: 0; }
499
+ .dxr[data-chrome] .dxr-surface[data-view="continuous"] .docx-hf-band ~ .docx-body-flow { box-shadow: none; }
500
+ .dxr[data-chrome] .dxr-surface[data-view="continuous"]:has(.docx-hf-band) .docx-body-flow { padding: 18px 0; }
501
+ .dxr-surface .docx-hf-band::after {
502
+ content: ""; position: absolute; left: 0; right: 0; bottom: 0; border-bottom: 1px dashed var(--dxr-rule-strong);
503
+ }
504
+ .dxr-surface .docx-hf-band[data-hf-band="footer"]::after { top: 0; bottom: auto; border-bottom: 0; border-top: 1px dashed var(--dxr-rule-strong); }
505
+ .dxr-surface .docx-hf-band:hover::after, .dxr-surface .docx-hf-band[data-hf-active]::after { border-color: var(--dxr-accent); }
506
+ .dxr-surface .docx-hf-tag {
507
+ position: absolute; left: 72px; bottom: -9px; z-index: 2;
508
+ display: flex; align-items: center; gap: 8px;
509
+ padding: 1px 7px; border: 1px solid var(--dxr-rule-strong); border-radius: 4px;
510
+ background: #fff; color: var(--dxr-muted);
511
+ font-family: var(--dxr-ui); font-size: 9.5px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
512
+ }
513
+ .dxr-surface .docx-hf-band[data-hf-band="footer"] .docx-hf-tag { bottom: auto; top: -9px; }
514
+ .dxr-surface .docx-hf-band[data-hf-active] .docx-hf-tag { border-color: var(--dxr-accent); color: var(--dxr-accent); }
515
+ .dxr-surface .docx-hf-kinds { display: inline-flex; gap: 2px; }
516
+ .dxr-surface .docx-hf-kinds button {
517
+ min-height: 0; padding: 1px 6px; border-radius: 3px; font-family: var(--dxr-ui); font-size: 9.5px; font-weight: 600;
518
+ letter-spacing: .06em; text-transform: uppercase; color: var(--dxr-muted); background: none;
519
+ }
520
+ .dxr-surface .docx-hf-kinds button[data-on] { background: var(--dxr-wash-on); color: var(--dxr-accent); }
521
+ .dxr-surface .docx-hf-inherited { font-weight: 400; letter-spacing: normal; text-transform: none; font-style: italic; }
522
+ .dxr-surface .docx-hf-placeholder { color: var(--dxr-faint); font-style: italic; cursor: text; }
523
+ /* Bands only: a page host's height is the band the paginator reserved, and a floor here would
524
+ read as "the story grew" and trigger a needless re-paginate. */
525
+ .dxr-surface .docx-hf-band .docx-hf-body { min-height: 1.4em; }
526
+ /* Page view: the page's own header/footer area is click-to-edit. */
527
+ .dxr-surface .page-header[data-hf-page], .dxr-surface .page-footer[data-hf-page] { cursor: text; }
528
+ .dxr-surface .page-header[data-hf-page]:hover, .dxr-surface .page-footer[data-hf-page]:hover {
529
+ outline: 1px dashed var(--dxr-rule-strong); outline-offset: 2px;
530
+ }
531
+ .dxr-surface .page-header[data-hf-active], .dxr-surface .page-footer[data-hf-active] {
532
+ outline: 1px dashed var(--dxr-accent); outline-offset: 2px; z-index: 5; background: #fff;
533
+ }
534
+ .dxr-surface [data-hf-active][data-hf-label]::before {
535
+ content: attr(data-hf-label); position: absolute; left: -2px; top: -18px;
536
+ padding: 1px 6px; border: 1px solid var(--dxr-accent); border-radius: 4px; background: #fff; color: var(--dxr-accent);
537
+ font-family: var(--dxr-ui); font-size: 9px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; line-height: 1.4;
538
+ }
539
+ .dxr-surface .page-footer[data-hf-active][data-hf-label]::before { top: auto; bottom: -18px; }
465
540
  /* The sheet IS the page. The editor's viewport sizes .docx-body-flow to the section's page
466
541
  width and its section wrappers to the authored text column, so the horizontal gutters here
467
542
  are the document's own w:sectPr margins, not a padding this chrome invents; only the
@@ -476,25 +551,6 @@ export const RIBBON_CSS = `
476
551
  box-shadow: 0 1px 3px rgba(15, 23, 42, .08), 0 12px 24px rgba(15, 23, 42, .06);
477
552
  }
478
553
 
479
- /* ── Review: comment threads ──────────────────────────────────────────────── */
480
- .dxr-threads {
481
- display: flex; flex-direction: column; gap: 4px;
482
- max-height: 92px; overflow-y: auto; min-width: 260px; padding-right: 2px;
483
- }
484
- .dxr-thread {
485
- display: flex; align-items: center; gap: 6px;
486
- font-size: 11px; line-height: 1.3; padding: 3px 6px;
487
- border: 1px solid var(--dxr-rule); border-radius: 4px; background: var(--dxr-chrome-sunk);
488
- }
489
- .dxr-thread[data-resolved] { opacity: 0.55; }
490
- .dxr-thread[data-reply] { margin-left: 14px; }
491
- .dxr-thread .dxr-tauthor { font-weight: 600; white-space: nowrap; }
492
- .dxr-thread .dxr-ttext {
493
- flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px;
494
- }
495
- .dxr-thread button { flex: none; }
496
- .dxr-group.dxr-grow { flex: 1; min-width: 0; }
497
-
498
554
  /* ── Table size picker ────────────────────────────────────────────────────── */
499
555
  .dxr-pop {
500
556
  display: none;
@@ -526,6 +582,12 @@ export const RIBBON_CSS = `
526
582
  margin-top: 8px;
527
583
  font-size: 12px;
528
584
  }
585
+ /* Small prompt popovers (link URL, page setup), anchored under their button. */
586
+ .dxr-pop form { display: flex; flex-direction: column; gap: 8px; min-width: 260px; font-size: 12.5px; }
587
+ .dxr-pop form label { display: flex; flex-direction: column; gap: 3px; color: var(--dxr-muted); font-size: 11.5px; }
588
+ .dxr-pop form .dxr-row { justify-content: flex-end; }
589
+ .dxr-pop form button[type="submit"] { background: var(--dxr-accent); color: #fff; }
590
+ .dxr-pop form button[type="submit"]:hover { background: var(--dxr-accent-hover); }
529
591
 
530
592
  /* ── Compact chrome (the mobile-first base) ───────────────────────────────────
531
593
  One scrolling strip per tab instead of a multi-row ribbon: group labels turn
@@ -533,13 +595,19 @@ export const RIBBON_CSS = `
533
595
  bottom sheet because there is no room to hang a popover off a button. */
534
596
  /* A 320px phone cannot hold the product name AND the file actions, and the file
535
597
  actions are what a user came for. The strip also scrolls, so nothing is stranded. */
598
+ /* Touch targets grow with the LAYOUT, not the pointer alone: a phone-width chrome is driven
599
+ by a thumb whether or not the browser reports a coarse pointer (an emulated narrow viewport
600
+ does not), so the compact strip adopts the same 40px control floor the coarse-pointer media
601
+ query sets, keeping every icon button at a real tap size. */
602
+ .dxr[data-chrome="compact"] { --dxr-tap: 40px; }
603
+ /* The floor buys tap SIZE; the rows must not spend it again on padding. A phone stacks three
604
+ chrome rows — title, tabs, commands — above the page, so every pixel of row padding is paid
605
+ three times before the document starts, and the arcade demo's card (which sizes itself to the
606
+ game screen plus its controls) runs out of viewport when it is. */
536
607
  .dxr[data-chrome="compact"] .dxr-titlebar {
537
608
  gap: 6px;
538
- padding: 5px 8px 0;
539
- overflow-x: auto;
540
- scrollbar-width: none;
609
+ padding: 0 8px;
541
610
  }
542
- .dxr[data-chrome="compact"] .dxr-titlebar::-webkit-scrollbar { display: none; }
543
611
  .dxr[data-chrome="compact"] .dxr-brandname { display: none; }
544
612
  .dxr[data-chrome="compact"] .dxr-brand { flex: 0 1 auto; }
545
613
  /* A filename clipped to "do..." tells you less than no filename at all, so it keeps a
@@ -549,12 +617,16 @@ export const RIBBON_CSS = `
549
617
  .dxr[data-chrome="compact"] .dxr-quick button,
550
618
  .dxr[data-chrome="compact"] .dxr-quick label.dxr-btn { padding: 4px 8px; }
551
619
  .dxr[data-chrome="compact"] .dxr-status { display: none; }
552
- .dxr[data-chrome="compact"] .dxr-tabs { padding: 5px 8px 0; }
553
- .dxr[data-chrome="compact"] .dxr-tab { padding: 6px 12px 7px; font-size: 12px; }
620
+ .dxr[data-chrome="compact"] .dxr-tabs { padding: 0 8px; }
621
+ /* A tab is a WIDE target — its label plus 11px of side padding already clears the tap floor
622
+ horizontally — and it is navigation rather than a command, so it does not need the command
623
+ row's full height. Word's own phone ribbon keeps its tab strip slim for the same reason: the
624
+ page starts higher. The commands below keep the full 40px floor. */
625
+ .dxr[data-chrome="compact"] .dxr-tab { min-height: 34px; padding: 6px 11px 7px; font-size: 12px; }
554
626
  .dxr[data-chrome="compact"] .dxr-panel {
555
627
  align-items: center;
556
628
  gap: 4px;
557
- padding: 5px 8px;
629
+ padding: 2px 8px;
558
630
  scroll-snap-type: x proximity;
559
631
  }
560
632
  .dxr[data-chrome="compact"] .dxr-group {
@@ -568,14 +640,17 @@ export const RIBBON_CSS = `
568
640
  .dxr[data-chrome="compact"] .dxr-note { display: none; }
569
641
  .dxr[data-chrome="compact"] .dxr-rail { display: none; }
570
642
  .dxr[data-chrome="compact"] .dxr-hint { display: none; }
643
+ .dxr[data-chrome="compact"] .dxr-row + .dxr-row { margin-top: 0; }
571
644
  /* Compact trims the chrome around the page, never the page: the document's own column width
572
645
  is what the viewport's fit-to-width zoom scales, so a phone shows a whole smaller page
573
646
  instead of a narrower one that breaks its lines somewhere Word never would. */
574
647
  .dxr[data-chrome="compact"] .dxr-surface { margin: 12px auto; padding: 0 10px 64px; }
575
648
  .dxr[data-chrome="compact"] .dxr-surface[data-view="continuous"] .docx-body-flow { padding: 22px 0; }
576
- .dxr[data-chrome="compact"] .dxr-surface .docx-hf-band { padding: 9px 18px 13px; }
577
- .dxr[data-chrome="compact"] .dxr-surface .docx-hf-chrome,
578
- .dxr[data-chrome="compact"] .dxr-surface .docx-hf-warning { margin-left: 0; }
649
+ .dxr[data-chrome="compact"] .dxr-surface .docx-hf-band { padding: 26px 18px 10px; }
650
+ .dxr[data-chrome="compact"] .dxr-surface .docx-hf-band[data-hf-band="footer"] { padding: 10px 18px 26px; }
651
+ .dxr[data-chrome="compact"] .dxr-surface .docx-hf-tag { left: 18px; }
652
+ /* No room for a markup column on a phone: comments stay highlighted, bubbles step aside. */
653
+ .dxr[data-chrome="compact"] .docx-comment-gutter, .dxr[data-chrome="compact"] .docx-comment-leaders { display: none; }
579
654
  /* A popover anchored to a button has nowhere to go on a narrow surface, so the
580
655
  picker docks to the bottom edge where a thumb already is. */
581
656
  .dxr[data-chrome="compact"] .dxr-pop[data-open] {
@@ -791,13 +866,37 @@ export const RIBBON_CSS = `
791
866
  }
792
867
  `;
793
868
  const ICON_ALIGN = (bars) => `<svg width="15" height="13" viewBox="0 0 15 13" aria-hidden="true">${bars}</svg>`;
869
+ /** Word's highlighter palette (ST_HighlightColor) with its screen colours, in Word's order. */
870
+ export const HIGHLIGHT_COLORS = [
871
+ { value: "yellow", label: "Yellow", css: "#ffff00" },
872
+ { value: "green", label: "Bright green", css: "#00ff00" },
873
+ { value: "cyan", label: "Turquoise", css: "#00ffff" },
874
+ { value: "magenta", label: "Pink", css: "#ff00ff" },
875
+ { value: "blue", label: "Blue", css: "#0000ff" },
876
+ { value: "red", label: "Red", css: "#ff0000" },
877
+ { value: "darkBlue", label: "Dark blue", css: "#000080" },
878
+ { value: "darkCyan", label: "Teal", css: "#008080" },
879
+ { value: "darkGreen", label: "Green", css: "#008000" },
880
+ { value: "darkMagenta", label: "Violet", css: "#800080" },
881
+ { value: "darkRed", label: "Dark red", css: "#800000" },
882
+ { value: "darkYellow", label: "Dark yellow", css: "#808000" },
883
+ { value: "darkGray", label: "Gray 50%", css: "#808080" },
884
+ { value: "lightGray", label: "Gray 25%", css: "#c0c0c0" },
885
+ { value: "black", label: "Black", css: "#000000" },
886
+ ];
887
+ /** The fonts every Word install has; the document's own fonts are added on top at open. */
888
+ export const COMMON_FONTS = [
889
+ "Calibri", "Cambria", "Arial", "Times New Roman", "Georgia", "Garamond", "Verdana",
890
+ "Tahoma", "Trebuchet MS", "Book Antiqua", "Century Gothic", "Courier New", "Consolas",
891
+ "Segoe UI", "Helvetica",
892
+ ];
794
893
  /**
795
894
  * The ribbon's DOM, as one template.
796
895
  *
797
896
  * `data-dxr` names are the addressing contract (see the module header); the
798
897
  * behavioural attributes (`data-cmd`, `data-align`, `data-indent`, `data-list`,
799
- * `data-tt`) are what `ribbon.ts` delegates on, so adding a control here that
800
- * follows an existing convention needs no wiring change.
898
+ * `data-tt`, `data-hf`, `data-rev`, `data-cmt`) are what `ribbon.ts` delegates on,
899
+ * so adding a control here that follows an existing convention needs no wiring change.
801
900
  */
802
901
  export const RIBBON_HTML = `
803
902
  <div class="dxr-chrome">
@@ -821,25 +920,22 @@ export const RIBBON_HTML = `
821
920
  <button type="button" class="dxr-tab" role="tab" data-tab="home" aria-selected="true">Home</button>
822
921
  <button type="button" class="dxr-tab" role="tab" data-tab="insert" aria-selected="false">Insert</button>
823
922
  <button type="button" class="dxr-tab" role="tab" data-tab="layout" aria-selected="false">Layout</button>
923
+ <button type="button" class="dxr-tab" role="tab" data-tab="references" aria-selected="false">References</button>
824
924
  <button type="button" class="dxr-tab" role="tab" data-tab="review" aria-selected="false">Review</button>
925
+ <button type="button" class="dxr-tab" role="tab" data-tab="view" aria-selected="false">View</button>
825
926
  <button type="button" class="dxr-tab" role="tab" data-tab="table" data-contextual aria-selected="false" hidden>Table</button>
927
+ <button type="button" class="dxr-tab" role="tab" data-tab="headerfooter" data-contextual aria-selected="false" hidden>Header &amp; Footer</button>
826
928
  </div>
827
929
 
828
930
  <div class="dxr-ribbon" data-dxr="ribbon" aria-disabled="true">
829
931
  <!-- HOME ──────────────────────────────────────────────────────────────── -->
830
932
  <div class="dxr-panel" data-panel="home" data-active>
831
933
  <div class="dxr-group">
832
- <span class="dxr-glabel">Text</span>
833
- <div class="dxr-row">
834
- <button type="button" class="dxr-icon" data-cmd="bold" title="Bold (Ctrl+B)"><b>B</b></button>
835
- <button type="button" class="dxr-icon" data-cmd="italic" title="Italic (Ctrl+I)"><i>I</i></button>
836
- <button type="button" class="dxr-icon" data-cmd="underline" title="Underline (Ctrl+U)"><u>U</u></button>
837
- <button type="button" class="dxr-icon" data-cmd="strike" title="Strikethrough"><s>S</s></button>
838
- <button type="button" class="dxr-icon" data-cmd="code" title="Inline code">&lt;/&gt;</button>
839
- <button type="button" class="dxr-icon" data-cmd="superscript" title="Superscript">x&#178;</button>
840
- <button type="button" class="dxr-icon" data-cmd="subscript" title="Subscript">x&#8322;</button>
841
- </div>
934
+ <span class="dxr-glabel">Font</span>
842
935
  <div class="dxr-row">
936
+ <select data-dxr="fontfamily" title="Font family — applies to the selection">
937
+ <option value="">Font&#8230;</option>
938
+ </select>
843
939
  <input data-dxr="fontsize" data-dxr-list="fontsizes" type="number" min="1" max="1638" step="0.5"
844
940
  placeholder="pt" title="Font size in points — type any value or pick a preset" />
845
941
  <datalist data-dxr="fontsizes">
@@ -848,34 +944,74 @@ export const RIBBON_HTML = `
848
944
  <option>20</option><option>24</option><option>28</option><option>36</option>
849
945
  <option>48</option><option>72</option><option>96</option>
850
946
  </datalist>
851
- <select data-dxr="fontfamily" title="Font family — applies to the selection">
852
- <option value="">Font&#8230;</option>
853
- <option>Calibri</option><option>Times New Roman</option><option>Arial</option>
854
- <option>Georgia</option><option>Cambria</option><option>Courier New</option>
855
- <option>Verdana</option><option>Garamond</option>
856
- </select>
947
+ <button type="button" class="dxr-icon" data-dxr="fontgrow" title="Increase font size (Ctrl+])">A<sup>&#9650;</sup></button>
948
+ <button type="button" class="dxr-icon" data-dxr="fontshrink" title="Decrease font size (Ctrl+[)">A<sub>&#9660;</sub></button>
949
+ <button type="button" class="dxr-icon" data-dxr="clearformat" title="Clear all formatting">A&#10008;</button>
950
+ </div>
951
+ <div class="dxr-row">
952
+ <button type="button" class="dxr-icon" data-cmd="bold" title="Bold (Ctrl+B)"><b>B</b></button>
953
+ <button type="button" class="dxr-icon" data-cmd="italic" title="Italic (Ctrl+I)"><i>I</i></button>
954
+ <button type="button" class="dxr-icon" data-cmd="underline" title="Underline (Ctrl+U)"><u>U</u></button>
955
+ <button type="button" class="dxr-icon" data-cmd="strike" title="Strikethrough"><s>S</s></button>
956
+ <button type="button" class="dxr-icon" data-cmd="superscript" title="Superscript">x&#178;</button>
957
+ <button type="button" class="dxr-icon" data-cmd="subscript" title="Subscript">x&#8322;</button>
958
+ <button type="button" class="dxr-icon" data-dxr="smallcaps" title="Small caps" aria-pressed="false"
959
+ style="font-variant: small-caps;">Aa</button>
960
+ <button type="button" class="dxr-icon" data-cmd="code" title="Inline code">&lt;/&gt;</button>
961
+ <span class="dxr-swatch" title="Font color">
962
+ <button type="button" class="dxr-icon" data-dxr="fontcolorbtn" aria-label="Font color"><b>A</b></button>
963
+ <span class="dxr-swatch-bar" data-dxr="fontcolorbar"></span>
964
+ <input type="color" data-dxr="fontcolor" value="#1e293b" aria-label="Pick a font color" />
965
+ </span>
966
+ <span class="dxr-swatch" title="Text highlight color">
967
+ <select data-dxr="highlight" aria-label="Text highlight color">
968
+ <option value="">&#9998; Highlight</option>
969
+ <option value="none">No color</option>
970
+ </select>
971
+ </span>
857
972
  </div>
858
973
  </div>
859
974
 
860
975
  <div class="dxr-group">
861
976
  <span class="dxr-glabel">Paragraph</span>
862
977
  <div class="dxr-row">
863
- <button type="button" class="dxr-icon" data-align="left" title="Align left">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="0" y="3.8" width="9" height="1.6"/><rect x="0" y="7.6" width="15" height="1.6"/><rect x="0" y="11.4" width="9" height="1.6"/>')}</button>
864
- <button type="button" class="dxr-icon" data-align="center" title="Align center">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="3" y="3.8" width="9" height="1.6"/><rect x="0" y="7.6" width="15" height="1.6"/><rect x="3" y="11.4" width="9" height="1.6"/>')}</button>
865
- <button type="button" class="dxr-icon" data-align="right" title="Align right">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="6" y="3.8" width="9" height="1.6"/><rect x="0" y="7.6" width="15" height="1.6"/><rect x="6" y="11.4" width="9" height="1.6"/>')}</button>
866
- <button type="button" class="dxr-icon" data-align="justify" title="Justify">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="0" y="3.8" width="15" height="1.6"/><rect x="0" y="7.6" width="15" height="1.6"/><rect x="0" y="11.4" width="15" height="1.6"/>')}</button>
978
+ <button type="button" class="dxr-icon" data-list="bullet" title="Bullets">&#8226;&#8801;</button>
979
+ <button type="button" class="dxr-icon" data-list="decimal" title="Numbering">1.&#8801;</button>
980
+ <select data-dxr="listmenu" title="Numbering format">
981
+ <option value="">List style&#8230;</option>
982
+ <option value="bullet">&#8226; Bullet</option>
983
+ <option value="decimal">1. 2. 3.</option>
984
+ <option value="decimalParenthesis">(1) (2) (3)</option>
985
+ <option value="lowerLetter">a. b. c.</option>
986
+ <option value="lowerLetterParenthesis">(a) (b) (c)</option>
987
+ <option value="upperLetter">A. B. C.</option>
988
+ <option value="lowerRoman">i. ii. iii.</option>
989
+ <option value="lowerRomanParenthesis">(i) (ii) (iii)</option>
990
+ <option value="upperRoman">I. II. III.</option>
991
+ <option value="none">No list</option>
992
+ </select>
867
993
  <button type="button" class="dxr-icon" data-indent="-720" title="Decrease indent">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="6" y="3.8" width="9" height="1.6"/><rect x="6" y="7.6" width="9" height="1.6"/><rect x="0" y="11.4" width="15" height="1.6"/><path d="M4.6 4.2v4.6L0.6 6.5z"/>')}</button>
868
994
  <button type="button" class="dxr-icon" data-indent="720" title="Increase indent">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="6" y="3.8" width="9" height="1.6"/><rect x="6" y="7.6" width="9" height="1.6"/><rect x="0" y="11.4" width="15" height="1.6"/><path d="M0.6 4.2v4.6l4-2.3z"/>')}</button>
869
995
  </div>
870
996
  <div class="dxr-row">
871
- <button type="button" data-list="bullet" title="Bullet list">Bullets</button>
872
- <button type="button" data-list="decimal" title="Numbered list">Numbered</button>
873
- <button type="button" data-pagebreak title="Start this block on a new page">Page break</button>
997
+ <button type="button" class="dxr-icon" data-align="left" title="Align left (Ctrl+L)">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="0" y="3.8" width="9" height="1.6"/><rect x="0" y="7.6" width="15" height="1.6"/><rect x="0" y="11.4" width="9" height="1.6"/>')}</button>
998
+ <button type="button" class="dxr-icon" data-align="center" title="Center (Ctrl+E)">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="3" y="3.8" width="9" height="1.6"/><rect x="0" y="7.6" width="15" height="1.6"/><rect x="3" y="11.4" width="9" height="1.6"/>')}</button>
999
+ <button type="button" class="dxr-icon" data-align="right" title="Align right (Ctrl+R)">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="6" y="3.8" width="9" height="1.6"/><rect x="0" y="7.6" width="15" height="1.6"/><rect x="6" y="11.4" width="9" height="1.6"/>')}</button>
1000
+ <button type="button" class="dxr-icon" data-align="justify" title="Justify (Ctrl+J)">${ICON_ALIGN('<rect x="0" y="0" width="15" height="1.6"/><rect x="0" y="3.8" width="15" height="1.6"/><rect x="0" y="7.6" width="15" height="1.6"/><rect x="0" y="11.4" width="15" height="1.6"/>')}</button>
1001
+ <select data-dxr="linespacing" title="Line spacing">
1002
+ <option value="">&#8597; Spacing&#8230;</option>
1003
+ <option value="1">1.0</option>
1004
+ <option value="1.15">1.15</option>
1005
+ <option value="1.5">1.5</option>
1006
+ <option value="2">2.0</option>
1007
+ <option value="2.5">2.5</option>
1008
+ <option value="3">3.0</option>
1009
+ </select>
874
1010
  </div>
875
1011
  </div>
876
1012
 
877
1013
  <div class="dxr-group">
878
- <span class="dxr-glabel">Block</span>
1014
+ <span class="dxr-glabel">Styles</span>
879
1015
  <div class="dxr-row">
880
1016
  <select data-dxr="style" title="Paragraph style">
881
1017
  <option value="">Style&#8230;</option>
@@ -890,17 +1026,64 @@ export const RIBBON_HTML = `
890
1026
  <button type="button" data-dxr="delblock" class="dxr-danger" title="Delete the block the caret is in">Delete block</button>
891
1027
  </div>
892
1028
  </div>
1029
+
1030
+ <div class="dxr-group">
1031
+ <span class="dxr-glabel">Editing</span>
1032
+ <div class="dxr-row">
1033
+ <button type="button" data-dxr="findtoggle" title="Find (Ctrl+F)">&#128269; Find</button>
1034
+ </div>
1035
+ <div class="dxr-row">
1036
+ <button type="button" data-dxr="replacetoggle" title="Find and replace (Ctrl+H)">Replace</button>
1037
+ </div>
1038
+ </div>
893
1039
  </div>
894
1040
 
895
1041
  <!-- INSERT ────────────────────────────────────────────────────────────── -->
896
1042
  <div class="dxr-panel" data-panel="insert">
897
1043
  <div class="dxr-group">
898
- <span class="dxr-glabel">Table</span>
1044
+ <span class="dxr-glabel">Pages</span>
1045
+ <div class="dxr-row">
1046
+ <button type="button" data-pagebreak title="Start this block on a new page (Ctrl+Enter)">Page break</button>
1047
+ </div>
1048
+ </div>
1049
+ <div class="dxr-group">
1050
+ <span class="dxr-glabel">Tables</span>
899
1051
  <div class="dxr-row">
900
1052
  <button type="button" data-dxr="table" title="Insert a table — pick its size on the grid">&#9638; Table</button>
901
1053
  </div>
902
1054
  </div>
903
-
1055
+ <div class="dxr-group">
1056
+ <span class="dxr-glabel">Illustrations</span>
1057
+ <div class="dxr-row">
1058
+ <label class="dxr-btn" tabindex="0" title="Insert a picture from a file">&#128444; Picture<input data-dxr="picturefile" type="file" accept="image/png,image/jpeg,image/gif,image/bmp,image/webp,image/tiff" hidden /></label>
1059
+ </div>
1060
+ </div>
1061
+ <div class="dxr-group">
1062
+ <span class="dxr-glabel">Links</span>
1063
+ <div class="dxr-row">
1064
+ <button type="button" data-dxr="link" title="Link the selected text to a URL (Ctrl+K)">&#128279; Link</button>
1065
+ <button type="button" data-dxr="unlink" title="Remove the link at the caret">Unlink</button>
1066
+ </div>
1067
+ </div>
1068
+ <div class="dxr-group">
1069
+ <span class="dxr-glabel">Comments</span>
1070
+ <div class="dxr-row">
1071
+ <button type="button" data-dxr="insertcomment" title="Comment on the selection">&#128172; Comment</button>
1072
+ </div>
1073
+ </div>
1074
+ <div class="dxr-group">
1075
+ <span class="dxr-glabel">Header &amp; Footer</span>
1076
+ <div class="dxr-row">
1077
+ <button type="button" data-dxr="gotoheader" title="Edit the header">Header</button>
1078
+ <button type="button" data-dxr="gotofooter" title="Edit the footer">Footer</button>
1079
+ <select data-dxr="pagenummenu" title="Add a page number to the footer">
1080
+ <option value=""># Page number&#8230;</option>
1081
+ <option value="currentPage">Page number</option>
1082
+ <option value="totalPages">Total pages</option>
1083
+ <option value="pageOf">Page X of Y</option>
1084
+ </select>
1085
+ </div>
1086
+ </div>
904
1087
  <div class="dxr-group">
905
1088
  <span class="dxr-glabel">Rules</span>
906
1089
  <div class="dxr-row">
@@ -916,25 +1099,52 @@ export const RIBBON_HTML = `
916
1099
  <button type="button" data-dxr="hrClear" title="Remove the rule or paragraph border">Clear</button>
917
1100
  </div>
918
1101
  </div>
1102
+ </div>
919
1103
 
1104
+ <!-- LAYOUT ────────────────────────────────────────────────────────────── -->
1105
+ <div class="dxr-panel" data-panel="layout">
920
1106
  <div class="dxr-group">
921
- <span class="dxr-glabel">References</span>
1107
+ <span class="dxr-glabel">Page setup</span>
1108
+ <div class="dxr-row">
1109
+ <select data-dxr="margins" title="Page margins for this section">
1110
+ <option value="">Margins&#8230;</option>
1111
+ <option value="1440,1440,1440,1440">Normal — 1&quot; all round</option>
1112
+ <option value="720,720,720,720">Narrow — 0.5&quot; all round</option>
1113
+ <option value="1440,1440,1080,1080">Moderate — 1&quot; top/bottom, 0.75&quot; sides</option>
1114
+ <option value="1440,1440,2880,2880">Wide — 1&quot; top/bottom, 2&quot; sides</option>
1115
+ </select>
1116
+ <select data-dxr="orientation" title="Page orientation for this section">
1117
+ <option value="">Orientation&#8230;</option>
1118
+ <option value="portrait">Portrait</option>
1119
+ <option value="landscape">Landscape</option>
1120
+ </select>
1121
+ </div>
922
1122
  <div class="dxr-row">
923
- <button type="button" data-dxr="footnote" title="Cite a new footnote at the caret">Footnote</button>
924
- <button type="button" data-dxr="endnote" title="Cite a new endnote at the caret">Endnote</button>
1123
+ <select data-dxr="pagesize" title="Paper size for this section">
1124
+ <option value="">Size&#8230;</option>
1125
+ <option value="12240,15840">Letter — 8.5 &times; 11&quot;</option>
1126
+ <option value="12240,20160">Legal — 8.5 &times; 14&quot;</option>
1127
+ <option value="11906,16838">A4 — 210 &times; 297 mm</option>
1128
+ <option value="15840,24480">Tabloid — 11 &times; 17&quot;</option>
1129
+ </select>
1130
+ <span class="dxr-note" data-dxr="pagesetupnote"></span>
925
1131
  </div>
926
1132
  </div>
927
- </div>
928
1133
 
929
- <!-- LAYOUT ────────────────────────────────────────────────────────────── -->
930
- <div class="dxr-panel" data-panel="layout">
931
1134
  <div class="dxr-group">
932
- <span class="dxr-glabel">View</span>
1135
+ <span class="dxr-glabel">Paragraph</span>
933
1136
  <div class="dxr-row">
934
- <label class="dxr-toggle"><input data-dxr="paginated" type="checkbox" /> Page view</label>
1137
+ <label class="dxr-toggle">Before <input data-dxr="spacebefore" type="number" min="0" step="1" placeholder="pt" title="Space before the paragraph, in points" /></label>
1138
+ <label class="dxr-toggle">After <input data-dxr="spaceafter" type="number" min="0" step="1" placeholder="pt" title="Space after the paragraph, in points" /></label>
935
1139
  </div>
936
1140
  <div class="dxr-row">
937
- <label class="dxr-toggle"><input data-dxr="headerfooter" type="checkbox" /> Header &amp; footer bands</label>
1141
+ <select data-dxr="specialindent" title="First-line or hanging indent">
1142
+ <option value="">Special&#8230;</option>
1143
+ <option value="none">(none)</option>
1144
+ <option value="firstLine">First line</option>
1145
+ <option value="hanging">Hanging</option>
1146
+ </select>
1147
+ <label class="dxr-toggle">by <input data-dxr="specialindentval" type="number" min="0" step="0.1" value="0.5" title="Indent amount, in inches" /> in</label>
938
1148
  </div>
939
1149
  </div>
940
1150
 
@@ -960,42 +1170,115 @@ export const RIBBON_HTML = `
960
1170
  </div>
961
1171
  </div>
962
1172
 
963
- <!-- The field lands in the running footer (Word's convention, and where the band
964
- puts it), so it belongs with the section's numbering rather than under Insert,
965
- whose controls all act at the caret. -->
1173
+ <div class="dxr-group">
1174
+ <span class="dxr-glabel">View</span>
1175
+ <div class="dxr-row">
1176
+ <label class="dxr-toggle"><input data-dxr="paginated" type="checkbox" /> Page view</label>
1177
+ </div>
1178
+ <div class="dxr-row">
1179
+ <label class="dxr-toggle"><input data-dxr="headerfooter" type="checkbox" checked /> Headers &amp; footers</label>
1180
+ </div>
1181
+ </div>
1182
+ </div>
1183
+
1184
+ <!-- REFERENCES ───────────────────────────────────────────────────────── -->
1185
+ <div class="dxr-panel" data-panel="references">
1186
+ <div class="dxr-group">
1187
+ <span class="dxr-glabel">Table of contents</span>
1188
+ <div class="dxr-row">
1189
+ <button type="button" data-dxr="toc" title="Insert a table of contents built from the document's headings">&#9776; Table of Contents</button>
1190
+ </div>
1191
+ </div>
1192
+ <div class="dxr-group">
1193
+ <span class="dxr-glabel">Footnotes</span>
1194
+ <div class="dxr-row">
1195
+ <button type="button" data-dxr="footnote" title="Cite a new footnote at the caret">Insert Footnote</button>
1196
+ <button type="button" data-dxr="endnote" title="Cite a new endnote at the caret">Insert Endnote</button>
1197
+ </div>
1198
+ </div>
966
1199
  <div class="dxr-group">
967
1200
  <span class="dxr-glabel">Footer fields</span>
968
1201
  <div class="dxr-row">
969
1202
  <button type="button" data-dxr="pagenum" title="Add a page-number field to the footer">Page number</button>
970
1203
  <button type="button" data-dxr="totalpages" title="Add a total-pages field to the footer">Total pages</button>
971
1204
  </div>
972
- <div class="dxr-row">
973
- <span class="dxr-note">Added to the footer story.</span>
974
- </div>
975
1205
  </div>
976
1206
  </div>
977
1207
 
978
- <!-- REVIEW ────────────────────────────────────────────────────────────
979
- Comment authoring over the session's native comment family (issue #580):
980
- the button comments the selection (the whole block when collapsed), and
981
- the thread list is live session truth with resolve/reopen per thread. -->
1208
+ <!-- REVIEW ────────────────────────────────────────────────────────────── -->
982
1209
  <div class="dxr-panel" data-panel="review">
983
1210
  <div class="dxr-group">
984
1211
  <span class="dxr-glabel">Comments</span>
985
1212
  <div class="dxr-row">
986
- <input data-dxr="commenttext" type="text" placeholder="Comment text&#8230;"
987
- title="The comment body; markdown is honored" />
988
- <button type="button" data-dxr="comment"
989
- title="Comment on the selection, or on the whole block when nothing is selected">&#128172; Comment</button>
1213
+ <button type="button" data-dxr="comment" title="Comment on the selection (Ctrl+Alt+M)">&#128172; New Comment</button>
1214
+ <button type="button" data-dxr="commentdelete" class="dxr-danger" title="Delete the active comment thread">Delete</button>
1215
+ <button type="button" data-dxr="commentprev" class="dxr-icon" title="Previous comment">&#9650;</button>
1216
+ <button type="button" data-dxr="commentnext" class="dxr-icon" title="Next comment">&#9660;</button>
990
1217
  </div>
991
1218
  <div class="dxr-row">
992
- <span class="dxr-note">Comments the selected text; the whole block when the caret is collapsed.</span>
1219
+ <button type="button" data-dxr="commentresolve" title="Resolve or reopen the active thread">Resolve</button>
1220
+ <label class="dxr-toggle"><input data-dxr="showcomments" type="checkbox" checked /> Show comments</label>
1221
+ <span class="dxr-note" data-dxr="commentcount"></span>
993
1222
  </div>
994
1223
  </div>
995
- <div class="dxr-group dxr-grow">
996
- <span class="dxr-glabel">Threads</span>
997
- <div class="dxr-threads" data-dxr="threads" aria-live="polite">
998
- <span class="dxr-note">No comments yet.</span>
1224
+ <div class="dxr-group">
1225
+ <span class="dxr-glabel">Tracking</span>
1226
+ <div class="dxr-row">
1227
+ <label class="dxr-toggle"><input data-dxr="trackchanges" type="checkbox" /> Track changes</label>
1228
+ </div>
1229
+ <div class="dxr-row">
1230
+ <select data-dxr="markup" title="How revisions are shown">
1231
+ <option value="all">All markup</option>
1232
+ <option value="none">No markup</option>
1233
+ </select>
1234
+ <input data-dxr="author" type="text" placeholder="Author" title="Name stamped on your comments and revisions" style="width: 110px;" />
1235
+ </div>
1236
+ </div>
1237
+ <div class="dxr-group">
1238
+ <span class="dxr-glabel">Changes</span>
1239
+ <div class="dxr-row">
1240
+ <button type="button" data-rev="accept" title="Accept the change at the caret">&#10003; Accept</button>
1241
+ <button type="button" data-rev="reject" title="Reject the change at the caret">&#10007; Reject</button>
1242
+ <button type="button" data-rev="prev" class="dxr-icon" title="Previous change">&#9650;</button>
1243
+ <button type="button" data-rev="next" class="dxr-icon" title="Next change">&#9660;</button>
1244
+ </div>
1245
+ <div class="dxr-row">
1246
+ <button type="button" data-rev="acceptall" title="Accept every tracked change">Accept all</button>
1247
+ <button type="button" data-rev="rejectall" title="Reject every tracked change">Reject all</button>
1248
+ <span class="dxr-note" data-dxr="revcount"></span>
1249
+ </div>
1250
+ </div>
1251
+ </div>
1252
+
1253
+ <!-- VIEW ──────────────────────────────────────────────────────────────── -->
1254
+ <div class="dxr-panel" data-panel="view">
1255
+ <div class="dxr-group">
1256
+ <span class="dxr-glabel">Views</span>
1257
+ <div class="dxr-row">
1258
+ <button type="button" data-dxr="viewpage" title="Page view — real page boxes, headers and footers in the margins">&#128441; Page view</button>
1259
+ <button type="button" data-dxr="viewweb" title="Continuous view — one sheet, no page breaks">&#9776; Continuous</button>
1260
+ </div>
1261
+ </div>
1262
+ <div class="dxr-group">
1263
+ <span class="dxr-glabel">Zoom</span>
1264
+ <div class="dxr-row">
1265
+ <select data-dxr="zoom" title="Zoom">
1266
+ <option value="0.5">50%</option>
1267
+ <option value="0.75">75%</option>
1268
+ <option value="0.9">90%</option>
1269
+ <option value="1" selected>100%</option>
1270
+ <option value="1.25">125%</option>
1271
+ <option value="1.5">150%</option>
1272
+ <option value="2">200%</option>
1273
+ </select>
1274
+ <button type="button" data-dxr="zoomfit" title="Fit the page to the window">Fit width</button>
1275
+ </div>
1276
+ </div>
1277
+ <div class="dxr-group">
1278
+ <span class="dxr-glabel">Show</span>
1279
+ <div class="dxr-row">
1280
+ <label class="dxr-toggle"><input data-dxr="showrail" type="checkbox" checked /> Status bar</label>
1281
+ <label class="dxr-toggle"><input data-dxr="showhint" type="checkbox" checked /> Editing hint</label>
999
1282
  </div>
1000
1283
  </div>
1001
1284
  </div>
@@ -1020,21 +1303,118 @@ export const RIBBON_HTML = `
1020
1303
  <button type="button" data-tt="delCol" class="dxr-danger" title="Delete this column">Delete column</button>
1021
1304
  </div>
1022
1305
  </div>
1306
+ <div class="dxr-group">
1307
+ <span class="dxr-glabel">Merge</span>
1308
+ <div class="dxr-row">
1309
+ <button type="button" data-tt="mergeRight" title="Merge this cell with the one to its right">Merge right</button>
1310
+ <button type="button" data-tt="mergeDown" title="Merge this cell with the one below">Merge down</button>
1311
+ <button type="button" data-tt="unmerge" title="Split a merged cell back into its cells">Split</button>
1312
+ </div>
1313
+ </div>
1314
+ <div class="dxr-group">
1315
+ <span class="dxr-glabel">Design</span>
1316
+ <div class="dxr-row">
1317
+ <button type="button" data-tt="borders" title="Draw all borders">Borders</button>
1318
+ <button type="button" data-tt="noborders" title="Remove all borders">No borders</button>
1319
+ <span class="dxr-swatch" title="Cell shading">
1320
+ <button type="button" class="dxr-icon" data-dxr="shadebtn" aria-label="Cell shading">&#9638;</button>
1321
+ <span class="dxr-swatch-bar" data-dxr="shadebar" style="--dxr-swatch: #fef08a;"></span>
1322
+ <input type="color" data-dxr="shade" value="#fef08a" aria-label="Pick a cell shading" />
1323
+ </span>
1324
+ <button type="button" data-tt="noshade" title="Remove cell shading">No shading</button>
1325
+ </div>
1326
+ <div class="dxr-row">
1327
+ <label class="dxr-toggle"><input data-dxr="repeatheader" type="checkbox" /> Repeat header row</label>
1328
+ <button type="button" data-tt="delTable" class="dxr-danger" title="Delete the whole table">Delete table</button>
1329
+ </div>
1330
+ </div>
1331
+ </div>
1332
+
1333
+ <!-- HEADER & FOOTER (contextual) ────────────────────────────────────────
1334
+ Word's Header & Footer Tools: the two option checkboxes, page-number fields,
1335
+ navigation between the stories, and Close. -->
1336
+ <div class="dxr-panel" data-panel="headerfooter">
1337
+ <div class="dxr-group">
1338
+ <span class="dxr-glabel">Options</span>
1339
+ <div class="dxr-row">
1340
+ <label class="dxr-toggle"><input data-dxr="hfFirst" type="checkbox" /> Different first page</label>
1341
+ </div>
1342
+ <div class="dxr-row">
1343
+ <label class="dxr-toggle"><input data-dxr="hfEven" type="checkbox" /> Different odd &amp; even pages</label>
1344
+ </div>
1345
+ </div>
1346
+ <div class="dxr-group">
1347
+ <span class="dxr-glabel">Insert</span>
1348
+ <div class="dxr-row">
1349
+ <button type="button" data-hf="pagenum" title="Insert the page number at the end of this line">Page number</button>
1350
+ <button type="button" data-hf="totalpages" title="Insert the total page count">Total pages</button>
1351
+ <button type="button" data-hf="pageof" title="Insert &quot;Page X of Y&quot;">Page X of Y</button>
1352
+ </div>
1353
+ </div>
1354
+ <div class="dxr-group">
1355
+ <span class="dxr-glabel">Navigation</span>
1356
+ <div class="dxr-row">
1357
+ <button type="button" data-hf="header" title="Go to the header">Go to Header</button>
1358
+ <button type="button" data-hf="footer" title="Go to the footer">Go to Footer</button>
1359
+ </div>
1360
+ <div class="dxr-row">
1361
+ <span class="dxr-note" data-dxr="hfstory">Header</span>
1362
+ </div>
1363
+ </div>
1364
+ <div class="dxr-group">
1365
+ <span class="dxr-glabel">Close</span>
1366
+ <div class="dxr-row">
1367
+ <button type="button" data-hf="close" title="Return to the document body (Esc)">&#10005; Close Header and Footer</button>
1368
+ </div>
1369
+ </div>
1023
1370
  </div>
1024
1371
  </div>
1025
1372
 
1026
- <!-- Anchor rail — live engine state, not decoration. -->
1027
- <div class="dxr-rail" data-dxr-rail>
1028
- <span class="dxr-cell"><span class="dxr-k">anchor</span><span class="dxr-v" data-dxr="railAnchor">&#8212;</span></span>
1029
- <span class="dxr-cell"><span class="dxr-k">blocks</span><span class="dxr-v" data-dxr="railBlocks">&#8212;</span></span>
1030
- <span class="dxr-cell"><span class="dxr-k">session</span><span class="dxr-v" data-dxr="railSession">&#8212;</span></span>
1031
- <span class="dxr-cell"><span class="dxr-k">last op</span><span class="dxr-v" data-dxr="railOp">&#8212;</span></span>
1373
+ <!-- Find & replace strip, under the ribbon. -->
1374
+ <div class="dxr-findbar" data-dxr="findbar" hidden>
1375
+ <input data-dxr="findtext" type="search" placeholder="Find" aria-label="Find" />
1376
+ <button type="button" class="dxr-icon" data-dxr="findprev" title="Previous match (Shift+Enter)">&#9650;</button>
1377
+ <button type="button" class="dxr-icon" data-dxr="findnext" title="Next match (Enter)">&#9660;</button>
1378
+ <span class="dxr-findcount" data-dxr="findcount"></span>
1379
+ <label class="dxr-toggle"><input data-dxr="findcase" type="checkbox" /> Match case</label>
1380
+ <span data-dxr="replacegroup">
1381
+ <input data-dxr="replacetext" type="text" placeholder="Replace with" aria-label="Replace with" />
1382
+ <button type="button" data-dxr="replaceone" title="Replace this match">Replace</button>
1383
+ <button type="button" data-dxr="replaceall" title="Replace every match">Replace all</button>
1384
+ </span>
1385
+ <span class="dxr-spacer" style="flex:1"></span>
1386
+ <button type="button" class="dxr-icon" data-dxr="findclose" title="Close (Esc)">&#10005;</button>
1032
1387
  </div>
1033
1388
  </div>
1034
1389
 
1035
1390
  <div class="dxr-scroll" data-dxr-scroll>
1036
1391
  <p class="dxr-hint" data-dxr-hint></p>
1037
- <div class="dxr-surface" data-dxr="editor" data-dxr-surface data-view="continuous"></div>
1392
+ <div class="dxr-surface" data-dxr="editor" data-dxr-surface data-view="continuous" data-comments="on"></div>
1393
+ </div>
1394
+
1395
+ <!-- Status bar — live engine state (the anchor rail), Word's page/word cells, and zoom. -->
1396
+ <div class="dxr-rail" data-dxr-rail>
1397
+ <span class="dxr-cell"><span class="dxr-k">anchor</span><span class="dxr-v" data-dxr="railAnchor">&#8212;</span></span>
1398
+ <span class="dxr-cell"><span class="dxr-k">blocks</span><span class="dxr-v" data-dxr="railBlocks">&#8212;</span></span>
1399
+ <span class="dxr-cell"><span class="dxr-k">session</span><span class="dxr-v" data-dxr="railSession">&#8212;</span></span>
1400
+ <span class="dxr-cell"><span class="dxr-k">last op</span><span class="dxr-v" data-dxr="railOp">&#8212;</span></span>
1401
+ <span class="dxr-cell"><span class="dxr-v dxr-plain" data-dxr="pageinfo">&#8212;</span></span>
1402
+ <span class="dxr-cell"><span class="dxr-v dxr-plain" data-dxr="wordcount">&#8212;</span></span>
1403
+ <span class="dxr-cell dxr-cell-end">
1404
+ <span class="dxr-zoom">
1405
+ <button type="button" data-dxr="zoomout" title="Zoom out" aria-label="Zoom out">&#8722;</button>
1406
+ <select data-dxr="zoomlevel" title="Zoom" aria-label="Zoom level">
1407
+ <option value="0.5">50%</option>
1408
+ <option value="0.75">75%</option>
1409
+ <option value="0.9">90%</option>
1410
+ <option value="1" selected>100%</option>
1411
+ <option value="1.25">125%</option>
1412
+ <option value="1.5">150%</option>
1413
+ <option value="2">200%</option>
1414
+ </select>
1415
+ <button type="button" data-dxr="zoomin" title="Zoom in" aria-label="Zoom in">+</button>
1416
+ </span>
1417
+ </span>
1038
1418
  </div>
1039
1419
 
1040
1420
  <!-- Table size picker, anchored to the Insert tab's Table button. -->
@@ -1055,6 +1435,17 @@ export const RIBBON_HTML = `
1055
1435
  </div>
1056
1436
  </div>
1057
1437
 
1438
+ <!-- Link popover, anchored to the Insert tab's Link button. -->
1439
+ <div class="dxr-pop" data-dxr="linkpop">
1440
+ <form data-dxr="linkform">
1441
+ <label>Address <input data-dxr="linkurl" type="url" placeholder="https://" required /></label>
1442
+ <div class="dxr-row">
1443
+ <button type="button" data-dxr="linkcancel">Cancel</button>
1444
+ <button type="submit">Insert link</button>
1445
+ </div>
1446
+ </form>
1447
+ </div>
1448
+
1058
1449
  <div class="dxr-loader" data-dxr="loader" aria-live="polite" hidden>
1059
1450
  <div class="dxr-loader-grid">
1060
1451
  <div class="dxr-visual" aria-hidden="true">
@@ -1078,8 +1469,8 @@ export const RIBBON_HTML = `
1078
1469
  </div>
1079
1470
  `;
1080
1471
  /** The default hint copy, shown above the document in full chrome. */
1081
- export const RIBBON_HINT_HTML = "Click any paragraph, heading, table cell, footnote or header/footer line to edit it. " +
1472
+ export const RIBBON_HINT_HTML = "Click any paragraph, heading, table cell, footnote, header or footer to edit it. " +
1082
1473
  "<kbd>Enter</kbd> splits a block, <kbd>Backspace</kbd> at the start merges it into the previous one, " +
1083
- "<kbd>Ctrl</kbd>+<kbd>Z</kbd> undoes. Only the block you changed re-renders — everything else keeps " +
1084
- "full fidelity, and <b>Save</b> writes a lossless .docx.";
1474
+ "<kbd>Ctrl</kbd>+<kbd>Z</kbd> undoes. Select text and press <b>New Comment</b> to annotate it. Only the block you " +
1475
+ "changed re-renders — everything else keeps full fidelity, and <b>Save</b> writes a lossless .docx.";
1085
1476
  //# sourceMappingURL=ribbon-chrome.js.map