u2ui 1.5.3 → 1.5.7
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/el/ico/ico.js +5 -5
- package/el/rte/README.md +53 -13
- package/el/rte/manifest.json +19 -4
- package/el/rte/rte.css +31 -6
- package/el/rte/rte.js +174 -44
- package/el/rte0/README.md +37 -0
- package/el/rte0/manifest.json +33 -0
- package/el/rte0/rte0.css +9 -0
- package/el/rte0/rte0.js +56 -0
- package/el/system/styler.js +2 -0
- package/el/tooltip/tooltip.css +2 -2
- package/js/Placer/Placer.js +2 -1
- package/js/rte/AGENTS.md +142 -0
- package/js/rte/PLAN.md +487 -0
- package/js/rte/README.md +319 -25
- package/js/rte/blocks.js +7 -0
- package/js/rte/breaks.js +6 -0
- package/js/rte/classes.js +7 -0
- package/js/rte/docs/usage.md +845 -0
- package/js/rte/images.js +7 -0
- package/js/rte/link.js +8 -0
- package/js/rte/playground/README.md +95 -0
- package/js/rte/playground/hosts.html +553 -0
- package/js/rte/playground/index.html +835 -0
- package/js/rte/rte.js +56 -5
- package/js/rte/run.out +17 -0
- package/js/rte/source.js +12 -0
- package/js/rte/src/browser/README.md +40 -0
- package/js/rte/src/browser/interactive.js +17 -0
- package/js/rte/src/browser/range-rect.js +82 -0
- package/js/rte/src/client/README.md +385 -0
- package/js/rte/src/client/blocks.js +38 -0
- package/js/rte/src/client/breaks.js +212 -0
- package/js/rte/src/client/classes.js +48 -0
- package/js/rte/src/client/editor.js +472 -0
- package/js/rte/src/client/external-input.js +47 -0
- package/js/rte/src/client/history.js +13 -0
- package/js/rte/src/client/images.js +285 -0
- package/js/rte/src/client/link.js +567 -0
- package/js/rte/src/client/marks.js +25 -0
- package/js/rte/src/client/source.js +182 -0
- package/js/rte/src/client/structure.js +27 -0
- package/js/rte/src/client/tables.js +165 -0
- package/js/rte/src/client/unstyle.js +32 -0
- package/js/rte/src/command/README.md +520 -0
- package/js/rte/src/command/block-boundary.js +43 -0
- package/js/rte/src/command/block-style.js +120 -0
- package/js/rte/src/command/commands.js +159 -0
- package/js/rte/src/command/delete.js +119 -0
- package/js/rte/src/command/edit.js +78 -0
- package/js/rte/src/command/element.js +45 -0
- package/js/rte/src/command/enter.js +175 -0
- package/js/rte/src/command/fragment.js +76 -0
- package/js/rte/src/command/insert.js +52 -0
- package/js/rte/src/command/list.js +284 -0
- package/js/rte/src/command/mark.js +583 -0
- package/js/rte/src/command/pending-marks.js +216 -0
- package/js/rte/src/command/table.js +207 -0
- package/js/rte/src/command/unstyle.js +248 -0
- package/js/rte/src/config/README.md +96 -0
- package/js/rte/src/config/config.js +134 -0
- package/js/rte/src/core/README.md +63 -0
- package/js/rte/src/core/core.js +247 -0
- package/js/rte/src/history/README.md +83 -0
- package/js/rte/src/history/history.js +283 -0
- package/js/rte/src/input/README.md +173 -0
- package/js/rte/src/input/external-input.js +130 -0
- package/js/rte/src/input/input-pipeline.js +459 -0
- package/js/rte/src/mark/README.md +219 -0
- package/js/rte/src/mark/dom-adapter.js +100 -0
- package/js/rte/src/mark/mark.js +133 -0
- package/js/rte/src/mark/standard.js +76 -0
- package/js/rte/src/model/README.md +71 -0
- package/js/rte/src/model/content-model.js +213 -0
- package/js/rte/src/model/html/README.md +51 -0
- package/js/rte/src/model/html/html-model.js +90 -0
- package/js/rte/src/normalize/execute/README.md +33 -0
- package/js/rte/src/normalize/execute/repair-executor.js +129 -0
- package/js/rte/src/normalize/normalizer/README.md +48 -0
- package/js/rte/src/normalize/normalizer/normalizer.js +141 -0
- package/js/rte/src/normalize/repair/README.md +70 -0
- package/js/rte/src/normalize/repair/repair-planner.js +208 -0
- package/js/rte/src/sanitize/README.md +108 -0
- package/js/rte/src/sanitize/native.js +43 -0
- package/js/rte/src/sanitize/policy.js +206 -0
- package/js/rte/src/selection/README.md +39 -0
- package/js/rte/src/selection/map/README.md +53 -0
- package/js/rte/src/selection/map/point-map.js +307 -0
- package/js/rte/src/selection/ownership/README.md +32 -0
- package/js/rte/src/selection/ownership/ownership.js +42 -0
- package/js/rte/src/selection/point/README.md +34 -0
- package/js/rte/src/selection/point/point.js +83 -0
- package/js/rte/src/selection/range/README.md +38 -0
- package/js/rte/src/selection/range/edit-range.js +223 -0
- package/js/rte/src/selection/snapshot.js +64 -0
- package/js/rte/src/source/README.md +64 -0
- package/js/rte/src/source/source.js +193 -0
- package/js/rte/src/surface/README.md +36 -0
- package/js/rte/src/surface/content.js +16 -0
- package/js/rte/src/surface/surface.js +87 -0
- package/js/rte/src/transaction/README.md +35 -0
- package/js/rte/src/transaction/transaction.js +56 -0
- package/js/rte/src/ui/README.md +271 -0
- package/js/rte/src/ui/caret.js +17 -0
- package/js/rte/src/ui/chrome.js +214 -0
- package/js/rte/src/ui/handles.js +179 -0
- package/js/rte/src/ui/highlight.js +86 -0
- package/js/rte/src/ui/place.js +25 -0
- package/js/rte/src/ui/toolbar.js +258 -0
- package/js/rte/src/unstyle/README.md +103 -0
- package/js/rte/src/unstyle/unstyle.js +140 -0
- package/js/rte/tables.js +7 -0
- package/js/rte/unstyle.js +8 -0
- package/js/rte0/README.md +37 -0
- package/js/rte0/rte.js +7 -0
- package/js/{rte → rte0}/src/behavior.js +0 -1
- package/js/{rte → rte0}/src/events.js +0 -1
- package/js/{rte → rte0}/src/range.js +0 -2
- package/js/{rte → rte0}/src/ui.items.js +0 -2
- package/js/{rte → rte0}/src/ui.js +0 -1
- package/package.json +1 -1
- package/u2/Element.js +2 -2
- package/u2/plan-scoped-refactoring.md +133 -303
- package/u2/projects.json +18 -2
- package/u2/tools/update.repos.json.js +1 -1
- package/el/qrcode/package.json +0 -22
- /package/js/{rte → rte0}/old/NodeCleaner.js +0 -0
- /package/js/{rte → rte0}/old/Rte/Rte.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/Rte.ui.items.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/Rte.ui.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/crossbrowser.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/font/tinymce.svg +0 -0
- /package/js/{rte → rte0}/old/Rte/font/tinymce.woff +0 -0
- /package/js/{rte → rte0}/old/Rte/helpers.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/htmlparser.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/index.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/main.css +0 -0
- /package/js/{rte → rte0}/old/old.rte.js +0 -0
- /package/js/{rte → rte0}/old/range-selection.old.js +0 -0
- /package/js/{rte → rte0}/src/fixes.js +0 -0
- /package/js/{rte → rte0}/src/rte.js +0 -0
- /package/js/{rte → rte0}/src/ui/items/ai.js +0 -0
- /package/js/{rte → rte0}/src/ui/items/code.js +0 -0
- /package/js/{rte → rte0}/src/ui/items/undo.js +0 -0
- /package/js/{rte → rte0}/src/util/htmlparser.js +0 -0
- /package/js/{rte → rte0}/src/util/indentHtml.js +0 -0
package/js/rte/AGENTS.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# RTE Agent Instructions
|
|
2
|
+
|
|
3
|
+
These instructions apply to this directory and all descendants.
|
|
4
|
+
|
|
5
|
+
## Project status — important
|
|
6
|
+
|
|
7
|
+
RTE is new and not used in production or by downstream consumers yet. There
|
|
8
|
+
is no backward-compatibility obligation. Prefer the clean final API and remove
|
|
9
|
+
superseded names or designs instead of adding aliases, deprecations, shims, or
|
|
10
|
+
legacy layers. Existing behavior still needs tests, but compatibility with an
|
|
11
|
+
earlier RTE draft is not a reason to keep a weaker design.
|
|
12
|
+
|
|
13
|
+
## Start here
|
|
14
|
+
|
|
15
|
+
- Read [`README.md`](./README.md) for the architecture and requirements.
|
|
16
|
+
- Read [`PLAN.md`](./PLAN.md) for implemented phases and the next responsibility.
|
|
17
|
+
- Read the README beside every responsibility you change.
|
|
18
|
+
- Treat `../rte0` as a read-only behavioral reference. Never modify it or any
|
|
19
|
+
other file outside `rte` while working on RTE.
|
|
20
|
+
|
|
21
|
+
## Priorities
|
|
22
|
+
|
|
23
|
+
- Keep changes focused, compact, readable, and internally consistent.
|
|
24
|
+
- Prefer a small general solution to a collection of special cases. RTE is a
|
|
25
|
+
generic engine and must support use cases that were not anticipated.
|
|
26
|
+
- Treat reported examples as regression cases for a general rule. Do not ship
|
|
27
|
+
tag-, class-, fixture-, or browser-specific behavior when the underlying
|
|
28
|
+
concept can be expressed by configuration, the content model, or a reusable
|
|
29
|
+
primitive.
|
|
30
|
+
- KISS and YAGNI apply, but every contract must remain composable, replaceable,
|
|
31
|
+
and extensible.
|
|
32
|
+
- Shipped defaults are useful starting points, never fixed policy. Applications
|
|
33
|
+
may replace them.
|
|
34
|
+
- Fix obvious, local, undisputed defects encountered in the discussed area.
|
|
35
|
+
Do not expand work into subjective or speculative cleanup.
|
|
36
|
+
- Preserve existing comments and debugging functions unless their removal was
|
|
37
|
+
explicitly requested.
|
|
38
|
+
|
|
39
|
+
## Architecture
|
|
40
|
+
|
|
41
|
+
- Use native `contenteditable`, `Selection`, `Range`, Input Events, and modern
|
|
42
|
+
Web APIs. Do not build a parallel document model.
|
|
43
|
+
- Never use `document.execCommand()`.
|
|
44
|
+
- Keep one shared core per selection context as the normal architecture. A core
|
|
45
|
+
may coordinate multiple editable surfaces and multiple roaming or static UIs.
|
|
46
|
+
- Mutable state belongs to its core, surface, transaction, or module instance.
|
|
47
|
+
Module globals are only for immutable constants and stateless helpers.
|
|
48
|
+
- Nested editable hosts are isolation boundaries for events, selections,
|
|
49
|
+
traversal, normalization, and commands.
|
|
50
|
+
- DOM mutations owned by the editor go through transactions and explicit point
|
|
51
|
+
mapping. Preserve forward and backward selection intent.
|
|
52
|
+
- Keep responsibilities narrow. Content validity, repair planning, mechanical
|
|
53
|
+
mutation, selection mapping, input routing, commands, history, sanitizing,
|
|
54
|
+
browser policies, and UI remain separate modules.
|
|
55
|
+
- Later layers depend on public contracts, never private implementation details.
|
|
56
|
+
- Browser workarounds must be feature-detected, isolated near the affected
|
|
57
|
+
primitive, and covered by a named regression test. Do not branch on user-agent
|
|
58
|
+
strings.
|
|
59
|
+
- Security sanitizing and structural normalization are separate policies.
|
|
60
|
+
Never pass untrusted HTML to unsafe DOM sinks.
|
|
61
|
+
- Scope normalization to the smallest affected subtree and expand only when a
|
|
62
|
+
repair crosses that boundary. It must converge, be idempotent, and preserve
|
|
63
|
+
meaningful content.
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
- Give every module safe, useful, host-aware defaults without requiring setup.
|
|
68
|
+
- Prefer inheritable CSS custom properties for serializable behavior. Use
|
|
69
|
+
JavaScript configuration for functions, policy objects, and other values CSS
|
|
70
|
+
cannot express naturally.
|
|
71
|
+
- Different editing hosts have different semantics. Lists, inline-only hosts,
|
|
72
|
+
tables, generic blocks, atomic content, and nested editors must not be forced
|
|
73
|
+
through one generic block rule.
|
|
74
|
+
- Use current browser standards; compatibility with obsolete browsers is not a
|
|
75
|
+
goal. Modern CSS, including nesting, is allowed.
|
|
76
|
+
|
|
77
|
+
## Code style
|
|
78
|
+
|
|
79
|
+
- Code and code comments are English.
|
|
80
|
+
- Use standard ESM: `import`/`export`, `const`/`let`, classes, and functions. No
|
|
81
|
+
IIFEs, `var`, prototype assignment, or accidental globals. Assign to
|
|
82
|
+
`globalThis` only for deliberate compatibility or test instrumentation.
|
|
83
|
+
- Use clear, established, natural, preferably short names. One concept gets one
|
|
84
|
+
term throughout the project.
|
|
85
|
+
- Avoid redundant API names: context already carries meaning (`module.add()`,
|
|
86
|
+
not `module.addModule()`). Prefer symmetric names for symmetric operations.
|
|
87
|
+
- Keep control flow compact with small helpers and short early returns. Do not
|
|
88
|
+
compress code until its intent becomes harder to see.
|
|
89
|
+
- Place related code together. Add a file only for a real responsibility,
|
|
90
|
+
shared use, or separate testability—not merely to reduce line count.
|
|
91
|
+
- Remove middle-man helpers that only forward arguments without adding policy,
|
|
92
|
+
validation, normalization, or composition.
|
|
93
|
+
- Do not hardcode application-specific tags, classes, commands, or policies in
|
|
94
|
+
generic primitives.
|
|
95
|
+
- Use braces for multi-line and non-trivial control flow. Keep the local style
|
|
96
|
+
consistent when changing existing code.
|
|
97
|
+
- Do not run broad automatic formatters. Never run `deno fmt` in this workspace.
|
|
98
|
+
|
|
99
|
+
## HTML and CSS
|
|
100
|
+
|
|
101
|
+
- Keep production presentation independent from the engine. UI modules consume
|
|
102
|
+
editor state but do not become part of the core.
|
|
103
|
+
- Configure behavior through documented `--u2-rte-*` properties with good
|
|
104
|
+
fallbacks.
|
|
105
|
+
- Keep the playground compact and diagnostic. It must call production modules,
|
|
106
|
+
never contain a second implementation of editor behavior.
|
|
107
|
+
- Escape or sanitize user-controlled HTML with the selected sanitizing policy.
|
|
108
|
+
Parsing a trusted test fixture is not a substitute for a safe external-input
|
|
109
|
+
path.
|
|
110
|
+
|
|
111
|
+
## Documentation and tests
|
|
112
|
+
|
|
113
|
+
- Every production file has a dedicated comprehensive test file and focused
|
|
114
|
+
documentation in its responsibility README.
|
|
115
|
+
- Every responsibility README documents purpose, public contract, invariants,
|
|
116
|
+
browser considerations, and concrete TODOs.
|
|
117
|
+
- Update documentation and `PLAN.md` in the same change whenever a contract or
|
|
118
|
+
implementation status changes. Do not describe planned behavior as existing.
|
|
119
|
+
- Tests cover normal behavior, boundaries, invalid input, cleanup, teardown,
|
|
120
|
+
nesting, direction, cancellation, event order, and state isolation where
|
|
121
|
+
relevant.
|
|
122
|
+
- A browser quirk becomes a minimal failing regression test before it becomes a
|
|
123
|
+
policy.
|
|
124
|
+
- Tests leave no fixtures, selections, listeners, active cores, or other state
|
|
125
|
+
behind.
|
|
126
|
+
- Do not weaken assertions, add timing guesses, or hide failures behind
|
|
127
|
+
browser-specific skips. Test the stable contract and isolate genuinely
|
|
128
|
+
engine-specific behavior.
|
|
129
|
+
- Run `/u2/js/rte/tests/` through HTTP. A revision is cross-browser verified
|
|
130
|
+
only when that exact revision passes in current Chromium, Firefox, and WebKit.
|
|
131
|
+
- Use `/u2/js/rte/playground/` for visual inspection of normalization, mapping,
|
|
132
|
+
selection, and input behavior.
|
|
133
|
+
|
|
134
|
+
## Before handing off
|
|
135
|
+
|
|
136
|
+
- Review the diff for unrelated changes, redundant code, naming consistency,
|
|
137
|
+
cleanup, and stale documentation.
|
|
138
|
+
- Ensure every changed production responsibility has matching tests and docs.
|
|
139
|
+
- Report the exact browser revisions or engines actually tested. Never infer a
|
|
140
|
+
cross-browser pass from one engine.
|
|
141
|
+
- Mention changes outside the discussed area prominently, with reason and
|
|
142
|
+
effect.
|
package/js/rte/PLAN.md
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
# RTE implementation plan
|
|
2
|
+
|
|
3
|
+
Each phase ends with documentation, dedicated tests for every production file,
|
|
4
|
+
real-browser verification, and no known failing invariant. Later phases may
|
|
5
|
+
refine earlier contracts, but may not bypass their responsibility boundaries.
|
|
6
|
+
|
|
7
|
+
The working `../rte0` is a behavioral reference throughout the project. Existing
|
|
8
|
+
behavior, especially host-specific editing, selection handling, and browser
|
|
9
|
+
fixes, is first captured as black-box tests. RTE then preserves or deliberately
|
|
10
|
+
improves it through smaller responsibilities; structural weaknesses in the old
|
|
11
|
+
implementation are not mistaken for behavioral failures.
|
|
12
|
+
|
|
13
|
+
## Resume work
|
|
14
|
+
|
|
15
|
+
Phases 0–3 provide the core, selection/range primitives, content model, scoped
|
|
16
|
+
normalization, playground, and input pipeline. The command layer, the prevented
|
|
17
|
+
input path, and Enter close the phase-3 gap between configuration and behavior.
|
|
18
|
+
Phase 4 has started with immutable mark types, serializable values, equivalence,
|
|
19
|
+
directional conflicts, deterministic sets, removal, and replaceable DOM
|
|
20
|
+
adapters. Generic non-collapsed range commands now apply and remove one mark,
|
|
21
|
+
including reusable inline elements and neutral-span cleanup, without
|
|
22
|
+
`execCommand()`. Selection and caret state, toggle, and pending ordinary text
|
|
23
|
+
input are implemented. Adapters can explicitly remove semantic wrappers while
|
|
24
|
+
preserving unrelated attributes, and the standard HTML adapters for bold,
|
|
25
|
+
italic, underline, strike, code, and links exercise the whole path. Pending
|
|
26
|
+
overrides now carry through native IME composition without
|
|
27
|
+
intercepting its mutations. Complete mark sets now resolve exclusions, remove
|
|
28
|
+
configured values absent from an exact target, and apply their canonical result
|
|
29
|
+
inside one mapped operation.
|
|
30
|
+
|
|
31
|
+
The browser runner covers every phase below: mark algebra, pending overrides
|
|
32
|
+
carried through native IME composition, complete and nested canonical mark sets,
|
|
33
|
+
state-based history with its commands, lists and their nesting levels,
|
|
34
|
+
model-placed element insertion, the HTML source responsibility with its dialog,
|
|
35
|
+
value marks and the contextual link editor, CSS-declared content classes shared
|
|
36
|
+
by the style control, the sanitizer and presentation cleanup, one shared
|
|
37
|
+
placement policy, the full remove-format ladder, registry-owned shortcuts, the
|
|
38
|
+
attribute and element policies on imports with their aliases, the table
|
|
39
|
+
structure commands and the image frame with their contextual handles, and the
|
|
40
|
+
session rule that takes all of it away at once — everything the convention
|
|
41
|
+
client draws in one shadow root per editor. The playground configures its
|
|
42
|
+
prototype surface live from a panel built out of the registered modules, so
|
|
43
|
+
every host property can be tried without editing the page.
|
|
44
|
+
|
|
45
|
+
How many tests that is, the runner says itself; the number is not repeated here,
|
|
46
|
+
because a number in prose is stale the moment the next test is written. A result
|
|
47
|
+
is cross-browser evidence only after that exact revision has run in current
|
|
48
|
+
Chromium, Firefox, and WebKit. The last such confirmation was Chrome 152 and
|
|
49
|
+
Firefox 154; WebKit verification of that revision is pending.
|
|
50
|
+
|
|
51
|
+
Remove-format now runs a ladder that does not stop at the application's own
|
|
52
|
+
presentation: six presentation rungs in `Unstyle` (styles, presentational
|
|
53
|
+
attributes, foreign classes, formatting wrappers, declared content classes, and
|
|
54
|
+
the remaining semantic inline elements) plus a seventh `blocks` rung in the
|
|
55
|
+
command that reduces lists, tables, quotes, and headings to the host's default
|
|
56
|
+
block. The action is unavailable only when the selection is already plain text,
|
|
57
|
+
and a collapsed caret reaches the whole content rather than nothing.
|
|
58
|
+
Keyboard shortcuts moved from toolbar controls to the command registry, so a key
|
|
59
|
+
works whether or not a control is on screen, and `Tab`/`Shift+Tab` reach the
|
|
60
|
+
list-nesting commands only inside a list.
|
|
61
|
+
|
|
62
|
+
What may arrive is now its own question. `--u2-rte-import-elements` defaults to
|
|
63
|
+
the `@content` preset — headings, text, lists, tables, media and the text-level
|
|
64
|
+
semantics — while `--u2-rte-elements` stays what a host tolerates in content it
|
|
65
|
+
already owns. Neither can widen the sanitize policy.
|
|
66
|
+
|
|
67
|
+
An element the import list does not carry, but whose meaning a listed one does,
|
|
68
|
+
is replaced rather than dropped: `<b>` becomes `<strong>`, `<i>` becomes `<em>`.
|
|
69
|
+
Nothing did that afterwards — the bold mark recognizes `<b>`, but only a mark
|
|
70
|
+
command ever makes an element canonical — so a strict list would otherwise have
|
|
71
|
+
turned every pasted emphasis into plain text.
|
|
72
|
+
|
|
73
|
+
A native paste now meets the attribute policy. Nothing is parsed there — the
|
|
74
|
+
browser inserts its own payload — so it was the one import path without an
|
|
75
|
+
allowlist, and layout ids, tracking attributes and inline styles came straight
|
|
76
|
+
through. The pipeline applies `SanitizePolicy.clean()` to the nodes that
|
|
77
|
+
arrived, before presentation cleanup and structural repair, so an id removed in
|
|
78
|
+
the first stage is what lets the third dissolve the wrapper carrying it.
|
|
79
|
+
`--u2-rte-import-sanitize: none` opts a host out.
|
|
80
|
+
|
|
81
|
+
Pasted content is now cleaned where it landed. The cleanup scope was derived
|
|
82
|
+
from the caret, which sits at the *end* of a paste, so a pasted document was
|
|
83
|
+
only ever repaired in its last block — the reason legacy markup survived intact
|
|
84
|
+
even after the planner learned to remove it. The nodes that arrived are already
|
|
85
|
+
tracked, and now decide the scope.
|
|
86
|
+
|
|
87
|
+
Redundant markup is now removed at any depth. A neutral generic block was only
|
|
88
|
+
ever reshaped as a direct child of the root, so legacy documents kept their
|
|
89
|
+
nested bare `div`s — valid HTML, which is exactly why the content model alone
|
|
90
|
+
never removed them. `canonical` also stopped being a synonym for `structural`:
|
|
91
|
+
it now dissolves a generic inline wrapper that carries nothing.
|
|
92
|
+
|
|
93
|
+
The same work made normalization measurably cheaper. Measured in Chrome 152 as
|
|
94
|
+
the best of five runs against the previous revision: `ContentModel.allows()`
|
|
95
|
+
14.8 → 3.2 µs, one paragraph normalized 213 → 85 µs, a clean 264-element article
|
|
96
|
+
18.7 → 7.1 ms. The model no longer allocates a string per rule lookup and skips
|
|
97
|
+
the ancestor-exclusion walk for children nothing can exclude; the normalizer no
|
|
98
|
+
longer asks the executor to confirm the plans that change nothing, and no longer
|
|
99
|
+
re-validates every element the walk just collected.
|
|
100
|
+
|
|
101
|
+
A review pass consolidated four duplicated concepts into single definitions
|
|
102
|
+
(`isEditingBoundary`, `elementOf`, `indexOf`, `narrow`), removed a duplicated
|
|
103
|
+
availability check and a second placement policy, and made `edit.model` resolve
|
|
104
|
+
lazily. Measured in Chrome 152: an availability check that does not consult the
|
|
105
|
+
content model dropped from 10.0 to 1.8 µs, the list scan from 31.4 to 21.0 µs,
|
|
106
|
+
and one toolbar refresh from 490 to 437 µs. A lazy `config()` was measured and
|
|
107
|
+
rejected: building eight accessor descriptors costs more than parsing the values
|
|
108
|
+
eagerly.
|
|
109
|
+
|
|
110
|
+
Measured again in Chrome after the host-shape work, then sampled with the CPU
|
|
111
|
+
profiler over 400 keystrokes. What a keystroke costs was never normalization: a
|
|
112
|
+
third of it was reading CSS the caller did not need. `Commands.model` and
|
|
113
|
+
`Edit.model` narrow the content model by one property and were parsing all
|
|
114
|
+
eleven, and the toolbar resolved a computed style four times per refresh. Both
|
|
115
|
+
now read what they use, without a cache anywhere — the settings stay live.
|
|
116
|
+
|
|
117
|
+
| | before | after |
|
|
118
|
+
| --- | --- | --- |
|
|
119
|
+
| `Commands.model` | 8.7 µs | 0.7 µs |
|
|
120
|
+
| `commands.enabled('bold')` | 11.3 µs | 6.1 µs |
|
|
121
|
+
| `Toolbar.refresh` (8 controls) | 160 µs | 120 µs |
|
|
122
|
+
| one keystroke, roaming toolbar | 519 µs | 415 µs |
|
|
123
|
+
|
|
124
|
+
What is left is flat: `getBoundingClientRect` for the toolbar's placement is the
|
|
125
|
+
largest single item at 14% of a keystroke, then event dispatch, range cloning,
|
|
126
|
+
and the model's own rules. A click inside a surface costs 8.0 µs against 6.0 µs
|
|
127
|
+
for the same event outside the editor, a keydown 4.6 against 3.4, and drawing
|
|
128
|
+
the saved selection 0.5 µs per selection change.
|
|
129
|
+
Treat the number shown by `/u2/js/rte/tests/` as authoritative.
|
|
130
|
+
|
|
131
|
+
## 0. Foundation
|
|
132
|
+
|
|
133
|
+
Status: implemented. The foundation and policy suite passed in current
|
|
134
|
+
Chromium, Firefox, and WebKit. Later phase tests extend the same browser runner.
|
|
135
|
+
|
|
136
|
+
- CSS configuration with semantic host defaults.
|
|
137
|
+
- One core per selection context and multiple editable surfaces.
|
|
138
|
+
- Direction-preserving selection snapshots based on live ranges.
|
|
139
|
+
- Synchronous transactions, dirty-node collection, lifecycle events, and
|
|
140
|
+
nested transaction reuse.
|
|
141
|
+
- Dependency-free browser test runner.
|
|
142
|
+
|
|
143
|
+
Exit criteria:
|
|
144
|
+
|
|
145
|
+
- Static checks pass.
|
|
146
|
+
- Foundation suites pass in current Chromium, Firefox, and WebKit.
|
|
147
|
+
- Focus, selection, nested editors, cancellation, and teardown leak no state.
|
|
148
|
+
|
|
149
|
+
## 1. Positions and ranges
|
|
150
|
+
|
|
151
|
+
Status: ownership, live points, range splitting, text/block traversal,
|
|
152
|
+
containment, and explicit operation mapping are implemented. The browser
|
|
153
|
+
baseline passed in all target engines. Generated cases cover range traversal and
|
|
154
|
+
the content-preserving point-map operations; `remove`, `replace`, and `move`
|
|
155
|
+
still need an oracle of their own.
|
|
156
|
+
|
|
157
|
+
- Define logical DOM points with affinity at text, element, and atomic bounds.
|
|
158
|
+
- Map points through split, wrap, unwrap, replace, move, merge, and remove.
|
|
159
|
+
- Traverse selected content by blocks and editable boundaries.
|
|
160
|
+
- Preserve forward/backward selections and collapsed carets through every
|
|
161
|
+
primitive operation.
|
|
162
|
+
|
|
163
|
+
Tests emphasize every boundary offset, empty nodes, `<br>`, replaced endpoints,
|
|
164
|
+
atomic nodes, nested hosts, and generated forward/backward ranges.
|
|
165
|
+
|
|
166
|
+
## 2. Content model and normalization
|
|
167
|
+
|
|
168
|
+
Status: the immutable policy engine, standards-oriented HTML rules, pure repair
|
|
169
|
+
planning, mapped repair execution, scoped fixed-point normalization, and a
|
|
170
|
+
visual playground are implemented. Generated cases assert convergence,
|
|
171
|
+
idempotence, preserved text, and valid output across levels and host tags.
|
|
172
|
+
|
|
173
|
+
- Model permitted parents/children from HTML content categories plus editor
|
|
174
|
+
policy overrides.
|
|
175
|
+
- Implement minimal, structural, and canonical normalization as convergent
|
|
176
|
+
rules: repair, lift, split, unwrap, convert, merge, and remove.
|
|
177
|
+
- Preserve meaningful attributes, classes, and styles while removing neutral
|
|
178
|
+
wrappers and empty metadata.
|
|
179
|
+
- Reduce transaction touches to the smallest stable dirty scopes; expand only
|
|
180
|
+
when a repair crosses the boundary.
|
|
181
|
+
- Make normalization idempotent and canonical output deterministic.
|
|
182
|
+
|
|
183
|
+
The useful ideas in the old `NodeCleaner`—allow-lists, modernization, removal of
|
|
184
|
+
empty metadata, block-only wrapper detection, and Unicode normalization—become
|
|
185
|
+
separate policies. Computed-style comparison, prototype mutation, and unrelated
|
|
186
|
+
whole-tree cleanup are not carried forward.
|
|
187
|
+
|
|
188
|
+
## 3. Input pipeline
|
|
189
|
+
|
|
190
|
+
Status: post-native event classification, CSS cleanup triggers, live target
|
|
191
|
+
ranges, local normalization scope, composition deferral, nested-host isolation,
|
|
192
|
+
selection mapping, disposal, command routing with native text data for
|
|
193
|
+
prevented input, and replacement by an already prepared DOM fragment are
|
|
194
|
+
implemented. `ExternalInput` consumes rich paste/drop `dataTransfer` and the
|
|
195
|
+
native target range, applies the selected sanitizer and optional Unstyle
|
|
196
|
+
policy, and invokes mapped fragment insertion while failures stay fail-closed.
|
|
197
|
+
Without an explicitly selected sanitizer, native paste/drop remains
|
|
198
|
+
browser-owned; the pipeline removes classes and styles only from observed added
|
|
199
|
+
roots before structural normalization while preserving elements that predate
|
|
200
|
+
the input.
|
|
201
|
+
Collapsed backward and forward deletion at a
|
|
202
|
+
mergeable block boundary are routed explicitly; ordinary character and
|
|
203
|
+
selected-range key deletion remain native. Contextual plain-text and quotation
|
|
204
|
+
import are next.
|
|
205
|
+
|
|
206
|
+
- Route `beforeinput`, `input`, composition, paste, drop, delete, and Enter by
|
|
207
|
+
`inputType` and host policy.
|
|
208
|
+
- Let native editing proceed only where its result is interoperable and covered
|
|
209
|
+
by postconditions; otherwise prevent it and execute an explicit command.
|
|
210
|
+
- Sanitize external fragments before insertion, then normalize only the fragment
|
|
211
|
+
and insertion neighborhood.
|
|
212
|
+
- Give lists, inline hosts, tables, generic blocks, and atomic content semantic
|
|
213
|
+
default behavior that CSS can override.
|
|
214
|
+
|
|
215
|
+
Tests use trusted browser input where automation allows it and direct event/
|
|
216
|
+
command contracts for exhaustive edge cases.
|
|
217
|
+
|
|
218
|
+
## 4. Marks and inline commands
|
|
219
|
+
|
|
220
|
+
Status: started. Mark values define equivalence, directional exclusion,
|
|
221
|
+
canonical ordering, and removal. Replaceable adapters parse existing elements
|
|
222
|
+
and render canonical wrappers for semantic tags, classes, attributes, styles,
|
|
223
|
+
and custom policies. Generic commands apply and remove one configured mark over
|
|
224
|
+
arbitrary non-collapsed ranges, preserve selection direction, reuse suitable
|
|
225
|
+
inline elements, join adjacent canonical wrappers, clean up neutral spans, and
|
|
226
|
+
derive selection or structural caret state for toggle. A closed adapter
|
|
227
|
+
universe can now report and set exact canonical mark sets while resolving type
|
|
228
|
+
conflicts in the same transaction. Redundant nesting is removed, canonical
|
|
229
|
+
single-child wrappers follow mark rank, and newly exposed siblings merge to a
|
|
230
|
+
fixed point without crossing meaningful or atomic boundaries. Formatting other
|
|
231
|
+
than the standard HTML policies remains application-defined.
|
|
232
|
+
`PendingMarks` routes ordinary `insertText` through mapped input and applies the
|
|
233
|
+
same caret overrides to a live native composition range only after IME ends.
|
|
234
|
+
The ready-made adapters cover bold, italic, underline, strike, code, and links.
|
|
235
|
+
They render one canonical tag, remove their semantic wrappers explicitly, and
|
|
236
|
+
preserve unrelated attributes. Links use a small structured value without
|
|
237
|
+
coupling URL policy to mark representation.
|
|
238
|
+
|
|
239
|
+
- Apply, remove, and toggle over arbitrary ranges without
|
|
240
|
+
`document.execCommand()`.
|
|
241
|
+
- Support semantic elements, class tokens, attributes, style declarations, and
|
|
242
|
+
custom mark adapters through one algebra.
|
|
243
|
+
- Split boundaries minimally; merge equivalent siblings; eliminate redundant,
|
|
244
|
+
conflicting, or empty wrappers.
|
|
245
|
+
- Keep staged remove-format separate as Unstyle because it spans multiple mark
|
|
246
|
+
representations. A future link-control module owns URL entry and validation.
|
|
247
|
+
|
|
248
|
+
Tests combine partial text, multiple blocks, nested marks, overlapping removal,
|
|
249
|
+
backward selections, atomic content, repeated toggles, and exact undo.
|
|
250
|
+
|
|
251
|
+
## 5. Structural commands
|
|
252
|
+
|
|
253
|
+
Status: started. The command registry, the `Edit` execution context, the
|
|
254
|
+
`PointMap.split()` primitive, and the Enter/line-break commands are implemented.
|
|
255
|
+
Enter also exits an empty item from a nested list without crossing its surface,
|
|
256
|
+
splits content-model text blocks such as headings, and creates the configured
|
|
257
|
+
default block after a non-default text block's exact end. Backspace and Delete
|
|
258
|
+
merge compatible content-model blocks at either boundary and treat every caret
|
|
259
|
+
representation inside an empty block identically.
|
|
260
|
+
`BlockStyles` converts one or several known text-block wrappers, preserves
|
|
261
|
+
selection and unrelated attributes, reports mixed state, and supports custom
|
|
262
|
+
selector/tag/write/clear definitions without treating layout blocks as text
|
|
263
|
+
styles.
|
|
264
|
+
`Lists` owns one closed group of container elements and takes item elements from
|
|
265
|
+
the model's `defaultChild`, so no command names `li`. `toggle(tag)` converts
|
|
266
|
+
blocks into items, converts an existing list of another kind, and lifts items
|
|
267
|
+
back out; a partly selected list is split so only the selected run changes, and
|
|
268
|
+
a result that meets a list of its own kind joins it. `indent` and `outdent` move
|
|
269
|
+
a run between nesting levels and claim `formatIndent` and `formatOutdent`.
|
|
270
|
+
`insertNode(create, inputTypes)` inserts one prepared element where the content
|
|
271
|
+
model accepts it, splitting the caret's block only that far, and covers the
|
|
272
|
+
horizontal rule.
|
|
273
|
+
|
|
274
|
+
- Extend paragraph and heading conversion to loose pre-normalization text.
|
|
275
|
+
- Blockquotes, links, tables, and configurable atomic nodes.
|
|
276
|
+
- Preserve ordered-list numbering across a split or lift.
|
|
277
|
+
- Delete a non-collapsed selection before splitting or inserting, instead of
|
|
278
|
+
leaving that case native.
|
|
279
|
+
|
|
280
|
+
## 6. History
|
|
281
|
+
|
|
282
|
+
Status: implemented as a state model. `History` records one entry per change of
|
|
283
|
+
one surface, storing the content as a cloned fragment with a path-addressed
|
|
284
|
+
selection that survives content replacement. A `MutationObserver` covers every
|
|
285
|
+
origin, so native typing, commands, paste, drop, and unrelated application
|
|
286
|
+
scripts all enter history without the engine owning their mutations.
|
|
287
|
+
Transactions supply the grouping boundaries: the `input` trigger coalesces into
|
|
288
|
+
one entry per interval, every other trigger records a discrete step before and
|
|
289
|
+
after itself. `undo`/`redo` are ordinary commands claiming `historyUndo` and
|
|
290
|
+
`historyRedo`; the input pipeline routes Ctrl/Command+Z and +Y from `keydown`
|
|
291
|
+
because a browser stops reporting those input types once its own stack no longer
|
|
292
|
+
matches replaced content. The convention client installs one history per
|
|
293
|
+
rich-text surface and contributes both toolbar controls.
|
|
294
|
+
|
|
295
|
+
- Store diffs instead of whole content states once transactions record
|
|
296
|
+
reversible operations, keeping the state model for unowned mutations.
|
|
297
|
+
- Group composition deliberately rather than relying on its mutations alone.
|
|
298
|
+
- Expose the coalescing interval and an off switch as `--u2-rte-history`.
|
|
299
|
+
- Decide whether the entry limit should be measured in bytes rather than
|
|
300
|
+
entries, and verify the cost on large documents.
|
|
301
|
+
|
|
302
|
+
## 7. Sanitizing and serialization
|
|
303
|
+
|
|
304
|
+
Status: started. `SanitizePolicy` now defines one immutable element, global and
|
|
305
|
+
per-element attribute, URL-protocol, comment, and data-attribute contract. The
|
|
306
|
+
first `NativeSanitizer` adapter parses into a detached fragment exclusively
|
|
307
|
+
through `Element.setHTML()`, intersects surface elements without broadening the
|
|
308
|
+
security policy, and fails explicitly when that safe sink is unavailable. It
|
|
309
|
+
has no unsafe fallback. Native paste/drop needs no parser fallback because its
|
|
310
|
+
payload stays browser-owned. `Source` now serializes a surface and parses source text back through the same
|
|
311
|
+
policy. A canonical serializer and contextual parsing remain open.
|
|
312
|
+
The independent immutable `Unstyle` policy supplies ordered presentation
|
|
313
|
+
levels to both the selection command and safely parsed external fragments, so
|
|
314
|
+
paste/drop will not grow a second formatting-cleanup list.
|
|
315
|
+
`insertFragment` now replaces a selection with such a prepared fragment through
|
|
316
|
+
mapped removal and insertion. Its content-model lifting handles inline, block,
|
|
317
|
+
list, and nested-link contexts without browser editing commands.
|
|
318
|
+
`ExternalInput` composes the three responsibilities for applications that
|
|
319
|
+
explicitly replace rich paste/drop. The default cross-engine path instead
|
|
320
|
+
cleans the browser's inserted DOM without reading its HTML payload.
|
|
321
|
+
|
|
322
|
+
- Resolve the surface's structural element policy once and use it as the upper
|
|
323
|
+
bound for cleanup, command availability, paste/drop sanitizing, and toolbar
|
|
324
|
+
choices. A sanitizer may narrow that policy but never broaden it.
|
|
325
|
+
`--u2-rte-classes` declares the class names a host treats as content. It is one
|
|
326
|
+
declaration with three consumers: the style control offers exactly those names,
|
|
327
|
+
`SanitizePolicy.clean({classes})` keeps only those from external HTML, and
|
|
328
|
+
`Unstyle` narrows the class attribute to them and leaves a wrapper carrying one,
|
|
329
|
+
because a declared class is content rather than presentation.
|
|
330
|
+
|
|
331
|
+
- Keep additional sanitizer adapters application-supplied behind the same
|
|
332
|
+
policy contract unless a general engine use case requires one.
|
|
333
|
+
- Define allowed global/per-element attributes, URL protocols, class/data
|
|
334
|
+
policy, inline-style policy, and comments separately from allowed elements.
|
|
335
|
+
- Keep security sanitizing separate from structural normalization.
|
|
336
|
+
- Serialize canonical HTML without editor-only markers or browser debris.
|
|
337
|
+
- Test hostile fragments, contextual parsing, mutation-XSS regressions, URL
|
|
338
|
+
attributes, custom elements, pasted office markup, and policy narrowing.
|
|
339
|
+
|
|
340
|
+
## 8. UI adapters
|
|
341
|
+
|
|
342
|
+
Status: started. The optional roaming `Toolbar` binds application-owned markup
|
|
343
|
+
to the active surface's resolved command registry, reflects availability and
|
|
344
|
+
boolean/mixed state, keeps saved selections across UI focus, supports simple
|
|
345
|
+
Ctrl/Command shortcuts, shows and hides with the active surface, and
|
|
346
|
+
delegates placement and presentation. Application-supplied manual popovers and
|
|
347
|
+
the convention toolbar now enter and leave the browser top layer with the same
|
|
348
|
+
visibility contract. `--u2-rte-toolbar-when: selection` optionally requires a
|
|
349
|
+
non-collapsed saved selection.
|
|
350
|
+
|
|
351
|
+
The first batteries-included convention-client prototype is implemented: one
|
|
352
|
+
side-effect import plus `--u2-rte` and `--u2-rte-toolbar` lazily wires standard
|
|
353
|
+
Enter/input behavior, Bold, and one shared default toolbar. It allocates no UI
|
|
354
|
+
before the first active rich-text surface and leaves `plaintext-only` native.
|
|
355
|
+
The explicit `rte.js` API remains the composable engine layer. Optional
|
|
356
|
+
extensions install atomically into current and future surfaces through
|
|
357
|
+
`Editor.add()`, contribute controls, and are removed everywhere through
|
|
358
|
+
`Editor.delete()`. The optional `blocks.js` module exercises this with a
|
|
359
|
+
command-valued Paragraph/H1/H2/H3 select and replaceable custom style policy.
|
|
360
|
+
The optional `breaks.js` probe adds one editor-wide `setup()` lifetime, one
|
|
361
|
+
per-surface `attach()` lifetime, a non-transactional view command, and a
|
|
362
|
+
presentation-only top-layer overlay without inserting markers into editable
|
|
363
|
+
HTML. Optional `unstyle.js` contributes a staged action whose first applicable
|
|
364
|
+
level changes only selected content; levels and labels are replaceable.
|
|
365
|
+
|
|
366
|
+
- Publish command availability and active/mixed state as observable editor
|
|
367
|
+
state.
|
|
368
|
+
- Extend roaming controls beyond pressed buttons and command-valued selects to
|
|
369
|
+
menus and application-owned custom controls. A select's `options` may already
|
|
370
|
+
be a function of the surface, so a control's choices can come from the host's
|
|
371
|
+
configuration; the client refills it before the toolbar is shown.
|
|
372
|
+
- Presence follows the configuration and availability the selection: a control
|
|
373
|
+
this editor does not offer is absent, one that cannot act right now is
|
|
374
|
+
disabled. A toolbar that rearranged itself as the caret moved would move its
|
|
375
|
+
targets out from under the pointer. `--u2-rte-toolbar-unavailable: hide`
|
|
376
|
+
trades that away where a host prefers a toolbar of only what it can do.
|
|
377
|
+
- Implement static UIs bound to one surface; allow several UIs and command
|
|
378
|
+
subsets to coexist.
|
|
379
|
+
- Verify reusable placement policy across carets, ranges, writing modes, and
|
|
380
|
+
viewport edges.
|
|
381
|
+
- Exercise the lifecycle with another contextual UI before adding dependency,
|
|
382
|
+
ordering, or asynchronous setup concepts.
|
|
383
|
+
|
|
384
|
+
### Optional extension probes
|
|
385
|
+
|
|
386
|
+
These are concrete architecture tests, not commitments to one large plugin
|
|
387
|
+
framework. Each extension must install explicitly, add no resources while it is
|
|
388
|
+
absent, work for current and future surfaces, and release everything through
|
|
389
|
+
`dispose()` when it or its editor is removed.
|
|
390
|
+
|
|
391
|
+
1. **Visible line breaks — prototype implemented.** It is non-destructive but needs
|
|
392
|
+
extension-owned presentation and per-surface state. Provide a CSS default,
|
|
393
|
+
an optional toolbar toggle, and an always-on mode that needs no button. Do
|
|
394
|
+
not insert marker nodes into editable HTML. Chromium does not render
|
|
395
|
+
generated content on `<br>`, so the prototype uses one scheduled top-layer
|
|
396
|
+
overlay without a mutation observer. Firefox and WebKit still need visual
|
|
397
|
+
confirmation.
|
|
398
|
+
2. **HTML source editor — implemented.** `Source` serializes a surface by
|
|
399
|
+
walking its DOM and reports where the selection lands in that text, so the
|
|
400
|
+
view opens at the caret without inserting marker nodes. Formatting breaks a
|
|
401
|
+
level into lines only where whitespace cannot be significant, and writing
|
|
402
|
+
removes exactly those breaks again, so a read/write round trip is lossless.
|
|
403
|
+
Writing always parses through the sanitizer and never assigns `innerHTML`.
|
|
404
|
+
`source.js` adds the top-layer dialog, whose text area is wrapped in
|
|
405
|
+
`<u2-code>` so the source is highlighted where that element is defined and
|
|
406
|
+
plain where it is not; the engine itself gains no dependency. Mapping the
|
|
407
|
+
dialog's own selection back into the DOM and a canonical serializer remain
|
|
408
|
+
open.
|
|
409
|
+
3. **Link editor — implemented.** `valueMark(adapter)` generalizes the value
|
|
410
|
+
half: one command creates, changes, and removes a mark whose value is
|
|
411
|
+
content, and at a caret it acts on the whole mark it sits on, so an existing
|
|
412
|
+
link's address can be changed without selecting its text. `link.js` adds the
|
|
413
|
+
contextual form, which follows the caret the way the table and image handles
|
|
414
|
+
do: it appears at a link, anchored on it, and goes when the caret leaves, so
|
|
415
|
+
the toolbar control is left with creating one. Its address field is plain text
|
|
416
|
+
because native url validation rejects relative paths, fragments, and
|
|
417
|
+
application schemes; protocol and attribute policy stays with the sanitizer.
|
|
418
|
+
The form applies as it is typed rather than on an Apply button, marking each
|
|
419
|
+
run as ongoing input so history keeps an address as one step, and it edits the
|
|
420
|
+
one link it appeared on. `normalize`, `suggest`, and `complete` leave what an
|
|
421
|
+
address means to the application, the last of them filling a list of entries
|
|
422
|
+
that may carry markup. Editing the link text itself remains open.
|
|
423
|
+
4. **Table tools — implemented as commands.** `Tables` groups insertion,
|
|
424
|
+
row and column insertion and deletion, and table removal. It names no
|
|
425
|
+
section, row, or cell tag: each comes from the model's `defaultChild`, one
|
|
426
|
+
level apart. Every change is an ordinary mapped mutation, so it is one undo
|
|
427
|
+
step and the caret lands where the edit happened. Spanning cells make the
|
|
428
|
+
index-counted actions unavailable rather than shifting the wrong cells.
|
|
429
|
+
`tables.js` puts them on the table itself: row handles down its left edge and
|
|
430
|
+
column handles along its top, each lined up with the cell the caret is in,
|
|
431
|
+
repositioned from selection, change, input, scroll and resize rather than
|
|
432
|
+
from a timer. Cell movement and span-aware column arithmetic remain open.
|
|
433
|
+
5. **Image sizing — implemented.** `selectedElement()` and
|
|
434
|
+
`elementAttributes()` make the one element a selection covers addressable and
|
|
435
|
+
give it a value command over a fixed set of attributes; `images.js` adds the
|
|
436
|
+
frame and the pointer interaction. The drag moves the frame only and the size
|
|
437
|
+
is written once on release, so a resize is one undo step and the overlay owns
|
|
438
|
+
no size policy: `width` and `height` are what the sanitize policy allows on an
|
|
439
|
+
image. What is resizable is a selector. Its three handles sit on the trailing
|
|
440
|
+
edges only, because the flow holds an image's top and start edges: the corner
|
|
441
|
+
keeps the proportion, the two edges change one measurement each. An alt-text
|
|
442
|
+
editor and keyboard resizing remain open, as does a hook for regenerating the
|
|
443
|
+
file server-side rather than letting the browser stretch it.
|
|
444
|
+
|
|
445
|
+
All roaming and contextual extension UI must be created in the core's
|
|
446
|
+
`Document` or `ShadowRoot`, use the browser top layer through Popover or Dialog
|
|
447
|
+
where appropriate, and share one placement policy.
|
|
448
|
+
|
|
449
|
+
Everything the convention client draws lives in one `Chrome`: a single shadow
|
|
450
|
+
root per editor holding the toolbar, the contextual handles, the link form and
|
|
451
|
+
the source dialog. An editor is chrome inside someone else's document and has to
|
|
452
|
+
survive their `button {}` rule; one encapsulated root also means the application
|
|
453
|
+
sees a single element instead of one per piece of UI. There is no `::part`
|
|
454
|
+
surface on purpose — it would make the chrome's internal structure a public
|
|
455
|
+
contract, and an application that wants different chrome builds its own on the
|
|
456
|
+
commands, which `Toolbar` has always supported by binding markup it is given.
|
|
457
|
+
The chrome now follows the active surface through the closest modal dialog,
|
|
458
|
+
open popover, or fullscreen boundary and returns to its original root when that
|
|
459
|
+
boundary closes. Native inertness makes descendant mounting mandatory; core
|
|
460
|
+
retention keeps the temporary UI child out of focus deactivation, Source, and
|
|
461
|
+
History even when the top-layer element is itself the editing host.
|
|
462
|
+
|
|
463
|
+
The handles inside it are one shared `Handles` component that owns no editor
|
|
464
|
+
concept, so anything with a rectangle can use it, inside this engine or outside
|
|
465
|
+
it: given a document instead of a shadow root it brings its own. Shadow-root style ownership,
|
|
466
|
+
focus transitions, disposal, and coexistence of multiple UIs are release-gate
|
|
467
|
+
tests, not application-specific details.
|
|
468
|
+
|
|
469
|
+
## 9. Browser policies and release gate
|
|
470
|
+
|
|
471
|
+
Status: started with `rangeRect()`, a non-mutating geometry fallback for
|
|
472
|
+
collapsed native ranges. Browser policies live in dedicated, independently
|
|
473
|
+
tested files rather than one global fixes bundle; semantic editing behavior
|
|
474
|
+
stays in its owning command, input, normalization, or UI responsibility.
|
|
475
|
+
|
|
476
|
+
- Isolate feature-detected Chromium, Firefox, and WebKit differences by the
|
|
477
|
+
primitive or input behavior they correct.
|
|
478
|
+
- Promote every discovered browser quirk into a minimal regression test before
|
|
479
|
+
adding a policy.
|
|
480
|
+
- Run generated range/DOM cases and interaction suites repeatedly in all target
|
|
481
|
+
engines.
|
|
482
|
+
- Verify teardown, memory-sensitive listener ownership, accessibility, IME,
|
|
483
|
+
mobile selection, touch, clipboard, drag/drop, and Shadow DOM.
|
|
484
|
+
|
|
485
|
+
The first release requires all suites green in the latest stable target
|
|
486
|
+
browsers, complete responsibility READMEs/TODOs, and no `document.execCommand()`
|
|
487
|
+
usage anywhere in RTE.
|