docxodus 9.1.0 → 9.2.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.
- package/README.md +39 -18
- package/dist/editor-reconcile.d.ts.map +1 -1
- package/dist/editor-reconcile.js +33 -5
- package/dist/editor-reconcile.js.map +1 -1
- package/dist/editor.bundle.js +1912 -24
- package/dist/editor.d.ts +52 -3
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +261 -18
- package/dist/editor.js.map +1 -1
- package/dist/embed.bundle.js +1955 -22
- package/dist/embed.d.ts +26 -1
- package/dist/embed.d.ts.map +1 -1
- package/dist/embed.iife.js +1955 -22
- package/dist/embed.js +70 -1
- package/dist/embed.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/ribbon-chrome.d.ts +45 -0
- package/dist/ribbon-chrome.d.ts.map +1 -0
- package/dist/ribbon-chrome.js +965 -0
- package/dist/ribbon-chrome.js.map +1 -0
- package/dist/ribbon.d.ts +153 -0
- package/dist/ribbon.d.ts.map +1 -0
- package/dist/ribbon.js +830 -0
- package/dist/ribbon.js.map +1 -0
- package/dist/wasm/_framework/Docxodus.wasm +0 -0
- package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +3 -3
- package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
- package/package.json +3 -3
|
@@ -0,0 +1,965 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ribbon surface's markup and stylesheet — the two static assets `ribbon.ts`
|
|
3
|
+
* wires behaviour onto.
|
|
4
|
+
*
|
|
5
|
+
* They live in their own module because they are *data*: one HTML template and
|
|
6
|
+
* one stylesheet, both authored to be read as a unit. Keeping them out of
|
|
7
|
+
* `ribbon.ts` leaves that file as pure wiring.
|
|
8
|
+
*
|
|
9
|
+
* ── Addressing ──────────────────────────────────────────────────────────────
|
|
10
|
+
* Every control carries `data-dxr="<name>"`. `ribbon.ts` walks those on mount
|
|
11
|
+
* and stamps `id = idPrefix + name`, so the historical element ids from the
|
|
12
|
+
* standalone demo page survive (specs and bookmarklets keep working) while a
|
|
13
|
+
* second ribbon on the same page can take a prefix and stay collision-free.
|
|
14
|
+
* `data-dxr-list` does the same job for the `<input list>` → `<datalist id>`
|
|
15
|
+
* link, which is by-id and cannot use a data attribute directly.
|
|
16
|
+
*
|
|
17
|
+
* ── Responsiveness ──────────────────────────────────────────────────────────
|
|
18
|
+
* Layout keys off `data-chrome` on the root ("full" | "compact"), which
|
|
19
|
+
* `ribbon.ts` sets from a ResizeObserver on the ROOT rather than from viewport
|
|
20
|
+
* media queries. A narrow embed inside a wide desktop page is narrow, and only
|
|
21
|
+
* container measurement gets that right. Compact is the base (mobile-first);
|
|
22
|
+
* `[data-chrome="full"]` adds back the desktop affordances.
|
|
23
|
+
*/
|
|
24
|
+
/** Bumped whenever RIBBON_CSS changes, so a stale injected stylesheet is replaced. */
|
|
25
|
+
export const RIBBON_STYLE_VERSION = "5";
|
|
26
|
+
export const RIBBON_STYLE_ATTR = "data-docxodus-ribbon-styles";
|
|
27
|
+
/**
|
|
28
|
+
* The ribbon stylesheet. Every rule is scoped under `.dxr` so injecting it is
|
|
29
|
+
* safe on a host page we do not control — the same guarantee `embed.ts` gives
|
|
30
|
+
* for the converter's document CSS, achieved here by authoring rather than by
|
|
31
|
+
* rewriting.
|
|
32
|
+
*/
|
|
33
|
+
export const RIBBON_CSS = `
|
|
34
|
+
.dxr {
|
|
35
|
+
--dxr-ink: #101418;
|
|
36
|
+
--dxr-sheet: #ffffff;
|
|
37
|
+
--dxr-chrome: #eef1f6;
|
|
38
|
+
--dxr-chrome-sunk: #e3e8f0;
|
|
39
|
+
--dxr-rule: #d3dae4;
|
|
40
|
+
--dxr-accent: #1f5fd0;
|
|
41
|
+
--dxr-wash: #dde8fb;
|
|
42
|
+
--dxr-muted: #5d6975;
|
|
43
|
+
--dxr-data: #0b6f6a;
|
|
44
|
+
--dxr-danger: #a3341f;
|
|
45
|
+
--dxr-desk: #dfe3ea;
|
|
46
|
+
--dxr-ui: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
47
|
+
--dxr-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;
|
|
48
|
+
--dxr-tap: 30px;
|
|
49
|
+
|
|
50
|
+
position: relative;
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
height: 100%;
|
|
54
|
+
min-height: 0;
|
|
55
|
+
isolation: isolate;
|
|
56
|
+
font-family: var(--dxr-ui);
|
|
57
|
+
color: var(--dxr-ink);
|
|
58
|
+
background: var(--dxr-desk);
|
|
59
|
+
-webkit-text-size-adjust: 100%;
|
|
60
|
+
}
|
|
61
|
+
.dxr *, .dxr *::before, .dxr *::after { box-sizing: border-box; }
|
|
62
|
+
|
|
63
|
+
/* Touch devices get bigger hit targets everywhere the token is used. */
|
|
64
|
+
@media (pointer: coarse) { .dxr { --dxr-tap: 40px; } }
|
|
65
|
+
|
|
66
|
+
/* ── Chrome shell ─────────────────────────────────────────────────────────── */
|
|
67
|
+
.dxr-chrome {
|
|
68
|
+
position: sticky;
|
|
69
|
+
top: 0;
|
|
70
|
+
z-index: 10;
|
|
71
|
+
flex: 0 0 auto;
|
|
72
|
+
background: var(--dxr-chrome);
|
|
73
|
+
border-bottom: 1px solid var(--dxr-rule);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.dxr-titlebar {
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
gap: 10px;
|
|
80
|
+
padding: 6px 10px 0;
|
|
81
|
+
}
|
|
82
|
+
.dxr-brand {
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: baseline;
|
|
85
|
+
gap: 7px;
|
|
86
|
+
min-width: 0;
|
|
87
|
+
font-size: 13px;
|
|
88
|
+
font-weight: 600;
|
|
89
|
+
letter-spacing: -.01em;
|
|
90
|
+
}
|
|
91
|
+
.dxr-brand .dxr-mark {
|
|
92
|
+
flex: 0 0 auto;
|
|
93
|
+
width: 9px;
|
|
94
|
+
height: 9px;
|
|
95
|
+
background: var(--dxr-accent);
|
|
96
|
+
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
|
|
97
|
+
}
|
|
98
|
+
.dxr-brand .dxr-docname {
|
|
99
|
+
overflow: hidden;
|
|
100
|
+
max-width: 22ch;
|
|
101
|
+
color: var(--dxr-muted);
|
|
102
|
+
font-size: 12px;
|
|
103
|
+
font-weight: 400;
|
|
104
|
+
text-overflow: ellipsis;
|
|
105
|
+
white-space: nowrap;
|
|
106
|
+
}
|
|
107
|
+
.dxr-quick { flex: 0 0 auto; display: flex; align-items: center; gap: 3px; }
|
|
108
|
+
.dxr-titlebar .dxr-spacer { flex: 1; }
|
|
109
|
+
.dxr-status {
|
|
110
|
+
overflow: hidden;
|
|
111
|
+
max-width: 42ch;
|
|
112
|
+
color: var(--dxr-muted);
|
|
113
|
+
font-family: var(--dxr-mono);
|
|
114
|
+
font-size: 11.5px;
|
|
115
|
+
text-overflow: ellipsis;
|
|
116
|
+
white-space: nowrap;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ── Tab strip ────────────────────────────────────────────────────────────── */
|
|
120
|
+
.dxr-tabs {
|
|
121
|
+
display: flex;
|
|
122
|
+
gap: 1px;
|
|
123
|
+
padding: 6px 10px 0;
|
|
124
|
+
overflow-x: auto;
|
|
125
|
+
scrollbar-width: none;
|
|
126
|
+
}
|
|
127
|
+
.dxr-tabs::-webkit-scrollbar { display: none; }
|
|
128
|
+
.dxr-tab {
|
|
129
|
+
flex: 0 0 auto;
|
|
130
|
+
padding: 6px 15px 7px;
|
|
131
|
+
border: 1px solid transparent;
|
|
132
|
+
border-bottom: none;
|
|
133
|
+
border-radius: 5px 5px 0 0;
|
|
134
|
+
background: none;
|
|
135
|
+
color: var(--dxr-muted);
|
|
136
|
+
font: inherit;
|
|
137
|
+
font-size: 12.5px;
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
}
|
|
140
|
+
.dxr-tab:hover { color: var(--dxr-ink); background: #e6ebf3; }
|
|
141
|
+
.dxr-tab[aria-selected="true"] {
|
|
142
|
+
color: var(--dxr-ink);
|
|
143
|
+
font-weight: 600;
|
|
144
|
+
background: var(--dxr-chrome-sunk);
|
|
145
|
+
border-color: var(--dxr-rule);
|
|
146
|
+
box-shadow: inset 0 2px 0 var(--dxr-accent);
|
|
147
|
+
}
|
|
148
|
+
/* Contextual tab — present only while the caret is inside a table. */
|
|
149
|
+
.dxr-tab[data-contextual] { color: var(--dxr-accent); }
|
|
150
|
+
.dxr-tab[hidden] { display: none; }
|
|
151
|
+
|
|
152
|
+
/* ── Ribbon panels ────────────────────────────────────────────────────────── */
|
|
153
|
+
.dxr-ribbon { background: var(--dxr-chrome-sunk); border-top: 1px solid var(--dxr-rule); }
|
|
154
|
+
.dxr-ribbon[aria-disabled="true"] { opacity: .45; pointer-events: none; }
|
|
155
|
+
.dxr-panel {
|
|
156
|
+
display: none;
|
|
157
|
+
align-items: stretch;
|
|
158
|
+
padding: 7px 10px 8px;
|
|
159
|
+
overflow-x: auto;
|
|
160
|
+
overscroll-behavior-x: contain;
|
|
161
|
+
}
|
|
162
|
+
.dxr-panel[data-active] { display: flex; }
|
|
163
|
+
/* Group label sits ABOVE its controls (a spec-sheet reading), with hairline
|
|
164
|
+
dividers rather than boxes — lighter than the boxed, label-under convention. */
|
|
165
|
+
.dxr-group {
|
|
166
|
+
flex: 0 0 auto;
|
|
167
|
+
display: flex;
|
|
168
|
+
flex-direction: column;
|
|
169
|
+
gap: 5px;
|
|
170
|
+
padding: 0 13px;
|
|
171
|
+
border-right: 1px solid var(--dxr-rule);
|
|
172
|
+
}
|
|
173
|
+
.dxr-group:last-child { border-right: none; }
|
|
174
|
+
.dxr-group:first-child { padding-left: 0; }
|
|
175
|
+
.dxr-glabel {
|
|
176
|
+
color: var(--dxr-muted);
|
|
177
|
+
font-size: 9.5px;
|
|
178
|
+
font-weight: 600;
|
|
179
|
+
letter-spacing: .11em;
|
|
180
|
+
text-transform: uppercase;
|
|
181
|
+
}
|
|
182
|
+
.dxr-row { display: flex; gap: 3px; align-items: center; }
|
|
183
|
+
|
|
184
|
+
/* ── Controls ─────────────────────────────────────────────────────────────── */
|
|
185
|
+
.dxr button, .dxr label.dxr-btn {
|
|
186
|
+
min-height: var(--dxr-tap);
|
|
187
|
+
padding: 5px 9px;
|
|
188
|
+
border: 1px solid transparent;
|
|
189
|
+
border-radius: 4px;
|
|
190
|
+
background: none;
|
|
191
|
+
color: var(--dxr-ink);
|
|
192
|
+
font: inherit;
|
|
193
|
+
font-size: 13px;
|
|
194
|
+
line-height: 1.15;
|
|
195
|
+
cursor: pointer;
|
|
196
|
+
}
|
|
197
|
+
.dxr label.dxr-btn { display: inline-flex; align-items: center; }
|
|
198
|
+
.dxr button:hover, .dxr label.dxr-btn:hover { background: #d6deea; }
|
|
199
|
+
.dxr button:active { background: #c8d3e3; }
|
|
200
|
+
.dxr button:disabled { opacity: .38; cursor: default; background: none; }
|
|
201
|
+
.dxr button.dxr-on { color: var(--dxr-accent); background: var(--dxr-wash); border-color: #b3ccf2; }
|
|
202
|
+
.dxr-quick button, .dxr-quick label.dxr-btn {
|
|
203
|
+
padding: 4px 10px;
|
|
204
|
+
border-color: var(--dxr-rule);
|
|
205
|
+
background: #f7f9fc;
|
|
206
|
+
font-size: 12.5px;
|
|
207
|
+
}
|
|
208
|
+
.dxr-quick button:hover, .dxr-quick label.dxr-btn:hover { background: #fff; border-color: #b9c4d4; }
|
|
209
|
+
.dxr-quick button:disabled:hover { background: #f7f9fc; border-color: var(--dxr-rule); }
|
|
210
|
+
.dxr button.dxr-icon { min-width: var(--dxr-tap); text-align: center; }
|
|
211
|
+
/* Icons are inline SVG drawn from the same shapes the control acts on (text lines,
|
|
212
|
+
rules), so alignment and indent read at a glance instead of relying on arrow
|
|
213
|
+
glyphs that collide with undo/redo. currentColor keeps them in step with state. */
|
|
214
|
+
.dxr button svg { display: block; margin: 0 auto; fill: currentColor; }
|
|
215
|
+
.dxr button.dxr-wide { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
|
|
216
|
+
.dxr button.dxr-danger:hover { color: var(--dxr-danger); background: #f6dcd6; }
|
|
217
|
+
.dxr select, .dxr input[type="number"], .dxr input[type="text"] {
|
|
218
|
+
min-height: var(--dxr-tap);
|
|
219
|
+
padding: 4px 6px;
|
|
220
|
+
border: 1px solid var(--dxr-rule);
|
|
221
|
+
border-radius: 4px;
|
|
222
|
+
background: #f7f9fc;
|
|
223
|
+
color: var(--dxr-ink);
|
|
224
|
+
font: inherit;
|
|
225
|
+
font-size: 12.5px;
|
|
226
|
+
}
|
|
227
|
+
.dxr select:focus-visible, .dxr input:focus-visible, .dxr button:focus-visible,
|
|
228
|
+
.dxr .dxr-tab:focus-visible, .dxr label.dxr-btn:focus-within {
|
|
229
|
+
outline: 2px solid var(--dxr-accent);
|
|
230
|
+
outline-offset: 1px;
|
|
231
|
+
}
|
|
232
|
+
.dxr [data-dxr="fontsize"] { width: 62px; }
|
|
233
|
+
.dxr [data-dxr="fontfamily"] { max-width: 132px; }
|
|
234
|
+
.dxr [data-dxr="pgstart"] { width: 64px; }
|
|
235
|
+
.dxr-toggle {
|
|
236
|
+
display: inline-flex;
|
|
237
|
+
gap: 5px;
|
|
238
|
+
align-items: center;
|
|
239
|
+
color: var(--dxr-ink);
|
|
240
|
+
font-size: 12.5px;
|
|
241
|
+
white-space: nowrap;
|
|
242
|
+
cursor: pointer;
|
|
243
|
+
}
|
|
244
|
+
.dxr-note { color: var(--dxr-muted); font-size: 11.5px; }
|
|
245
|
+
|
|
246
|
+
/* ── Anchor rail ──────────────────────────────────────────────────────────────
|
|
247
|
+
The addressing spine made permanent chrome. Every block in this editor is
|
|
248
|
+
addressable as kind:scope:unid, and the model of record is a live WASM session —
|
|
249
|
+
so the surface states both, live, instead of hiding them behind devtools. It also
|
|
250
|
+
reports each command's real cost, which is what a smoke test needs to see. */
|
|
251
|
+
.dxr-rail {
|
|
252
|
+
display: flex;
|
|
253
|
+
align-items: center;
|
|
254
|
+
height: 25px;
|
|
255
|
+
padding: 0 10px;
|
|
256
|
+
overflow-x: auto;
|
|
257
|
+
background: #f7f9fc;
|
|
258
|
+
border-top: 1px solid var(--dxr-rule);
|
|
259
|
+
color: var(--dxr-muted);
|
|
260
|
+
font-family: var(--dxr-mono);
|
|
261
|
+
font-size: 11.5px;
|
|
262
|
+
scrollbar-width: none;
|
|
263
|
+
}
|
|
264
|
+
.dxr-rail::-webkit-scrollbar { display: none; }
|
|
265
|
+
.dxr-rail .dxr-cell {
|
|
266
|
+
display: flex;
|
|
267
|
+
align-items: baseline;
|
|
268
|
+
gap: 6px;
|
|
269
|
+
padding: 0 13px;
|
|
270
|
+
border-right: 1px solid var(--dxr-rule);
|
|
271
|
+
white-space: nowrap;
|
|
272
|
+
}
|
|
273
|
+
.dxr-rail .dxr-cell:first-child { padding-left: 0; }
|
|
274
|
+
.dxr-rail .dxr-cell:last-child { border-right: none; }
|
|
275
|
+
.dxr-rail .dxr-k {
|
|
276
|
+
color: #8b96a3;
|
|
277
|
+
font-family: var(--dxr-ui);
|
|
278
|
+
font-size: 9.5px;
|
|
279
|
+
letter-spacing: .1em;
|
|
280
|
+
text-transform: uppercase;
|
|
281
|
+
}
|
|
282
|
+
.dxr-rail .dxr-v { color: var(--dxr-data); }
|
|
283
|
+
.dxr-rail .dxr-v.dxr-flash { animation: dxr-railflash .45s ease-out; }
|
|
284
|
+
@keyframes dxr-railflash { from { background: #b9ecdf; } to { background: transparent; } }
|
|
285
|
+
|
|
286
|
+
/* ── Hint ─────────────────────────────────────────────────────────────────── */
|
|
287
|
+
.dxr-hint {
|
|
288
|
+
flex: 0 0 auto;
|
|
289
|
+
max-width: 920px;
|
|
290
|
+
margin: 0 auto;
|
|
291
|
+
padding: 9px 16px 0;
|
|
292
|
+
color: var(--dxr-muted);
|
|
293
|
+
font-size: 12px;
|
|
294
|
+
line-height: 1.5;
|
|
295
|
+
}
|
|
296
|
+
.dxr-hint kbd {
|
|
297
|
+
padding: 0 4px;
|
|
298
|
+
border: 1px solid var(--dxr-rule);
|
|
299
|
+
border-radius: 3px;
|
|
300
|
+
background: #e8ecf3;
|
|
301
|
+
font-family: var(--dxr-mono);
|
|
302
|
+
font-size: 11px;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* ── Document surface ──────────────────────────────────────────────────────────
|
|
306
|
+
The surface is also the element the converter's own stylesheet treats as the
|
|
307
|
+
document body: in an embed, its "body { margin: 20px }" is rewritten to
|
|
308
|
+
[data-docxodus-embed-root="dN"] [data-dxr-surface] — (0,2,0), and inserted AFTER
|
|
309
|
+
this sheet, so it wins a tie. Rules that own the SHEET BOX (centering, page
|
|
310
|
+
padding, the paper itself) therefore qualify with the root's data-chrome to reach
|
|
311
|
+
(0,3,0). Rules that only style CONTENT stay unqualified — there the converter
|
|
312
|
+
SHOULD win.
|
|
313
|
+
(No backticks in this file's comments: the stylesheet is a template literal.) */
|
|
314
|
+
.dxr-scroll { flex: 1 1 auto; min-height: 0; overflow: auto; -webkit-overflow-scrolling: touch; }
|
|
315
|
+
.dxr[data-chrome] .dxr-surface { max-width: 920px; margin: 26px auto; padding: 0 16px 96px; }
|
|
316
|
+
.dxr-surface[data-view="continuous"] > * { background: var(--dxr-sheet); }
|
|
317
|
+
.dxr[data-chrome] .dxr-surface[data-view="continuous"] {
|
|
318
|
+
padding: 56px 72px;
|
|
319
|
+
border-radius: 3px;
|
|
320
|
+
background: var(--dxr-sheet);
|
|
321
|
+
box-shadow: 0 1px 3px rgba(16, 20, 24, .14), 0 8px 24px rgba(16, 20, 24, .05);
|
|
322
|
+
}
|
|
323
|
+
.dxr-surface [contenteditable="true"]:focus {
|
|
324
|
+
outline: 2px solid var(--dxr-accent);
|
|
325
|
+
outline-offset: 2px;
|
|
326
|
+
border-radius: 2px;
|
|
327
|
+
}
|
|
328
|
+
@media (hover: hover) { .dxr-surface [contenteditable="true"]:hover { background: #f2f7ff; } }
|
|
329
|
+
|
|
330
|
+
/* Header/footer bands: stories live in their own OOXML parts outside the body,
|
|
331
|
+
so they dock as their own regions. Styled from the same tokens as the ribbon
|
|
332
|
+
so the surface reads as one instrument rather than two apps. */
|
|
333
|
+
.dxr-surface .docx-hf-band {
|
|
334
|
+
margin: 0 0 14px;
|
|
335
|
+
padding: 9px 72px 13px;
|
|
336
|
+
border: 1px solid var(--dxr-rule);
|
|
337
|
+
border-left: 2px solid #c2ccd9;
|
|
338
|
+
border-radius: 3px;
|
|
339
|
+
background: var(--dxr-sheet);
|
|
340
|
+
}
|
|
341
|
+
.dxr-surface .docx-hf-band + .docx-body-flow { margin-top: 0; }
|
|
342
|
+
.dxr-surface .docx-hf-band[data-hf-band="footer"] { margin: 14px 0 0; }
|
|
343
|
+
.dxr-surface .docx-hf-chrome {
|
|
344
|
+
display: flex;
|
|
345
|
+
gap: 8px;
|
|
346
|
+
align-items: center;
|
|
347
|
+
flex-wrap: wrap;
|
|
348
|
+
margin: 0 0 7px -60px;
|
|
349
|
+
color: var(--dxr-muted);
|
|
350
|
+
font-size: 9.5px;
|
|
351
|
+
font-weight: 600;
|
|
352
|
+
letter-spacing: .11em;
|
|
353
|
+
text-transform: uppercase;
|
|
354
|
+
}
|
|
355
|
+
.dxr-surface .docx-hf-chrome select, .dxr-surface .docx-hf-chrome input {
|
|
356
|
+
padding: 3px 5px;
|
|
357
|
+
border: 1px solid var(--dxr-rule);
|
|
358
|
+
border-radius: 4px;
|
|
359
|
+
background: #f7f9fc;
|
|
360
|
+
color: var(--dxr-ink);
|
|
361
|
+
font: inherit;
|
|
362
|
+
font-family: var(--dxr-ui);
|
|
363
|
+
font-size: 12.5px;
|
|
364
|
+
font-weight: 400;
|
|
365
|
+
letter-spacing: normal;
|
|
366
|
+
text-transform: none;
|
|
367
|
+
}
|
|
368
|
+
.dxr-surface .docx-hf-chrome input[data-hf-pagestart] { width: 64px; }
|
|
369
|
+
.dxr-surface .docx-hf-label { font-weight: 600; }
|
|
370
|
+
.dxr-surface .docx-hf-warning {
|
|
371
|
+
margin: 0 0 8px -60px;
|
|
372
|
+
padding: 7px 9px;
|
|
373
|
+
border: 1px solid #e8d9a8;
|
|
374
|
+
border-left: 2px solid #c9a227;
|
|
375
|
+
border-radius: 3px;
|
|
376
|
+
background: #fdf6e3;
|
|
377
|
+
color: #6b5310;
|
|
378
|
+
font-size: 12px;
|
|
379
|
+
line-height: 1.45;
|
|
380
|
+
}
|
|
381
|
+
.dxr-surface .docx-hf-warning button {
|
|
382
|
+
margin-left: 6px;
|
|
383
|
+
padding: 2px 8px;
|
|
384
|
+
border-color: #e8d9a8;
|
|
385
|
+
background: #fff;
|
|
386
|
+
font-size: 12px;
|
|
387
|
+
}
|
|
388
|
+
.dxr-surface .docx-hf-placeholder { color: #9aa4b1; font-style: italic; }
|
|
389
|
+
.dxr-surface .docx-hf-inherited {
|
|
390
|
+
color: var(--dxr-muted);
|
|
391
|
+
font-style: italic;
|
|
392
|
+
font-weight: 400;
|
|
393
|
+
letter-spacing: normal;
|
|
394
|
+
text-transform: none;
|
|
395
|
+
}
|
|
396
|
+
.dxr-surface .docx-hf-band[data-hf-inherited] { border-style: dashed; }
|
|
397
|
+
.dxr[data-chrome] .dxr-surface[data-view="continuous"]:has(.docx-body-flow) {
|
|
398
|
+
padding: 0;
|
|
399
|
+
background: transparent;
|
|
400
|
+
box-shadow: none;
|
|
401
|
+
}
|
|
402
|
+
.dxr[data-chrome] .dxr-surface[data-view="continuous"] .docx-body-flow {
|
|
403
|
+
padding: 56px 72px;
|
|
404
|
+
border-radius: 3px;
|
|
405
|
+
background: var(--dxr-sheet);
|
|
406
|
+
box-shadow: 0 1px 3px rgba(16, 20, 24, .14), 0 8px 24px rgba(16, 20, 24, .05);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* ── Table size picker ────────────────────────────────────────────────────── */
|
|
410
|
+
.dxr-pop {
|
|
411
|
+
display: none;
|
|
412
|
+
position: absolute;
|
|
413
|
+
z-index: 30;
|
|
414
|
+
padding: 9px;
|
|
415
|
+
border: 1px solid var(--dxr-rule);
|
|
416
|
+
border-radius: 6px;
|
|
417
|
+
background: #fff;
|
|
418
|
+
box-shadow: 0 4px 16px rgba(16, 20, 24, .18);
|
|
419
|
+
}
|
|
420
|
+
.dxr-pop[data-open] { display: block; }
|
|
421
|
+
.dxr-gridcells { display: grid; grid-template-columns: repeat(10, 16px); gap: 2px; }
|
|
422
|
+
.dxr-gridcells div {
|
|
423
|
+
width: 16px;
|
|
424
|
+
height: 16px;
|
|
425
|
+
border: 1px solid var(--dxr-rule);
|
|
426
|
+
border-radius: 2px;
|
|
427
|
+
background: #f2f5f9;
|
|
428
|
+
cursor: pointer;
|
|
429
|
+
}
|
|
430
|
+
.dxr-gridcells div[data-on] { background: var(--dxr-wash); border-color: var(--dxr-accent); }
|
|
431
|
+
.dxr-popfoot {
|
|
432
|
+
display: flex;
|
|
433
|
+
justify-content: space-between;
|
|
434
|
+
align-items: center;
|
|
435
|
+
flex-wrap: wrap;
|
|
436
|
+
gap: 10px;
|
|
437
|
+
margin-top: 8px;
|
|
438
|
+
font-size: 12px;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* ── Compact chrome (the mobile-first base) ───────────────────────────────────
|
|
442
|
+
One scrolling strip per tab instead of a multi-row ribbon: group labels turn
|
|
443
|
+
into inline dividers, the rail and hint step aside, and the picker becomes a
|
|
444
|
+
bottom sheet because there is no room to hang a popover off a button. */
|
|
445
|
+
/* A 320px phone cannot hold the product name AND the file actions, and the file
|
|
446
|
+
actions are what a user came for. The strip also scrolls, so nothing is stranded. */
|
|
447
|
+
.dxr[data-chrome="compact"] .dxr-titlebar {
|
|
448
|
+
gap: 6px;
|
|
449
|
+
padding: 5px 8px 0;
|
|
450
|
+
overflow-x: auto;
|
|
451
|
+
scrollbar-width: none;
|
|
452
|
+
}
|
|
453
|
+
.dxr[data-chrome="compact"] .dxr-titlebar::-webkit-scrollbar { display: none; }
|
|
454
|
+
.dxr[data-chrome="compact"] .dxr-brandname { display: none; }
|
|
455
|
+
.dxr[data-chrome="compact"] .dxr-brand { flex: 0 1 auto; }
|
|
456
|
+
/* A filename clipped to "do..." tells you less than no filename at all, so it keeps a
|
|
457
|
+
readable floor and the strip scrolls instead. The tighter file-action padding is what
|
|
458
|
+
buys that floor back on a 390px screen. */
|
|
459
|
+
.dxr[data-chrome="compact"] .dxr-brand .dxr-docname { min-width: 6ch; max-width: 14ch; }
|
|
460
|
+
.dxr[data-chrome="compact"] .dxr-quick button,
|
|
461
|
+
.dxr[data-chrome="compact"] .dxr-quick label.dxr-btn { padding: 4px 8px; }
|
|
462
|
+
.dxr[data-chrome="compact"] .dxr-status { display: none; }
|
|
463
|
+
.dxr[data-chrome="compact"] .dxr-tabs { padding: 5px 8px 0; }
|
|
464
|
+
.dxr[data-chrome="compact"] .dxr-tab { padding: 6px 12px 7px; font-size: 12px; }
|
|
465
|
+
.dxr[data-chrome="compact"] .dxr-panel {
|
|
466
|
+
align-items: center;
|
|
467
|
+
gap: 4px;
|
|
468
|
+
padding: 5px 8px;
|
|
469
|
+
scroll-snap-type: x proximity;
|
|
470
|
+
}
|
|
471
|
+
.dxr[data-chrome="compact"] .dxr-group {
|
|
472
|
+
flex-direction: row;
|
|
473
|
+
align-items: center;
|
|
474
|
+
gap: 4px;
|
|
475
|
+
padding: 0 8px;
|
|
476
|
+
scroll-snap-align: start;
|
|
477
|
+
}
|
|
478
|
+
.dxr[data-chrome="compact"] .dxr-glabel { display: none; }
|
|
479
|
+
.dxr[data-chrome="compact"] .dxr-note { display: none; }
|
|
480
|
+
.dxr[data-chrome="compact"] .dxr-rail { display: none; }
|
|
481
|
+
.dxr[data-chrome="compact"] .dxr-hint { display: none; }
|
|
482
|
+
.dxr[data-chrome="compact"] .dxr-surface { margin: 12px auto; padding: 0 10px 64px; }
|
|
483
|
+
.dxr[data-chrome="compact"] .dxr-surface[data-view="continuous"] { padding: 22px 18px; }
|
|
484
|
+
.dxr[data-chrome="compact"] .dxr-surface[data-view="continuous"] .docx-body-flow { padding: 22px 18px; }
|
|
485
|
+
.dxr[data-chrome="compact"] .dxr-surface .docx-hf-band { padding: 9px 18px 13px; }
|
|
486
|
+
.dxr[data-chrome="compact"] .dxr-surface .docx-hf-chrome,
|
|
487
|
+
.dxr[data-chrome="compact"] .dxr-surface .docx-hf-warning { margin-left: 0; }
|
|
488
|
+
/* A popover anchored to a button has nowhere to go on a narrow surface, so the
|
|
489
|
+
picker docks to the bottom edge where a thumb already is. */
|
|
490
|
+
.dxr[data-chrome="compact"] .dxr-pop[data-open] {
|
|
491
|
+
position: fixed;
|
|
492
|
+
left: 50%;
|
|
493
|
+
right: auto;
|
|
494
|
+
bottom: 12px;
|
|
495
|
+
top: auto !important;
|
|
496
|
+
transform: translateX(-50%);
|
|
497
|
+
max-width: calc(100vw - 20px);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/* ── Loading overlay ──────────────────────────────────────────────────────────
|
|
501
|
+
Kept from the shipped demo: the wait is real (a .NET runtime is streaming), so
|
|
502
|
+
the surface spends it explaining what is being built rather than showing a
|
|
503
|
+
spinner. It covers the whole instrument so half-built chrome never flashes. */
|
|
504
|
+
.dxr-loader {
|
|
505
|
+
position: absolute;
|
|
506
|
+
inset: 0;
|
|
507
|
+
z-index: 40;
|
|
508
|
+
display: grid;
|
|
509
|
+
place-items: center;
|
|
510
|
+
padding: 24px;
|
|
511
|
+
color: #f4f9ff;
|
|
512
|
+
background:
|
|
513
|
+
radial-gradient(circle at 20% 20%, rgba(63, 128, 255, .24), transparent 22rem),
|
|
514
|
+
radial-gradient(circle at 82% 80%, rgba(176, 91, 255, .22), transparent 24rem),
|
|
515
|
+
linear-gradient(145deg, #071221 0%, #0b1930 52%, #101426 100%);
|
|
516
|
+
transition: opacity .55s ease, visibility .55s ease;
|
|
517
|
+
}
|
|
518
|
+
.dxr-loader[hidden] { display: none; }
|
|
519
|
+
/* pointer-events drops the instant the fade starts: the surface underneath is already
|
|
520
|
+
live, so a click during the half-second fade should reach it rather than be eaten. */
|
|
521
|
+
.dxr-loader[data-done] { opacity: 0; visibility: hidden; pointer-events: none; }
|
|
522
|
+
.dxr-loader-grid {
|
|
523
|
+
width: min(850px, 100%);
|
|
524
|
+
display: grid;
|
|
525
|
+
grid-template-columns: 1fr;
|
|
526
|
+
align-items: center;
|
|
527
|
+
gap: 22px;
|
|
528
|
+
text-align: center;
|
|
529
|
+
}
|
|
530
|
+
.dxr-visual { position: relative; width: min(170px, 52vw); aspect-ratio: 1; margin: 0 auto; }
|
|
531
|
+
.dxr-orbit {
|
|
532
|
+
position: absolute;
|
|
533
|
+
inset: 7%;
|
|
534
|
+
border: 1px solid rgba(115, 204, 255, .22);
|
|
535
|
+
border-radius: 50%;
|
|
536
|
+
animation: dxr-spin 9s linear infinite;
|
|
537
|
+
}
|
|
538
|
+
.dxr-orbit.dxr-two {
|
|
539
|
+
inset: 20%;
|
|
540
|
+
border-style: dashed;
|
|
541
|
+
border-color: rgba(196, 128, 255, .32);
|
|
542
|
+
animation-duration: 6s;
|
|
543
|
+
animation-direction: reverse;
|
|
544
|
+
}
|
|
545
|
+
.dxr-orbit::before, .dxr-orbit::after {
|
|
546
|
+
position: absolute;
|
|
547
|
+
width: 10px;
|
|
548
|
+
height: 10px;
|
|
549
|
+
border-radius: 50%;
|
|
550
|
+
content: "";
|
|
551
|
+
background: #52e5ff;
|
|
552
|
+
box-shadow: 0 0 18px #52e5ff;
|
|
553
|
+
}
|
|
554
|
+
.dxr-orbit::before { top: -5px; left: 50%; }
|
|
555
|
+
.dxr-orbit::after { right: 7%; bottom: 12%; background: #b26cff; box-shadow: 0 0 18px #b26cff; }
|
|
556
|
+
.dxr-card {
|
|
557
|
+
position: absolute;
|
|
558
|
+
inset: 24% 28%;
|
|
559
|
+
padding: 20px 15px;
|
|
560
|
+
border: 1px solid rgba(255, 255, 255, .33);
|
|
561
|
+
border-radius: 14px;
|
|
562
|
+
background: linear-gradient(150deg, rgba(255, 255, 255, .18), rgba(255, 255, 255, .06));
|
|
563
|
+
box-shadow: 0 22px 50px rgba(0, 0, 0, .35), 0 0 45px rgba(71, 150, 255, .13);
|
|
564
|
+
backdrop-filter: blur(10px);
|
|
565
|
+
animation: dxr-float 3.6s ease-in-out infinite;
|
|
566
|
+
}
|
|
567
|
+
.dxr-card::before {
|
|
568
|
+
position: absolute;
|
|
569
|
+
top: -9px;
|
|
570
|
+
right: -9px;
|
|
571
|
+
padding: 4px 7px;
|
|
572
|
+
border-radius: 6px;
|
|
573
|
+
background: #52e5ff;
|
|
574
|
+
color: #06111d;
|
|
575
|
+
content: "DOCX";
|
|
576
|
+
font-size: 8px;
|
|
577
|
+
font-weight: 900;
|
|
578
|
+
letter-spacing: .12em;
|
|
579
|
+
}
|
|
580
|
+
.dxr-card i {
|
|
581
|
+
display: block;
|
|
582
|
+
height: 4px;
|
|
583
|
+
margin-bottom: 10px;
|
|
584
|
+
border-radius: 4px;
|
|
585
|
+
background: rgba(255, 255, 255, .56);
|
|
586
|
+
transform-origin: left;
|
|
587
|
+
animation: dxr-pulse 2.2s ease-in-out infinite;
|
|
588
|
+
}
|
|
589
|
+
.dxr-card i:nth-child(2) { width: 76%; animation-delay: -.45s; }
|
|
590
|
+
.dxr-card i:nth-child(3) { width: 88%; animation-delay: -.85s; }
|
|
591
|
+
.dxr-card i:nth-child(4) { width: 58%; background: rgba(255, 111, 139, .75); animation-delay: -1.2s; }
|
|
592
|
+
.dxr-chip {
|
|
593
|
+
position: absolute;
|
|
594
|
+
display: grid;
|
|
595
|
+
width: 34px;
|
|
596
|
+
height: 34px;
|
|
597
|
+
place-items: center;
|
|
598
|
+
border: 1px solid rgba(255, 255, 255, .17);
|
|
599
|
+
border-radius: 11px;
|
|
600
|
+
background: rgba(12, 28, 49, .88);
|
|
601
|
+
box-shadow: 0 12px 30px rgba(0, 0, 0, .28);
|
|
602
|
+
font-size: 11px;
|
|
603
|
+
font-weight: 850;
|
|
604
|
+
}
|
|
605
|
+
.dxr-chip.dxr-b { left: 2%; top: 30%; color: #52e5ff; animation: dxr-chip 3s ease-in-out infinite; }
|
|
606
|
+
.dxr-chip.dxr-r { right: -2%; bottom: 24%; color: #ff8da0; animation: dxr-chip 3s -1.4s ease-in-out infinite; }
|
|
607
|
+
.dxr-chip.dxr-s { left: 20%; bottom: 0; color: #52e0a2; animation: dxr-chip 3s -.7s ease-in-out infinite; }
|
|
608
|
+
.dxr-eyebrow { color: #52e5ff; font-size: 10px; font-weight: 850; letter-spacing: .16em; text-transform: uppercase; }
|
|
609
|
+
.dxr-loader h2 {
|
|
610
|
+
margin: 10px auto 10px;
|
|
611
|
+
max-width: 520px;
|
|
612
|
+
font-size: clamp(23px, 5vw, 40px);
|
|
613
|
+
line-height: 1.05;
|
|
614
|
+
letter-spacing: -.045em;
|
|
615
|
+
}
|
|
616
|
+
.dxr-loader-copy > p { margin: 0 auto; max-width: 46ch; color: #9eb2c9; font-size: 13.5px; line-height: 1.6; }
|
|
617
|
+
.dxr-ad {
|
|
618
|
+
display: flex;
|
|
619
|
+
gap: 12px;
|
|
620
|
+
margin: 20px auto 0;
|
|
621
|
+
max-width: 420px;
|
|
622
|
+
padding: 13px;
|
|
623
|
+
border: 1px solid rgba(126, 160, 200, .16);
|
|
624
|
+
border-radius: 13px;
|
|
625
|
+
background: rgba(255, 255, 255, .04);
|
|
626
|
+
text-align: left;
|
|
627
|
+
}
|
|
628
|
+
.dxr-ad .dxr-num { color: #b26cff; font: 800 10px/1.4 var(--dxr-mono); }
|
|
629
|
+
.dxr-ad strong { display: block; font-size: 12.5px; }
|
|
630
|
+
.dxr-ad .dxr-adcopy { display: block; margin-top: 4px; color: #849bb5; font-size: 11.5px; line-height: 1.45; }
|
|
631
|
+
.dxr-ad[data-swap] { animation: dxr-swap .4s ease; }
|
|
632
|
+
.dxr-track {
|
|
633
|
+
height: 3px;
|
|
634
|
+
margin: 22px auto 0;
|
|
635
|
+
max-width: 420px;
|
|
636
|
+
overflow: hidden;
|
|
637
|
+
border-radius: 999px;
|
|
638
|
+
background: rgba(255, 255, 255, .09);
|
|
639
|
+
}
|
|
640
|
+
.dxr-bar {
|
|
641
|
+
width: 12%;
|
|
642
|
+
height: 100%;
|
|
643
|
+
border-radius: inherit;
|
|
644
|
+
background: linear-gradient(90deg, #52e5ff, #5c7cff, #b26cff);
|
|
645
|
+
box-shadow: 0 0 16px rgba(82, 229, 255, .55);
|
|
646
|
+
transition: width .65s cubic-bezier(.22, .8, .28, 1);
|
|
647
|
+
}
|
|
648
|
+
.dxr-meta {
|
|
649
|
+
display: flex;
|
|
650
|
+
justify-content: space-between;
|
|
651
|
+
gap: 14px;
|
|
652
|
+
margin: 9px auto 0;
|
|
653
|
+
max-width: 420px;
|
|
654
|
+
color: #647e9c;
|
|
655
|
+
font: 700 9px/1 var(--dxr-mono);
|
|
656
|
+
letter-spacing: .09em;
|
|
657
|
+
text-transform: uppercase;
|
|
658
|
+
}
|
|
659
|
+
.dxr-retry {
|
|
660
|
+
display: none;
|
|
661
|
+
margin-top: 18px;
|
|
662
|
+
padding: 9px 14px;
|
|
663
|
+
border: 1px solid rgba(255, 127, 145, .4);
|
|
664
|
+
border-radius: 9px;
|
|
665
|
+
background: rgba(255, 127, 145, .12);
|
|
666
|
+
color: #fff;
|
|
667
|
+
cursor: pointer;
|
|
668
|
+
}
|
|
669
|
+
.dxr-loader[data-error] .dxr-retry { display: inline-flex; }
|
|
670
|
+
.dxr-loader[data-error] .dxr-visual { opacity: .35; }
|
|
671
|
+
|
|
672
|
+
/* Two columns once there is room — the visual earns its space beside the copy. */
|
|
673
|
+
@media (min-width: 760px) {
|
|
674
|
+
.dxr-loader-grid {
|
|
675
|
+
grid-template-columns: minmax(220px, .8fr) minmax(300px, 1.2fr);
|
|
676
|
+
gap: clamp(28px, 6vw, 72px);
|
|
677
|
+
text-align: left;
|
|
678
|
+
}
|
|
679
|
+
.dxr-visual { width: min(280px, 30vw); }
|
|
680
|
+
.dxr-loader h2 { margin-left: 0; }
|
|
681
|
+
.dxr-loader-copy > p { margin-left: 0; min-height: 44px; }
|
|
682
|
+
.dxr-ad, .dxr-track, .dxr-meta { margin-left: 0; }
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
@keyframes dxr-spin { to { transform: rotate(360deg); } }
|
|
686
|
+
@keyframes dxr-float { 0%, 100% { transform: translateY(-5px) rotate(-2deg); } 50% { transform: translateY(7px) rotate(1deg); } }
|
|
687
|
+
@keyframes dxr-chip { 0%, 100% { transform: translateY(-4px); } 50% { transform: translateY(5px); } }
|
|
688
|
+
@keyframes dxr-pulse { 0%, 100% { transform: scaleX(.65); opacity: .45; } 50% { transform: scaleX(1); opacity: .95; } }
|
|
689
|
+
@keyframes dxr-swap { from { opacity: .1; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
|
|
690
|
+
|
|
691
|
+
@media (prefers-reduced-motion: reduce) {
|
|
692
|
+
.dxr *, .dxr *::before, .dxr *::after {
|
|
693
|
+
animation-duration: .01ms !important;
|
|
694
|
+
animation-iteration-count: 1 !important;
|
|
695
|
+
transition-duration: .01ms !important;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
`;
|
|
699
|
+
const ICON_ALIGN = (bars) => `<svg width="15" height="13" viewBox="0 0 15 13" aria-hidden="true">${bars}</svg>`;
|
|
700
|
+
/**
|
|
701
|
+
* The ribbon's DOM, as one template.
|
|
702
|
+
*
|
|
703
|
+
* `data-dxr` names are the addressing contract (see the module header); the
|
|
704
|
+
* behavioural attributes (`data-cmd`, `data-align`, `data-indent`, `data-list`,
|
|
705
|
+
* `data-tt`) are what `ribbon.ts` delegates on, so adding a control here that
|
|
706
|
+
* follows an existing convention needs no wiring change.
|
|
707
|
+
*/
|
|
708
|
+
export const RIBBON_HTML = `
|
|
709
|
+
<div class="dxr-chrome">
|
|
710
|
+
<div class="dxr-titlebar">
|
|
711
|
+
<span class="dxr-brand"><span class="dxr-mark"></span><span class="dxr-brandname">Docxodus</span>
|
|
712
|
+
<span class="dxr-docname" data-dxr="docname">no document</span></span>
|
|
713
|
+
<div class="dxr-quick" data-dxr-files>
|
|
714
|
+
<button type="button" data-dxr="new" title="Start a new blank document">New</button>
|
|
715
|
+
<label class="dxr-btn" tabindex="0">Open<input data-dxr="file" type="file" accept=".docx" hidden /></label>
|
|
716
|
+
<button type="button" data-dxr="save" disabled>Save</button>
|
|
717
|
+
</div>
|
|
718
|
+
<div class="dxr-quick">
|
|
719
|
+
<button type="button" class="dxr-icon" data-dxr="undo" title="Undo (Ctrl+Z)" aria-label="Undo">↶</button>
|
|
720
|
+
<button type="button" class="dxr-icon" data-dxr="redo" title="Redo (Ctrl+Shift+Z)" aria-label="Redo">↷</button>
|
|
721
|
+
</div>
|
|
722
|
+
<span class="dxr-spacer"></span>
|
|
723
|
+
<span class="dxr-status" data-dxr="status" role="status" aria-live="polite">Booting WASM…</span>
|
|
724
|
+
</div>
|
|
725
|
+
|
|
726
|
+
<div class="dxr-tabs" role="tablist">
|
|
727
|
+
<button type="button" class="dxr-tab" role="tab" data-tab="home" aria-selected="true">Home</button>
|
|
728
|
+
<button type="button" class="dxr-tab" role="tab" data-tab="insert" aria-selected="false">Insert</button>
|
|
729
|
+
<button type="button" class="dxr-tab" role="tab" data-tab="layout" aria-selected="false">Layout</button>
|
|
730
|
+
<button type="button" class="dxr-tab" role="tab" data-tab="table" data-contextual aria-selected="false" hidden>Table</button>
|
|
731
|
+
</div>
|
|
732
|
+
|
|
733
|
+
<div class="dxr-ribbon" data-dxr="ribbon" aria-disabled="true">
|
|
734
|
+
<!-- HOME ──────────────────────────────────────────────────────────────── -->
|
|
735
|
+
<div class="dxr-panel" data-panel="home" data-active>
|
|
736
|
+
<div class="dxr-group">
|
|
737
|
+
<span class="dxr-glabel">Text</span>
|
|
738
|
+
<div class="dxr-row">
|
|
739
|
+
<button type="button" class="dxr-icon" data-cmd="bold" title="Bold (Ctrl+B)"><b>B</b></button>
|
|
740
|
+
<button type="button" class="dxr-icon" data-cmd="italic" title="Italic (Ctrl+I)"><i>I</i></button>
|
|
741
|
+
<button type="button" class="dxr-icon" data-cmd="underline" title="Underline (Ctrl+U)"><u>U</u></button>
|
|
742
|
+
<button type="button" class="dxr-icon" data-cmd="strike" title="Strikethrough"><s>S</s></button>
|
|
743
|
+
<button type="button" class="dxr-icon" data-cmd="code" title="Inline code"></></button>
|
|
744
|
+
<button type="button" class="dxr-icon" data-cmd="superscript" title="Superscript">x²</button>
|
|
745
|
+
<button type="button" class="dxr-icon" data-cmd="subscript" title="Subscript">x₂</button>
|
|
746
|
+
</div>
|
|
747
|
+
<div class="dxr-row">
|
|
748
|
+
<input data-dxr="fontsize" data-dxr-list="fontsizes" type="number" min="1" max="1638" step="0.5"
|
|
749
|
+
placeholder="pt" title="Font size in points — type any value or pick a preset" />
|
|
750
|
+
<datalist data-dxr="fontsizes">
|
|
751
|
+
<option>8</option><option>9</option><option>10</option><option>11</option>
|
|
752
|
+
<option>12</option><option>14</option><option>16</option><option>18</option>
|
|
753
|
+
<option>20</option><option>24</option><option>28</option><option>36</option>
|
|
754
|
+
<option>48</option><option>72</option><option>96</option>
|
|
755
|
+
</datalist>
|
|
756
|
+
<select data-dxr="fontfamily" title="Font family — applies to the selection">
|
|
757
|
+
<option value="">Font…</option>
|
|
758
|
+
<option>Calibri</option><option>Times New Roman</option><option>Arial</option>
|
|
759
|
+
<option>Georgia</option><option>Cambria</option><option>Courier New</option>
|
|
760
|
+
<option>Verdana</option><option>Garamond</option>
|
|
761
|
+
</select>
|
|
762
|
+
</div>
|
|
763
|
+
</div>
|
|
764
|
+
|
|
765
|
+
<div class="dxr-group">
|
|
766
|
+
<span class="dxr-glabel">Paragraph</span>
|
|
767
|
+
<div class="dxr-row">
|
|
768
|
+
<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>
|
|
769
|
+
<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>
|
|
770
|
+
<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>
|
|
771
|
+
<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>
|
|
772
|
+
<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>
|
|
773
|
+
<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>
|
|
774
|
+
</div>
|
|
775
|
+
<div class="dxr-row">
|
|
776
|
+
<button type="button" data-list="bullet" title="Bullet list">• List</button>
|
|
777
|
+
<button type="button" data-list="decimal" title="Numbered list">1. List</button>
|
|
778
|
+
<button type="button" data-pagebreak title="Start this block on a new page">Page break</button>
|
|
779
|
+
</div>
|
|
780
|
+
</div>
|
|
781
|
+
|
|
782
|
+
<div class="dxr-group">
|
|
783
|
+
<span class="dxr-glabel">Block</span>
|
|
784
|
+
<div class="dxr-row">
|
|
785
|
+
<select data-dxr="style" title="Paragraph style">
|
|
786
|
+
<option value="">Style…</option>
|
|
787
|
+
<option value="Normal">Normal</option>
|
|
788
|
+
<option value="Heading1">Heading 1</option>
|
|
789
|
+
<option value="Heading2">Heading 2</option>
|
|
790
|
+
<option value="Heading3">Heading 3</option>
|
|
791
|
+
<option value="Title">Title</option>
|
|
792
|
+
</select>
|
|
793
|
+
</div>
|
|
794
|
+
<div class="dxr-row">
|
|
795
|
+
<button type="button" data-dxr="delblock" class="dxr-danger" title="Delete the block the caret is in">Delete block</button>
|
|
796
|
+
</div>
|
|
797
|
+
</div>
|
|
798
|
+
</div>
|
|
799
|
+
|
|
800
|
+
<!-- INSERT ────────────────────────────────────────────────────────────── -->
|
|
801
|
+
<div class="dxr-panel" data-panel="insert">
|
|
802
|
+
<div class="dxr-group">
|
|
803
|
+
<span class="dxr-glabel">Table</span>
|
|
804
|
+
<div class="dxr-row">
|
|
805
|
+
<button type="button" data-dxr="table" title="Insert a table — pick its size on the grid">▦ Table</button>
|
|
806
|
+
</div>
|
|
807
|
+
</div>
|
|
808
|
+
|
|
809
|
+
<div class="dxr-group">
|
|
810
|
+
<span class="dxr-glabel">Rules</span>
|
|
811
|
+
<div class="dxr-row">
|
|
812
|
+
<button type="button" data-dxr="hr" class="dxr-wide" title="Insert a single rule"><svg width="22" height="8" viewBox="0 0 22 8" aria-hidden="true"><rect x="0" y="3.2" width="22" height="1.4"/></svg>Single</button>
|
|
813
|
+
<button type="button" data-dxr="hrThick" class="dxr-wide" title="Insert a thick rule"><svg width="22" height="8" viewBox="0 0 22 8" aria-hidden="true"><rect x="0" y="2.4" width="22" height="3.2"/></svg>Thick</button>
|
|
814
|
+
<button type="button" data-dxr="hrDouble" class="dxr-wide" title="Insert a double rule"><svg width="22" height="8" viewBox="0 0 22 8" aria-hidden="true"><rect x="0" y="1.6" width="22" height="1.3"/><rect x="0" y="5" width="22" height="1.3"/></svg>Double</button>
|
|
815
|
+
</div>
|
|
816
|
+
<div class="dxr-row">
|
|
817
|
+
<select data-dxr="rulepos" title="Where the rule lands relative to the current block">
|
|
818
|
+
<option value="below">Below block</option>
|
|
819
|
+
<option value="above">Above block</option>
|
|
820
|
+
</select>
|
|
821
|
+
<button type="button" data-dxr="hrClear" title="Remove the rule or paragraph border">Clear</button>
|
|
822
|
+
</div>
|
|
823
|
+
</div>
|
|
824
|
+
|
|
825
|
+
<div class="dxr-group">
|
|
826
|
+
<span class="dxr-glabel">References</span>
|
|
827
|
+
<div class="dxr-row">
|
|
828
|
+
<button type="button" data-dxr="footnote" title="Cite a new footnote at the caret">Footnote</button>
|
|
829
|
+
<button type="button" data-dxr="endnote" title="Cite a new endnote at the caret">Endnote</button>
|
|
830
|
+
</div>
|
|
831
|
+
</div>
|
|
832
|
+
</div>
|
|
833
|
+
|
|
834
|
+
<!-- LAYOUT ────────────────────────────────────────────────────────────── -->
|
|
835
|
+
<div class="dxr-panel" data-panel="layout">
|
|
836
|
+
<div class="dxr-group">
|
|
837
|
+
<span class="dxr-glabel">View</span>
|
|
838
|
+
<div class="dxr-row">
|
|
839
|
+
<label class="dxr-toggle"><input data-dxr="paginated" type="checkbox" /> Page view</label>
|
|
840
|
+
</div>
|
|
841
|
+
<div class="dxr-row">
|
|
842
|
+
<label class="dxr-toggle"><input data-dxr="headerfooter" type="checkbox" /> Header & footer bands</label>
|
|
843
|
+
</div>
|
|
844
|
+
</div>
|
|
845
|
+
|
|
846
|
+
<div class="dxr-group">
|
|
847
|
+
<span class="dxr-glabel">Page numbers</span>
|
|
848
|
+
<div class="dxr-row">
|
|
849
|
+
<select data-dxr="pgfmt" title="Number format for this section">
|
|
850
|
+
<option value="">Format…</option>
|
|
851
|
+
<option value="decimal">1, 2, 3</option>
|
|
852
|
+
<option value="lowerLetter">a, b, c</option>
|
|
853
|
+
<option value="upperLetter">A, B, C</option>
|
|
854
|
+
<option value="lowerRoman">i, ii, iii</option>
|
|
855
|
+
<option value="upperRoman">I, II, III</option>
|
|
856
|
+
</select>
|
|
857
|
+
<label class="dxr-toggle">Start at
|
|
858
|
+
<input data-dxr="pgstart" type="number" min="1" step="1"
|
|
859
|
+
title="Restart this section's numbering at this value" />
|
|
860
|
+
</label>
|
|
861
|
+
<button type="button" data-dxr="pgclear" title="Continue the previous section's numbering">Clear</button>
|
|
862
|
+
</div>
|
|
863
|
+
<div class="dxr-row">
|
|
864
|
+
<span class="dxr-note">Applies to the section holding the caret.</span>
|
|
865
|
+
</div>
|
|
866
|
+
</div>
|
|
867
|
+
|
|
868
|
+
<!-- The field lands in the running footer (Word's convention, and where the band
|
|
869
|
+
puts it), so it belongs with the section's numbering rather than under Insert,
|
|
870
|
+
whose controls all act at the caret. -->
|
|
871
|
+
<div class="dxr-group">
|
|
872
|
+
<span class="dxr-glabel">Footer fields</span>
|
|
873
|
+
<div class="dxr-row">
|
|
874
|
+
<button type="button" data-dxr="pagenum" title="Add a page-number field to the footer">Page number</button>
|
|
875
|
+
<button type="button" data-dxr="totalpages" title="Add a total-pages field to the footer">Total pages</button>
|
|
876
|
+
</div>
|
|
877
|
+
<div class="dxr-row">
|
|
878
|
+
<span class="dxr-note">Added to the footer story.</span>
|
|
879
|
+
</div>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
|
|
883
|
+
<!-- TABLE (contextual) ──────────────────────────────────────────────────
|
|
884
|
+
Row/column editing lives here rather than in a floating toolbar: a docked
|
|
885
|
+
contextual tab cannot overlap the cell you are editing. -->
|
|
886
|
+
<div class="dxr-panel" data-panel="table">
|
|
887
|
+
<div class="dxr-group">
|
|
888
|
+
<span class="dxr-glabel">Rows</span>
|
|
889
|
+
<div class="dxr-row">
|
|
890
|
+
<button type="button" data-tt="rowAbove" title="Insert a row above this one">Insert above</button>
|
|
891
|
+
<button type="button" data-tt="rowBelow" title="Insert a row below this one">Insert below</button>
|
|
892
|
+
<button type="button" data-tt="delRow" class="dxr-danger" title="Delete this row">Delete row</button>
|
|
893
|
+
</div>
|
|
894
|
+
</div>
|
|
895
|
+
<div class="dxr-group">
|
|
896
|
+
<span class="dxr-glabel">Columns</span>
|
|
897
|
+
<div class="dxr-row">
|
|
898
|
+
<button type="button" data-tt="colLeft" title="Insert a column to the left">Insert left</button>
|
|
899
|
+
<button type="button" data-tt="colRight" title="Insert a column to the right">Insert right</button>
|
|
900
|
+
<button type="button" data-tt="delCol" class="dxr-danger" title="Delete this column">Delete column</button>
|
|
901
|
+
</div>
|
|
902
|
+
</div>
|
|
903
|
+
</div>
|
|
904
|
+
</div>
|
|
905
|
+
|
|
906
|
+
<!-- Anchor rail — live engine state, not decoration. -->
|
|
907
|
+
<div class="dxr-rail" data-dxr-rail>
|
|
908
|
+
<span class="dxr-cell"><span class="dxr-k">anchor</span><span class="dxr-v" data-dxr="railAnchor">—</span></span>
|
|
909
|
+
<span class="dxr-cell"><span class="dxr-k">blocks</span><span class="dxr-v" data-dxr="railBlocks">—</span></span>
|
|
910
|
+
<span class="dxr-cell"><span class="dxr-k">session</span><span class="dxr-v" data-dxr="railSession">—</span></span>
|
|
911
|
+
<span class="dxr-cell"><span class="dxr-k">last op</span><span class="dxr-v" data-dxr="railOp">—</span></span>
|
|
912
|
+
</div>
|
|
913
|
+
</div>
|
|
914
|
+
|
|
915
|
+
<div class="dxr-scroll" data-dxr-scroll>
|
|
916
|
+
<p class="dxr-hint" data-dxr-hint></p>
|
|
917
|
+
<div class="dxr-surface" data-dxr="editor" data-dxr-surface data-view="continuous"></div>
|
|
918
|
+
</div>
|
|
919
|
+
|
|
920
|
+
<!-- Table size picker, anchored to the Insert tab's Table button. -->
|
|
921
|
+
<div class="dxr-pop" data-dxr="gridpicker">
|
|
922
|
+
<div class="dxr-gridcells" data-dxr="gridcells"></div>
|
|
923
|
+
<div class="dxr-popfoot">
|
|
924
|
+
<span data-dxr="gridlabel">0 × 0</span>
|
|
925
|
+
<span style="display:inline-flex; align-items:center; gap:10px;">
|
|
926
|
+
<label class="dxr-toggle">Align
|
|
927
|
+
<select data-dxr="gridalign">
|
|
928
|
+
<option value="left">Left</option>
|
|
929
|
+
<option value="center">Center</option>
|
|
930
|
+
<option value="right">Right</option>
|
|
931
|
+
</select>
|
|
932
|
+
</label>
|
|
933
|
+
<label class="dxr-toggle"><input data-dxr="gridborderless" type="checkbox" checked /> Borderless</label>
|
|
934
|
+
</span>
|
|
935
|
+
</div>
|
|
936
|
+
</div>
|
|
937
|
+
|
|
938
|
+
<div class="dxr-loader" data-dxr="loader" aria-live="polite" hidden>
|
|
939
|
+
<div class="dxr-loader-grid">
|
|
940
|
+
<div class="dxr-visual" aria-hidden="true">
|
|
941
|
+
<div class="dxr-orbit"></div><div class="dxr-orbit dxr-two"></div>
|
|
942
|
+
<div class="dxr-card"><i></i><i></i><i></i><i></i></div>
|
|
943
|
+
<span class="dxr-chip dxr-b">B</span><span class="dxr-chip dxr-r">±</span><span class="dxr-chip dxr-s">↓</span>
|
|
944
|
+
</div>
|
|
945
|
+
<div class="dxr-loader-copy">
|
|
946
|
+
<div class="dxr-eyebrow" data-dxr="loaderEyebrow">Running locally in this tab</div>
|
|
947
|
+
<h2 data-dxr="loaderTitle">Booting .NET inside your browser</h2>
|
|
948
|
+
<p data-dxr="loaderCopy">Streaming the trimmed WebAssembly runtime.</p>
|
|
949
|
+
<div class="dxr-ad" data-dxr="loaderAd">
|
|
950
|
+
<span class="dxr-num" data-dxr="loaderNumber">01</span>
|
|
951
|
+
<div><strong data-dxr="loaderAdTitle"></strong><span class="dxr-adcopy" data-dxr="loaderAdCopy"></span></div>
|
|
952
|
+
</div>
|
|
953
|
+
<div class="dxr-track" aria-hidden="true"><div class="dxr-bar" data-dxr="loaderBar"></div></div>
|
|
954
|
+
<div class="dxr-meta"><span data-dxr="loaderLabel">Loading engine</span><span data-dxr="loaderMeta">DOCX → WASM → DOCX</span></div>
|
|
955
|
+
<button type="button" class="dxr-retry" data-dxr="loaderRetry">Retry loading</button>
|
|
956
|
+
</div>
|
|
957
|
+
</div>
|
|
958
|
+
</div>
|
|
959
|
+
`;
|
|
960
|
+
/** The default hint copy, shown above the document in full chrome. */
|
|
961
|
+
export const RIBBON_HINT_HTML = "Click any paragraph, heading, table cell, footnote or header/footer line to edit it. " +
|
|
962
|
+
"<kbd>Enter</kbd> splits a block, <kbd>Backspace</kbd> at the start merges it into the previous one, " +
|
|
963
|
+
"<kbd>Ctrl</kbd>+<kbd>Z</kbd> undoes. Only the block you changed re-renders — everything else keeps " +
|
|
964
|
+
"full fidelity, and <b>Save</b> writes a lossless .docx.";
|
|
965
|
+
//# sourceMappingURL=ribbon-chrome.js.map
|