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 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
- Relative URLs use the explicit `relative` protocol token. Known URL attributes
42
- without a matching protocol rule are removed rather than accepted implicitly.
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', 'relative']}},
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', 'relative', 'tel'])}),
24
- blockquote: Object.freeze({cite: Object.freeze(['http', 'https', 'relative'])}),
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', 'relative'])}),
30
- q: Object.freeze({cite: Object.freeze(['http', 'https', 'relative'])}),
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
- const protocol = urlProtocol(value, base);
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
- function urlProtocol(value, base) {
221
- const input = String(value).trim();
222
- const compact = input.replace(/[\u0000-\u0020]/g, '');
223
- if (!/^[a-z][a-z\d+.-]*:/i.test(compact)) return 'relative';
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
- return new URL(input, base).protocol.slice(0, -1).toLowerCase();
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 null;
229
+ return own; // nothing to resolve against — an opaque document keeps its own urls
228
230
  }
229
231
  }
230
232
 
@@ -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
- The useful interaction model comes from the original `../rte0` toolbar: one
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. RTE replaces the old global item registry,
11
- `execCommand()`, delayed focus repair, and automatic style injection with the
12
- existing core, surface, and command contracts.
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.15",
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`);
@@ -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();