u2ui 1.5.3 → 1.5.4

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 (142) hide show
  1. package/el/ico/ico.js +5 -5
  2. package/el/rte/README.md +53 -13
  3. package/el/rte/manifest.json +19 -4
  4. package/el/rte/rte.css +31 -6
  5. package/el/rte/rte.js +174 -44
  6. package/el/rte0/README.md +37 -0
  7. package/el/rte0/manifest.json +33 -0
  8. package/el/rte0/rte0.css +9 -0
  9. package/el/rte0/rte0.js +56 -0
  10. package/el/system/styler.js +2 -0
  11. package/el/tooltip/tooltip.css +2 -2
  12. package/js/Placer/Placer.js +2 -1
  13. package/js/rte/AGENTS.md +142 -0
  14. package/js/rte/PLAN.md +488 -0
  15. package/js/rte/README.md +319 -25
  16. package/js/rte/blocks.js +7 -0
  17. package/js/rte/breaks.js +6 -0
  18. package/js/rte/classes.js +7 -0
  19. package/js/rte/docs/usage.md +845 -0
  20. package/js/rte/images.js +7 -0
  21. package/js/rte/link.js +8 -0
  22. package/js/rte/playground/README.md +95 -0
  23. package/js/rte/playground/hosts.html +518 -0
  24. package/js/rte/playground/index.html +824 -0
  25. package/js/rte/rte.js +56 -5
  26. package/js/rte/source.js +12 -0
  27. package/js/rte/src/browser/README.md +40 -0
  28. package/js/rte/src/browser/range-rect.js +82 -0
  29. package/js/rte/src/client/README.md +378 -0
  30. package/js/rte/src/client/blocks.js +38 -0
  31. package/js/rte/src/client/breaks.js +212 -0
  32. package/js/rte/src/client/classes.js +48 -0
  33. package/js/rte/src/client/editor.js +461 -0
  34. package/js/rte/src/client/external-input.js +47 -0
  35. package/js/rte/src/client/history.js +13 -0
  36. package/js/rte/src/client/images.js +275 -0
  37. package/js/rte/src/client/link.js +572 -0
  38. package/js/rte/src/client/marks.js +25 -0
  39. package/js/rte/src/client/source.js +182 -0
  40. package/js/rte/src/client/structure.js +27 -0
  41. package/js/rte/src/client/tables.js +162 -0
  42. package/js/rte/src/client/unstyle.js +32 -0
  43. package/js/rte/src/command/README.md +517 -0
  44. package/js/rte/src/command/block-boundary.js +43 -0
  45. package/js/rte/src/command/block-style.js +120 -0
  46. package/js/rte/src/command/commands.js +158 -0
  47. package/js/rte/src/command/delete.js +112 -0
  48. package/js/rte/src/command/edit.js +72 -0
  49. package/js/rte/src/command/element.js +45 -0
  50. package/js/rte/src/command/enter.js +175 -0
  51. package/js/rte/src/command/fragment.js +76 -0
  52. package/js/rte/src/command/insert.js +52 -0
  53. package/js/rte/src/command/list.js +284 -0
  54. package/js/rte/src/command/mark.js +583 -0
  55. package/js/rte/src/command/pending-marks.js +216 -0
  56. package/js/rte/src/command/table.js +207 -0
  57. package/js/rte/src/command/unstyle.js +248 -0
  58. package/js/rte/src/config/README.md +96 -0
  59. package/js/rte/src/config/config.js +127 -0
  60. package/js/rte/src/core/README.md +41 -0
  61. package/js/rte/src/core/core.js +156 -0
  62. package/js/rte/src/history/README.md +83 -0
  63. package/js/rte/src/history/history.js +283 -0
  64. package/js/rte/src/input/README.md +155 -0
  65. package/js/rte/src/input/external-input.js +130 -0
  66. package/js/rte/src/input/input-pipeline.js +414 -0
  67. package/js/rte/src/mark/README.md +219 -0
  68. package/js/rte/src/mark/dom-adapter.js +100 -0
  69. package/js/rte/src/mark/mark.js +133 -0
  70. package/js/rte/src/mark/standard.js +76 -0
  71. package/js/rte/src/model/README.md +71 -0
  72. package/js/rte/src/model/content-model.js +213 -0
  73. package/js/rte/src/model/html/README.md +51 -0
  74. package/js/rte/src/model/html/html-model.js +90 -0
  75. package/js/rte/src/normalize/execute/README.md +33 -0
  76. package/js/rte/src/normalize/execute/repair-executor.js +129 -0
  77. package/js/rte/src/normalize/normalizer/README.md +48 -0
  78. package/js/rte/src/normalize/normalizer/normalizer.js +141 -0
  79. package/js/rte/src/normalize/repair/README.md +70 -0
  80. package/js/rte/src/normalize/repair/repair-planner.js +208 -0
  81. package/js/rte/src/sanitize/README.md +105 -0
  82. package/js/rte/src/sanitize/native.js +43 -0
  83. package/js/rte/src/sanitize/policy.js +202 -0
  84. package/js/rte/src/selection/README.md +39 -0
  85. package/js/rte/src/selection/map/README.md +53 -0
  86. package/js/rte/src/selection/map/point-map.js +307 -0
  87. package/js/rte/src/selection/ownership/README.md +32 -0
  88. package/js/rte/src/selection/ownership/ownership.js +42 -0
  89. package/js/rte/src/selection/point/README.md +34 -0
  90. package/js/rte/src/selection/point/point.js +83 -0
  91. package/js/rte/src/selection/range/README.md +38 -0
  92. package/js/rte/src/selection/range/edit-range.js +223 -0
  93. package/js/rte/src/selection/snapshot.js +64 -0
  94. package/js/rte/src/source/README.md +64 -0
  95. package/js/rte/src/source/source.js +193 -0
  96. package/js/rte/src/surface/README.md +36 -0
  97. package/js/rte/src/surface/content.js +16 -0
  98. package/js/rte/src/surface/surface.js +87 -0
  99. package/js/rte/src/transaction/README.md +35 -0
  100. package/js/rte/src/transaction/transaction.js +56 -0
  101. package/js/rte/src/ui/README.md +255 -0
  102. package/js/rte/src/ui/chrome.js +214 -0
  103. package/js/rte/src/ui/handles.js +179 -0
  104. package/js/rte/src/ui/place.js +25 -0
  105. package/js/rte/src/ui/toolbar.js +283 -0
  106. package/js/rte/src/unstyle/README.md +103 -0
  107. package/js/rte/src/unstyle/unstyle.js +140 -0
  108. package/js/rte/tables.js +7 -0
  109. package/js/rte/unstyle.js +8 -0
  110. package/js/rte0/README.md +37 -0
  111. package/js/rte0/rte.js +7 -0
  112. package/js/{rte → rte0}/src/behavior.js +0 -1
  113. package/js/{rte → rte0}/src/events.js +0 -1
  114. package/js/{rte → rte0}/src/range.js +0 -2
  115. package/js/{rte → rte0}/src/ui.items.js +0 -2
  116. package/js/{rte → rte0}/src/ui.js +0 -1
  117. package/package.json +1 -1
  118. package/u2/Element.js +2 -2
  119. package/u2/plan-scoped-refactoring.md +133 -303
  120. package/u2/projects.json +18 -2
  121. package/u2/tools/update.repos.json.js +1 -1
  122. package/el/qrcode/package.json +0 -22
  123. /package/js/{rte → rte0}/old/NodeCleaner.js +0 -0
  124. /package/js/{rte → rte0}/old/Rte/Rte.mjs +0 -0
  125. /package/js/{rte → rte0}/old/Rte/Rte.ui.items.mjs +0 -0
  126. /package/js/{rte → rte0}/old/Rte/Rte.ui.mjs +0 -0
  127. /package/js/{rte → rte0}/old/Rte/crossbrowser.mjs +0 -0
  128. /package/js/{rte → rte0}/old/Rte/font/tinymce.svg +0 -0
  129. /package/js/{rte → rte0}/old/Rte/font/tinymce.woff +0 -0
  130. /package/js/{rte → rte0}/old/Rte/helpers.mjs +0 -0
  131. /package/js/{rte → rte0}/old/Rte/htmlparser.mjs +0 -0
  132. /package/js/{rte → rte0}/old/Rte/index.mjs +0 -0
  133. /package/js/{rte → rte0}/old/Rte/main.css +0 -0
  134. /package/js/{rte → rte0}/old/old.rte.js +0 -0
  135. /package/js/{rte → rte0}/old/range-selection.old.js +0 -0
  136. /package/js/{rte → rte0}/src/fixes.js +0 -0
  137. /package/js/{rte → rte0}/src/rte.js +0 -0
  138. /package/js/{rte → rte0}/src/ui/items/ai.js +0 -0
  139. /package/js/{rte → rte0}/src/ui/items/code.js +0 -0
  140. /package/js/{rte → rte0}/src/ui/items/undo.js +0 -0
  141. /package/js/{rte → rte0}/src/util/htmlparser.js +0 -0
  142. /package/js/{rte → rte0}/src/util/indentHtml.js +0 -0
@@ -0,0 +1,7 @@
1
+ import {editor} from './rte.js';
2
+ import {images} from './src/client/images.js';
3
+
4
+ export {images, imageTools} from './src/client/images.js';
5
+ export {elementAttributes, selectedElement} from './src/command/element.js';
6
+
7
+ editor.add(images);
package/js/rte/link.js ADDED
@@ -0,0 +1,8 @@
1
+ import {editor} from './rte.js';
2
+ import {link} from './src/client/link.js';
3
+
4
+ export {link, linkEditor} from './src/client/link.js';
5
+ export {linkHtml} from './src/mark/standard.js';
6
+ export {valueMark} from './src/command/mark.js';
7
+
8
+ editor.add(link);
@@ -0,0 +1,95 @@
1
+ # RTE playground
2
+
3
+ `index.html` is a dependency-free visual client of the real `RepairPlanner`,
4
+ `Normalizer`, `PointMap`, range classes, command registry, and mark commands. It
5
+ contains no alternative editor or normalization implementation.
6
+
7
+ `hosts.html` is a focused live matrix for comparing the convention client
8
+ across block, text, interactive, list, table, and custom editing hosts. It also
9
+ covers links in both nesting directions, non-modal and declarative `show-modal`
10
+ dialogs, `showPopover()` and fullscreen hosts, plain-text editing, empty hosts,
11
+ and non-editable islands. The shared editor chrome follows an active native
12
+ top-layer boundary and returns to the document when that boundary closes.
13
+ It reports registration, resolved host defaults, selection, serialized content,
14
+ and recent native events without turning the observations into cross-browser
15
+ assertions.
16
+
17
+ Open `/u2/js/rte/playground/` through the local HTTP server. Included scenarios
18
+ cover inline runs, generic blocks, redundant wrappers, lists, lift-and-split,
19
+ nested editable isolation, CSS-class marks, and the original invalid `p/div`
20
+ example.
21
+
22
+ ## Capabilities
23
+
24
+ - Analyze planned actions without changing DOM.
25
+ - Execute exactly one normalizer operation or run to a fixed point.
26
+ - Change cleanup level and default block through the production CSS custom
27
+ properties; `auto` demonstrates semantic host defaults such as `li` for a
28
+ `ul` editor.
29
+ - Dispatch selected Input Events through the production `InputPipeline` and
30
+ control its `input paste drop command` triggers with `--u2-rte-clean-on`.
31
+ - Observe the prevented-input path: `insertParagraph` and `insertLineBreak` run
32
+ the registered `enter` and `lineBreak` commands instead of native editing;
33
+ structural Backspace uses the same production command as the convention
34
+ client.
35
+ - Apply, remove, or toggle the `.x` mark over a live selection. The toggle
36
+ exposes active, mixed, and caret state; the dedicated scenario makes wrapper
37
+ creation, reuse of an existing inline element, and cleanup of a neutral
38
+ `span` visible in the DOM tree and serialized HTML. At a caret, toggling also
39
+ formats the next ordinary text input through `PendingMarks`.
40
+ - Exercise the convention prototype's full ready toolbar: undo and redo, the
41
+ standard inline marks, bulleted and numbered lists with their nesting levels,
42
+ and a separator. The readout under that surface reports how many history
43
+ entries exist and where it stands, which makes grouping visible — continuous
44
+ typing coalesces into one entry, while each command is its own step.
45
+ - Configure the prototype surface live from the panel beside it: tick the
46
+ controls `--u2-rte-toolbar` lists, and switch the other host properties. A
47
+ change and its effect stay in view at once, and the control names come from
48
+ the registered modules, so the panel cannot drift from what the editor offers.
49
+ - Give a selection one of the host's declared content classes and watch the same
50
+ declaration drive the sanitizer and remove-format.
51
+ - Click the image and drag a corner to resize it, or reset it to its own size.
52
+ - Put the caret in a table cell and use the handles on the table to add or drop
53
+ its rows and columns.
54
+ - Create a link from a selection or edit an existing one from a caret through
55
+ the contextual form, and remove one by emptying its address.
56
+ - Open the surface's HTML in the modal source dialog, syntax highlighted through
57
+ `<u2-code>`, edit it, and apply it as one undo step.
58
+ - Toggle the ready-made bold mark by button or Ctrl/Command+B. The scenario
59
+ exposes `<b>` alias recognition, canonical `<strong>` output, semantic
60
+ removal, and pending bold input at a caret.
61
+ - Exercise the production roaming `Toolbar` with application-owned markup: it
62
+ follows the active surface, reflects command availability and mark state,
63
+ preserves the selection during pointer or keyboard interaction, and uses the
64
+ production collapsed-range geometry in its replaceable placement callback.
65
+ - Exercise the one-import `rte.js` convention client on a visible surface.
66
+ It is opted in only through CSS and exposes its lazy default Bold toolbar,
67
+ optional `blocks.js` value control, context-aware Enter behavior, and the
68
+ optional staged `unstyle.js` action and non-mutating `breaks.js` line-break
69
+ marker without playground-side wiring.
70
+ - Edit content directly or parse serialized HTML through the browser.
71
+ - Compare the live DOM tree with `innerHTML` serialization.
72
+ - Display executed actions, unresolved repairs, and selection endpoints. Green
73
+ anchor and blue focus `┃` markers also show the exact boundary directly in
74
+ the rendered DOM tree; element-boundary carets get their own marker line
75
+ between the affected children.
76
+ - Map and restore a live selection through each normalization run.
77
+
78
+ Scenarios that browsers repair during HTML parsing are constructed with DOM
79
+ methods. The raw HTML button remains available specifically to expose that
80
+ difference.
81
+
82
+ `tests/playground.test.js` loads the complete page in a same-origin frame. It
83
+ checks bootstrapping, pure analysis, one-step selection restoration, complete
84
+ normalization, action reporting, host-specific list defaults, synthetic input
85
+ routing including structural Backspace, and the roaming toolbar. It also drives
86
+ the class-mark controls and checks their exact DOM result in every target
87
+ browser.
88
+
89
+ ## TODO
90
+
91
+ - Highlight the nodes affected by the current plan in the editable view.
92
+ - Add scope selection and transaction dirty-scope visualization.
93
+ - Show before/after range boundaries for every individual operation.
94
+ - Export and import minimized custom scenarios as JSON.
95
+ - Add clipboard/drop payload and history panels when those modules exist.
@@ -0,0 +1,518 @@
1
+ <!doctype html>
2
+ <html lang=en>
3
+ <head>
4
+ <meta charset=utf-8>
5
+ <meta name=viewport content="width=device-width, initial-scale=1">
6
+ <title>RTE host matrix</title>
7
+ <style>
8
+ :root {
9
+ color-scheme: light dark;
10
+ font: 14px/1.4 system-ui, sans-serif;
11
+ --border: color-mix(in srgb, CanvasText 25%, transparent);
12
+ --panel: color-mix(in srgb, CanvasText 5%, Canvas);
13
+ }
14
+
15
+ * { box-sizing: border-box; }
16
+ body { margin: 0 auto; max-inline-size: 90rem; padding: 1rem; }
17
+ h1 { font-size: 1.35rem; margin-block: 0 .25rem; }
18
+ h2 { font-size: 1.1rem; margin-block: .35rem 0; }
19
+ h3 { font-size: 1rem; margin-block: 0 .65rem; }
20
+ p { margin-block: .35rem .8rem; }
21
+ code, pre { font: .85rem/1.4 ui-monospace, monospace; }
22
+
23
+ .matrix {
24
+ display: grid;
25
+ gap: .8rem;
26
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
27
+ }
28
+ .group { grid-column: 1 / -1; }
29
+ .case, .inspector {
30
+ background: var(--panel);
31
+ border: 1px solid var(--border);
32
+ border-radius: .4rem;
33
+ min-inline-size: 0;
34
+ padding: .8rem;
35
+ }
36
+ .case[data-active=true] { border-color: Highlight; }
37
+ .case > output {
38
+ display: block;
39
+ margin-block-start: .65rem;
40
+ overflow-wrap: anywhere;
41
+ }
42
+ .shell { margin: 0; padding-inline-start: 1.5rem; }
43
+ .shell-table { border-collapse: collapse; inline-size: 100%; }
44
+ .shell-table :is(th, td) { border: 1px solid var(--border); padding: .35rem; }
45
+
46
+ .rte-host {
47
+ --u2-rte: true;
48
+ --u2-rte-toolbar: undo redo block style bold italic underline strike code link bullets numbers outdent indent rule insertTable imageOriginal unstyle breaks source;
49
+ border: 2px solid color-mix(in srgb, Highlight 48%, var(--border));
50
+ min-block-size: 2.8rem;
51
+ outline: 0;
52
+ padding: .45rem;
53
+
54
+ &:focus { border-color: Highlight; }
55
+ }
56
+ ul.rte-host { padding-inline-start: 2rem; }
57
+ li.rte-host { display: list-item; }
58
+ x-editor.rte-host { display: block; }
59
+ table.rte-host { border-collapse: collapse; inline-size: 100%; }
60
+ table.rte-host :is(th, td) { border: 1px solid var(--border); padding: .35rem; }
61
+ dialog.rte-host[open]:not([data-modal]) { inline-size: 100%; margin: 0; position: static; }
62
+ dialog.rte-host[data-modal] { inline-size: min(30rem, calc(100% - 2rem)); }
63
+ dialog.rte-host::backdrop { background: #0008; }
64
+ .rte-host:popover-open { inline-size: min(30rem, calc(100% - 2rem)); }
65
+ .rte-host:fullscreen { background: Canvas; box-sizing: border-box; padding: 2rem; }
66
+ button.rte-host { color: inherit; font: inherit; inline-size: 100%; text-align: start; }
67
+ a.rte-host, a > .rte-host { display: inline-block; min-inline-size: 10rem; }
68
+ [contenteditable=false] { border: 1px dashed var(--border); padding: .2rem; }
69
+
70
+ .inspector { margin-block-start: .8rem; }
71
+ .inspector header { align-items: center; display: flex; gap: .8rem; justify-content: space-between; }
72
+ .inspector h2 { font-size: 1rem; margin: 0; }
73
+ .inspector h3 { font-size: .9rem; font-weight: 500; margin: 0; }
74
+ .inspector button { font: inherit; }
75
+ .readout {
76
+ display: grid;
77
+ gap: .8rem;
78
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
79
+ margin-block-start: .7rem;
80
+ }
81
+ pre {
82
+ background: Canvas;
83
+ border: 1px solid var(--border);
84
+ margin: .25rem 0 0;
85
+ min-block-size: 5rem;
86
+ overflow: auto;
87
+ padding: .5rem;
88
+ white-space: pre-wrap;
89
+ }
90
+ </style>
91
+ </head>
92
+ <body>
93
+ <nav><a href=index.html>Normalization playground</a></nav>
94
+ <h1>RTE host matrix</h1>
95
+ <p>
96
+ Focus and edit every surface, especially with Enter and Backspace. These
97
+ are representative content models rather than a whitelist of supported
98
+ tags. Link activation and element-specific browser behavior remain native.
99
+ </p>
100
+
101
+ <main class=matrix>
102
+ <h2 class=group>Block containers</h2>
103
+
104
+ <section class=case>
105
+ <h3><code>&lt;div contenteditable&gt;</code></h3>
106
+ <div class=rte-host contenteditable data-name=div>Editable generic block</div>
107
+ <output></output>
108
+ </section>
109
+
110
+ <section class=case>
111
+ <h3><code>&lt;article contenteditable&gt;</code></h3>
112
+ <article class=rte-host contenteditable data-name=article>Editable article</article>
113
+ <output></output>
114
+ </section>
115
+
116
+ <section class=case>
117
+ <h3><code>&lt;blockquote contenteditable&gt;</code></h3>
118
+ <blockquote class=rte-host contenteditable data-name=blockquote>Editable quotation</blockquote>
119
+ <output></output>
120
+ </section>
121
+
122
+ <section class=case>
123
+ <h3><code>&lt;figure contenteditable&gt;</code></h3>
124
+ <figure class=rte-host contenteditable data-name=figure>Editable figure content</figure>
125
+ <output></output>
126
+ </section>
127
+
128
+ <section class=case>
129
+ <h3><code>&lt;figcaption contenteditable&gt;</code></h3>
130
+ <figure>
131
+ <figcaption class=rte-host contenteditable data-name=figcaption>Editable caption</figcaption>
132
+ </figure>
133
+ <output></output>
134
+ </section>
135
+
136
+ <section class=case>
137
+ <h3><code>&lt;details contenteditable&gt;</code></h3>
138
+ <details class=rte-host contenteditable data-name=details open>
139
+ <summary>Editable summary in host</summary>
140
+ Editable details content
141
+ </details>
142
+ <output></output>
143
+ </section>
144
+
145
+ <section class=case>
146
+ <h3><code>&lt;x-editor contenteditable&gt;</code></h3>
147
+ <x-editor class=rte-host contenteditable data-name=custom>Editable custom element</x-editor>
148
+ <output></output>
149
+ </section>
150
+
151
+ <h2 class=group>Text and interactive hosts</h2>
152
+
153
+ <section class=case>
154
+ <h3><code>&lt;p contenteditable&gt;</code></h3>
155
+ <p class=rte-host contenteditable data-name=paragraph>Editable paragraph host</p>
156
+ <output></output>
157
+ </section>
158
+
159
+ <section class=case>
160
+ <h3><code>&lt;h2 contenteditable&gt;</code></h3>
161
+ <h2 class=rte-host contenteditable data-name=heading>Editable heading host</h2>
162
+ <output></output>
163
+ </section>
164
+
165
+ <section class=case>
166
+ <h3><code>&lt;pre contenteditable&gt;</code></h3>
167
+ <pre class=rte-host contenteditable data-name=pre>Editable preformatted host</pre>
168
+ <output></output>
169
+ </section>
170
+
171
+ <section class=case>
172
+ <h3><code>&lt;span contenteditable&gt;</code></h3>
173
+ <span class=rte-host contenteditable data-name=span>Editable inline host</span>
174
+ <output></output>
175
+ </section>
176
+
177
+ <section class=case>
178
+ <h3><code>&lt;code contenteditable&gt;</code></h3>
179
+ <code class=rte-host contenteditable data-name=code>Editable code host</code>
180
+ <output></output>
181
+ </section>
182
+
183
+ <section class=case>
184
+ <h3><code>&lt;label contenteditable&gt;</code></h3>
185
+ <label for="native-input" class=rte-host contenteditable data-name=label>Editable label host</label>
186
+ <input id="native-input" type=text placeholder="Native input" />
187
+ <output></output>
188
+ </section>
189
+
190
+ <section class=case>
191
+ <h3><code>&lt;output contenteditable&gt;</code></h3>
192
+ <p><output class=rte-host contenteditable data-name=output>Editable output host</output></p>
193
+ <output></output>
194
+ </section>
195
+
196
+ <section class=case>
197
+ <h3><code>&lt;summary contenteditable&gt;</code></h3>
198
+ <details open>
199
+ <summary class=rte-host contenteditable data-name=summary>Editable summary host</summary>
200
+ Non-editable details content
201
+ </details>
202
+ <output></output>
203
+ </section>
204
+
205
+ <section class=case>
206
+ <h3><code>&lt;button contenteditable&gt;</code></h3>
207
+ <button class=rte-host contenteditable data-name=button type=button>Editable button</button>
208
+ <output></output>
209
+ </section>
210
+
211
+ <section class=case id=links>
212
+ <h3><code>&lt;a contenteditable&gt;</code></h3>
213
+ <a class=rte-host contenteditable data-name=link href=#links>Editable link host</a>
214
+ <output></output>
215
+ </section>
216
+
217
+ <section class=case>
218
+ <h3><code>&lt;a&gt; &lt;span contenteditable&gt;</code></h3>
219
+ <a href=#links>
220
+ Before <span class=rte-host contenteditable data-name=inside-link>editable host</span> after
221
+ </a>
222
+ <output></output>
223
+ </section>
224
+
225
+ <section class=case>
226
+ <h3><code>&lt;div contenteditable&gt; &lt;a&gt;</code></h3>
227
+ <div class=rte-host contenteditable data-name=around-link>
228
+ Editable host around <a href=#links>a link</a>
229
+ </div>
230
+ <output></output>
231
+ </section>
232
+
233
+ <h2 class=group>Lists</h2>
234
+
235
+ <section class=case>
236
+ <h3><code>&lt;ul contenteditable&gt;</code></h3>
237
+ <ul class=rte-host contenteditable data-name=ul>
238
+ <li>First item
239
+ <li>Second item
240
+ </ul>
241
+ <output></output>
242
+ </section>
243
+
244
+ <section class=case>
245
+ <h3><code>&lt;ol contenteditable&gt;</code></h3>
246
+ <ol class=rte-host contenteditable data-name=ol>
247
+ <li>First item
248
+ <li>Second item
249
+ </ol>
250
+ <output></output>
251
+ </section>
252
+
253
+ <section class=case>
254
+ <h3><code>&lt;li contenteditable&gt;</code></h3>
255
+ <ul class=shell>
256
+ <li class=rte-host contenteditable data-name=li>Editable list item
257
+ </ul>
258
+ <output></output>
259
+ </section>
260
+
261
+ <section class=case>
262
+ <h3><code>&lt;dl contenteditable&gt;</code></h3>
263
+ <dl class=rte-host contenteditable data-name=description-list>
264
+ <dt>Term
265
+ <dd>Description
266
+ </dl>
267
+ <output></output>
268
+ </section>
269
+
270
+ <section class=case>
271
+ <h3><code>&lt;dt contenteditable&gt;</code></h3>
272
+ <dl>
273
+ <dt class=rte-host contenteditable data-name=term>Editable term
274
+ <dd>Description
275
+ </dl>
276
+ <output></output>
277
+ </section>
278
+
279
+ <section class=case>
280
+ <h3><code>&lt;dd contenteditable&gt;</code></h3>
281
+ <dl>
282
+ <dt>Term
283
+ <dd class=rte-host contenteditable data-name=description>Editable description
284
+ </dl>
285
+ <output></output>
286
+ </section>
287
+
288
+ <h2 class=group>Tables</h2>
289
+
290
+ <section class=case>
291
+ <h3><code>&lt;table contenteditable&gt;</code></h3>
292
+ <table class=rte-host contenteditable data-name=table>
293
+ <tbody>
294
+ <tr><th>One<th>Two
295
+ <tr><td>Alpha<td>Beta
296
+ </tbody>
297
+ </table>
298
+ <output></output>
299
+ </section>
300
+
301
+ <section class=case>
302
+ <h3><code>&lt;thead contenteditable&gt;</code></h3>
303
+ <table class=shell-table>
304
+ <thead class=rte-host contenteditable data-name=thead><tr><th>Editable head<th>Second cell
305
+ <tbody><tr><td>Body<td>Body
306
+ </table>
307
+ <output></output>
308
+ </section>
309
+
310
+ <section class=case>
311
+ <h3><code>&lt;tbody contenteditable&gt;</code></h3>
312
+ <table class=shell-table>
313
+ <tbody class=rte-host contenteditable data-name=tbody><tr><td>Editable body<td>Second cell
314
+ </table>
315
+ <output></output>
316
+ </section>
317
+
318
+ <section class=case>
319
+ <h3><code>&lt;tfoot contenteditable&gt;</code></h3>
320
+ <table class=shell-table>
321
+ <tbody><tr><td>Body<td>Body
322
+ <tfoot class=rte-host contenteditable data-name=tfoot><tr><td>Editable foot<td>Second cell
323
+ </table>
324
+ <output></output>
325
+ </section>
326
+
327
+ <section class=case>
328
+ <h3><code>&lt;tr contenteditable&gt;</code></h3>
329
+ <table class=shell-table>
330
+ <tbody><tr class=rte-host contenteditable data-name=row><td>Editable row<td>Second cell
331
+ </table>
332
+ <output></output>
333
+ </section>
334
+
335
+ <section class=case>
336
+ <h3><code>&lt;th contenteditable&gt;</code></h3>
337
+ <table class=shell-table>
338
+ <tbody><tr><th class=rte-host contenteditable data-name=header-cell>Editable header cell<th>Peer
339
+ </table>
340
+ <output></output>
341
+ </section>
342
+
343
+ <section class=case>
344
+ <h3><code>&lt;td contenteditable&gt;</code></h3>
345
+ <table class=shell-table>
346
+ <tbody><tr><td class=rte-host contenteditable data-name=cell>Editable cell<td>Peer
347
+ </table>
348
+ <output></output>
349
+ </section>
350
+
351
+ <section class=case>
352
+ <h3><code>&lt;caption contenteditable&gt;</code></h3>
353
+ <table class=shell-table>
354
+ <caption class=rte-host contenteditable data-name=caption>Editable table caption</caption>
355
+ <tbody><tr><td>Body<td>Body
356
+ </table>
357
+ <output></output>
358
+ </section>
359
+
360
+ <h2 class=group>Dialogs and editing boundaries</h2>
361
+
362
+ <section class=case>
363
+ <h3><code>&lt;dialog open contenteditable&gt;</code></h3>
364
+ <dialog class=rte-host contenteditable data-name=dialog open>Editable non-modal dialog</dialog>
365
+ <output></output>
366
+ </section>
367
+
368
+ <section class=case>
369
+ <h3><code>command="show-modal"</code></h3>
370
+ <button commandfor=modal-host command=show-modal type=button>Show modal editor</button>
371
+ <dialog class=rte-host contenteditable data-name=modal-dialog data-modal id=modal-host tabindex=-1>
372
+ <p>Edit this modal dialog.</p>
373
+ <button commandfor=modal-host command=close contenteditable=false type=button>Close</button>
374
+ </dialog>
375
+ <output></output>
376
+ </section>
377
+
378
+ <section class=case>
379
+ <h3><code>showPopover()</code></h3>
380
+ <button id=show-popover-host type=button>Show popover editor</button>
381
+ <div class=rte-host contenteditable data-name=popover id=popover-host popover=manual tabindex=-1>
382
+ Edit this popover host.
383
+ <button popovertarget=popover-host popovertargetaction=hide contenteditable=false type=button>Close</button>
384
+ </div>
385
+ <output></output>
386
+ </section>
387
+
388
+ <section class=case>
389
+ <h3><code>requestFullscreen()</code></h3>
390
+ <button id=show-fullscreen-host type=button>Show fullscreen editor</button>
391
+ <div class=rte-host contenteditable data-name=fullscreen id=fullscreen-host tabindex=-1>
392
+ <p>Edit this fullscreen host.</p>
393
+ <button id=hide-fullscreen-host contenteditable=false type=button>Exit fullscreen</button>
394
+ </div>
395
+ <output></output>
396
+ </section>
397
+
398
+ <section class=case>
399
+ <h3><code>contenteditable="plaintext-only"</code></h3>
400
+ <div class=rte-host contenteditable=plaintext-only data-name=plain-text>Native plain-text host</div>
401
+ <output></output>
402
+ </section>
403
+
404
+ <section class=case>
405
+ <h3><code>contenteditable="false"</code> island</h3>
406
+ <div class=rte-host contenteditable data-name=false-island>
407
+ Editable before <span contenteditable=false>fixed island</span> editable after
408
+ </div>
409
+ <output></output>
410
+ </section>
411
+
412
+ <section class=case>
413
+ <h3>Empty editing host</h3>
414
+ <div class=rte-host contenteditable data-name=empty></div>
415
+ <output></output>
416
+ </section>
417
+ </main>
418
+
419
+ <section class=inspector>
420
+ <header>
421
+ <h2>Live inspector</h2>
422
+ <button id=clear type=button>Clear events</button>
423
+ </header>
424
+ <div class=readout>
425
+ <section><h3>Active surface</h3><pre id=active>None — focus a surface.</pre></section>
426
+ <section><h3>Selection</h3><pre id=selection>None</pre></section>
427
+ <section><h3>HTML</h3><pre id=html>None</pre></section>
428
+ <section><h3>Recent events</h3><pre id=events>None</pre></section>
429
+ </div>
430
+ </section>
431
+
432
+ <script type=module>
433
+ import {config, rte, Source} from '../rte.js';
434
+
435
+ const hosts = [...document.querySelectorAll('.rte-host')];
436
+ const events = [];
437
+ const active = document.querySelector('#active');
438
+ const selection = document.querySelector('#selection');
439
+ const html = document.querySelector('#html');
440
+ const eventLog = document.querySelector('#events');
441
+
442
+ const name = host => host?.dataset.name || 'none';
443
+ const node = value => {
444
+ if (!value) return 'none';
445
+ if (value.nodeType === Node.TEXT_NODE) {
446
+ const text = value.data.replace(/\s+/g, ' ').trim();
447
+ return `#text "${text.slice(0, 32)}"`;
448
+ }
449
+ return `<${value.localName}>`;
450
+ };
451
+
452
+ function render() {
453
+ for (const host of hosts) {
454
+ const surface = rte.get(host);
455
+ const settings = config(host);
456
+ const state = surface?.active ? 'active' : surface ? 'registered' : 'focus to register';
457
+ const mode = host.getAttribute('contenteditable') === 'plaintext-only' ? ' · plain text' : '';
458
+ host.closest('.case').dataset.active = surface?.active || false;
459
+ host.closest('.case').querySelector(':scope > output').textContent =
460
+ `${state}${mode} · block ${settings.block || 'none'} · Enter ${settings.enter}`;
461
+ }
462
+
463
+ const host = rte.active?.element;
464
+ if (!host) {
465
+ active.textContent = 'None — focus a surface.';
466
+ selection.textContent = 'None';
467
+ html.textContent = 'None';
468
+ } else {
469
+ const settings = rte.active.config;
470
+ active.textContent = `${name(host)}\nblock: ${settings.block || 'none'}\nEnter: ${settings.enter}`;
471
+ html.textContent = new Source(rte.active).read().html;
472
+ const range = getSelection();
473
+ selection.textContent = range?.rangeCount
474
+ ? `anchor: ${node(range.anchorNode)} @ ${range.anchorOffset}\nfocus: ${node(range.focusNode)} @ ${range.focusOffset}\ncollapsed: ${range.isCollapsed}`
475
+ : 'None';
476
+ }
477
+ eventLog.textContent = events.length ? events.join('\n') : 'None';
478
+ }
479
+
480
+ function record(event) {
481
+ const host = event.composedPath().find(item => item?.matches?.('.rte-host'));
482
+ if (!host) return;
483
+ const detail = event.inputType || event.key || '';
484
+ events.push(`${name(host)} · ${event.type}${detail ? ` · ${detail}` : ''}`);
485
+ events.splice(0, Math.max(0, events.length - 16));
486
+ queueMicrotask(render);
487
+ }
488
+
489
+ for (const type of ['focusin', 'focusout', 'beforeinput', 'input', 'keydown', 'click', 'cancel', 'close']) {
490
+ document.addEventListener(type, record, true);
491
+ }
492
+ rte.addEventListener('u2-rte-add', event => {
493
+ events.push(`${name(event.detail.surface.element)} · registered`);
494
+ render();
495
+ });
496
+ rte.addEventListener('u2-rte-activechange', render);
497
+ document.addEventListener('selectionchange', render);
498
+ document.querySelector('#clear').addEventListener('click', () => {
499
+ events.length = 0;
500
+ render();
501
+ });
502
+ const popover = document.querySelector('#popover-host');
503
+ document.querySelector('#show-popover-host').addEventListener('click', () => {
504
+ popover.showPopover();
505
+ popover.focus();
506
+ });
507
+ const fullscreen = document.querySelector('#fullscreen-host');
508
+ const enterFullscreen = document.querySelector('#show-fullscreen-host');
509
+ enterFullscreen.disabled = typeof fullscreen.requestFullscreen !== 'function';
510
+ enterFullscreen.addEventListener('click', async () => {
511
+ await fullscreen.requestFullscreen();
512
+ fullscreen.focus();
513
+ });
514
+ document.querySelector('#hide-fullscreen-host').addEventListener('click', () => document.exitFullscreen());
515
+ render();
516
+ </script>
517
+ </body>
518
+ </html>