u2ui 1.5.15 → 1.5.17
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/rte/README.md +0 -3
- package/js/rte/AGENTS.md +0 -3
- package/js/rte/PLAN.md +0 -6
- package/js/rte/README.md +1 -4
- package/js/rte/src/sanitize/README.md +22 -3
- package/js/rte/src/sanitize/policy.js +14 -12
- package/js/rte/src/ui/README.md +4 -5
- package/package.json +1 -1
- package/u2/projects.json +0 -16
- package/u2/tools/update.repos.json.js +2 -0
- package/u2/tools/update.skill.js +3 -1
package/el/rte/README.md
CHANGED
|
@@ -56,9 +56,6 @@ DOM edited by RTE. They should not be added as aliases or partial parsers.
|
|
|
56
56
|
|
|
57
57
|
## TODO
|
|
58
58
|
|
|
59
|
-
- Add `el/rte` to U2's generated project catalog when the module is ready for
|
|
60
|
-
automatic loading. It currently requires the explicit module import shown
|
|
61
|
-
above because that catalog lives outside this module.
|
|
62
59
|
- RTE needs a shared root-aware convention-client entry point before this
|
|
63
60
|
adapter can support editors placed inside an application ShadowRoot. The
|
|
64
61
|
current public convenience client owns the document root only.
|
package/js/rte/AGENTS.md
CHANGED
|
@@ -15,9 +15,6 @@ earlier RTE draft is not a reason to keep a weaker design.
|
|
|
15
15
|
- Read [`README.md`](./README.md) for the architecture and requirements.
|
|
16
16
|
- Read [`PLAN.md`](./PLAN.md) for implemented phases and the next responsibility.
|
|
17
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
18
|
## Priorities
|
|
22
19
|
|
|
23
20
|
- Keep changes focused, compact, readable, and internally consistent.
|
package/js/rte/PLAN.md
CHANGED
|
@@ -4,12 +4,6 @@ Each phase ends with documentation, dedicated tests for every production file,
|
|
|
4
4
|
real-browser verification, and no known failing invariant. Later phases may
|
|
5
5
|
refine earlier contracts, but may not bypass their responsibility boundaries.
|
|
6
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
7
|
## Resume work
|
|
14
8
|
|
|
15
9
|
Phases 0–3 provide the core, selection/range primitives, content model, scoped
|
package/js/rte/README.md
CHANGED
|
@@ -13,6 +13,7 @@ import * as module from "https://cdn.jsdelivr.net/gh/u2ui/u2@main/js/rte/rte.js"
|
|
|
13
13
|
|
|
14
14
|
## Demos
|
|
15
15
|
|
|
16
|
+
[webkit-data-image-drag.html](http://gcdn.li/u2ui/u2@main/js/rte/tests/webkit-data-image-drag.html)
|
|
16
17
|
[index.html](http://gcdn.li/u2ui/u2@main/js/rte/tests/index.html)
|
|
17
18
|
|
|
18
19
|
## Start here
|
|
@@ -35,9 +36,6 @@ normalization interactively at `/u2/js/rte/playground/`. The runner displays
|
|
|
35
36
|
its own test count and result. A result is cross-browser evidence only after
|
|
36
37
|
that exact revision has run in current Chromium, Firefox, and WebKit.
|
|
37
38
|
|
|
38
|
-
`../rte0` is a read-only behavioral reference. Reuse proven behavior and CSS
|
|
39
|
-
configuration ideas, but do not copy its architecture or modify its files.
|
|
40
|
-
|
|
41
39
|
## Intended features
|
|
42
40
|
|
|
43
41
|
- Native `contenteditable`, `Selection`, `Range`, `beforeinput`, and Input
|
|
@@ -329,7 +327,6 @@ transactional undo/redo.
|
|
|
329
327
|
- Exercise the new synchronous extension lifecycle with another contextual UI
|
|
330
328
|
before adding dependency, ordering, or asynchronous setup concepts.
|
|
331
329
|
- Build the browser test matrix for current Chromium, Firefox, and WebKit.
|
|
332
|
-
- Port only the proven ideas from `../rte0`; keep its implementation untouched.
|
|
333
330
|
|
|
334
331
|
## About
|
|
335
332
|
|
|
@@ -38,8 +38,13 @@ Event attributes, comments, and data attributes
|
|
|
38
38
|
are not enabled by default. Inline `style` is: it executes nothing, and where
|
|
39
39
|
foreign presentation is unwanted the Unstyle policy already removes it from what
|
|
40
40
|
an import brought — a policy that must not carry it at all narrows `attributes`.
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
A URL that resolves inside the document — `/page`, `page.html`, `#anchor`,
|
|
42
|
+
`?q=1` — passes wherever the attribute has a rule at all. It reaches nowhere the
|
|
43
|
+
document is not, so there is nothing for a policy to allow and nothing an
|
|
44
|
+
application can forbid by forgetting to name it. `//host/x` and `\\host/x` name no
|
|
45
|
+
scheme either but resolve elsewhere: they are absolute URLs in disguise, judged
|
|
46
|
+
as the `http`/`https` ones they turn into. Known URL attributes without a
|
|
47
|
+
matching protocol rule are removed rather than accepted implicitly.
|
|
43
48
|
|
|
44
49
|
`policy.with(options)` copies a policy with single axes replaced, and
|
|
45
50
|
`policyFor(config, base)` is what a host gets: its own `--u2-rte-elements`,
|
|
@@ -52,7 +57,7 @@ returns the base itself.
|
|
|
52
57
|
const policy = new SanitizePolicy({
|
|
53
58
|
elements: ['p', 'a', 'strong'],
|
|
54
59
|
attributes: {'*': ['class'], a: ['href', 'rel']},
|
|
55
|
-
protocols: {a: {href: ['https'
|
|
60
|
+
protocols: {a: {href: ['https']}},
|
|
56
61
|
});
|
|
57
62
|
```
|
|
58
63
|
|
|
@@ -127,6 +132,20 @@ through `--u2-rte-classes`; external HTML then cannot smuggle in classes the
|
|
|
127
132
|
host does not know, and the same list drives the style control and presentation
|
|
128
133
|
cleanup.
|
|
129
134
|
|
|
135
|
+
## Possible extensions
|
|
136
|
+
|
|
137
|
+
Neither is planned; both are written down because the protocol list invites them
|
|
138
|
+
and answers neither.
|
|
139
|
+
|
|
140
|
+
- **A host allowlist.** A protocol list says `https` is fine and cannot say
|
|
141
|
+
*whose* `https`. An application that wants its own images and no foreign
|
|
142
|
+
tracking pixel has no way to state it here, and stating it as a protocol —
|
|
143
|
+
which an earlier `relative` token tried — describes the wrong axis.
|
|
144
|
+
- **Reading the document's CSP.** `img-src`, `media-src` and `frame-src` already
|
|
145
|
+
declare what the page will load at all. An editor that read them would stop
|
|
146
|
+
offering what the browser is about to refuse, without a second list to keep in
|
|
147
|
+
step.
|
|
148
|
+
|
|
130
149
|
## TODO
|
|
131
150
|
|
|
132
151
|
- Add contextual parsing for table and list insertion sites.
|
|
@@ -20,14 +20,14 @@ const DEFAULT_ATTRIBUTES = Object.freeze({
|
|
|
20
20
|
time: Object.freeze(['datetime']),
|
|
21
21
|
});
|
|
22
22
|
const DEFAULT_PROTOCOLS = Object.freeze({
|
|
23
|
-
a: Object.freeze({href: Object.freeze(['http', 'https', 'mailto', '
|
|
24
|
-
blockquote: Object.freeze({cite: Object.freeze(['http', 'https'
|
|
23
|
+
a: Object.freeze({href: Object.freeze(['http', 'https', 'mailto', 'tel'])}),
|
|
24
|
+
blockquote: Object.freeze({cite: Object.freeze(['http', 'https'])}),
|
|
25
25
|
// `data:` belongs to an image and to nothing else here: an image executes
|
|
26
26
|
// nothing, not even an SVG one, which browsers draw in a script-free
|
|
27
27
|
// context. In a navigating attribute the same protocol is a page, so `a`
|
|
28
28
|
// and the citations keep it out.
|
|
29
|
-
img: Object.freeze({src: Object.freeze(['data', 'http', 'https'
|
|
30
|
-
q: Object.freeze({cite: Object.freeze(['http', 'https'
|
|
29
|
+
img: Object.freeze({src: Object.freeze(['data', 'http', 'https'])}),
|
|
30
|
+
q: Object.freeze({cite: Object.freeze(['http', 'https'])}),
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
// Elements a browser never renders the children of. Unwrapping one turns source — a stylesheet, a
|
|
@@ -84,8 +84,7 @@ export class SanitizePolicy {
|
|
|
84
84
|
const protocols = this.#protocols[element.localName.toLowerCase()]?.[attributeName]
|
|
85
85
|
|| this.#protocols['*']?.[attributeName];
|
|
86
86
|
if (!protocols) return false;
|
|
87
|
-
|
|
88
|
-
return protocol !== null && protocols.includes(protocol);
|
|
87
|
+
return allowedUrl(value, base, protocols);
|
|
89
88
|
}
|
|
90
89
|
|
|
91
90
|
// Reduces a subtree to the elements this policy allows, keeping the content
|
|
@@ -217,14 +216,17 @@ function protocolGroups(value) {
|
|
|
217
216
|
return Object.freeze(result);
|
|
218
217
|
}
|
|
219
218
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
219
|
+
// A url naming no scheme that lands on the document's own origin — `/page`, `#anchor`, `?q=1` —
|
|
220
|
+
// reaches nowhere the document is not: nothing to allow, and nothing to forbid by forgetting to
|
|
221
|
+
// name it. `//host` and `\\host` name none either but go elsewhere, so their real scheme decides.
|
|
222
|
+
function allowedUrl(value, base, protocols) {
|
|
223
|
+
const input = String(value).trim().replace(/[\u0000-\u0020]/g, '');
|
|
224
|
+
const own = !/^([a-z][a-z\d+.-]*:|[/\\]{2})/i.test(input);
|
|
224
225
|
try {
|
|
225
|
-
|
|
226
|
+
const url = new URL(input, base);
|
|
227
|
+
return own ? url.origin === new URL(base).origin : protocols.includes(url.protocol.slice(0, -1));
|
|
226
228
|
} catch {
|
|
227
|
-
return
|
|
229
|
+
return own; // nothing to resolve against — an opaque document keeps its own urls
|
|
228
230
|
}
|
|
229
231
|
}
|
|
230
232
|
|
package/js/rte/src/ui/README.md
CHANGED
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
the currently active surface. It provides the roaming behavior; it does not
|
|
5
5
|
create buttons, inject styles, choose icons, or define editor commands.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
shared toolbar follows the active editor, its items reflect the current
|
|
7
|
+
One shared toolbar follows the active editor, its items reflect the current
|
|
9
8
|
selection, pointer interaction keeps the editor selection, and keyboard
|
|
10
|
-
shortcuts invoke the same actions.
|
|
11
|
-
`execCommand()`, delayed focus repair, and
|
|
12
|
-
|
|
9
|
+
shortcuts invoke the same actions. The core, surface, and command contracts
|
|
10
|
+
replace global item registries, `execCommand()`, delayed focus repair, and
|
|
11
|
+
automatic style injection.
|
|
13
12
|
|
|
14
13
|
## Contract
|
|
15
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u2ui",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.17",
|
|
4
4
|
"description": "HTML Advanced: More Capabilities, Same Feel, Same Simplicity",
|
|
5
5
|
"keywords": ["web-components","pattern-library","component-library","ui-toolkit","design-system"],
|
|
6
6
|
"author": "Tobias Buschor",
|
package/u2/projects.json
CHANGED
|
@@ -196,14 +196,6 @@
|
|
|
196
196
|
}
|
|
197
197
|
},
|
|
198
198
|
"el": {
|
|
199
|
-
"rte0": {
|
|
200
|
-
"name": "rte0",
|
|
201
|
-
"description": "\"Rich\" Rich Text Editor (alpha)",
|
|
202
|
-
"beta": false,
|
|
203
|
-
"deprecated": false,
|
|
204
|
-
"css": true,
|
|
205
|
-
"js": true
|
|
206
|
-
},
|
|
207
199
|
"tabs": {
|
|
208
200
|
"name": "tabs",
|
|
209
201
|
"description": "Turns HTML structures with headings into an interactive, tabbed interface, using the content between headings as each tab’s content.",
|
|
@@ -568,14 +560,6 @@
|
|
|
568
560
|
}
|
|
569
561
|
},
|
|
570
562
|
"js": {
|
|
571
|
-
"rte0": {
|
|
572
|
-
"name": "rte0",
|
|
573
|
-
"description": "Easily Make Elements Editable with Intuitive Controls",
|
|
574
|
-
"beta": false,
|
|
575
|
-
"deprecated": false,
|
|
576
|
-
"css": false,
|
|
577
|
-
"js": false
|
|
578
|
-
},
|
|
579
563
|
"serviceWorker": {
|
|
580
564
|
"name": "serviceWorker",
|
|
581
565
|
"description": "",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// deno run -A --no-lock ./u2/tools/update.repos.json.js
|
|
2
2
|
|
|
3
3
|
const repos = {};
|
|
4
|
+
const HIDDEN = new Set(['rte0']);
|
|
4
5
|
|
|
5
6
|
import * as fs from 'https://deno.land/std@0.100.0/fs/mod.ts';
|
|
6
7
|
|
|
@@ -13,6 +14,7 @@ base = base.replace(/file:\/\/\//, '/');
|
|
|
13
14
|
for await (const entry of Deno.readDir(base+category)) {
|
|
14
15
|
if (!entry.isDirectory) continue;
|
|
15
16
|
const name = entry.name;
|
|
17
|
+
if (HIDDEN.has(name)) continue;
|
|
16
18
|
const data = await writeReadMe(category, entry);
|
|
17
19
|
|
|
18
20
|
const css = fs.existsSync(`${base}${category}/${name}/${name}.css`);
|
package/u2/tools/update.skill.js
CHANGED
|
@@ -6,6 +6,7 @@ let base = import.meta.resolve('../../');
|
|
|
6
6
|
base = base.replace(/file:\/\/\//, '/');
|
|
7
7
|
|
|
8
8
|
const categories = ['attr', 'class', 'el', 'css', 'js'];
|
|
9
|
+
const HIDDEN = new Set(['rte0']);
|
|
9
10
|
|
|
10
11
|
async function generateSkillMd() {
|
|
11
12
|
const repos = {};
|
|
@@ -16,6 +17,7 @@ async function generateSkillMd() {
|
|
|
16
17
|
for await (const entry of Deno.readDir(base + category)) {
|
|
17
18
|
if (!entry.isDirectory) continue;
|
|
18
19
|
const name = entry.name;
|
|
20
|
+
if (HIDDEN.has(name)) continue;
|
|
19
21
|
|
|
20
22
|
const readmePath = `${base}${category}/${name}/README.md`;
|
|
21
23
|
let description = '';
|
|
@@ -177,4 +179,4 @@ function trimLines(text) {
|
|
|
177
179
|
return lines.join('\n');
|
|
178
180
|
}
|
|
179
181
|
|
|
180
|
-
generateSkillMd();
|
|
182
|
+
generateSkillMd();
|