@openleaf-editor/ui 0.1.0-beta.2 → 0.1.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -0
- package/dist/block-type.d.ts +5 -0
- package/dist/block-type.d.ts.map +1 -0
- package/dist/block-type.js +133 -0
- package/dist/block-type.js.map +1 -0
- package/dist/content-css.d.ts +18 -0
- package/dist/content-css.d.ts.map +1 -1
- package/dist/content-css.js +231 -16
- package/dist/content-css.js.map +1 -1
- package/dist/css.d.ts +23 -0
- package/dist/css.d.ts.map +1 -0
- package/dist/css.js +846 -0
- package/dist/css.js.map +1 -0
- package/dist/dialog.d.ts +93 -0
- package/dist/dialog.d.ts.map +1 -1
- package/dist/dialog.js +414 -86
- package/dist/dialog.js.map +1 -1
- package/dist/floating.d.ts +4 -0
- package/dist/floating.d.ts.map +1 -1
- package/dist/floating.js +50 -2
- package/dist/floating.js.map +1 -1
- package/dist/help.d.ts +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +29 -5
- package/dist/help.js.map +1 -1
- package/dist/i18n.d.ts +8 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +32 -7
- package/dist/i18n.js.map +1 -1
- package/dist/icons.d.ts +7 -1
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +6 -2
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/items.d.ts +0 -1
- package/dist/items.d.ts.map +1 -1
- package/dist/items.js +211 -20
- package/dist/items.js.map +1 -1
- package/dist/live.d.ts +42 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +83 -0
- package/dist/live.js.map +1 -0
- package/dist/menu.d.ts +31 -2
- package/dist/menu.d.ts.map +1 -1
- package/dist/menu.js +163 -23
- package/dist/menu.js.map +1 -1
- package/dist/openleaf.css +147 -12
- package/dist/overflow.d.ts +47 -4
- package/dist/overflow.d.ts.map +1 -1
- package/dist/overflow.js +338 -83
- package/dist/overflow.js.map +1 -1
- package/dist/registry.d.ts +72 -18
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +7 -6
- package/dist/registry.js.map +1 -1
- package/dist/skins.d.ts.map +1 -1
- package/dist/skins.js +48 -14
- package/dist/skins.js.map +1 -1
- package/dist/styles.d.ts +61 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +77 -637
- package/dist/styles.js.map +1 -1
- package/dist/testing.d.ts +3 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +3 -0
- package/dist/testing.js.map +1 -0
- package/dist/toolbar.d.ts +71 -12
- package/dist/toolbar.d.ts.map +1 -1
- package/dist/toolbar.js +410 -166
- package/dist/toolbar.js.map +1 -1
- package/dist/upload.d.ts +32 -7
- package/dist/upload.d.ts.map +1 -1
- package/dist/upload.js +64 -8
- package/dist/upload.js.map +1 -1
- package/package.json +17 -3
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# @openleaf-editor/ui
|
|
2
|
+
|
|
3
|
+
Toolbar, menubar, dialogs, icons, skins and theme tokens. Plain CSS, no
|
|
4
|
+
framework, themeable by custom properties.
|
|
5
|
+
|
|
6
|
+
> **Editor output is untrusted input.** Whatever the editor produces — and
|
|
7
|
+
> whatever a user pasted into it — must be sanitized **on your server** before it
|
|
8
|
+
> is stored or rendered as HTML. Client-side sanitization is a user-experience
|
|
9
|
+
> feature, not a security control: anything the editor strips can be put back
|
|
10
|
+
> with developer tools, because the editor runs under the user's control.
|
|
11
|
+
>
|
|
12
|
+
> [`@openleaf-editor/sanitize`](https://github.com/PeytonNowlin/openleaf/tree/main/packages/sanitize) ships the
|
|
13
|
+
> canonical allowlist as data and generates configuration for DOMPurify, Python
|
|
14
|
+
> `bleach` and PHP HTMLPurifier from it, so client and server enforce the same
|
|
15
|
+
> rules. Read [SECURITY.md](https://github.com/PeytonNowlin/openleaf/blob/main/SECURITY.md) before you ship.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
npm install @openleaf-editor/ui@beta
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Keep every `@openleaf-editor/*` package on the same version. They pin each other
|
|
24
|
+
exactly, so mixing versions installs two copies of the schema and the toolbar
|
|
25
|
+
registry -- and a node built by one is not a node type the other accepts.
|
|
26
|
+
|
|
27
|
+
## What it is for
|
|
28
|
+
|
|
29
|
+
[`@openleaf-editor/element`](../element) already uses this. Import it directly to
|
|
30
|
+
register your own toolbar control, or to reuse the dialogs and icons.
|
|
31
|
+
|
|
32
|
+
`promptForLink` is the one path behind the Link toolbar button, the link
|
|
33
|
+
context menu, and the selection toolbar. On Save it keeps the mark's `id` and
|
|
34
|
+
merges window-safety tokens (`noopener noreferrer`) into `rel` instead of
|
|
35
|
+
replacing author tokens.
|
|
36
|
+
|
|
37
|
+
## Registering a control
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { registerToolbarItem } from '@openleaf-editor/ui'
|
|
41
|
+
import { toggleBold } from '@openleaf-editor/core'
|
|
42
|
+
|
|
43
|
+
registerToolbarItem({
|
|
44
|
+
id: 'bold', type: 'button', kind: 'toggle',
|
|
45
|
+
label: 'Bold', icon: 'bold', command: toggleBold,
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Three item types. `button` is the common case. `select` builds a native
|
|
50
|
+
`<select>` from `options`, `getValue` and `applyValue` -- use it for a fixed
|
|
51
|
+
preset list. `custom` hands you `render` and expects a `ToolbarControl` back,
|
|
52
|
+
which is how the colour swatch grid and the block-type control exist without the
|
|
53
|
+
toolbar knowing their ids. The block-type dropdown disables Heading and Paragraph
|
|
54
|
+
when those commands would not apply -- a captioned figure is a textblock, but
|
|
55
|
+
retyping it is how those entries used to destroy the `<figure>`.
|
|
56
|
+
|
|
57
|
+
Registering an item declares capability; it does not rearrange anyone's toolbar.
|
|
58
|
+
Layout is the integrator's `toolbar` attribute.
|
|
59
|
+
|
|
60
|
+
The built-in `image` item inserts, or edits the selected image -- the same
|
|
61
|
+
pattern as the insert-media control. `labelFor` is how its accessible name
|
|
62
|
+
becomes "Edit image" while a picture is selected.
|
|
63
|
+
|
|
64
|
+
## The toolbar is one tab stop
|
|
65
|
+
|
|
66
|
+
It is a `role="toolbar"` with a roving tabindex, which is the whole reason the
|
|
67
|
+
custom-control contract is strict about focus: exactly one focusable element in
|
|
68
|
+
what you return, and if it is not a `button.ol-btn` then name it as `focusable`.
|
|
69
|
+
Two makes the bar two tab stops where the author expects one.
|
|
70
|
+
|
|
71
|
+
[Authoring OpenLeaf plugins](../../docs/authoring-plugins.md) documents the rest,
|
|
72
|
+
including the `mousedown` `preventDefault` that keeps the selection alive and why
|
|
73
|
+
`destroy` runs before every re-render rather than only on teardown.
|
|
74
|
+
|
|
75
|
+
## Styling
|
|
76
|
+
|
|
77
|
+
A constructable stylesheet, adopted into the document, so no bundler CSS import
|
|
78
|
+
is required and nothing is injected inline. Every colour is a custom property;
|
|
79
|
+
`skin` and `theme` attributes switch them without rebuilding the editor or
|
|
80
|
+
costing the author their undo history. On a browser without
|
|
81
|
+
`adoptedStyleSheets`, link `openleaf.css` and call `markStylesExternal()`.
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
Apache-2.0.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type FormatSpec } from '@openleaf-editor/core';
|
|
2
|
+
import type { EditorView } from 'prosemirror-view';
|
|
3
|
+
import type { ToolbarControl } from './registry.js';
|
|
4
|
+
export declare function blockTypeControl(view: EditorView, host: HTMLElement, formats: readonly FormatSpec[]): ToolbarControl;
|
|
5
|
+
//# sourceMappingURL=block-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-type.d.ts","sourceRoot":"","sources":["../src/block-type.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,UAAU,EAChB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,SAAS,UAAU,EAAE,GAC7B,cAAc,CA+FhB"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { activeBlockClass, activeHeadingLevel, formatParts, isNodeActive, setBlockClass, setHeading, setParagraph, toggleHeading, } from '@openleaf-editor/core';
|
|
2
|
+
import { t } from './i18n.js';
|
|
3
|
+
export function blockTypeControl(view, host, formats) {
|
|
4
|
+
const select = host.ownerDocument.createElement('select');
|
|
5
|
+
select.className = 'ol-select';
|
|
6
|
+
select.dataset['olId'] = 'blockType';
|
|
7
|
+
select.setAttribute('aria-label', t('Paragraph style'));
|
|
8
|
+
const options = [
|
|
9
|
+
['p', t('Paragraph')], ['1', t('Heading 1')], ['2', t('Heading 2')],
|
|
10
|
+
['3', t('Heading 3')], ['4', t('Heading 4')], ['5', t('Heading 5')], ['6', t('Heading 6')],
|
|
11
|
+
];
|
|
12
|
+
const addOption = (label, value) => {
|
|
13
|
+
const option = host.ownerDocument.createElement('option');
|
|
14
|
+
option.textContent = label;
|
|
15
|
+
option.value = value;
|
|
16
|
+
select.add(option);
|
|
17
|
+
};
|
|
18
|
+
// Empty value for selections that are not a paragraph or heading (a figure
|
|
19
|
+
// caption). Without it the control would display "Paragraph" over a node
|
|
20
|
+
// setParagraph cannot convert.
|
|
21
|
+
addOption('', '');
|
|
22
|
+
select.options[0].hidden = true;
|
|
23
|
+
select.options[0].disabled = true;
|
|
24
|
+
for (const [value, label] of options)
|
|
25
|
+
addOption(label, value);
|
|
26
|
+
for (const format of formats)
|
|
27
|
+
addOption(t(format.label), `format:${format.token}`);
|
|
28
|
+
let pointerDriven = false;
|
|
29
|
+
select.addEventListener('pointerdown', () => { pointerDriven = true; });
|
|
30
|
+
select.addEventListener('mousedown', (event) => event.stopPropagation());
|
|
31
|
+
select.addEventListener('keydown', (event) => {
|
|
32
|
+
pointerDriven = false;
|
|
33
|
+
if (event.key === 'Enter') {
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
view.focus();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
select.addEventListener('change', () => {
|
|
39
|
+
// `select.disabled` as well as the attribute, because readonly is not the
|
|
40
|
+
// only reason this control goes unavailable: the toolbar disables it in
|
|
41
|
+
// source view too, where the document this would format is hidden behind a
|
|
42
|
+
// textarea that gets reparsed over the result. A native disabled select
|
|
43
|
+
// cannot fire `change` at all -- this is the guard for a synthetic one, and
|
|
44
|
+
// for the window before the first `update()`.
|
|
45
|
+
if (host.hasAttribute('readonly') || select.disabled)
|
|
46
|
+
return;
|
|
47
|
+
const value = select.value;
|
|
48
|
+
if (!value)
|
|
49
|
+
return;
|
|
50
|
+
if (value.startsWith('format:')) {
|
|
51
|
+
const { element, className } = formatParts(value.slice('format:'.length));
|
|
52
|
+
if (element === 'p')
|
|
53
|
+
setParagraph(view.state, view.dispatch, view);
|
|
54
|
+
else if (element !== null && /^h[1-6]$/.test(element)) {
|
|
55
|
+
setHeading(Number(element.slice(1)))(view.state, view.dispatch, view);
|
|
56
|
+
}
|
|
57
|
+
setBlockClass(className)(view.state, view.dispatch, view);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const command = value === 'p' ? setParagraph : toggleHeading(Number(value));
|
|
61
|
+
command(view.state, view.dispatch, view);
|
|
62
|
+
}
|
|
63
|
+
if (pointerDriven) {
|
|
64
|
+
pointerDriven = false;
|
|
65
|
+
view.focus();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
el: select,
|
|
70
|
+
focusable: select,
|
|
71
|
+
update(state) {
|
|
72
|
+
// Both, and the real `disabled` is the load-bearing half: with only
|
|
73
|
+
// `aria-disabled` the control still opened and still changed its own
|
|
74
|
+
// displayed value, so a readonly editor showed "Heading 2" over a document
|
|
75
|
+
// that was still a paragraph.
|
|
76
|
+
const readonly = host.hasAttribute('readonly');
|
|
77
|
+
select.setAttribute('aria-disabled', readonly ? 'true' : 'false');
|
|
78
|
+
select.disabled = readonly;
|
|
79
|
+
for (const option of Array.from(select.options)) {
|
|
80
|
+
if (option.value === '')
|
|
81
|
+
continue;
|
|
82
|
+
option.disabled = readonly || !blockTypeAvailable(state, option.value);
|
|
83
|
+
}
|
|
84
|
+
const formatClass = activeBlockClass(state);
|
|
85
|
+
const level = activeHeadingLevel(state);
|
|
86
|
+
const inParagraph = isNodeActive(state, 'paragraph');
|
|
87
|
+
const activeElement = level === null ? (inParagraph ? 'p' : null) : `h${level}`;
|
|
88
|
+
const matching = formats.find((format) => {
|
|
89
|
+
const { element, className } = formatParts(format.token);
|
|
90
|
+
return className === formatClass && (element === null || element === activeElement);
|
|
91
|
+
});
|
|
92
|
+
const next = matching
|
|
93
|
+
? `format:${matching.token}`
|
|
94
|
+
: level !== null
|
|
95
|
+
? String(level)
|
|
96
|
+
: inParagraph
|
|
97
|
+
? 'p'
|
|
98
|
+
: '';
|
|
99
|
+
if (select.value !== next)
|
|
100
|
+
select.value = next;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Whether this dropdown entry's command would apply. A figure is a textblock
|
|
106
|
+
* but not a paragraph; without this check Heading and Paragraph stay enabled
|
|
107
|
+
* in a caption and choosing one destroys the figure.
|
|
108
|
+
*
|
|
109
|
+
* A format entry runs TWO commands, so it is available when either half has
|
|
110
|
+
* work to do. `p.lead` over a paragraph converts nothing -- `setParagraph`
|
|
111
|
+
* declines a block that is already a paragraph -- but it still has a class to
|
|
112
|
+
* set, and testing only the element half disabled the entry in the one place
|
|
113
|
+
* an author would reach for it. The class half is consulted only when the
|
|
114
|
+
* token actually names a class: `h2` alone in a caption stays disabled rather
|
|
115
|
+
* than becoming an entry that strips the caption's class and nothing else.
|
|
116
|
+
*/
|
|
117
|
+
function blockTypeAvailable(state, value) {
|
|
118
|
+
if (value.startsWith('format:')) {
|
|
119
|
+
const { element, className } = formatParts(value.slice('format:'.length));
|
|
120
|
+
const elementApplies = element === 'p'
|
|
121
|
+
? setParagraph(state)
|
|
122
|
+
: element !== null && /^h[1-6]$/.test(element)
|
|
123
|
+
? setHeading(Number(element.slice(1)))(state)
|
|
124
|
+
: null;
|
|
125
|
+
if (elementApplies === null)
|
|
126
|
+
return true;
|
|
127
|
+
return elementApplies || (className !== null && setBlockClass(className)(state));
|
|
128
|
+
}
|
|
129
|
+
if (value === 'p')
|
|
130
|
+
return setParagraph(state);
|
|
131
|
+
return setHeading(Number(value))(state);
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=block-type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-type.js","sourceRoot":"","sources":["../src/block-type.ts"],"names":[],"mappings":"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"}
|
package/dist/content-css.d.ts
CHANGED
|
@@ -6,6 +6,24 @@
|
|
|
6
6
|
* stylesheet the published template uses -- a CMS "styles.css" -- that is not
|
|
7
7
|
* on the admin page. Selectors are scoped under the editor canvas so a rule
|
|
8
8
|
* written for `p.lead` cannot restyle the rest of the admin chrome.
|
|
9
|
+
*
|
|
10
|
+
* ## Why this is a scanner and not a regex
|
|
11
|
+
*
|
|
12
|
+
* It used to be one `String.replace`, matching a selector only where it was
|
|
13
|
+
* preceded by the start of the input or a `}`. A rule inside `@media` is
|
|
14
|
+
* preceded by the media block's `{`, so it never matched and shipped
|
|
15
|
+
* **unscoped** into the host page -- and nearly every real stylesheet uses
|
|
16
|
+
* media queries, so that was the common case rather than an edge case. The same
|
|
17
|
+
* pass split selector lists on every `,`, which turned `a:is(.b, .c)` into an
|
|
18
|
+
* invalid selector the browser drops on the floor.
|
|
19
|
+
*
|
|
20
|
+
* ## Why not CSSOM
|
|
21
|
+
*
|
|
22
|
+
* `new CSSStyleSheet()` + `replaceSync` would parse this properly and is the
|
|
23
|
+
* obvious answer. It is not available everywhere this function has to run:
|
|
24
|
+
* jsdom implements `CSSStyleSheet` without `replaceSync`, and this module is
|
|
25
|
+
* imported by code that runs server-side. A scoper that throws outside a
|
|
26
|
+
* browser is a scoper the tests cannot cover, so the parsing is done here.
|
|
9
27
|
*/
|
|
10
28
|
export declare function scopeContentCss(css: string): string;
|
|
11
29
|
export declare function loadContentCss(doc: Document, urls: readonly string[]): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-css.d.ts","sourceRoot":"","sources":["../src/content-css.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"content-css.d.ts","sourceRoot":"","sources":["../src/content-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAsNH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB1F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,CAM7D"}
|
package/dist/content-css.js
CHANGED
|
@@ -6,24 +6,239 @@
|
|
|
6
6
|
* stylesheet the published template uses -- a CMS "styles.css" -- that is not
|
|
7
7
|
* on the admin page. Selectors are scoped under the editor canvas so a rule
|
|
8
8
|
* written for `p.lead` cannot restyle the rest of the admin chrome.
|
|
9
|
+
*
|
|
10
|
+
* ## Why this is a scanner and not a regex
|
|
11
|
+
*
|
|
12
|
+
* It used to be one `String.replace`, matching a selector only where it was
|
|
13
|
+
* preceded by the start of the input or a `}`. A rule inside `@media` is
|
|
14
|
+
* preceded by the media block's `{`, so it never matched and shipped
|
|
15
|
+
* **unscoped** into the host page -- and nearly every real stylesheet uses
|
|
16
|
+
* media queries, so that was the common case rather than an edge case. The same
|
|
17
|
+
* pass split selector lists on every `,`, which turned `a:is(.b, .c)` into an
|
|
18
|
+
* invalid selector the browser drops on the floor.
|
|
19
|
+
*
|
|
20
|
+
* ## Why not CSSOM
|
|
21
|
+
*
|
|
22
|
+
* `new CSSStyleSheet()` + `replaceSync` would parse this properly and is the
|
|
23
|
+
* obvious answer. It is not available everywhere this function has to run:
|
|
24
|
+
* jsdom implements `CSSStyleSheet` without `replaceSync`, and this module is
|
|
25
|
+
* imported by code that runs server-side. A scoper that throws outside a
|
|
26
|
+
* browser is a scoper the tests cannot cover, so the parsing is done here.
|
|
27
|
+
*/
|
|
28
|
+
const SCOPE = '.ol-editor .ol-content .ProseMirror';
|
|
29
|
+
/**
|
|
30
|
+
* At-rules whose block is NOT a selector list. Everything else is descended into.
|
|
31
|
+
*
|
|
32
|
+
* A denylist rather than an allowlist, and the direction matters: the two ways
|
|
33
|
+
* of being wrong are not symmetric. Failing to descend into a rule-bearing
|
|
34
|
+
* at-rule ships its selectors UNSCOPED into the host page, which is the leak
|
|
35
|
+
* this whole function exists to prevent. Descending into a declaration block
|
|
36
|
+
* that merely looks like one costs nothing -- it has no inner blocks to rewrite,
|
|
37
|
+
* so its declarations pass through as they are. An allowlist means every at-rule
|
|
38
|
+
* CSS gains is a leak until somebody remembers to add it, which
|
|
39
|
+
* `@starting-style` had already demonstrated.
|
|
40
|
+
*
|
|
41
|
+
* `@keyframes` is why this list is not empty: `from`, `to` and `0%` are offsets,
|
|
42
|
+
* not selectors, and prefixing one with a class produces an offset that matches
|
|
43
|
+
* nothing and silently kills the animation.
|
|
44
|
+
*/
|
|
45
|
+
const OPAQUE_AT_RULES = new Set([
|
|
46
|
+
'keyframes', '-webkit-keyframes', '-moz-keyframes', '-o-keyframes',
|
|
47
|
+
'font-face', 'font-feature-values', 'font-palette-values',
|
|
48
|
+
'page', 'property', 'counter-style', 'color-profile', 'position-try', 'viewport',
|
|
49
|
+
]);
|
|
50
|
+
/** Index just past the string literal starting at `start`. */
|
|
51
|
+
function endOfString(css, start) {
|
|
52
|
+
const quote = css[start];
|
|
53
|
+
let i = start + 1;
|
|
54
|
+
while (i < css.length) {
|
|
55
|
+
const ch = css[i];
|
|
56
|
+
if (ch === '\\') {
|
|
57
|
+
i += 2;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (ch === quote)
|
|
61
|
+
return i + 1;
|
|
62
|
+
i += 1;
|
|
63
|
+
}
|
|
64
|
+
return css.length;
|
|
65
|
+
}
|
|
66
|
+
/** Index just past the comment starting at `start`. */
|
|
67
|
+
function endOfComment(css, start) {
|
|
68
|
+
const end = css.indexOf('*/', start + 2);
|
|
69
|
+
return end === -1 ? css.length : end + 2;
|
|
70
|
+
}
|
|
71
|
+
/** Index of the `}` matching the `{` at `open`, or -1 if there is none. */
|
|
72
|
+
function matchingBrace(css, open) {
|
|
73
|
+
let depth = 0;
|
|
74
|
+
let i = open;
|
|
75
|
+
while (i < css.length) {
|
|
76
|
+
const ch = css[i];
|
|
77
|
+
// A backslash escape can carry any character, brace and semicolon included:
|
|
78
|
+
// `.foo\{bar` is one valid class name, not the start of a block.
|
|
79
|
+
if (ch === '\\') {
|
|
80
|
+
i += 2;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (ch === '/' && css[i + 1] === '*') {
|
|
84
|
+
i = endOfComment(css, i);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (ch === '"' || ch === "'") {
|
|
88
|
+
i = endOfString(css, i);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (ch === '{')
|
|
92
|
+
depth += 1;
|
|
93
|
+
else if (ch === '}') {
|
|
94
|
+
depth -= 1;
|
|
95
|
+
if (depth === 0)
|
|
96
|
+
return i;
|
|
97
|
+
}
|
|
98
|
+
i += 1;
|
|
99
|
+
}
|
|
100
|
+
return -1;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Leading whitespace and comments, split off so they are emitted BEFORE the
|
|
104
|
+
* rule rather than swept into its first selector.
|
|
9
105
|
*/
|
|
106
|
+
function splitLeading(prelude) {
|
|
107
|
+
let i = 0;
|
|
108
|
+
for (;;) {
|
|
109
|
+
while (i < prelude.length && /\s/.test(prelude[i] ?? ''))
|
|
110
|
+
i += 1;
|
|
111
|
+
if (prelude.startsWith('/*', i)) {
|
|
112
|
+
i = endOfComment(prelude, i);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
return [prelude.slice(0, i), prelude.slice(i)];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Split a selector list on its top-level commas only.
|
|
121
|
+
*
|
|
122
|
+
* The commas inside `:is()`, `:where()`, `:has()` and `:not()` belong to the
|
|
123
|
+
* function, and splitting on them is what produced
|
|
124
|
+
* `a:is(.b, SCOPE .c)` -- an invalid selector whose rule the browser drops.
|
|
125
|
+
* Attribute values are skipped for the same reason: `[title="a,b"]`.
|
|
126
|
+
*/
|
|
127
|
+
function splitSelectorList(selectors) {
|
|
128
|
+
const parts = [];
|
|
129
|
+
let depth = 0;
|
|
130
|
+
let start = 0;
|
|
131
|
+
let i = 0;
|
|
132
|
+
while (i < selectors.length) {
|
|
133
|
+
const ch = selectors[i];
|
|
134
|
+
// `.foo\,bar` is one class name whose name contains a comma.
|
|
135
|
+
if (ch === '\\') {
|
|
136
|
+
i += 2;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (ch === '/' && selectors[i + 1] === '*') {
|
|
140
|
+
i = endOfComment(selectors, i);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (ch === '"' || ch === "'") {
|
|
144
|
+
i = endOfString(selectors, i);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (ch === '(' || ch === '[')
|
|
148
|
+
depth += 1;
|
|
149
|
+
else if (ch === ')' || ch === ']')
|
|
150
|
+
depth = Math.max(0, depth - 1);
|
|
151
|
+
else if (ch === ',' && depth === 0) {
|
|
152
|
+
parts.push(selectors.slice(start, i));
|
|
153
|
+
start = i + 1;
|
|
154
|
+
}
|
|
155
|
+
i += 1;
|
|
156
|
+
}
|
|
157
|
+
parts.push(selectors.slice(start));
|
|
158
|
+
return parts;
|
|
159
|
+
}
|
|
160
|
+
function scopeSelectorList(selectors) {
|
|
161
|
+
return splitSelectorList(selectors)
|
|
162
|
+
.map((part) => {
|
|
163
|
+
const trimmed = part.trim();
|
|
164
|
+
if (!trimmed)
|
|
165
|
+
return part;
|
|
166
|
+
// Already scoped by the author. Left exactly as written, because a
|
|
167
|
+
// stylesheet that names the canvas is talking about the canvas.
|
|
168
|
+
if (trimmed.startsWith('.ol-editor') || trimmed.startsWith('.ol-content'))
|
|
169
|
+
return trimmed;
|
|
170
|
+
return `${SCOPE} ${trimmed}`;
|
|
171
|
+
})
|
|
172
|
+
.join(', ');
|
|
173
|
+
}
|
|
174
|
+
function scopeRules(css) {
|
|
175
|
+
let out = '';
|
|
176
|
+
let prelude = '';
|
|
177
|
+
let i = 0;
|
|
178
|
+
while (i < css.length) {
|
|
179
|
+
const ch = css[i];
|
|
180
|
+
// Escapes first, so `.foo\;bar` is one class name rather than a statement
|
|
181
|
+
// that ends mid-selector -- which split the rule and left both halves
|
|
182
|
+
// matching nothing.
|
|
183
|
+
if (ch === '\\') {
|
|
184
|
+
prelude += css.slice(i, i + 2);
|
|
185
|
+
i += 2;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (ch === '/' && css[i + 1] === '*') {
|
|
189
|
+
const stop = endOfComment(css, i);
|
|
190
|
+
prelude += css.slice(i, stop);
|
|
191
|
+
i = stop;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (ch === '"' || ch === "'") {
|
|
195
|
+
const stop = endOfString(css, i);
|
|
196
|
+
prelude += css.slice(i, stop);
|
|
197
|
+
i = stop;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (ch === '{') {
|
|
201
|
+
const close = matchingBrace(css, i);
|
|
202
|
+
if (close === -1) {
|
|
203
|
+
// Unbalanced. Emitting the remainder verbatim is the honest failure: a
|
|
204
|
+
// truncated stylesheet is the integrator's to fix, and half-scoping it
|
|
205
|
+
// would make the result harder to recognise than leaving it alone.
|
|
206
|
+
return out + prelude + css.slice(i);
|
|
207
|
+
}
|
|
208
|
+
const [leading, rule] = splitLeading(prelude);
|
|
209
|
+
const body = css.slice(i + 1, close);
|
|
210
|
+
out += leading;
|
|
211
|
+
if (rule.startsWith('@')) {
|
|
212
|
+
const name = /^@(-?[\w-]+)/.exec(rule)?.[1]?.toLowerCase() ?? '';
|
|
213
|
+
// Descended into unless its block is known not to hold rules.
|
|
214
|
+
out += `${rule}{${OPAQUE_AT_RULES.has(name) ? body : scopeRules(body)}}`;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// The body is NOT recursed into. A rule nested inside a style rule is
|
|
218
|
+
// relative to its parent, which this has already scoped -- scoping it
|
|
219
|
+
// again would put the canvas prefix in twice.
|
|
220
|
+
out += `${scopeSelectorList(rule)}{${body}}`;
|
|
221
|
+
}
|
|
222
|
+
prelude = '';
|
|
223
|
+
i = close + 1;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (ch === ';') {
|
|
227
|
+
// A statement at-rule: @import, @charset, @namespace, `@layer a, b;`.
|
|
228
|
+
// None of them carries a selector.
|
|
229
|
+
out += prelude + ';';
|
|
230
|
+
prelude = '';
|
|
231
|
+
i += 1;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
prelude += ch;
|
|
235
|
+
i += 1;
|
|
236
|
+
}
|
|
237
|
+
// Trailing whitespace, or a selector with no block. Kept as found.
|
|
238
|
+
return out + prelude;
|
|
239
|
+
}
|
|
10
240
|
export function scopeContentCss(css) {
|
|
11
|
-
return css
|
|
12
|
-
if (selector.trim().startsWith('@'))
|
|
13
|
-
return full;
|
|
14
|
-
const scoped = selector
|
|
15
|
-
.split(',')
|
|
16
|
-
.map((part) => {
|
|
17
|
-
const trimmed = part.trim();
|
|
18
|
-
if (!trimmed)
|
|
19
|
-
return part;
|
|
20
|
-
if (trimmed.startsWith('.ol-editor') || trimmed.startsWith('.ol-content'))
|
|
21
|
-
return trimmed;
|
|
22
|
-
return `.ol-editor .ol-content .ProseMirror ${trimmed}`;
|
|
23
|
-
})
|
|
24
|
-
.join(', ');
|
|
25
|
-
return `${brace}${space}${scoped}{`;
|
|
26
|
-
});
|
|
241
|
+
return scopeRules(css);
|
|
27
242
|
}
|
|
28
243
|
export async function loadContentCss(doc, urls) {
|
|
29
244
|
if (typeof CSSStyleSheet === 'undefined' || !('adoptedStyleSheets' in Document.prototype)) {
|
package/dist/content-css.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-css.js","sourceRoot":"","sources":["../src/content-css.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"content-css.js","sourceRoot":"","sources":["../src/content-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,KAAK,GAAG,qCAAqC,CAAA;AAEnD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,WAAW,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,cAAc;IAClE,WAAW,EAAE,qBAAqB,EAAE,qBAAqB;IACzD,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU;CACjF,CAAC,CAAA;AAEF,8DAA8D;AAC9D,SAAS,WAAW,CAAC,GAAW,EAAE,KAAa;IAC7C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAA;IACxB,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,CAAA;IACjB,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAA;QACjB,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,CAAC,IAAI,CAAC,CAAA;YACN,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,CAAA;QAC9B,CAAC,IAAI,CAAC,CAAA;IACR,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,CAAA;AACnB,CAAC;AAED,uDAAuD;AACvD,SAAS,YAAY,CAAC,GAAW,EAAE,KAAa;IAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;IACxC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAA;AAC1C,CAAC;AAED,2EAA2E;AAC3E,SAAS,aAAa,CAAC,GAAW,EAAE,IAAY;IAC9C,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,CAAC,GAAG,IAAI,CAAA;IACZ,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAA;QACjB,4EAA4E;QAC5E,iEAAiE;QACjE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,CAAC,IAAI,CAAC,CAAA;YACN,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACrC,CAAC,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACxB,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAC7B,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACvB,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,IAAI,CAAC,CAAA;aACrB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACpB,KAAK,IAAI,CAAC,CAAA;YACV,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,CAAC,CAAA;QAC3B,CAAC;QACD,CAAC,IAAI,CAAC,CAAA;IACR,CAAC;IACD,OAAO,CAAC,CAAC,CAAA;AACX,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,OAAe;IACnC,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,SAAS,CAAC;QACR,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAAE,CAAC,IAAI,CAAC,CAAA;QAChE,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YAChC,CAAC,GAAG,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;YAC5B,SAAQ;QACV,CAAC;QACD,MAAK;IACP,CAAC;IACD,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AAChD,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;QACvB,6DAA6D;QAC7D,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,CAAC,IAAI,CAAC,CAAA;YACN,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC3C,CAAC,GAAG,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;YAC9B,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAC7B,CAAC,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;YAC7B,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,IAAI,CAAC,CAAA;aACnC,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;aAC5D,IAAI,EAAE,KAAK,GAAG,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAA;YACrC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAA;QACf,CAAC;QACD,CAAC,IAAI,CAAC,CAAA;IACR,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IAClC,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,OAAO,iBAAiB,CAAC,SAAS,CAAC;SAChC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QACzB,mEAAmE;QACnE,gEAAgE;QAChE,IAAI,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC;YAAE,OAAO,OAAO,CAAA;QACzF,OAAO,GAAG,KAAK,IAAI,OAAO,EAAE,CAAA;IAC9B,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,IAAI,OAAO,GAAG,EAAE,CAAA;IAChB,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAA;QACjB,0EAA0E;QAC1E,sEAAsE;QACtE,oBAAoB;QACpB,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;YAC9B,CAAC,IAAI,CAAC,CAAA;YACN,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACjC,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;YAC7B,CAAC,GAAG,IAAI,CAAA;YACR,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YAChC,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;YAC7B,CAAC,GAAG,IAAI,CAAA;YACR,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACnC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjB,uEAAuE;gBACvE,uEAAuE;gBACvE,mEAAmE;gBACnE,OAAO,GAAG,GAAG,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACrC,CAAC;YACD,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;YAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;YACpC,GAAG,IAAI,OAAO,CAAA;YACd,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAA;gBAChE,8DAA8D;gBAC9D,GAAG,IAAI,GAAG,IAAI,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAA;YAC1E,CAAC;iBAAM,CAAC;gBACN,sEAAsE;gBACtE,sEAAsE;gBACtE,8CAA8C;gBAC9C,GAAG,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAA;YAC9C,CAAC;YACD,OAAO,GAAG,EAAE,CAAA;YACZ,CAAC,GAAG,KAAK,GAAG,CAAC,CAAA;YACb,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,sEAAsE;YACtE,mCAAmC;YACnC,GAAG,IAAI,OAAO,GAAG,GAAG,CAAA;YACpB,OAAO,GAAG,EAAE,CAAA;YACZ,CAAC,IAAI,CAAC,CAAA;YACN,SAAQ;QACV,CAAC;QACD,OAAO,IAAI,EAAE,CAAA;QACb,CAAC,IAAI,CAAC,CAAA;IACR,CAAC;IACD,mEAAmE;IACnE,OAAO,GAAG,GAAG,OAAO,CAAA;AACtB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,UAAU,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAa,EAAE,IAAuB;IACzE,IAAI,OAAO,aAAa,KAAK,WAAW,IAAI,CAAC,CAAC,oBAAoB,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1F,OAAO,CAAC,IAAI,CACV,yEAAyE;YACvE,yDAAyD,CAC5D,CAAA;QACD,OAAM;IACR,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA;YACjC,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;YAC5D,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;YAClD,MAAM,KAAK,GAAG,IAAI,aAAa,EAAE,CAAA;YACjC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;YACtB,GAAG,CAAC,kBAAkB,GAAG,CAAC,GAAG,GAAG,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAA;QAC7D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CACV,wCAAwC,GAAG,wBAAwB,EACnE,KAAK,CACN,CAAA;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAoB;IACjD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,OAAO,KAAK;SACT,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC,CAAA;AACpB,CAAC"}
|
package/dist/css.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor stylesheet, as a string, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is not in `styles.ts`
|
|
5
|
+
*
|
|
6
|
+
* This is 20 KB of the ~24 KB `@openleaf-editor/ui` ships, and it is needed by
|
|
7
|
+
* exactly one function -- `ensureStyles`. While it lived beside `registerStyles`
|
|
8
|
+
* in `styles.ts`, every consumer that reached that module for any reason paid for
|
|
9
|
+
* the whole stylesheet: `skins.ts` imports `registerStyles`, so a bundle that
|
|
10
|
+
* touched a skin -- or, through the barrel, one that touched nothing but `t()` --
|
|
11
|
+
* carried the full sheet.
|
|
12
|
+
*
|
|
13
|
+
* Alone in a module whose only top-level binding is a string literal, it is
|
|
14
|
+
* trivially droppable. A bundler keeps it when something calls `ensureStyles` and
|
|
15
|
+
* discards it when the integrator has linked `openleaf.css` themselves, which is
|
|
16
|
+
* the distinction `markStylesExternal()` has always claimed to offer and could
|
|
17
|
+
* not previously deliver at bundle time.
|
|
18
|
+
*
|
|
19
|
+
* `dist/openleaf.css` is generated from this constant by `scripts/emit-css.mjs`,
|
|
20
|
+
* so the constructable-stylesheet path and the linked-file path cannot drift.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CSS = "\n.ol-editor {\n /* Two families, named before either is public API. A singular\n `--openleaf-font` implies \"the\" font, and source view already has a\n monospace surface -- bolting a mono token on beside a singular name later\n is the awkward outcome worth avoiding now. `--openleaf-font` is still\n honoured as a fallback. */\n --ol-font: var(--openleaf-font-ui, var(--openleaf-font, system-ui, -apple-system, \"Segoe UI\", sans-serif));\n --ol-font-mono: var(--openleaf-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);\n --ol-font-size: var(--openleaf-font-size, 14px);\n --ol-radius: var(--openleaf-radius, 4px);\n --ol-text: var(--openleaf-color-text, #1f2328);\n --ol-text-muted: var(--openleaf-color-text-muted, #59636e);\n --ol-surface: var(--openleaf-color-surface, #ffffff);\n --ol-surface-hover: var(--openleaf-color-surface-hover, #f0f1f3);\n --ol-surface-active: var(--openleaf-color-surface-active, #dbe9ff);\n --ol-border: var(--openleaf-color-border, #d1d9e0);\n /* Control boundaries, not decoration. See \"Two border tokens\" above. */\n --ol-border-strong: var(--openleaf-color-border-strong, #6e7781);\n --ol-accent: var(--openleaf-color-accent, #0550ae);\n --ol-focus: var(--openleaf-color-focus, #0969da);\n --ol-danger: var(--openleaf-color-danger, #cf222e);\n --ol-button-size: var(--openleaf-button-size, 32px);\n --ol-icon-size: var(--openleaf-icon-size, 16px);\n --ol-gap: var(--openleaf-gap, 2px);\n /* A 2009 WordPress admin bar sits at z-index 99999 and Drupal's toolbar plays\n similar games. Without a sanctioned escape hatch integrators fix a toolbar\n rendered behind a sticky host header with !important -- the exact thing the\n token API exists to prevent. */\n --ol-z: var(--openleaf-z-index, 1);\n /* Thickness and offset, not just colour: \"you can change the colour but not\n the thickness\" is a poor answer to a Section 508 team citing WCAG 2.2\n Focus Appearance. */\n --ol-focus-width: var(--openleaf-focus-width, 2px);\n --ol-focus-offset: var(--openleaf-focus-offset, 1px);\n\n display: block;\n position: relative;\n color: var(--ol-text);\n}\n\n/* Three ways into the dark palette, and one way out of it.\n `data-ol-scheme` is set from the applied skin's declared scheme, and a skin\n that declares one outranks both the attribute and the system -- it has to,\n because its tokens have already replaced the palette outright. Without the\n `:not()` guards a light skin on a dark machine would take these fallbacks for\n every token it did not itself set, which is a light surface carrying dark-mode\n muted text. */\n@media (prefers-color-scheme: dark) {\n .ol-editor:not([data-ol-theme=\"light\"]):not([data-ol-scheme]) {\n --ol-text: var(--openleaf-color-text, #e6edf3);\n --ol-text-muted: var(--openleaf-color-text-muted, #9198a1);\n --ol-surface: var(--openleaf-color-surface, #0d1117);\n --ol-surface-hover: var(--openleaf-color-surface-hover, #21262d);\n --ol-surface-active: var(--openleaf-color-surface-active, #1f3a5f);\n --ol-border: var(--openleaf-color-border, #3d444d);\n --ol-border-strong: var(--openleaf-color-border-strong, #8b949e);\n --ol-accent: var(--openleaf-color-accent, #79c0ff);\n --ol-focus: var(--openleaf-color-focus, #79c0ff);\n --ol-danger: var(--openleaf-color-danger, #ff8182);\n}\n}\n\n.ol-editor[data-ol-theme=\"dark\"]:not([data-ol-scheme]) {\n --ol-text: var(--openleaf-color-text, #e6edf3);\n --ol-text-muted: var(--openleaf-color-text-muted, #9198a1);\n --ol-surface: var(--openleaf-color-surface, #0d1117);\n --ol-surface-hover: var(--openleaf-color-surface-hover, #21262d);\n --ol-surface-active: var(--openleaf-color-surface-active, #1f3a5f);\n --ol-border: var(--openleaf-color-border, #3d444d);\n --ol-border-strong: var(--openleaf-color-border-strong, #8b949e);\n --ol-accent: var(--openleaf-color-accent, #79c0ff);\n --ol-focus: var(--openleaf-color-focus, #79c0ff);\n --ol-danger: var(--openleaf-color-danger, #ff8182);\n}\n\n.ol-editor[data-ol-scheme=\"dark\"] {\n --ol-text: var(--openleaf-color-text, #e6edf3);\n --ol-text-muted: var(--openleaf-color-text-muted, #9198a1);\n --ol-surface: var(--openleaf-color-surface, #0d1117);\n --ol-surface-hover: var(--openleaf-color-surface-hover, #21262d);\n --ol-surface-active: var(--openleaf-color-surface-active, #1f3a5f);\n --ol-border: var(--openleaf-color-border, #3d444d);\n --ol-border-strong: var(--openleaf-color-border-strong, #8b949e);\n --ol-accent: var(--openleaf-color-accent, #79c0ff);\n --ol-focus: var(--openleaf-color-focus, #79c0ff);\n --ol-danger: var(--openleaf-color-danger, #ff8182);\n}\n\n/* Native widget chrome -- the popup a `select` opens, scrollbars, the caret,\n form control backgrounds -- is painted by the browser from `color-scheme`,\n not from any property we can hand an integrator. Left alone it follows the\n page, which is right while the editor's own palette also follows the page and\n wrong the moment either attribute pins the editor to one world. So it is set\n exactly when the editor stops following along, and inherited from the host\n otherwise. */\n.ol-editor[data-ol-theme=\"light\"] { color-scheme: light; }\n.ol-editor[data-ol-theme=\"dark\"] { color-scheme: dark; }\n.ol-editor[data-ol-scheme=\"light\"] { color-scheme: light; }\n.ol-editor[data-ol-scheme=\"dark\"] { color-scheme: dark; }\n\n/* Wrapping, not scrolling and not an overflow menu. Both of those hide\n controls -- one off-screen, one behind a click -- and a formatting control\n the author cannot see is a control they do not have. */\n.ol-editor .ol-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--ol-gap);\n box-sizing: border-box;\n margin: 0;\n padding: 4px;\n border: 1px solid var(--ol-border-strong);\n border-bottom: 0;\n border-radius: var(--ol-radius) var(--ol-radius) 0 0;\n background: var(--ol-surface);\n font: inherit;\n position: relative;\n z-index: var(--ol-z);\n}\n\n.ol-editor .ol-group {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--ol-gap);\n}\n\n/* The divider is a BORDER on the group, not a standalone flex item.\n As its own element it could wrap onto the trailing edge of a row with nothing\n after it -- a stranded line floating at the end of a row. Wrapping begins\n around 690-740px, well inside ordinary desktop widths (a CMS sidebar, a\n half-width split pane), so this was not a 360px-only edge case.\n border-inline-start rather than border-left, so it flips under RTL.\n\n This selector is why the renderer emits NO element between groups. It used to\n emit an .ol-sep div, which made the two groups non-adjacent and stopped this\n rule from ever matching -- so no divider rendered in any theme, and every bar\n carried inert empty divs. Adding a separator element back here breaks this. */\n.ol-editor .ol-group + .ol-group {\n border-inline-start: 1px solid var(--ol-border);\n padding-inline-start: 5px;\n margin-inline-start: 1px;\n}\n\n/* Every property a host reset or `button {}` rule is likely to touch is set\n here explicitly. Specificity is (0,2,0) via the descendant selector, which\n beats a bare element or single-class host rule without resorting to\n !important. */\n.ol-editor .ol-btn {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: var(--ol-button-size);\n height: var(--ol-button-size);\n min-width: var(--ol-button-size);\n min-height: var(--ol-button-size);\n max-width: none;\n padding: 0;\n margin: 0;\n border: 1px solid transparent;\n border-radius: var(--ol-radius);\n background: transparent;\n color: var(--ol-text);\n font: inherit;\n font-family: var(--ol-font);\n font-size: var(--ol-font-size);\n font-weight: 400;\n line-height: 1;\n letter-spacing: normal;\n text-transform: none;\n text-align: center;\n text-decoration: none;\n text-shadow: none;\n box-shadow: none;\n opacity: 1;\n cursor: pointer;\n appearance: none;\n -webkit-appearance: none;\n outline-offset: var(--ol-focus-offset);\n -webkit-tap-highlight-color: transparent;\n transition: background-color 120ms ease, border-color 120ms ease;\n}\n\n.ol-editor .ol-btn:hover {\n background: var(--ol-surface-hover);\n}\n\n/* :focus-visible only, so a mouse click does not leave a ring behind, but the\n ring is never removed for keyboard users. */\n.ol-editor .ol-btn:focus-visible {\n outline: var(--ol-focus-width) solid var(--ol-focus);\n outline-offset: var(--ol-focus-offset);\n}\n\n/* Pressed state is distinguished from hover by THREE signals, not just colour:\n a filled background, a visible border, and an inset shadow that reads as\n physically depressed. Colour alone would fail for a colour-blind author and\n would be invisible in forced-colours mode. */\n.ol-editor .ol-btn[aria-pressed=\"true\"] {\n background: var(--ol-surface-active);\n border-color: var(--ol-accent);\n color: var(--ol-accent);\n box-shadow: inset 0 1px 2px rgb(0 0 0 / 12%);\n}\n\n.ol-editor .ol-btn[aria-pressed=\"true\"]:hover {\n background: var(--ol-surface-active);\n border-color: var(--ol-accent);\n}\n\n/* aria-disabled rather than the disabled attribute: a disabled button is\n removed from the roving tabindex and cannot be reached or announced, so an\n author using a screen reader cannot discover that the control exists. */\n.ol-editor .ol-btn[aria-disabled=\"true\"] {\n opacity: 0.55;\n cursor: default;\n}\n\n.ol-editor .ol-btn[aria-disabled=\"true\"]:hover {\n background: transparent;\n}\n\n.ol-editor .ol-icon {\n width: var(--ol-icon-size);\n height: var(--ol-icon-size);\n display: block;\n pointer-events: none;\n flex: 0 0 auto;\n}\n\n/* Block-type select. Sized and coloured to sit level with the icon buttons so\n it does not read as bolted on, but it stays a native <select> -- a custom\n listbox is a large amount of ARIA that would then owe real screen reader\n testing to be worth anything. */\n.ol-editor .ol-select {\n box-sizing: border-box;\n height: var(--ol-button-size);\n max-width: 11em;\n padding: 0 22px 0 6px;\n margin: 0;\n border: 1px solid var(--ol-border-strong);\n border-radius: var(--ol-radius);\n background-color: transparent;\n background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),\n linear-gradient(135deg, currentColor 50%, transparent 50%);\n background-position: right 10px center, right 6px center;\n background-size: 4px 4px, 4px 4px;\n background-repeat: no-repeat;\n color: var(--ol-text);\n font-family: var(--ol-font);\n font-size: var(--ol-font-size);\n font-weight: 400;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n box-shadow: none;\n cursor: pointer;\n appearance: none;\n -webkit-appearance: none;\n}\n\n.ol-editor .ol-select--wide {\n max-width: 14em;\n}\n\n.ol-editor .ol-select:hover {\n background-color: var(--ol-surface-hover);\n}\n\n.ol-editor .ol-select:focus-visible {\n outline: var(--ol-focus-width) solid var(--ol-focus);\n outline-offset: var(--ol-focus-offset);\n}\n\n/* The option list is painted by the OS, which does not inherit our tokens, so\n give it explicit colours or dark mode shows black text on black. */\n.ol-editor .ol-select option {\n background: var(--ol-surface);\n color: var(--ol-text);\n}\n\n.ol-editor .ol-content {\n box-sizing: border-box;\n border: 1px solid var(--ol-border-strong);\n border-radius: 0 0 var(--ol-radius) var(--ol-radius);\n background: var(--ol-surface);\n}\n\n/* Deliberately minimal: the content area inherits the host's typography, which\n is the entire reason this project does not use Shadow DOM. Only padding and\n the focus ring are ours. */\n.ol-editor .ol-content .ProseMirror {\n padding: 12px;\n min-height: 8rem;\n outline: none;\n}\n\n.ol-editor .ol-content:focus-within {\n outline: 2px solid var(--ol-focus);\n outline-offset: -1px;\n}\n\n/* Tables.\n These styles live in core, not in the opt-in table plugin, because table\n NODES live in core: every deployment reads and renders tables even where\n editing them is switched off. Unstyled tables would render as runs of\n undelimited text. */\n.ol-editor .ol-content .ProseMirror table {\n border-collapse: collapse;\n table-layout: fixed;\n width: 100%;\n margin: 0 0 1em;\n overflow: hidden;\n}\n\n.ol-editor .ol-content .ProseMirror td,\n.ol-editor .ol-content .ProseMirror th {\n border: 1px solid var(--ol-border);\n padding: 6px 8px;\n vertical-align: top;\n /* A zero-width cell cannot be clicked into, so it cannot be repaired. */\n min-width: 2em;\n position: relative;\n}\n\n/* Presentational valign is otherwise beaten by the rule above, which would\n make the cell-properties dialog a no-op on screen. */\n.ol-editor .ol-content .ProseMirror td[valign=\"middle\"],\n.ol-editor .ol-content .ProseMirror th[valign=\"middle\"] { vertical-align: middle; }\n.ol-editor .ol-content .ProseMirror td[valign=\"bottom\"],\n.ol-editor .ol-content .ProseMirror th[valign=\"bottom\"] { vertical-align: bottom; }\n.ol-editor .ol-content .ProseMirror td[valign=\"baseline\"],\n.ol-editor .ol-content .ProseMirror th[valign=\"baseline\"] { vertical-align: baseline; }\n\n.ol-editor .ol-content .ProseMirror th {\n background: var(--ol-surface-hover);\n font-weight: 600;\n text-align: start;\n}\n\n/* prosemirror-tables marks cells in a rectangular selection with this class.\n An ::after overlay rather than a background so it composes with a cell that\n already has one, and so a header cell still reads as a header.\n\n The ring, not the tint, is the indicator -- see \"Cell selection\" above. */\n.ol-editor .ol-content .ProseMirror .selectedCell {\n outline: 2px solid var(--ol-accent);\n outline-offset: -2px;\n}\n.ol-editor .ol-content .ProseMirror .selectedCell::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background: var(--ol-surface-active);\n opacity: 0.4;\n pointer-events: none;\n}\n\n/* The column resize handle, drawn by prosemirror-tables' columnResizing. */\n.ol-editor .ol-content .ProseMirror .column-resize-handle {\n position: absolute;\n right: -2px;\n top: 0;\n bottom: 0;\n width: 4px;\n background: var(--ol-accent);\n pointer-events: none;\n z-index: 20;\n}\n\n.ol-editor .ol-content .ProseMirror.resize-cursor {\n cursor: col-resize;\n}\n\n.ol-editor .ol-content .ProseMirror img.ol-float-left {\n float: left;\n margin: 0 1em 0.5em 0;\n}\n.ol-editor .ol-content .ProseMirror img.ol-float-right {\n float: right;\n margin: 0 0 0.5em 1em;\n}\n.ol-editor .ol-content .ProseMirror img.ol-align-center {\n display: block;\n margin: 0 auto 0.5em;\n}\n.ol-editor .ol-content .ProseMirror figure {\n margin: 0 0 1em;\n}\n.ol-editor .ol-content .ProseMirror figcaption {\n font-size: .9em;\n opacity: .8;\n margin-top: .35em;\n}\n.ol-editor .ol-content .ProseMirror details {\n border: 1px solid var(--ol-border);\n border-radius: var(--ol-radius);\n padding: .5em .75em;\n margin: 0 0 1em;\n}\n.ol-editor .ol-content .ProseMirror summary {\n cursor: pointer;\n font-weight: 600;\n}\n.ol-editor .ol-content .ProseMirror hr.ol-pagebreak {\n border: 0;\n border-top: 2px dashed var(--ol-border);\n margin: 1.5em 0;\n}\n.ol-editor .ol-content .ProseMirror iframe,\n.ol-editor .ol-content .ProseMirror video,\n.ol-editor .ol-content .ProseMirror audio {\n max-width: 100%;\n}\n\n/* Preserved-but-unrecognised markup, surfaced rather than hidden. */\n.ol-editor .ol-content .ProseMirror-selectednode {\n outline: 2px solid var(--ol-focus);\n outline-offset: 2px;\n border-radius: 2px;\n}\n\n/* Gap cursor: the caret beside a block atom or isolating node, where no\n textblock exists. Hidden until the view is focused so it cannot paint over\n an inactive canvas. */\n.ol-editor .ol-content .ProseMirror-gapcursor {\n display: none;\n pointer-events: none;\n position: absolute;\n}\n.ol-editor .ol-content .ProseMirror-gapcursor:after {\n content: \"\";\n display: block;\n position: absolute;\n top: -2px;\n width: 20px;\n border-top: 1px solid var(--ol-text);\n animation: ol-gapcursor-blink 1.1s steps(2, start) infinite;\n}\n@keyframes ol-gapcursor-blink {\n to { visibility: hidden; }\n}\n.ol-editor .ol-content .ProseMirror-focused .ProseMirror-gapcursor {\n display: block;\n}\n\n.ol-editor .ol-source {\n box-sizing: border-box;\n display: block;\n width: 100%;\n min-height: 12rem;\n padding: 12px;\n border: 1px solid var(--ol-border-strong);\n border-radius: 0 0 var(--ol-radius) var(--ol-radius);\n background: var(--ol-surface);\n color: var(--ol-text);\n font-family: var(--ol-font-mono);\n font-size: 13px;\n line-height: 1.5;\n resize: vertical;\n white-space: pre-wrap;\n}\n\n/* Directional icons under RTL.\n Group order reverses for free with flexbox, but a curved undo arrow does not:\n in an RTL document \"back\" points the other way. Only genuinely directional\n icons are flipped -- bold and italic must not be mirrored. */\n.ol-editor[dir=\"rtl\"] .ol-icon--directional,\n[dir=\"rtl\"] .ol-editor .ol-icon--directional {\n transform: scaleX(-1);\n}\n\n/* The announcement region. Visually hidden but not display:none, which would\n remove it from the accessibility tree and silence it. */\n.ol-editor .ol-live {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n/* Menubar, menus, floating chrome, visual aids, fullscreen, inline. */\n.ol-editor .ol-menubar {\n display: flex;\n flex-wrap: wrap;\n gap: var(--ol-gap);\n box-sizing: border-box;\n margin: 0;\n padding: 2px 4px;\n border: 1px solid var(--ol-border-strong);\n border-bottom: 0;\n border-radius: var(--ol-radius) var(--ol-radius) 0 0;\n background: var(--ol-surface);\n position: relative;\n z-index: var(--ol-z);\n}\n.ol-editor .ol-menubar + .ol-toolbar {\n border-radius: 0;\n}\n.ol-editor .ol-menu-trigger {\n box-sizing: border-box;\n margin: 0;\n padding: 4px 8px;\n border: 1px solid transparent;\n border-radius: var(--ol-radius);\n background: transparent;\n color: var(--ol-text);\n font: inherit;\n font-family: var(--ol-font);\n font-size: var(--ol-font-size);\n cursor: pointer;\n appearance: none;\n}\n.ol-editor .ol-menu-trigger:hover,\n.ol-editor .ol-menu-trigger[aria-expanded=\"true\"] {\n background: var(--ol-surface-hover);\n}\n.ol-editor .ol-menu-trigger:focus-visible {\n outline: var(--ol-focus-width) solid var(--ol-focus);\n outline-offset: var(--ol-focus-offset);\n}\n.ol-editor .ol-menu {\n position: absolute;\n z-index: calc(var(--ol-z) + 20);\n min-width: 12rem;\n /* Capped, so a deep menu opened low on a short window can be flipped above\n its trigger instead of running off the bottom of the page. */\n max-height: 60vh;\n overflow-y: auto;\n margin: 0;\n padding: 4px;\n border: 1px solid var(--ol-border-strong);\n border-radius: var(--ol-radius);\n background: var(--ol-surface);\n box-shadow: 0 8px 24px rgb(0 0 0 / 12%);\n}\n.ol-editor .ol-menu-item {\n display: block;\n box-sizing: border-box;\n width: 100%;\n margin: 0;\n padding: 6px 10px;\n border: 0;\n border-radius: var(--ol-radius);\n background: transparent;\n color: var(--ol-text);\n font: inherit;\n font-family: var(--ol-font);\n font-size: var(--ol-font-size);\n text-align: start;\n cursor: pointer;\n appearance: none;\n}\n.ol-editor .ol-menu-item:hover {\n background: var(--ol-surface-hover);\n}\n/* Was outline:none plus a 1.13:1 background swap. See \"Menu focus\" above. */\n.ol-editor .ol-menu-item:focus-visible {\n background: var(--ol-surface-hover);\n outline: var(--ol-focus-width) solid var(--ol-focus);\n outline-offset: -2px;\n}\n/* 0.55, not 0.4: see \"Disabled is not invisible\" above. */\n.ol-editor .ol-menu-item[aria-disabled=\"true\"] {\n opacity: 0.55;\n cursor: default;\n}\n/* Third time for the same specificity trap, after the floating bar and the\n overflow panel below: `.ol-editor .ol-btn` is (0,2,0) and its\n `display: inline-flex` outranks the UA's `[hidden]` rule, so\n `button.hidden = true` set the attribute and painted the button anyway. A bar\n wide enough to need no overflow still showed its More trigger, and pressing\n it opened an empty panel. */\n.ol-editor .ol-btn[hidden] {\n display: none;\n}\n.ol-editor .ol-menu-sep {\n height: 1px;\n margin: 4px 0;\n background: var(--ol-border);\n}\n/* `display: flex` on .ol-toolbar above outranks the UA's `[hidden]` rule --\n a class selector beats an attribute selector on specificity -- so setting\n `el.hidden = true` in floating.ts styled a hidden bar and showed it anyway.\n Both floating bars sat over the prose at rest, with no selection to format.\n The overflow panel needed the same rule for the same reason; see below. */\n.ol-editor .ol-toolbar.ol-floating[hidden] {\n display: none;\n}\n.ol-editor .ol-toolbar.ol-floating {\n position: absolute;\n flex-wrap: nowrap;\n border: 1px solid var(--ol-border);\n border-radius: var(--ol-radius);\n box-shadow: 0 8px 24px rgb(0 0 0 / 12%);\n z-index: calc(var(--ol-z) + 10);\n}\n.ol-editor .ol-toolbar.ol-toolbar--overflow {\n flex-wrap: nowrap;\n overflow: hidden;\n}\n/* The More panel. Positioned in viewport coordinates from script, because the\n bar it belongs to may live inside a host with `overflow: hidden` -- which is\n the situation the whole feature exists for. */\n.ol-editor .ol-overflow-menu {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--ol-gap);\n max-height: 70vh;\n overflow-y: auto;\n}\n.ol-editor .ol-overflow-menu[hidden] {\n display: none;\n}\n/* Vertical: the panel exists because there is no horizontal room left. */\n.ol-editor .ol-overflow-menu .ol-group {\n flex-direction: column;\n align-items: stretch;\n}\n.ol-editor .ol-overflow-menu .ol-group + .ol-group {\n border-inline-start: 0;\n border-block-start: 1px solid var(--ol-border);\n padding-inline-start: 0;\n margin-inline-start: 0;\n padding-block-start: 4px;\n margin-block-start: 3px;\n}\n.ol-editor.ol-visual-aids .ol-empty-block {\n outline: 1px dashed var(--ol-border);\n outline-offset: 2px;\n min-height: 1.2em;\n}\n.ol-editor.ol-visual-aids .ol-nbsp {\n background: color-mix(in srgb, var(--ol-accent) 25%, transparent);\n box-shadow: inset 0 -1px 0 var(--ol-accent);\n}\n.ol-editor.ol-visual-aids .ol-hidden-structure {\n outline: 1px dotted var(--ol-accent);\n outline-offset: 2px;\n}\n.ol-editor .ol-noneditable {\n cursor: default;\n background: repeating-linear-gradient(\n -45deg,\n transparent,\n transparent 6px,\n color-mix(in srgb, var(--ol-border) 35%, transparent) 6px,\n color-mix(in srgb, var(--ol-border) 35%, transparent) 7px\n );\n}\n.ol-editor.ol-fullscreen {\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n margin: 0;\n border-radius: 0;\n background: var(--ol-surface);\n display: flex;\n flex-direction: column;\n}\n.ol-editor.ol-fullscreen .ol-content,\n.ol-editor.ol-fullscreen .ol-source {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n.ol-editor.ol-autoresize .ol-content .ProseMirror {\n overflow: hidden;\n}\n.ol-editor.ol-inline:not(.ol-inline-active) .ol-toolbar,\n.ol-editor.ol-inline:not(.ol-inline-active) .ol-menubar {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n}\n.ol-editor.ol-inline .ol-content {\n border-radius: var(--ol-radius);\n}\n.ol-help-table {\n width: 100%;\n border-collapse: collapse;\n font: inherit;\n}\n.ol-help-table th,\n.ol-help-table td {\n padding: 4px 8px;\n text-align: start;\n vertical-align: top;\n border-bottom: 1px solid var(--openleaf-color-border, #d1d9e0);\n}\n.ol-help-table th {\n font-weight: 600;\n white-space: nowrap;\n}\n\n/* Coarse pointers get a larger target. WCAG 2.2 SC 2.5.8 asks for 24x24 CSS px\n minimum; 32 clears that on a mouse and 40 is comfortable on a thumb. */\n@media (pointer: coarse) {\n .ol-editor {\n --ol-button-size: var(--openleaf-button-size, 40px);\n --ol-gap: var(--openleaf-gap, 4px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ol-editor .ol-btn {\n transition: none;\n }\n}\n\n/* Forced colours (Windows high contrast) replaces our palette wholesale. The\n pressed state must not depend on a background we no longer control, so it is\n re-expressed as a border, which the mode preserves. */\n@media (forced-colors: active) {\n .ol-editor .ol-btn {\n border-color: ButtonBorder;\n color: ButtonText;\n }\n .ol-editor .ol-btn[aria-pressed=\"true\"] {\n border-color: Highlight;\n color: Highlight;\n box-shadow: none;\n }\n .ol-editor .ol-btn:focus-visible {\n outline-color: Highlight;\n }\n .ol-editor .ol-btn[aria-disabled=\"true\"] {\n color: GrayText;\n opacity: 1;\n }\n .ol-editor .ol-menu-item:focus-visible {\n outline-color: Highlight;\n }\n .ol-editor .ol-menu-item[aria-disabled=\"true\"] {\n color: GrayText;\n opacity: 1;\n }\n .ol-editor .ol-menu-trigger[aria-expanded=\"true\"] {\n border-color: Highlight;\n color: Highlight;\n }\n .ol-editor .ol-content .ProseMirror .selectedCell {\n outline-color: Highlight;\n }\n .ol-editor .ol-content .ProseMirror .selectedCell::after {\n background: transparent;\n }\n .ol-editor .ol-content .ProseMirror .column-resize-handle {\n background: Highlight;\n }\n .ol-editor.ol-visual-aids .ol-nbsp {\n background: transparent;\n box-shadow: inset 0 -2px 0 Highlight;\n }\n .ol-editor.ol-visual-aids .ol-hidden-structure {\n outline-color: Highlight;\n }\n .ol-editor.ol-visual-aids .ol-empty-block {\n outline-color: GrayText;\n }\n .ol-editor .ol-noneditable {\n background: transparent;\n outline: 1px dashed GrayText;\n }\n}\n";
|
|
23
|
+
//# sourceMappingURL=css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAkFH,eAAO,MAAM,GAAG,++yBAyuBf,CAAA"}
|