smartrte-react 0.3.5 → 1.0.0-beta.10

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 (147) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +205 -14
  3. package/dist/adapters/domChecklistCommandBridge.js +1 -1
  4. package/dist/adapters/domCommandBridge.d.ts +2 -2
  5. package/dist/adapters/domCommandBridge.js +2 -2
  6. package/dist/adapters/domInlineAtomCommandBridge.d.ts +10 -9
  7. package/dist/adapters/domInlineAtomCommandBridge.js +154 -50
  8. package/dist/adapters/domInlineImageCommandBridge.d.ts +7 -7
  9. package/dist/adapters/domInlineImageCommandBridge.js +6 -72
  10. package/dist/adapters/domMoveCommandBridge.js +1 -1
  11. package/dist/adapters/domSelectionBridge.d.ts +3 -3
  12. package/dist/adapters/domSmartDocument.d.ts +4 -4
  13. package/dist/adapters/domSmartDocument.js +2 -2
  14. package/dist/adapters/domTableCommandBridge.d.ts +42 -16
  15. package/dist/adapters/domTableCommandBridge.js +203 -173
  16. package/dist/adapters/internalFlags.d.ts +0 -8
  17. package/dist/adapters/internalFlags.js +0 -24
  18. package/dist/adapters/legacyListShadowComparator.d.ts +1 -1
  19. package/dist/adapters/legacyListShadowComparator.js +1 -1
  20. package/dist/adapters/pdfPrint.d.ts +8 -0
  21. package/dist/adapters/pdfPrint.js +22 -0
  22. package/dist/adapters/shadowMode.d.ts +3 -3
  23. package/dist/adapters/shadowMode.js +1 -1
  24. package/dist/canonicalEditorRuntime.d.ts +65 -1
  25. package/dist/canonicalEditorRuntime.js +63 -5
  26. package/dist/capabilityPresets.d.ts +27 -0
  27. package/dist/capabilityPresets.js +21 -0
  28. package/dist/collabTransport.d.ts +66 -0
  29. package/dist/collabTransport.js +1 -0
  30. package/dist/commentProvider.d.ts +24 -0
  31. package/dist/commentProvider.js +1 -0
  32. package/dist/components/CanonicalAuthorityEditor.d.ts +56 -1
  33. package/dist/components/CanonicalAuthorityEditor.js +1304 -116
  34. package/dist/components/ClassicEditorAuthority.d.ts +93 -22
  35. package/dist/components/ClassicEditorAuthority.js +11 -43
  36. package/dist/components/ColorPickerPopover.d.ts +39 -0
  37. package/dist/components/ColorPickerPopover.js +287 -0
  38. package/dist/components/CommentMarkers.d.ts +25 -0
  39. package/dist/components/CommentMarkers.js +105 -0
  40. package/dist/components/CommentThreadPanel.d.ts +26 -0
  41. package/dist/components/CommentThreadPanel.js +47 -0
  42. package/dist/components/ContextMenu.d.ts +23 -0
  43. package/dist/components/ContextMenu.js +141 -0
  44. package/dist/components/FormulaLibraryPopover.d.ts +16 -0
  45. package/dist/components/FormulaLibraryPopover.js +107 -0
  46. package/dist/components/LinkEditorPopover.d.ts +3 -1
  47. package/dist/components/LinkEditorPopover.js +22 -6
  48. package/dist/components/MediaDetailsPopover.d.ts +35 -0
  49. package/dist/components/MediaDetailsPopover.js +136 -0
  50. package/dist/components/MediaManager.d.ts +4 -20
  51. package/dist/components/MediaManager.js +40 -11
  52. package/dist/components/MediaOverlay.d.ts +27 -0
  53. package/dist/components/MediaOverlay.js +228 -0
  54. package/dist/components/SpecialCharacterPopover.d.ts +18 -0
  55. package/dist/components/SpecialCharacterPopover.js +99 -0
  56. package/dist/components/StructuralSuggestionMarkers.d.ts +20 -0
  57. package/dist/components/StructuralSuggestionMarkers.js +56 -0
  58. package/dist/components/SuggestionPanel.d.ts +26 -0
  59. package/dist/components/SuggestionPanel.js +33 -0
  60. package/dist/components/TableBorderPopover.d.ts +59 -0
  61. package/dist/components/TableBorderPopover.js +149 -0
  62. package/dist/components/TableResizeHandles.d.ts +25 -0
  63. package/dist/components/TableResizeHandles.js +323 -0
  64. package/dist/components/TableSizePickerPopover.d.ts +16 -0
  65. package/dist/components/TableSizePickerPopover.js +126 -0
  66. package/dist/components/ToolbarPrimitives.d.ts +161 -0
  67. package/dist/components/ToolbarPrimitives.js +237 -0
  68. package/dist/components/VersionHistoryPanel.d.ts +9 -0
  69. package/dist/components/VersionHistoryPanel.js +157 -0
  70. package/dist/components/fixedPositioning.d.ts +25 -0
  71. package/dist/components/fixedPositioning.js +42 -0
  72. package/dist/formulaLibrary.d.ts +31 -0
  73. package/dist/formulaLibrary.js +90 -0
  74. package/dist/index.d.ts +25 -24
  75. package/dist/index.js +15 -11
  76. package/dist/mediaManagerAdapter.d.ts +12 -0
  77. package/dist/mediaManagerAdapter.js +34 -0
  78. package/dist/mediaProvider.d.ts +21 -1
  79. package/dist/specialCharacters.d.ts +22 -0
  80. package/dist/specialCharacters.js +86 -0
  81. package/dist/standalone/classic-editor-embed.js +19 -12
  82. package/dist/suggestionProvider.d.ts +17 -0
  83. package/dist/suggestionProvider.js +1 -0
  84. package/dist/theme.d.ts +1 -1
  85. package/dist/theme.js +219 -30
  86. package/dist/toolbarTools.d.ts +95 -0
  87. package/dist/toolbarTools.js +17 -0
  88. package/dist/versionProvider.d.ts +29 -0
  89. package/dist/versionProvider.js +1 -0
  90. package/package.json +28 -14
  91. package/dist/adapters/canonicalInlineCommandBridge.d.ts +0 -15
  92. package/dist/adapters/canonicalInlineCommandBridge.js +0 -269
  93. package/dist/adapters/canonicalListCommandBridge.d.ts +0 -26
  94. package/dist/adapters/canonicalListCommandBridge.js +0 -430
  95. package/dist/adapters/coreBold.d.ts +0 -5
  96. package/dist/adapters/coreBold.js +0 -3
  97. package/dist/adapters/documentFormats.d.ts +0 -26
  98. package/dist/adapters/documentFormats.js +0 -49
  99. package/dist/adapters/docxFormat.d.ts +0 -5
  100. package/dist/adapters/docxFormat.js +0 -272
  101. package/dist/adapters/domBlockCommandBridge.d.ts +0 -19
  102. package/dist/adapters/domBlockCommandBridge.js +0 -79
  103. package/dist/adapters/inlineMarkCoreExecution.d.ts +0 -9
  104. package/dist/adapters/inlineMarkCoreExecution.js +0 -25
  105. package/dist/adapters/pdfFormat.d.ts +0 -23
  106. package/dist/adapters/pdfFormat.js +0 -204
  107. package/dist/adapters/portableDocxAtoms.d.ts +0 -4
  108. package/dist/adapters/portableDocxAtoms.js +0 -58
  109. package/dist/adapters/styledDocxFormat.d.ts +0 -8
  110. package/dist/adapters/styledDocxFormat.js +0 -190
  111. package/dist/builtInFormatDefinitions.d.ts +0 -5
  112. package/dist/builtInFormatDefinitions.js +0 -82
  113. package/dist/canonicalAuthorityFlag.d.ts +0 -32
  114. package/dist/canonicalAuthorityFlag.js +0 -49
  115. package/dist/canonicalClipboardRuntime.d.ts +0 -15
  116. package/dist/canonicalClipboardRuntime.js +0 -76
  117. package/dist/components/ClassicEditor.d.ts +0 -76
  118. package/dist/components/ClassicEditor.js +0 -6729
  119. package/dist/editorController.d.ts +0 -78
  120. package/dist/editorController.js +0 -298
  121. package/dist/formatFidelity.d.ts +0 -14
  122. package/dist/formatFidelity.js +0 -107
  123. package/dist/formatRuntime.d.ts +0 -50
  124. package/dist/formatRuntime.js +0 -20
  125. package/dist/pluginRuntime.d.ts +0 -68
  126. package/dist/pluginRuntime.js +0 -78
  127. package/dist/standalone/editor.js +0 -1068
  128. package/dist/test-harness/atomShadowComparator.d.ts +0 -14
  129. package/dist/test-harness/atomShadowComparator.js +0 -63
  130. package/dist/test-harness/blockShadowComparator.d.ts +0 -30
  131. package/dist/test-harness/blockShadowComparator.js +0 -140
  132. package/dist/test-harness/clipboardShadowComparator.d.ts +0 -12
  133. package/dist/test-harness/clipboardShadowComparator.js +0 -46
  134. package/dist/test-harness/inlineShadowComparator.d.ts +0 -32
  135. package/dist/test-harness/inlineShadowComparator.js +0 -152
  136. package/dist/test-harness/legacyAtomEngine.d.ts +0 -10
  137. package/dist/test-harness/legacyAtomEngine.js +0 -15
  138. package/dist/test-harness/legacyBlockEngine.d.ts +0 -31
  139. package/dist/test-harness/legacyBlockEngine.js +0 -31
  140. package/dist/test-harness/legacyClipboardEngine.d.ts +0 -14
  141. package/dist/test-harness/legacyClipboardEngine.js +0 -67
  142. package/dist/test-harness/legacyInlineEngine.d.ts +0 -19
  143. package/dist/test-harness/legacyInlineEngine.js +0 -49
  144. package/dist/test-harness/legacyTableEngine.d.ts +0 -12
  145. package/dist/test-harness/legacyTableEngine.js +0 -25
  146. package/dist/test-harness/tableShadowComparator.d.ts +0 -29
  147. package/dist/test-harness/tableShadowComparator.js +0 -101
package/dist/theme.js CHANGED
@@ -18,8 +18,8 @@ export const SRTE_DEFAULT_CSS = `
18
18
  --srte-input-border: var(--srte-border);
19
19
  --srte-modal-backdrop: rgba(0, 0, 0, 0.35);
20
20
  --srte-modal-backdrop-filter: blur(2px);
21
- --srte-modal-bg: #ffffff;
22
- --srte-modal-text: #000000;
21
+ --srte-modal-bg: var(--card, #ffffff);
22
+ --srte-modal-text: var(--foreground, #000000);
23
23
  --srte-menu-bg: var(--srte-background);
24
24
  --srte-menu-text: var(--srte-foreground);
25
25
  --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
@@ -27,7 +27,11 @@ export const SRTE_DEFAULT_CSS = `
27
27
  --srte-accent-bg: rgba(2, 132, 199, 0.12);
28
28
  --srte-danger: #dc2626;
29
29
  --srte-primary: #2563eb;
30
- --srte-surface-subtle: #f3f4f6;
30
+ /* --srte-primary on --srte-accent-bg measures 4.43:1, under WCAG AA's
31
+ 4.5:1 for normal text - used only for the pressed toolbar-button state
32
+ (see .srte-tool-button[aria-pressed="true"]), not a replacement for
33
+ --srte-primary generally. */
34
+ --srte-primary-pressed: #1d4ed8;
31
35
  --srte-on-primary: #ffffff;
32
36
  --srte-cancel-bg: #f3f4f6;
33
37
  --srte-code-bg: #f6f8fa;
@@ -51,8 +55,8 @@ export const SRTE_DEFAULT_CSS = `
51
55
  --srte-input-border: var(--srte-border);
52
56
  --srte-modal-backdrop: rgba(0, 0, 0, 0.22);
53
57
  --srte-modal-backdrop-filter: blur(10px) saturate(0.9);
54
- --srte-modal-bg: #1e293b;
55
- --srte-modal-text: #e0e0e0;
58
+ --srte-modal-bg: var(--card, #1e293b);
59
+ --srte-modal-text: var(--foreground, #e0e0e0);
56
60
  --srte-menu-bg: var(--srte-background);
57
61
  --srte-menu-text: var(--srte-foreground);
58
62
  --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
@@ -60,7 +64,7 @@ export const SRTE_DEFAULT_CSS = `
60
64
  --srte-accent-bg: rgba(56, 189, 248, 0.16);
61
65
  --srte-danger: #ef4444;
62
66
  --srte-primary: #3b82f6;
63
- --srte-surface-subtle: #333333;
67
+ --srte-primary-pressed: var(--srte-primary);
64
68
  --srte-on-primary: #ffffff;
65
69
  --srte-cancel-bg: #333333;
66
70
  --srte-code-bg: #111827;
@@ -71,6 +75,18 @@ export const SRTE_DEFAULT_CSS = `
71
75
  font-family: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
72
76
  container: srte-editor / inline-size;
73
77
  }
78
+ .srte-canonical-authority > .srte-editor[contenteditable] {
79
+ width: 100%;
80
+ padding: 16px 20px;
81
+ box-sizing: border-box;
82
+ border: 1px solid var(--srte-border);
83
+ border-radius: 0 0 var(--srte-radius) var(--srte-radius);
84
+ outline: none;
85
+ background: var(--srte-canvas);
86
+ color: var(--srte-foreground);
87
+ caret-color: var(--srte-foreground);
88
+ line-height: 1.6;
89
+ }
74
90
  .srte-toolbar {
75
91
  display: flex;
76
92
  flex-wrap: wrap;
@@ -134,7 +150,12 @@ export const SRTE_DEFAULT_CSS = `
134
150
  }
135
151
  .srte-tool-button.srte-active,
136
152
  .srte-tool-button[aria-pressed="true"] {
137
- color: var(--srte-primary);
153
+ /* var(--srte-primary) (#2563eb) on var(--srte-accent-bg) measured 4.43:1,
154
+ just under WCAG AA's 4.5:1 for normal text (axe-core, Phase 11 Tier 3) -
155
+ a dedicated, darker pressed-state color keeps the same hue family
156
+ while clearing the threshold, without changing --srte-primary's other,
157
+ already-compliant usages (e.g. against solid backgrounds). */
158
+ color: var(--srte-primary-pressed);
138
159
  background: var(--srte-accent-bg);
139
160
  border-color: color-mix(in srgb, var(--srte-primary) 35%, transparent);
140
161
  }
@@ -173,6 +194,20 @@ export const SRTE_DEFAULT_CSS = `
173
194
  color: var(--srte-menu-text);
174
195
  box-shadow: var(--srte-menu-shadow);
175
196
  }
197
+ /*
198
+ * ToolbarDropdown's own menu panel (docs/bugs/
199
+ * toolbar-dropdown-clipped-by-host-overflow-hidden.md): position:fixed with
200
+ * JS-measured left/top (ToolbarPrimitives.tsx) instead of the default
201
+ * position:absolute above, so it always escapes an ancestor host container's
202
+ * own overflow:hidden regardless of DOM depth - MobileMoreMenu's overflow
203
+ * menu (no data-srte-menu-fixed attribute) is unaffected and keeps its
204
+ * existing position:absolute/right-anchored behavior.
205
+ */
206
+ .srte-menu[data-srte-menu-fixed="true"] {
207
+ position: fixed;
208
+ top: 0;
209
+ left: 0;
210
+ }
176
211
  .srte-menu-item {
177
212
  display: flex;
178
213
  align-items: center;
@@ -241,6 +276,56 @@ export const SRTE_DEFAULT_CSS = `
241
276
  .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal-paren"][data-srte-list-depth="3"] > li::marker {
242
277
  content: counter(list-item, lower-roman) ") ";
243
278
  }
279
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="0"] > li::marker {
280
+ content: counter(list-item, decimal) ". ";
281
+ }
282
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="1"] > li::marker {
283
+ content: counter(list-item, lower-alpha) ". ";
284
+ }
285
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="2"] > li::marker,
286
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="3"] > li::marker {
287
+ content: counter(list-item, lower-roman) ". ";
288
+ }
289
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="0"] > li::marker {
290
+ content: counter(list-item, upper-alpha) ". ";
291
+ }
292
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="1"] > li::marker {
293
+ content: counter(list-item, lower-alpha) ". ";
294
+ }
295
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="2"] > li::marker,
296
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="3"] > li::marker {
297
+ content: counter(list-item, lower-roman) ". ";
298
+ }
299
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="0"] > li::marker {
300
+ content: counter(list-item, upper-roman) ". ";
301
+ }
302
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="1"] > li::marker {
303
+ content: counter(list-item, upper-alpha) ". ";
304
+ }
305
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="2"] > li::marker,
306
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="3"] > li::marker {
307
+ content: counter(list-item, decimal) ". ";
308
+ }
309
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="0"] > li::marker {
310
+ content: counter(list-item, decimal-leading-zero) ". ";
311
+ }
312
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="1"] > li::marker {
313
+ content: counter(list-item, lower-alpha) ". ";
314
+ }
315
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="2"] > li::marker,
316
+ .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="3"] > li::marker {
317
+ content: counter(list-item, lower-roman) ". ";
318
+ }
319
+ .srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="0"] > li::marker {
320
+ content: "● ";
321
+ }
322
+ .srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="1"] > li::marker {
323
+ content: "○ ";
324
+ }
325
+ .srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="2"] > li::marker,
326
+ .srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="3"] > li::marker {
327
+ content: "■ ";
328
+ }
244
329
  .srte-editor [contenteditable] ol[data-srte-list-preset="ordered-outline"] > li::marker {
245
330
  content: counters(list-item, ".") ". ";
246
331
  }
@@ -267,11 +352,26 @@ export const SRTE_DEFAULT_CSS = `
267
352
  justify-content: center;
268
353
  width: 1.1em;
269
354
  height: 1.6em;
355
+ min-width: 0;
356
+ box-sizing: border-box;
357
+ padding: 0;
358
+ border: 0;
359
+ border-radius: 0;
360
+ background: transparent;
361
+ color: inherit;
362
+ font: inherit;
363
+ cursor: pointer;
364
+ appearance: none;
365
+ outline: none;
270
366
  margin: 0;
271
367
  line-height: 1.6;
272
368
  vertical-align: top;
273
369
  position: relative;
274
370
  }
371
+ .srte-editor [contenteditable] ul[data-srte-checklist="true"] > li > [data-srte-check]:focus-visible {
372
+ outline: 2px solid var(--srte-ring);
373
+ outline-offset: 2px;
374
+ }
275
375
  .srte-editor [contenteditable] ul[data-srte-checklist="true"] > li > [data-srte-check]::before {
276
376
  content: "";
277
377
  width: .9em;
@@ -307,34 +407,32 @@ export const SRTE_DEFAULT_CSS = `
307
407
  border-color: var(--srte-ring) !important;
308
408
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--srte-ring) 18%, transparent);
309
409
  }
310
- @media (max-width: 639px) {
311
- .srte-toolbar { gap: 3px; padding: 6px; }
312
- .srte-tool-button, .srte-toolbar select { height: 40px; min-width: 40px; }
313
- .srte-toolbar-group[data-srte-priority="3"] { display: none; }
314
- .srte-toolbar-menu[data-srte-priority="2"] { display: none; }
315
- .srte-mobile-more { display: block; }
316
- .srte-mobile-more .srte-menu {
317
- left: auto;
318
- right: 0;
319
- width: min(280px, calc(100vw - 16px));
320
- min-width: 0;
321
- max-height: min(70dvh, 480px);
322
- overflow-x: hidden;
323
- overflow-y: auto;
324
- overscroll-behavior: contain;
325
- -webkit-overflow-scrolling: touch;
326
- }
327
- .srte-menu-item { height: 40px; }
328
- }
410
+ /*
411
+ * @container (element width), not @media (viewport width): a host can embed
412
+ * the editor at any fraction of the browser window - a split pane next to a
413
+ * preview panel is the case that surfaced this (docs/bugs/
414
+ * toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md). A
415
+ * plain @media breakpoint here would fire based on the *window*, wrongly
416
+ * switching tiers for an editor instance that's actually much narrower (or
417
+ * wider) than the window - .srte-editor already establishes its own
418
+ * container: srte-editor / inline-size (see the .srte-editor rule above),
419
+ * so @container always reflects this specific instance's real rendered width regardless of host
420
+ * layout, with no @media fallback needed (container queries have full
421
+ * support across this project's three target engines).
422
+ */
329
423
  @container srte-editor (max-width: 639px) {
330
424
  .srte-toolbar { gap: 3px; padding: 6px; }
331
425
  .srte-tool-button, .srte-toolbar select { height: 40px; min-width: 40px; }
332
426
  .srte-toolbar-group[data-srte-priority="3"],
333
427
  .srte-toolbar-menu[data-srte-priority="2"] { display: none; }
334
428
  .srte-mobile-more { display: block; }
429
+ /* left/top are no longer set here - ToolbarPrimitives.tsx now computes
430
+ them via inline style (JS-measured, position:fixed, viewport-clamped;
431
+ see MobileMoreMenu's own doc comment), which always wins over any
432
+ value set here regardless of specificity or source order. Sizing/
433
+ scroll behavior stays defined here since inline positioning doesn't
434
+ touch those properties. */
335
435
  .srte-mobile-more .srte-menu {
336
- left: auto;
337
- right: 0;
338
436
  width: min(280px, calc(100vw - 16px));
339
437
  min-width: 0;
340
438
  max-height: min(70dvh, 480px);
@@ -345,11 +443,61 @@ export const SRTE_DEFAULT_CSS = `
345
443
  }
346
444
  .srte-menu-item { height: 40px; }
347
445
  }
446
+ /*
447
+ * Wide-viewport promotion (docs/bugs/toolbar-priority-collapse-fixed-threshold-no-wide-promotion.md):
448
+ * below this, the single 639px breakpoint above was this system's ONLY
449
+ * threshold - every dropdown-grouped tool stayed hidden inside its dropdown
450
+ * at every width from 640px up to and past a 2200px+ desktop, since nothing
451
+ * ever measured or scaled with the extra room. A handful of tools frequent
452
+ * enough to matter once there's genuinely spare room (Superscript,
453
+ * Subscript, Text colour, Background colour, Font size, Font family, Remove
454
+ * link, Insert formula, Special characters) get a standalone always-visible
455
+ * ToolbarButton copy (data-srte-wide-promote, rendered directly in the
456
+ * toolbar row) that appears past this breakpoint, while their existing
457
+ * dropdown/mobile-menu ToolbarMenuItem copy (the exact same
458
+ * data-srte-wide-promote attribute, on the .srte-menu-item element instead)
459
+ * hides so the tool isn't offered twice. Below this breakpoint - including
460
+ * all of mobile AND the 640-1439px tablet/typical-laptop band, which keeps
461
+ * today's grouped-dropdown layout unchanged - the standalone copy stays
462
+ * hidden and the dropdown copy is what's reachable.
463
+ *
464
+ * 1440px, not 1280px: Playwright's own default test viewport is exactly
465
+ * 1280x720, and the vast majority of this suite's toolbar interactions run
466
+ * at that default without ever calling page.setViewportSize - a 1280px
467
+ * threshold would have flipped nearly every existing toolbar test's
468
+ * dropdown-item locators (role=menuitem) over to CSS display:none out from
469
+ * under them. 1440px clears that default with room to spare while still
470
+ * comfortably covering "wide desktop" (the ~2264px width the original
471
+ * report was filed against).
472
+ *
473
+ * @container only, no @media fallback - same reasoning as the mobile tier
474
+ * above (docs/bugs/toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md):
475
+ * a plain @media(min-width:1440px) fired whenever the *browser window* was
476
+ * wide, even when this editor instance was embedded in a much narrower
477
+ * split pane - promoting 9 extra buttons into a group with no room for
478
+ * them, which squeezed/overlapped the group's existing buttons instead of
479
+ * wrapping cleanly.
480
+ */
481
+ .srte-tool-button[data-srte-wide-promote="true"] { display: none; }
482
+ @container srte-editor (min-width: 1440px) {
483
+ .srte-tool-button[data-srte-wide-promote="true"] { display: inline-flex; }
484
+ .srte-menu-item[data-srte-wide-promote="true"] { display: none; }
485
+ }
348
486
  .srte-editor [contenteditable] blockquote {
349
487
  border-left: 4px solid var(--srte-accent);
350
488
  margin: 0.75em 0;
351
489
  padding: 0.5em 1em;
352
- background: var(--srte-surface-subtle);
490
+ /* --srte-muted, not the retired --srte-surface-subtle (docs/bugs/
491
+ srte-surface-subtle-not-host-theme-aware.md): every other neutral
492
+ surface/text token here (--srte-background, --srte-foreground,
493
+ --srte-muted, --srte-border) is defined as var(--host-token, fallback)
494
+ so it correctly follows a host's OWN light/dark state even if
495
+ .srte-dark never gets applied for some host-side wiring reason -
496
+ --srte-surface-subtle was the one exception, a bare hardcoded hex with
497
+ no host integration, which is exactly what let a real host (dark mode,
498
+ .srte-dark not reaching this editor instance) render this blockquote
499
+ with a light-mode background and dark-mode text simultaneously. */
500
+ background: var(--srte-muted);
353
501
  color: var(--srte-text);
354
502
  }
355
503
  .srte-editor [contenteditable] p,
@@ -419,8 +567,16 @@ export const SRTE_DEFAULT_CSS = `
419
567
  border: 1px solid var(--srte-border);
420
568
  vertical-align: top;
421
569
  }
570
+ .srte-editor [contenteditable] [data-smart-cell-selected="true"] {
571
+ background: color-mix(in srgb, var(--srte-primary) 12%, var(--srte-canvas));
572
+ box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--srte-primary) 65%, transparent);
573
+ }
574
+ .srte-editor [contenteditable][data-smart-cell-selection-active] ::selection {
575
+ background: transparent;
576
+ color: inherit;
577
+ }
422
578
  .srte-editor [contenteditable] th {
423
- background: var(--srte-surface-subtle);
579
+ background: var(--srte-muted);
424
580
  font-weight: 600;
425
581
  text-align: left;
426
582
  }
@@ -481,6 +637,39 @@ export const SRTE_DEFAULT_CSS = `
481
637
  .srte-editor [contenteditable] sup {
482
638
  line-height: 0;
483
639
  }
640
+ /*
641
+ * A page break has no native visual meaning in a continuously-scrolling
642
+ * web editor - its entire purpose is what happens at export/print time
643
+ * (see atom/formats.ts's atomToHtml doc comment). This gives it a clearly
644
+ * distinct live-editor appearance from the plain solid <hr> a horizontal
645
+ * line renders as by default, so the two are never confused: a dashed
646
+ * line with an explicit "Page break" label, per the feature's own design
647
+ * decision.
648
+ */
649
+ .srte-editor [contenteditable] [data-smart-type="page_break"] {
650
+ display: block;
651
+ position: relative;
652
+ height: 0;
653
+ margin: 1.5em 0;
654
+ border: none;
655
+ border-top: 2px dashed var(--srte-border);
656
+ cursor: default;
657
+ }
658
+ .srte-editor [contenteditable] [data-smart-type="page_break"]::after {
659
+ content: "Page break";
660
+ position: absolute;
661
+ top: -0.7em;
662
+ left: 50%;
663
+ transform: translateX(-50%);
664
+ background: var(--srte-canvas);
665
+ padding: 0 0.6em;
666
+ font-size: 11px;
667
+ font-weight: 600;
668
+ text-transform: uppercase;
669
+ letter-spacing: 0.05em;
670
+ color: var(--srte-text-muted);
671
+ white-space: nowrap;
672
+ }
484
673
  `;
485
674
  const SRTE_STYLE_ID = 'srte-theme-defaults';
486
675
  export function ensureStyleSheet() {
@@ -0,0 +1,95 @@
1
+ /**
2
+ * A named, per-tool toolbar-visibility toggle - "developers should be able
3
+ * to hide tools they don't want their users to see" (e.g. Version History,
4
+ * Review, Video, Audio), without wrapping/overriding the component or
5
+ * patching it with CSS. This is a UI-visibility layer, distinct from and
6
+ * composed with (never replacing) two existing, deeper mechanisms:
7
+ *
8
+ * - `EditorCapabilityPreset` (capabilityPresets.ts) controls real document
9
+ * *capability* - which plugins the schema itself is built with (today,
10
+ * only `table`). Excluding a plugin there already, correctly, hides its
11
+ * toolbar entry (`tablesEnabled`) - this file does not duplicate that.
12
+ * - Provider presence (`mediaProvider`, `versionProvider`, `commentProvider`,
13
+ * `suggestionProvider`) already gates whether a feature can function at
14
+ * all. `tools` never turns a tool ON when its required provider is
15
+ * absent - see `resolveToolbarTools`'s own doc comment.
16
+ *
17
+ * `tools` is for the remaining, larger set of toolbar entries that have no
18
+ * other on/off mechanism at all today (Bold, Italic, Video, Audio, Special
19
+ * characters, Version history, ...) - a host that wants to hide any of
20
+ * these previously had no supported way to do so.
21
+ *
22
+ * Deliberately NOT covered by individual keys (scoping decision, not an
23
+ * oversight): purely contextual actions that only ever act on something
24
+ * already selected - block move/indent, list-item indent/move/restart
25
+ * numbering, table row/column/cell operations, and the selected-media
26
+ * edit/resize/delete actions. None of these have independent product
27
+ * meaning (there is no real case for "let people insert a table but hide
28
+ * its 'Add row' action") - they stay governed solely by their owning
29
+ * top-level tool (`insertTable`, `bulletedList`/`numberedList`/`checklist`,
30
+ * `link`, `image`/`video`/`audio`).
31
+ */
32
+ export interface ToolbarTools {
33
+ bold: boolean;
34
+ italic: boolean;
35
+ underline: boolean;
36
+ strikethrough: boolean;
37
+ code: boolean;
38
+ superscript: boolean;
39
+ subscript: boolean;
40
+ textColor: boolean;
41
+ backgroundColor: boolean;
42
+ fontSize: boolean;
43
+ fontFamily: boolean;
44
+ /** The Paragraph/Heading 1-6/Code block <select>. */
45
+ blockType: boolean;
46
+ alignLeft: boolean;
47
+ alignCenter: boolean;
48
+ alignRight: boolean;
49
+ alignJustify: boolean;
50
+ /** The line-spacing dropdown (1/1.15/1.5/2/2.5 presets plus a custom value). */
51
+ lineHeight: boolean;
52
+ quote: boolean;
53
+ bulletedList: boolean;
54
+ numberedList: boolean;
55
+ checklist: boolean;
56
+ /** The named multi-level marker preset picker (decimal/alpha/roman/outline/bullet-glyph) - independent of the plain list-type toggles above. */
57
+ listPreset: boolean;
58
+ link: boolean;
59
+ removeLink: boolean;
60
+ /** Requires `mediaProvider` to actually render - see resolveToolbarTools. */
61
+ image: boolean;
62
+ /** Requires `mediaProvider` to actually render - see resolveToolbarTools. */
63
+ video: boolean;
64
+ /** Requires `mediaProvider` to actually render - see resolveToolbarTools. */
65
+ audio: boolean;
66
+ insertFormula: boolean;
67
+ specialCharacters: boolean;
68
+ /** Inserts a `divider` atom (a horizontal rule) - see docs/bugs/ for the toolbar-tools feature this was added alongside. */
69
+ horizontalLine: boolean;
70
+ /** Inserts a `page_break` atom - a print/export pagination marker, distinct from `horizontalLine`. */
71
+ pageBreak: boolean;
72
+ /** Requires the schema to actually have the table plugin (EditorCapabilityPreset) - see resolveToolbarTools. */
73
+ insertTable: boolean;
74
+ import: boolean;
75
+ saveAsHtml: boolean;
76
+ saveAsMarkdown: boolean;
77
+ saveAsWord: boolean;
78
+ saveAsPdf: boolean;
79
+ saveAsSmartRte: boolean;
80
+ /** Requires `versionProvider` to actually render. */
81
+ versionHistory: boolean;
82
+ /** Bundles "Add comment" and the Comments panel toggle - requires `commentProvider` to actually render. */
83
+ comments: boolean;
84
+ /** Bundles Suggest insertion/deletion/removal, the Suggestions panel toggle, and "Show edits" (track changes) - requires `suggestionProvider` to actually render. */
85
+ suggestions: boolean;
86
+ undo: boolean;
87
+ redo: boolean;
88
+ }
89
+ /** Every tool defaults to visible - an existing consumer who never passes `tools` sees 100% unchanged behavior. */
90
+ export declare const DEFAULT_TOOLBAR_TOOLS: ToolbarTools;
91
+ /**
92
+ * Merges a host's partial `tools` override onto the all-enabled default -
93
+ * a host only ever needs to name the tools they want OFF.
94
+ */
95
+ export declare const resolveToolbarTools: (tools?: Partial<ToolbarTools>) => ToolbarTools;
@@ -0,0 +1,17 @@
1
+ /** Every tool defaults to visible - an existing consumer who never passes `tools` sees 100% unchanged behavior. */
2
+ export const DEFAULT_TOOLBAR_TOOLS = {
3
+ bold: true, italic: true, underline: true, strikethrough: true, code: true,
4
+ superscript: true, subscript: true, textColor: true, backgroundColor: true, fontSize: true, fontFamily: true,
5
+ blockType: true, alignLeft: true, alignCenter: true, alignRight: true, alignJustify: true, lineHeight: true, quote: true,
6
+ bulletedList: true, numberedList: true, checklist: true, listPreset: true,
7
+ link: true, removeLink: true, image: true, video: true, audio: true, insertFormula: true, specialCharacters: true,
8
+ horizontalLine: true, pageBreak: true, insertTable: true,
9
+ import: true, saveAsHtml: true, saveAsMarkdown: true, saveAsWord: true, saveAsPdf: true, saveAsSmartRte: true,
10
+ versionHistory: true, comments: true, suggestions: true,
11
+ undo: true, redo: true,
12
+ };
13
+ /**
14
+ * Merges a host's partial `tools` override onto the all-enabled default -
15
+ * a host only ever needs to name the tools they want OFF.
16
+ */
17
+ export const resolveToolbarTools = (tools) => ({ ...DEFAULT_TOOLBAR_TOOLS, ...tools });
@@ -0,0 +1,29 @@
1
+ import type { DocumentVersion } from "smartrte-core/foundation";
2
+ /**
3
+ * The listing-cheap projection of a `DocumentVersion` - a host's backend
4
+ * shouldn't have to ship every saved version's full document body just to
5
+ * render a history list. Mirrors `MediaProvider.search` returning
6
+ * `MediaItem` (metadata) rather than raw asset bytes.
7
+ */
8
+ export interface VersionListEntry {
9
+ readonly id: string;
10
+ readonly label?: string;
11
+ readonly createdAt: number;
12
+ readonly authorId?: string;
13
+ }
14
+ /**
15
+ * Host-owned version-storage boundary, mirroring `MediaProvider`'s shape
16
+ * and its "the editor never persists anything itself" contract: the host
17
+ * backend is the source of truth, and `save`/`list`/`load`/`remove` are
18
+ * the entire boundary. There is deliberately no shipped default
19
+ * implementation, exactly like `MediaProvider` - when a host doesn't
20
+ * supply one, the version-history feature is simply unavailable, the same
21
+ * way the media toolbar buttons are disabled when `mediaProvider` is
22
+ * absent.
23
+ */
24
+ export interface VersionProvider {
25
+ save(version: DocumentVersion): Promise<VersionListEntry>;
26
+ list(): Promise<readonly VersionListEntry[]>;
27
+ load(id: string): Promise<DocumentVersion>;
28
+ remove(id: string): Promise<void>;
29
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,15 +1,26 @@
1
1
  {
2
2
  "name": "smartrte-react",
3
- "version": "0.3.5",
3
+ "version": "1.0.0-beta.10",
4
4
  "description": "A powerful, feature-rich Rich Text Editor for React with support for tables, mathematical formulas (LaTeX/KaTeX), and media management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./standalone/classic-editor-embed": {
15
+ "types": "./dist/standalone/classic-editor-embed.d.ts",
16
+ "import": "./dist/standalone/classic-editor-embed.js"
17
+ }
18
+ },
9
19
  "files": [
10
20
  "dist",
11
21
  "!dist/**/*.test.js",
12
22
  "!dist/**/*.test.d.ts",
23
+ "!dist/test-harness/**",
13
24
  "README.md",
14
25
  "CHANGELOG.md"
15
26
  ],
@@ -41,16 +52,6 @@
41
52
  },
42
53
  "author": "Smart RTE Contributors",
43
54
  "license": "MIT",
44
- "scripts": {
45
- "build": "tsc -p tsconfig.json",
46
- "prepublishOnly": "npm run build",
47
- "dev": "pnpm build",
48
- "lint": "tsc -p tsconfig.json --noEmit",
49
- "test": "vitest run --passWithNoTests",
50
- "storybook": "storybook dev -p 6006",
51
- "build-storybook": "storybook build",
52
- "e2e": "playwright test --pass-with-no-tests"
53
- },
54
55
  "publishConfig": {
55
56
  "access": "public"
56
57
  },
@@ -60,16 +61,18 @@
60
61
  "react-dom": ">=18"
61
62
  },
62
63
  "devDependencies": {
64
+ "@axe-core/playwright": "^4.12.1",
63
65
  "@playwright/test": "^1.48.2",
64
66
  "@storybook/addon-essentials": "^8.6.14",
65
67
  "@storybook/react": "^8.6.14",
66
68
  "@storybook/react-vite": "^8.6.14",
69
+ "@types/node": "^22",
67
70
  "@types/pdfjs-dist": "^2.10.377",
68
71
  "@types/react": "^18.3.4",
69
72
  "@types/react-dom": "^18.3.0",
73
+ "jsdom": "^25.0.1",
70
74
  "react": "18.3.1",
71
75
  "react-dom": "18.3.1",
72
- "jsdom": "^25.0.1",
73
76
  "storybook": "^8.6.14",
74
77
  "typescript": "^5.6.2",
75
78
  "vite": "^5.4.8",
@@ -77,8 +80,19 @@
77
80
  },
78
81
  "dependencies": {
79
82
  "jszip": "^3.10.1",
83
+ "katex": "^0.18.4",
84
+ "lucide-react": "^1.35.0",
80
85
  "mammoth": "^1.11.0",
81
86
  "pdfjs-dist": "^5.4.530",
82
- "smartrte-core": "^0.2.1"
87
+ "smartrte-core": "^1.0.0-beta.5"
88
+ },
89
+ "scripts": {
90
+ "build": "rm -rf dist && tsc -p tsconfig.json",
91
+ "dev": "pnpm build",
92
+ "lint": "tsc -p tsconfig.json --noEmit",
93
+ "test": "vitest run --passWithNoTests",
94
+ "storybook": "storybook dev -p 6006",
95
+ "build-storybook": "storybook build",
96
+ "e2e": "pnpm --filter smartrte-core build && pnpm build && playwright test --pass-with-no-tests"
83
97
  }
84
- }
98
+ }
@@ -1,15 +0,0 @@
1
- import { inlineToolDeclarations, type Attrs, type SmartMark } from "smartrte-core";
2
- export type CanonicalInlineToolId = typeof inlineToolDeclarations[number]["id"];
3
- export type CanonicalInlineIntent = "apply" | "remove" | "toggle" | "setAttrs" | "clearAll" | "editLink";
4
- export interface CanonicalInlineExecution {
5
- readonly changed: boolean;
6
- readonly operations: number;
7
- readonly coverage: "all" | "partial" | "none";
8
- }
9
- export declare const executeCanonicalInlineTool: (root: HTMLElement, id: CanonicalInlineToolId, intent?: CanonicalInlineIntent, attrs?: Attrs) => CanonicalInlineExecution;
10
- /** Consumes adapter stored marks for ordinary text input. Composition remains
11
- * owned by the canonical surface; atom-aware composition is explicitly Phase 7. */
12
- export declare const installCanonicalInlineStoredMarkInput: (root: HTMLElement) => (() => void);
13
- export declare const canonicalInlineStoredMarks: (root: HTMLElement) => readonly SmartMark[];
14
- export declare const hasCanonicalInlineStoredMarkOverride: (root: HTMLElement) => boolean;
15
- export declare const describeCanonicalInlineCoverage: (root: HTMLElement) => Readonly<Record<string, "all" | "partial">>;