@openleaf-editor/core 0.1.0-beta.1 → 0.1.0-beta.2

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 (58) hide show
  1. package/dist/autolink.d.ts +12 -0
  2. package/dist/autolink.d.ts.map +1 -0
  3. package/dist/autolink.js +98 -0
  4. package/dist/autolink.js.map +1 -0
  5. package/dist/commands.d.ts +69 -2
  6. package/dist/commands.d.ts.map +1 -1
  7. package/dist/commands.js +464 -18
  8. package/dist/commands.js.map +1 -1
  9. package/dist/css.d.ts +61 -9
  10. package/dist/css.d.ts.map +1 -1
  11. package/dist/css.js +315 -13
  12. package/dist/css.js.map +1 -1
  13. package/dist/embed.d.ts +52 -0
  14. package/dist/embed.d.ts.map +1 -0
  15. package/dist/embed.js +116 -0
  16. package/dist/embed.js.map +1 -0
  17. package/dist/extensions.d.ts.map +1 -1
  18. package/dist/extensions.js +22 -15
  19. package/dist/extensions.js.map +1 -1
  20. package/dist/formats.d.ts +44 -0
  21. package/dist/formats.d.ts.map +1 -0
  22. package/dist/formats.js +111 -0
  23. package/dist/formats.js.map +1 -0
  24. package/dist/index.d.ts +9 -3
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +14 -6
  27. package/dist/index.js.map +1 -1
  28. package/dist/keymap.d.ts.map +1 -1
  29. package/dist/keymap.js +6 -3
  30. package/dist/keymap.js.map +1 -1
  31. package/dist/noneditable.d.ts +14 -0
  32. package/dist/noneditable.d.ts.map +1 -0
  33. package/dist/noneditable.js +93 -0
  34. package/dist/noneditable.js.map +1 -0
  35. package/dist/preserve.d.ts +45 -0
  36. package/dist/preserve.d.ts.map +1 -1
  37. package/dist/preserve.js +113 -34
  38. package/dist/preserve.js.map +1 -1
  39. package/dist/schema.d.ts.map +1 -1
  40. package/dist/schema.js +211 -60
  41. package/dist/schema.js.map +1 -1
  42. package/dist/structure.d.ts +44 -0
  43. package/dist/structure.d.ts.map +1 -0
  44. package/dist/structure.js +379 -0
  45. package/dist/structure.js.map +1 -0
  46. package/dist/tables.d.ts +13 -0
  47. package/dist/tables.d.ts.map +1 -1
  48. package/dist/tables.js +224 -19
  49. package/dist/tables.js.map +1 -1
  50. package/dist/tokens.d.ts +28 -0
  51. package/dist/tokens.d.ts.map +1 -0
  52. package/dist/tokens.js +68 -0
  53. package/dist/tokens.js.map +1 -0
  54. package/dist/visual-aids.d.ts +10 -0
  55. package/dist/visual-aids.d.ts.map +1 -0
  56. package/dist/visual-aids.js +61 -0
  57. package/dist/visual-aids.js.map +1 -0
  58. package/package.json +3 -2
package/dist/css.d.ts CHANGED
@@ -26,9 +26,9 @@
26
26
  * `style` has a genuinely bad history: `expression()` in old IE executed
27
27
  * JavaScript, `url()` fetches, `position:fixed` lets pasted content cover the
28
28
  * page with something that looks like your UI. None of that is reachable
29
- * through a three-property allowlist whose values must match a colour or one of
30
- * four keywords -- and an allowlist stays safe against whatever CSS gains next,
31
- * which a list of known-bad functions does not.
29
+ * through a closed property allowlist whose values must match a colour, a
30
+ * length, a font name, or one of a few keywords -- and an allowlist stays safe
31
+ * against whatever CSS gains next, which a list of known-bad functions does not.
32
32
  */
33
33
  /** The four alignments a toolbar offers. */
34
34
  export type Align = 'left' | 'center' | 'right' | 'justify';
@@ -43,10 +43,22 @@ export declare const ALIGNMENTS: readonly Align[];
43
43
  export declare const MODELLED_PROPERTIES: readonly string[];
44
44
  /**
45
45
  * The subset a mark can represent, and therefore the subset the preservation
46
- * layer may unwrap a `<span>` for. `text-align` is deliberately absent: it is a
47
- * block attribute, so unwrapping the element that carries it loses it.
46
+ * layer may unwrap a `<span>` for. Block properties are deliberately absent:
47
+ * unwrapping the element that carries them would drop them.
48
48
  */
49
49
  export declare const COLOUR_PROPERTIES: readonly string[];
50
+ export declare const INLINE_STYLE_PROPERTIES: readonly string[];
51
+ /** Indent step written as `padding-inline-start`. Matches TinyMCE's 2em. */
52
+ export declare const INDENT_EM = 2;
53
+ export declare const MAX_INDENT = 8;
54
+ /** List styles the toolbar offers. `decimal` is the ordered-list default. */
55
+ export type ListStyle = 'disc' | 'circle' | 'square' | 'decimal' | 'lower-roman' | 'upper-roman' | 'lower-alpha' | 'upper-alpha' | 'lower-greek';
56
+ export declare const LIST_STYLES: readonly ListStyle[];
57
+ export type Dir = 'ltr' | 'rtl';
58
+ export declare const FONT_FAMILIES: readonly string[];
59
+ /** Preset sizes the toolbar lists, in pixels. The number input accepts any in range. */
60
+ export declare const FONT_SIZE_PRESETS: readonly number[];
61
+ export declare const LINE_HEIGHT_PRESETS: readonly string[];
50
62
  /**
51
63
  * Split a `style` attribute into declarations.
52
64
  *
@@ -77,6 +89,47 @@ export declare function serializeDeclarations(declarations: Map<string, string>)
77
89
  */
78
90
  export declare function safeAlign(value: string | null | undefined, dir?: string | null): Align | null;
79
91
  export declare function safeColor(value: string | null | undefined): string | null;
92
+ export declare function safeDir(value: string | null | undefined): Dir | null;
93
+ /**
94
+ * A font stack, or null.
95
+ *
96
+ * Names are identifiers or quoted strings of letters, digits, spaces and
97
+ * hyphens. Anything that can reach outside the declaration -- `url()`,
98
+ * `expression()`, `var()`, a quote that does not match -- is refused. A stack
99
+ * is a comma-separated list of those names, capped so a paste cannot dump a
100
+ * novel into the attribute.
101
+ */
102
+ export declare function safeFontFamily(value: string | null | undefined): string | null;
103
+ /**
104
+ * A font size, or null.
105
+ *
106
+ * Keywords and a closed set of length units. Values are clamped so a pasted
107
+ * `font-size: 400px` cannot blow the layout out; the toolbar's number input
108
+ * writes pixels inside the same range.
109
+ */
110
+ export declare function safeFontSize(value: string | null | undefined): string | null;
111
+ /** A line height: `normal`, a unitless multiplier, or a short length. */
112
+ export declare function safeLineHeight(value: string | null | undefined): string | null;
113
+ /**
114
+ * How many indent steps this declaration means, or null.
115
+ *
116
+ * Written as `padding-inline-start: 2em` per step. Inherited `padding-left`
117
+ * and `margin-left` in 40px or 2em multiples are read as the same thing, which
118
+ * is what TinyMCE and CKEditor stored for years.
119
+ */
120
+ export declare function indentLevels(value: string | null | undefined): number | null;
121
+ export declare function indentCss(levels: number): string;
122
+ export declare function safeListStyle(value: string | null | undefined): ListStyle | null;
123
+ /**
124
+ * A BCP 47-shaped language tag, or null.
125
+ *
126
+ * Not a complete parser: it admits `en`, `en-GB`, `zh-Hans-CN` and refuses
127
+ * anything that could not be a language tag. The point is to keep `lang`
128
+ * from becoming a free-form attribute for whatever a paste stuffed in it.
129
+ */
130
+ export declare function safeLang(value: string | null | undefined): string | null;
131
+ /** The canonical value for a modelled declaration, or null if it is not one. */
132
+ export declare function modelledValue(property: string, value: string): string | null;
80
133
  /**
81
134
  * Put CSS on an element without losing the author's spelling.
82
135
  *
@@ -115,10 +168,9 @@ export declare function applyStyleAttribute(el: Element, css: string): void;
115
168
  * Is every declaration in this `style` attribute one the schema models?
116
169
  *
117
170
  * The preservation layer asks this before unwrapping a `<span style>`. A span
118
- * carrying only colour is fully representable as marks, so unwrapping it loses
119
- * nothing and the text inside stays editable. One carrying `font-family` as well
120
- * is not, and it must stay an opaque preserved atom -- faithful, uneditable, and
121
- * exactly what happens today rather than a regression.
171
+ * carrying only colour and font is fully representable as marks, so unwrapping
172
+ * it loses nothing and the text inside stays editable. One carrying
173
+ * `letter-spacing` as well is not, and it must stay an opaque preserved atom.
122
174
  */
123
175
  export declare function isFullyModelledStyle(style: string | null | undefined, properties?: readonly string[]): boolean;
124
176
  //# sourceMappingURL=css.d.ts.map
package/dist/css.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,4CAA4C;AAC5C,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;AAE3D,eAAO,MAAM,UAAU,EAAE,SAAS,KAAK,EAA2C,CAAA;AAElF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAAgD,CAAA;AAEjG;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAkC,CAAA;AAEjF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAYvF;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAGtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,CAO7F;AAoBD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAyBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAOlE;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,UAAU,GAAE,SAAS,MAAM,EAAsB,GAChD,OAAO,CAST"}
1
+ {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"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"}
package/dist/css.js CHANGED
@@ -26,9 +26,9 @@
26
26
  * `style` has a genuinely bad history: `expression()` in old IE executed
27
27
  * JavaScript, `url()` fetches, `position:fixed` lets pasted content cover the
28
28
  * page with something that looks like your UI. None of that is reachable
29
- * through a three-property allowlist whose values must match a colour or one of
30
- * four keywords -- and an allowlist stays safe against whatever CSS gains next,
31
- * which a list of known-bad functions does not.
29
+ * through a closed property allowlist whose values must match a colour, a
30
+ * length, a font name, or one of a few keywords -- and an allowlist stays safe
31
+ * against whatever CSS gains next, which a list of known-bad functions does not.
32
32
  */
33
33
  export const ALIGNMENTS = ['left', 'center', 'right', 'justify'];
34
34
  /**
@@ -38,13 +38,60 @@ export const ALIGNMENTS = ['left', 'center', 'right', 'justify'];
38
38
  * property added here reaches the server-side policy rather than being stripped
39
39
  * from stored content by a policy that never heard of it.
40
40
  */
41
- export const MODELLED_PROPERTIES = ['text-align', 'color', 'background-color'];
41
+ export const MODELLED_PROPERTIES = [
42
+ 'text-align',
43
+ 'color',
44
+ 'background-color',
45
+ 'font-family',
46
+ 'font-size',
47
+ 'line-height',
48
+ 'padding-inline-start',
49
+ 'list-style-type',
50
+ ];
42
51
  /**
43
52
  * The subset a mark can represent, and therefore the subset the preservation
44
- * layer may unwrap a `<span>` for. `text-align` is deliberately absent: it is a
45
- * block attribute, so unwrapping the element that carries it loses it.
53
+ * layer may unwrap a `<span>` for. Block properties are deliberately absent:
54
+ * unwrapping the element that carries them would drop them.
46
55
  */
47
56
  export const COLOUR_PROPERTIES = ['color', 'background-color'];
57
+ export const INLINE_STYLE_PROPERTIES = [
58
+ 'color',
59
+ 'background-color',
60
+ 'font-family',
61
+ 'font-size',
62
+ ];
63
+ /** Indent step written as `padding-inline-start`. Matches TinyMCE's 2em. */
64
+ export const INDENT_EM = 2;
65
+ export const MAX_INDENT = 8;
66
+ export const LIST_STYLES = [
67
+ 'disc',
68
+ 'circle',
69
+ 'square',
70
+ 'decimal',
71
+ 'lower-roman',
72
+ 'upper-roman',
73
+ 'lower-alpha',
74
+ 'upper-alpha',
75
+ 'lower-greek',
76
+ ];
77
+ export const FONT_FAMILIES = [
78
+ 'Arial',
79
+ 'Georgia',
80
+ 'Times New Roman',
81
+ 'Courier New',
82
+ 'Verdana',
83
+ 'Tahoma',
84
+ 'Trebuchet MS',
85
+ 'Palatino',
86
+ 'Garamond',
87
+ 'system-ui',
88
+ 'serif',
89
+ 'sans-serif',
90
+ 'monospace',
91
+ ];
92
+ /** Preset sizes the toolbar lists, in pixels. The number input accepts any in range. */
93
+ export const FONT_SIZE_PRESETS = [8, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 48, 72];
94
+ export const LINE_HEIGHT_PRESETS = ['1', '1.15', '1.5', '2', '2.5'];
48
95
  /**
49
96
  * Split a `style` attribute into declarations.
50
97
  *
@@ -148,6 +195,263 @@ export function safeColor(value) {
148
195
  return candidate.toLowerCase();
149
196
  return null;
150
197
  }
198
+ export function safeDir(value) {
199
+ if (!value)
200
+ return null;
201
+ const candidate = value.trim().toLowerCase();
202
+ return candidate === 'ltr' || candidate === 'rtl' ? candidate : null;
203
+ }
204
+ const GENERIC_FAMILIES = new Set([
205
+ 'serif',
206
+ 'sans-serif',
207
+ 'monospace',
208
+ 'cursive',
209
+ 'fantasy',
210
+ 'system-ui',
211
+ 'ui-sans-serif',
212
+ 'ui-serif',
213
+ 'ui-monospace',
214
+ 'ui-rounded',
215
+ 'emoji',
216
+ 'math',
217
+ 'fangsong',
218
+ ]);
219
+ /**
220
+ * A font stack, or null.
221
+ *
222
+ * Names are identifiers or quoted strings of letters, digits, spaces and
223
+ * hyphens. Anything that can reach outside the declaration -- `url()`,
224
+ * `expression()`, `var()`, a quote that does not match -- is refused. A stack
225
+ * is a comma-separated list of those names, capped so a paste cannot dump a
226
+ * novel into the attribute.
227
+ */
228
+ export function safeFontFamily(value) {
229
+ if (!value)
230
+ return null;
231
+ const raw = value.trim();
232
+ if (raw === '' || raw.length > 160)
233
+ return null;
234
+ if (/url\s*\(|expression|var\s*\(|[@\\<>]/i.test(raw))
235
+ return null;
236
+ const parts = [];
237
+ let current = '';
238
+ let quote = null;
239
+ for (const char of raw) {
240
+ if (quote) {
241
+ if (char === quote)
242
+ quote = null;
243
+ current += char;
244
+ continue;
245
+ }
246
+ if (char === '"' || char === "'") {
247
+ quote = char;
248
+ current += char;
249
+ continue;
250
+ }
251
+ if (char === ',') {
252
+ parts.push(current);
253
+ current = '';
254
+ continue;
255
+ }
256
+ current += char;
257
+ }
258
+ if (quote !== null)
259
+ return null;
260
+ parts.push(current);
261
+ if (parts.length === 0 || parts.length > 6)
262
+ return null;
263
+ const out = [];
264
+ for (const part of parts) {
265
+ const family = oneFontFamily(part);
266
+ if (family === null)
267
+ return null;
268
+ out.push(family);
269
+ }
270
+ return out.join(',');
271
+ }
272
+ function oneFontFamily(part) {
273
+ const trimmed = part.trim();
274
+ if (trimmed === '' || trimmed.length > 64)
275
+ return null;
276
+ const quoted = /^(['"])(.*)\1$/.exec(trimmed);
277
+ // `?.[2]` rather than a ternary: a capture group is `string | undefined` to the
278
+ // type checker even when the match succeeded. `(.*)` matches the empty string,
279
+ // so a quoted empty name still reads as '' and is refused below -- the `??`
280
+ // only fires when there was no match at all.
281
+ const name = quoted?.[2] ?? trimmed;
282
+ if (name === '' || /[^a-zA-Z0-9 \-]/.test(name))
283
+ return null;
284
+ if (!/^[a-zA-Z]/.test(name))
285
+ return null;
286
+ const lower = name.toLowerCase();
287
+ if (GENERIC_FAMILIES.has(lower))
288
+ return lower;
289
+ return /\s/.test(name) ? `"${name}"` : name;
290
+ }
291
+ const FONT_SIZE_KEYWORDS = new Set([
292
+ 'xx-small',
293
+ 'x-small',
294
+ 'small',
295
+ 'medium',
296
+ 'large',
297
+ 'x-large',
298
+ 'xx-large',
299
+ 'xxx-large',
300
+ ]);
301
+ const FONT_SIZE_LENGTH = /^(\d+(?:\.\d+)?)(px|pt|em|rem|%)$/i;
302
+ /**
303
+ * A font size, or null.
304
+ *
305
+ * Keywords and a closed set of length units. Values are clamped so a pasted
306
+ * `font-size: 400px` cannot blow the layout out; the toolbar's number input
307
+ * writes pixels inside the same range.
308
+ */
309
+ export function safeFontSize(value) {
310
+ if (!value)
311
+ return null;
312
+ const candidate = value.trim().toLowerCase().replace(/\s+/g, '');
313
+ if (FONT_SIZE_KEYWORDS.has(candidate))
314
+ return candidate;
315
+ const match = FONT_SIZE_LENGTH.exec(candidate);
316
+ if (!match)
317
+ return null;
318
+ const amount = Number(match[1]);
319
+ const unit = match[2];
320
+ const min = { px: 8, pt: 6, em: 0.5, rem: 0.5, '%': 50 };
321
+ const max = { px: 96, pt: 72, em: 6, rem: 6, '%': 300 };
322
+ if (!(amount >= min[unit] && amount <= max[unit]))
323
+ return null;
324
+ const rendered = Number.isInteger(amount) ? String(amount) : String(amount);
325
+ return `${rendered}${unit}`;
326
+ }
327
+ const LINE_HEIGHT_LENGTH = /^(\d+(?:\.\d+)?)(px|em|rem|%)?$/i;
328
+ /** A line height: `normal`, a unitless multiplier, or a short length. */
329
+ export function safeLineHeight(value) {
330
+ if (!value)
331
+ return null;
332
+ const candidate = value.trim().toLowerCase().replace(/\s+/g, '');
333
+ if (candidate === 'normal')
334
+ return 'normal';
335
+ const match = LINE_HEIGHT_LENGTH.exec(candidate);
336
+ if (!match)
337
+ return null;
338
+ const amount = Number(match[1]);
339
+ const unit = match[2] ?? '';
340
+ if (unit === '') {
341
+ if (!(amount >= 0.5 && amount <= 4))
342
+ return null;
343
+ return Number.isInteger(amount) ? String(amount) : String(amount);
344
+ }
345
+ if (unit === '%') {
346
+ if (!(amount >= 50 && amount <= 400))
347
+ return null;
348
+ return `${formatNumber(amount)}%`;
349
+ }
350
+ if (!(amount >= 0.5 && amount <= 96))
351
+ return null;
352
+ return `${formatNumber(amount)}${unit}`;
353
+ }
354
+ function formatNumber(amount) {
355
+ return Number.isInteger(amount) ? String(amount) : String(amount);
356
+ }
357
+ /**
358
+ * How many indent steps this declaration means, or null.
359
+ *
360
+ * Written as `padding-inline-start: 2em` per step. Inherited `padding-left`
361
+ * and `margin-left` in 40px or 2em multiples are read as the same thing, which
362
+ * is what TinyMCE and CKEditor stored for years.
363
+ */
364
+ export function indentLevels(value) {
365
+ if (!value)
366
+ return null;
367
+ const candidate = value.trim().toLowerCase().replace(/\s+/g, '');
368
+ const em = /^(\d+(?:\.\d+)?)em$/.exec(candidate);
369
+ if (em)
370
+ return stepsFrom(Number(em[1]), INDENT_EM);
371
+ const px = /^(\d+(?:\.\d+)?)px$/.exec(candidate);
372
+ if (px)
373
+ return stepsFrom(Number(px[1]), 40);
374
+ return null;
375
+ }
376
+ function stepsFrom(amount, step) {
377
+ const steps = amount / step;
378
+ if (steps < 0.9 || steps > MAX_INDENT)
379
+ return null;
380
+ const rounded = Math.round(steps);
381
+ return Math.abs(steps - rounded) < 0.05 ? rounded : null;
382
+ }
383
+ export function indentCss(levels) {
384
+ return `${levels * INDENT_EM}em`;
385
+ }
386
+ const LIST_STYLE_ALIASES = {
387
+ disc: 'disc',
388
+ circle: 'circle',
389
+ square: 'square',
390
+ decimal: 'decimal',
391
+ 'lower-roman': 'lower-roman',
392
+ 'upper-roman': 'upper-roman',
393
+ 'lower-alpha': 'lower-alpha',
394
+ 'upper-alpha': 'upper-alpha',
395
+ 'lower-latin': 'lower-alpha',
396
+ 'upper-latin': 'upper-alpha',
397
+ 'lower-greek': 'lower-greek',
398
+ // HTML `type` on <ol>.
399
+ a: 'lower-alpha',
400
+ A: 'upper-alpha',
401
+ i: 'lower-roman',
402
+ I: 'upper-roman',
403
+ '1': 'decimal',
404
+ };
405
+ export function safeListStyle(value) {
406
+ if (!value)
407
+ return null;
408
+ const candidate = value.trim();
409
+ return LIST_STYLE_ALIASES[candidate] ?? LIST_STYLE_ALIASES[candidate.toLowerCase()] ?? null;
410
+ }
411
+ /**
412
+ * A BCP 47-shaped language tag, or null.
413
+ *
414
+ * Not a complete parser: it admits `en`, `en-GB`, `zh-Hans-CN` and refuses
415
+ * anything that could not be a language tag. The point is to keep `lang`
416
+ * from becoming a free-form attribute for whatever a paste stuffed in it.
417
+ */
418
+ export function safeLang(value) {
419
+ if (!value)
420
+ return null;
421
+ const candidate = value.trim();
422
+ if (candidate.length < 2 || candidate.length > 35)
423
+ return null;
424
+ if (!/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{1,8}){0,4}$/.test(candidate))
425
+ return null;
426
+ return candidate;
427
+ }
428
+ /** The canonical value for a modelled declaration, or null if it is not one. */
429
+ export function modelledValue(property, value) {
430
+ switch (property) {
431
+ case 'text-align':
432
+ return safeAlign(value);
433
+ case 'color':
434
+ case 'background-color':
435
+ return safeColor(value);
436
+ case 'font-family':
437
+ return safeFontFamily(value);
438
+ case 'font-size':
439
+ return safeFontSize(value);
440
+ case 'line-height':
441
+ return safeLineHeight(value);
442
+ case 'padding-inline-start':
443
+ case 'padding-left':
444
+ case 'margin-inline-start':
445
+ case 'margin-left': {
446
+ const steps = indentLevels(value);
447
+ return steps === null ? null : indentCss(steps);
448
+ }
449
+ case 'list-style-type':
450
+ return safeListStyle(value);
451
+ default:
452
+ return null;
453
+ }
454
+ }
151
455
  /**
152
456
  * Put CSS on an element without losing the author's spelling.
153
457
  *
@@ -194,20 +498,18 @@ export function applyStyleAttribute(el, css) {
194
498
  * Is every declaration in this `style` attribute one the schema models?
195
499
  *
196
500
  * The preservation layer asks this before unwrapping a `<span style>`. A span
197
- * carrying only colour is fully representable as marks, so unwrapping it loses
198
- * nothing and the text inside stays editable. One carrying `font-family` as well
199
- * is not, and it must stay an opaque preserved atom -- faithful, uneditable, and
200
- * exactly what happens today rather than a regression.
501
+ * carrying only colour and font is fully representable as marks, so unwrapping
502
+ * it loses nothing and the text inside stays editable. One carrying
503
+ * `letter-spacing` as well is not, and it must stay an opaque preserved atom.
201
504
  */
202
- export function isFullyModelledStyle(style, properties = COLOUR_PROPERTIES) {
505
+ export function isFullyModelledStyle(style, properties = INLINE_STYLE_PROPERTIES) {
203
506
  const declarations = parseDeclarations(style);
204
507
  if (declarations.size === 0)
205
508
  return false;
206
509
  for (const [name, value] of declarations) {
207
510
  if (!properties.includes(name))
208
511
  return false;
209
- const valid = name === 'text-align' ? safeAlign(value) : safeColor(value);
210
- if (valid === null)
512
+ if (modelledValue(name, value) === null)
211
513
  return false;
212
514
  }
213
515
  return true;
package/dist/css.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"css.js","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"css.js","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Which `<iframe>` sources the schema will store.
3
+ *
4
+ * An iframe is a nested browsing context. Allowing an arbitrary `src` is
5
+ * allowing a third-party page to run in yours, which is why the preservation
6
+ * layer used to drop every iframe it saw. Modelling embeds at all means
7
+ * answering "which hosts, on which paths, over which scheme" in one place --
8
+ * here -- so the schema, the insert command and `@openleaf-editor/sanitize`
9
+ * cannot drift.
10
+ *
11
+ * Relative iframe URLs are refused: a CMS that later hosts an uploaded HTML
12
+ * file at a relative path would otherwise turn a stored embed into a stored
13
+ * XSS. `http:` is refused for the same reason every other URL check prefers
14
+ * an allowlist of schemes: mixed content is not an embed, it is a warning.
15
+ */
16
+ export interface EmbedHostRule {
17
+ /** Hostname, compared case-insensitively, with a leading `www.` ignored. */
18
+ readonly host: string;
19
+ /** Path that must match. Absent means any path on that host. */
20
+ readonly path?: RegExp;
21
+ }
22
+ /**
23
+ * Hosts an author may embed.
24
+ *
25
+ * Each rule is a known player page, not a whole site. `youtube.com/` would
26
+ * let an author embed `youtube.com/watch` as a nested YouTube UI; `/embed/`
27
+ * is the documented iframe URL.
28
+ */
29
+ export declare const EMBED_HOSTS: readonly EmbedHostRule[];
30
+ /** True when this URL is an https embed the schema will store. */
31
+ export declare function isAllowedEmbedSrc(value: string | null | undefined): boolean;
32
+ /** The URL, or null when it must not be stored as an iframe `src`. */
33
+ export declare function safeEmbedSrc(value: string | null | undefined): string | null;
34
+ /**
35
+ * Permissions policy tokens an iframe may advertise.
36
+ *
37
+ * Anything not in the closed set is dropped rather than stored: `allow` is how
38
+ * an embed asks to skip the sandbox the rest of the page lives in.
39
+ *
40
+ * The attribute is a **semicolon**-delimited list of directives, each one a
41
+ * feature name followed by an optional origin allowlist -- `camera 'self';
42
+ * fullscreen *`. Splitting on whitespace instead read the standard YouTube
43
+ * string `allow="autoplay; fullscreen; picture-in-picture"` as the tokens
44
+ * "autoplay;" and "fullscreen;", so only the last survived, and a trailing
45
+ * semicolon lost every permission the author had.
46
+ *
47
+ * Only the feature name is kept. Dropping the origin allowlist leaves the
48
+ * attribute's default, which is the frame's own origin -- so `camera *` narrows
49
+ * to the embed itself rather than being stored as written.
50
+ */
51
+ export declare function safeAllowList(value: string | null | undefined): string | null;
52
+ //# sourceMappingURL=embed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,gEAAgE;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,aAAa,EAS/C,CAAA;AA8BD,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAY3E;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAE5E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAW7E"}