@aquera/nile-elements 1.9.7 → 1.9.9
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 +6 -0
- package/dist/{index-249de90a.esm.js → index-289706c7.esm.js} +1 -1
- package/dist/{index-df196058.cjs.js → index-d7e6f1c6.cjs.js} +2 -2
- package/dist/index-d7e6f1c6.cjs.js.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1587 -1276
- package/dist/katex-44476ab4.esm.js +1 -0
- package/dist/katex-f4186f87.cjs.js +2 -0
- package/dist/katex-f4186f87.cjs.js.map +1 -0
- package/dist/nile-color-picker/index.cjs.js +1 -1
- package/dist/nile-color-picker/index.esm.js +1 -1
- package/dist/nile-color-picker/nile-color-picker.cjs.js +1 -1
- package/dist/nile-color-picker/nile-color-picker.cjs.js.map +1 -1
- package/dist/nile-color-picker/nile-color-picker.esm.js +1 -1
- package/dist/nile-select/virtual-scroll-helper.cjs.js +1 -1
- package/dist/nile-select/virtual-scroll-helper.cjs.js.map +1 -1
- package/dist/nile-select/virtual-scroll-helper.esm.js +52 -44
- package/dist/nile-virtual-select/renderer.cjs.js +1 -1
- package/dist/nile-virtual-select/renderer.cjs.js.map +1 -1
- package/dist/nile-virtual-select/renderer.esm.js +23 -22
- package/dist/nile-wysiwyg-editor/engine/commands.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/commands.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/commands.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/create-state.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/index.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/index.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/keymap.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/math-view.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/style-block-view.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/style-block-view.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/style-block-view.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/sanitize.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/schema.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/schema.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/schema.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/selection-state.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/index.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/index.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/math/katex-renderer.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/math/katex-renderer.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/math/katex-renderer.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.esm.js +22 -3
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.esm.js +7 -6
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.esm.js +33 -54
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.esm.js +83 -95
- package/dist/nile-wysiwyg-editor/types.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/types.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/types.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/bubble-item.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/bubble-item.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/bubble-item.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.esm.js +23 -15
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.esm.js +3 -3
- package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-panel.esm.js +2 -2
- package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/math-panel.esm.js +0 -3
- package/dist/nile-wysiwyg-editor/ui/preview.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/preview.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/preview.esm.js +8 -0
- package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/source-view.esm.js +225 -24
- package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/suggest-list.esm.js +9 -9
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.esm.js +115 -13
- package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-menu.esm.js +13 -12
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/nile-color-picker/nile-color-picker.d.ts +1 -0
- package/dist/src/nile-color-picker/nile-color-picker.js +1 -0
- package/dist/src/nile-color-picker/nile-color-picker.js.map +1 -1
- package/dist/src/nile-select/virtual-scroll-helper.js +9 -1
- package/dist/src/nile-select/virtual-scroll-helper.js.map +1 -1
- package/dist/src/nile-virtual-select/renderer.js +1 -0
- package/dist/src/nile-virtual-select/renderer.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/index.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/index.js +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/index.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js +3 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.d.ts +19 -4
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js +23 -18
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.d.ts +8 -3
- package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js +41 -4
- package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/style-block-view.d.ts +17 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/style-block-view.js +174 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/style-block-view.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js +33 -2
- package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/pm.test.js +49 -8
- package/dist/src/nile-wysiwyg-editor/engine/pm.test.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.d.ts +12 -0
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.js +76 -8
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/schema.d.ts +2 -0
- package/dist/src/nile-wysiwyg-editor/engine/schema.js +154 -2
- package/dist/src/nile-wysiwyg-editor/engine/schema.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/index.d.ts +5 -0
- package/dist/src/nile-wysiwyg-editor/index.js +3 -0
- package/dist/src/nile-wysiwyg-editor/index.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/math/katex-renderer.d.ts +38 -0
- package/dist/src/nile-wysiwyg-editor/math/katex-renderer.js +40 -0
- package/dist/src/nile-wysiwyg-editor/math/katex-renderer.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js +22 -3
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.d.ts +36 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js +125 -8
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js +5 -2
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.d.ts +2 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.d.ts +55 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.js +79 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js +33 -54
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.d.ts +50 -2
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js +158 -102
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/types.d.ts +4 -2
- package/dist/src/nile-wysiwyg-editor/types.js +2 -2
- package/dist/src/nile-wysiwyg-editor/types.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/bubble-item.d.ts +48 -0
- package/dist/src/nile-wysiwyg-editor/ui/bubble-item.js +69 -0
- package/dist/src/nile-wysiwyg-editor/ui/bubble-item.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.d.ts +17 -2
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js +128 -35
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.d.ts +2 -1
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js +4 -3
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/math-panel.js +0 -3
- package/dist/src/nile-wysiwyg-editor/ui/math-panel.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/preview.d.ts +20 -0
- package/dist/src/nile-wysiwyg-editor/ui/preview.js +44 -0
- package/dist/src/nile-wysiwyg-editor/ui/preview.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/source-view.d.ts +35 -3
- package/dist/src/nile-wysiwyg-editor/ui/source-view.js +337 -24
- package/dist/src/nile-wysiwyg-editor/ui/source-view.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js +9 -0
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.d.ts +13 -2
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js +166 -10
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.js +10 -8
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/xml-25c4108a.esm.js +1 -0
- package/dist/xml-90fd974e.cjs.js +2 -0
- package/dist/xml-90fd974e.cjs.js.map +1 -0
- package/package.json +2 -1
- package/rollup.config.js +1 -0
- package/src/index.ts +8 -0
- package/src/nile-color-picker/nile-color-picker.ts +1 -0
- package/src/nile-select/virtual-scroll-helper.ts +9 -1
- package/src/nile-virtual-select/renderer.ts +1 -1
- package/src/nile-wysiwyg-editor/engine/index.ts +1 -0
- package/src/nile-wysiwyg-editor/engine/plugins/embed-view.ts +3 -0
- package/src/nile-wysiwyg-editor/engine/plugins/embeds.ts +26 -20
- package/src/nile-wysiwyg-editor/engine/plugins/math-view.ts +45 -4
- package/src/nile-wysiwyg-editor/engine/plugins/style-block-view.ts +178 -0
- package/src/nile-wysiwyg-editor/engine/plugins/tables.ts +34 -2
- package/src/nile-wysiwyg-editor/engine/pm.test.ts +58 -8
- package/src/nile-wysiwyg-editor/engine/sanitize.ts +71 -8
- package/src/nile-wysiwyg-editor/engine/schema.ts +158 -7
- package/src/nile-wysiwyg-editor/index.ts +5 -0
- package/src/nile-wysiwyg-editor/math/katex-renderer.ts +59 -0
- package/src/nile-wysiwyg-editor/math/katex.d.ts +17 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.ts +22 -3
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.ts +5 -2
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.ts +142 -7
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.ts +2 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.ts +68 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.ts +33 -54
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.ts +202 -108
- package/src/nile-wysiwyg-editor/types.ts +6 -4
- package/src/nile-wysiwyg-editor/ui/bubble-item.ts +60 -0
- package/src/nile-wysiwyg-editor/ui/bubble-menu.ts +147 -40
- package/src/nile-wysiwyg-editor/ui/embed-panel.ts +4 -3
- package/src/nile-wysiwyg-editor/ui/math-panel.ts +0 -3
- package/src/nile-wysiwyg-editor/ui/preview.ts +42 -0
- package/src/nile-wysiwyg-editor/ui/source-view.ts +339 -31
- package/src/nile-wysiwyg-editor/ui/suggest-list.ts +10 -0
- package/src/nile-wysiwyg-editor/ui/table-grid-picker.ts +160 -10
- package/src/nile-wysiwyg-editor/ui/table-menu.ts +10 -8
- package/vscode-html-custom-data.json +75 -6
- package/dist/index-df196058.cjs.js.map +0 -1
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
|
|
2
|
+
* Lazy-loaded KaTeX module (loaded once, only when the first formula renders,
|
|
3
|
+
* so editors without math pay nothing). Resolves to the katex default export.
|
|
4
|
+
*/
|
|
5
|
+
let katexPromise = null;
|
|
6
|
+
function loadKatex() {
|
|
7
|
+
if (!katexPromise) {
|
|
8
|
+
katexPromise = import('katex').then(m => m.default);
|
|
9
|
+
}
|
|
10
|
+
return katexPromise;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Inline atom rendering a math formula. Precedence:
|
|
14
|
+
* 1. A host-supplied `mathRenderer` (full control — e.g. KaTeX HTML, MathJax).
|
|
15
|
+
* 2. The built-in default: KaTeX compiled to **MathML**, which browsers render
|
|
16
|
+
* natively with no stylesheet or web fonts (works cleanly inside the
|
|
17
|
+
* editor's shadow root).
|
|
18
|
+
* 3. If KaTeX can't load / parse, the raw LaTeX stays visible.
|
|
5
19
|
*/
|
|
6
20
|
export class MathView {
|
|
7
21
|
constructor(node, renderer) {
|
|
22
|
+
/** Bumps on every render so a slow async KaTeX result for a stale node is dropped. */
|
|
23
|
+
this.token = 0;
|
|
8
24
|
this.renderer = renderer;
|
|
9
25
|
this.dom = document.createElement('span');
|
|
10
26
|
this.dom.contentEditable = 'false';
|
|
@@ -20,6 +36,8 @@ export class MathView {
|
|
|
20
36
|
this.dom.setAttribute('data-math', latex);
|
|
21
37
|
this.dom.setAttribute('data-math-display', display);
|
|
22
38
|
this.dom.title = latex;
|
|
39
|
+
const token = ++this.token;
|
|
40
|
+
// 1. Host renderer wins.
|
|
23
41
|
if (this.renderer) {
|
|
24
42
|
try {
|
|
25
43
|
// Host renderer output is trusted (same trust level as uploadHandler).
|
|
@@ -27,10 +45,29 @@ export class MathView {
|
|
|
27
45
|
return;
|
|
28
46
|
}
|
|
29
47
|
catch {
|
|
30
|
-
// Fall
|
|
48
|
+
// Fall through to the built-in renderer.
|
|
31
49
|
}
|
|
32
50
|
}
|
|
51
|
+
// 2. Built-in KaTeX → MathML. Show raw LaTeX until the (lazy) engine loads.
|
|
33
52
|
this.dom.textContent = latex;
|
|
53
|
+
loadKatex()
|
|
54
|
+
.then(katex => {
|
|
55
|
+
if (token !== this.token)
|
|
56
|
+
return; // node changed while loading
|
|
57
|
+
try {
|
|
58
|
+
this.dom.innerHTML = katex.renderToString(latex, {
|
|
59
|
+
output: 'mathml',
|
|
60
|
+
displayMode: display === 'block',
|
|
61
|
+
throwOnError: false,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
this.dom.textContent = latex;
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
.catch(() => {
|
|
69
|
+
/* KaTeX unavailable — keep the raw LaTeX already shown. */
|
|
70
|
+
});
|
|
34
71
|
}
|
|
35
72
|
update(node) {
|
|
36
73
|
if (node.type.name !== 'math')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"math-view.js","sourceRoot":"","sources":["../../../../../src/nile-wysiwyg-editor/engine/plugins/math-view.ts"],"names":[],"mappings":"AAMA
|
|
1
|
+
{"version":3,"file":"math-view.js","sourceRoot":"","sources":["../../../../../src/nile-wysiwyg-editor/engine/plugins/math-view.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,IAAI,YAAY,GACd,IAAI,CAAC;AACP,SAAS,SAAS;IAChB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,QAAQ;IAQnB,YAAY,IAAY,EAAE,QAAuB;QAHjD,sFAAsF;QAC9E,UAAK,GAAG,CAAC,CAAC;QAGhB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,CAAC,eAAe,GAAG,OAAO,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAEO,MAAM,CAAC,IAAY;QACzB,MAAM,KAAK,GAAI,IAAI,CAAC,KAAK,CAAC,KAAgB,IAAI,EAAE,CAAC;QACjD,MAAM,OAAO,GAAI,IAAI,CAAC,KAAK,CAAC,OAA8B,IAAI,QAAQ,CAAC;QACvE,IAAI,CAAC,GAAG,CAAC,SAAS;YAChB,OAAO,KAAK,OAAO;gBACjB,CAAC,CAAC,8CAA8C;gBAChD,CAAC,CAAC,oBAAoB,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC;QAEvB,MAAM,KAAK,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC;QAE3B,yBAAyB;QACzB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,uEAAuE;gBACvE,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBACnD,OAAO;YACT,CAAC;YAAC,MAAM,CAAC;gBACP,yCAAyC;YAC3C,CAAC;QACH,CAAC;QAED,4EAA4E;QAC5E,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,KAAK,CAAC;QAC7B,SAAS,EAAE;aACR,IAAI,CAAC,KAAK,CAAC,EAAE;YACZ,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK;gBAAE,OAAO,CAAC,6BAA6B;YAC/D,IAAI,CAAC;gBACH,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,cAAc,CAAC,KAAK,EAAE;oBAC/C,MAAM,EAAE,QAAQ;oBAChB,WAAW,EAAE,OAAO,KAAK,OAAO;oBAChC,YAAY,EAAE,KAAK;iBACpB,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,KAAK,CAAC;YAC/B,CAAC;QACH,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,2DAA2D;QAC7D,CAAC,CAAC,CAAC;IACP,CAAC;IAED,MAAM,CAAC,IAAY;QACjB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,KAAK,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,0EAA0E;IAC1E,cAAc;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;CACF","sourcesContent":["import { Node as PMNode } from 'prosemirror-model';\nimport { NodeView } from 'prosemirror-view';\n\n/** Turns a LaTeX string into HTML (KaTeX/MathJax/MathML). Host-provided. */\nexport type MathRenderer = (latex: string, display: 'inline' | 'block') => string;\n\n/**\n * Lazy-loaded KaTeX module (loaded once, only when the first formula renders,\n * so editors without math pay nothing). Resolves to the katex default export.\n */\nlet katexPromise: Promise<{ renderToString(tex: string, opts?: Record<string, unknown>): string }> | null =\n null;\nfunction loadKatex() {\n if (!katexPromise) {\n katexPromise = import('katex').then(m => m.default);\n }\n return katexPromise;\n}\n\n/**\n * Inline atom rendering a math formula. Precedence:\n * 1. A host-supplied `mathRenderer` (full control — e.g. KaTeX HTML, MathJax).\n * 2. The built-in default: KaTeX compiled to **MathML**, which browsers render\n * natively with no stylesheet or web fonts (works cleanly inside the\n * editor's shadow root).\n * 3. If KaTeX can't load / parse, the raw LaTeX stays visible.\n */\nexport class MathView implements NodeView {\n dom: HTMLSpanElement;\n\n private renderer?: MathRenderer;\n\n /** Bumps on every render so a slow async KaTeX result for a stale node is dropped. */\n private token = 0;\n\n constructor(node: PMNode, renderer?: MathRenderer) {\n this.renderer = renderer;\n this.dom = document.createElement('span');\n this.dom.contentEditable = 'false';\n this.render(node);\n }\n\n private render(node: PMNode): void {\n const latex = (node.attrs.latex as string) ?? '';\n const display = (node.attrs.display as 'inline' | 'block') ?? 'inline';\n this.dom.className =\n display === 'block'\n ? 'nile-wysiwyg__math nile-wysiwyg__math--block'\n : 'nile-wysiwyg__math';\n this.dom.setAttribute('data-math', latex);\n this.dom.setAttribute('data-math-display', display);\n this.dom.title = latex;\n\n const token = ++this.token;\n\n // 1. Host renderer wins.\n if (this.renderer) {\n try {\n // Host renderer output is trusted (same trust level as uploadHandler).\n this.dom.innerHTML = this.renderer(latex, display);\n return;\n } catch {\n // Fall through to the built-in renderer.\n }\n }\n\n // 2. Built-in KaTeX → MathML. Show raw LaTeX until the (lazy) engine loads.\n this.dom.textContent = latex;\n loadKatex()\n .then(katex => {\n if (token !== this.token) return; // node changed while loading\n try {\n this.dom.innerHTML = katex.renderToString(latex, {\n output: 'mathml',\n displayMode: display === 'block',\n throwOnError: false,\n });\n } catch {\n this.dom.textContent = latex;\n }\n })\n .catch(() => {\n /* KaTeX unavailable — keep the raw LaTeX already shown. */\n });\n }\n\n update(node: PMNode): boolean {\n if (node.type.name !== 'math') return false;\n this.render(node);\n return true;\n }\n\n /** Atom node: ProseMirror owns selection; never read our subtree back. */\n ignoreMutation(): boolean {\n return true;\n }\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Node as PMNode } from 'prosemirror-model';
|
|
2
|
+
import { NodeView } from 'prosemirror-view';
|
|
3
|
+
/**
|
|
4
|
+
* Node view for an author `<style>` block. It injects the CSS live — scoped to
|
|
5
|
+
* the content root — so the styles apply to the document as authored, with no
|
|
6
|
+
* visible chip. The block itself renders nothing (a hidden `<style>` host); it
|
|
7
|
+
* round-trips to `<style>` in the output via the schema's toDOM, and the same
|
|
8
|
+
* raw CSS is what the HTML-source preview renders.
|
|
9
|
+
*/
|
|
10
|
+
export declare class StyleBlockView implements NodeView {
|
|
11
|
+
private node;
|
|
12
|
+
dom: HTMLElement;
|
|
13
|
+
private styleEl;
|
|
14
|
+
constructor(node: PMNode);
|
|
15
|
+
update(node: PMNode): boolean;
|
|
16
|
+
ignoreMutation(): boolean;
|
|
17
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/** Content root the editor mounts the document under. Author CSS is scoped to
|
|
2
|
+
* it so a rule such as `div { background: green }` styles the content only and
|
|
3
|
+
* can never paint the editor chrome (toolbar, panels, surface) that shares the
|
|
4
|
+
* same shadow root. */
|
|
5
|
+
const CONTENT_SCOPE = '.ProseMirror';
|
|
6
|
+
/** Advances past a quoted string starting at `i` (the opening quote), honoring
|
|
7
|
+
* backslash escapes; returns the index just after the closing quote. */
|
|
8
|
+
function skipString(css, i) {
|
|
9
|
+
const quote = css[i];
|
|
10
|
+
i += 1;
|
|
11
|
+
while (i < css.length) {
|
|
12
|
+
if (css[i] === '\\') {
|
|
13
|
+
i += 2;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (css[i] === quote)
|
|
17
|
+
return i + 1;
|
|
18
|
+
i += 1;
|
|
19
|
+
}
|
|
20
|
+
return i;
|
|
21
|
+
}
|
|
22
|
+
/** Splits on a top-level separator, ignoring ones inside (), [] or strings. */
|
|
23
|
+
function splitTopLevel(str, sep) {
|
|
24
|
+
const parts = [];
|
|
25
|
+
let depth = 0;
|
|
26
|
+
let buf = '';
|
|
27
|
+
for (let i = 0; i < str.length; i += 1) {
|
|
28
|
+
const c = str[i];
|
|
29
|
+
if (c === '"' || c === "'") {
|
|
30
|
+
const end = skipString(str, i);
|
|
31
|
+
buf += str.slice(i, end);
|
|
32
|
+
i = end - 1;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (c === '(' || c === '[')
|
|
36
|
+
depth += 1;
|
|
37
|
+
else if (c === ')' || c === ']')
|
|
38
|
+
depth -= 1;
|
|
39
|
+
if (c === sep && depth === 0) {
|
|
40
|
+
parts.push(buf);
|
|
41
|
+
buf = '';
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
buf += c;
|
|
45
|
+
}
|
|
46
|
+
parts.push(buf);
|
|
47
|
+
return parts;
|
|
48
|
+
}
|
|
49
|
+
/** Confines one complex selector to the content root. `html`/`body`/`:root`
|
|
50
|
+
* map onto the root itself. */
|
|
51
|
+
function scopeSelector(selector, scope) {
|
|
52
|
+
const sel = selector.trim();
|
|
53
|
+
if (!sel)
|
|
54
|
+
return sel;
|
|
55
|
+
if (/^(?:html|body|:root)$/i.test(sel))
|
|
56
|
+
return scope;
|
|
57
|
+
const root = sel.match(/^(?:html|body|:root)\b\s*/i);
|
|
58
|
+
return root ? `${scope} ${sel.slice(root[0].length)}`.trim() : `${scope} ${sel}`;
|
|
59
|
+
}
|
|
60
|
+
/** Confines a comma-separated selector list to the content root. */
|
|
61
|
+
function scopeSelectorList(list, scope) {
|
|
62
|
+
return splitTopLevel(list, ',')
|
|
63
|
+
.map(part => scopeSelector(part, scope))
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.join(', ');
|
|
66
|
+
}
|
|
67
|
+
const NESTED_AT_RULES = new Set(['media', 'supports', 'container', 'layer', 'scope']);
|
|
68
|
+
/**
|
|
69
|
+
* Scopes every rule in a CSS block to the content root by prefixing selectors,
|
|
70
|
+
* keeping each declaration block **verbatim**. Declarations are never re-parsed
|
|
71
|
+
* (a CSSOM round-trip drops `var()` inside shorthands, e.g. gradient text), and
|
|
72
|
+
* strings/nested braces are tracked so `content: "}"` can't derail it. Nested
|
|
73
|
+
* conditionals (`@media`, `@supports`, …) recurse; other at-rules
|
|
74
|
+
* (`@keyframes`, `@font-face`, …) pass through untouched.
|
|
75
|
+
*/
|
|
76
|
+
function scopeBlock(css, scope) {
|
|
77
|
+
let out = '';
|
|
78
|
+
let i = 0;
|
|
79
|
+
const n = css.length;
|
|
80
|
+
while (i < n) {
|
|
81
|
+
let j = i;
|
|
82
|
+
while (j < n) {
|
|
83
|
+
const c = css[j];
|
|
84
|
+
if (c === '"' || c === "'") {
|
|
85
|
+
j = skipString(css, j);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (c === '{' || c === ';')
|
|
89
|
+
break;
|
|
90
|
+
j += 1;
|
|
91
|
+
}
|
|
92
|
+
if (j >= n) {
|
|
93
|
+
out += css.slice(i);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
if (css[j] === ';') {
|
|
97
|
+
// A statement at-rule (e.g. a stray @charset) — keep verbatim.
|
|
98
|
+
out += css.slice(i, j + 1);
|
|
99
|
+
i = j + 1;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const prelude = css.slice(i, j).trim();
|
|
103
|
+
let depth = 1;
|
|
104
|
+
let k = j + 1;
|
|
105
|
+
while (k < n && depth > 0) {
|
|
106
|
+
const c = css[k];
|
|
107
|
+
if (c === '"' || c === "'") {
|
|
108
|
+
k = skipString(css, k);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (c === '{')
|
|
112
|
+
depth += 1;
|
|
113
|
+
else if (c === '}')
|
|
114
|
+
depth -= 1;
|
|
115
|
+
k += 1;
|
|
116
|
+
}
|
|
117
|
+
const body = css.slice(j + 1, k - 1);
|
|
118
|
+
if (prelude.startsWith('@')) {
|
|
119
|
+
const name = (prelude.match(/^@([a-z-]+)/i)?.[1] ?? '').toLowerCase();
|
|
120
|
+
out += NESTED_AT_RULES.has(name)
|
|
121
|
+
? `${prelude} {${scopeBlock(body, scope)}}`
|
|
122
|
+
: `${prelude} {${body}}`;
|
|
123
|
+
}
|
|
124
|
+
else if (prelude) {
|
|
125
|
+
out += `${scopeSelectorList(prelude, scope)} {${body}}`;
|
|
126
|
+
}
|
|
127
|
+
i = k;
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
/** Returns author CSS confined to the content root. */
|
|
132
|
+
function scopeCss(css, scope) {
|
|
133
|
+
if (!css.trim())
|
|
134
|
+
return '';
|
|
135
|
+
try {
|
|
136
|
+
return scopeBlock(css, scope);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return css;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Node view for an author `<style>` block. It injects the CSS live — scoped to
|
|
144
|
+
* the content root — so the styles apply to the document as authored, with no
|
|
145
|
+
* visible chip. The block itself renders nothing (a hidden `<style>` host); it
|
|
146
|
+
* round-trips to `<style>` in the output via the schema's toDOM, and the same
|
|
147
|
+
* raw CSS is what the HTML-source preview renders.
|
|
148
|
+
*/
|
|
149
|
+
export class StyleBlockView {
|
|
150
|
+
constructor(node) {
|
|
151
|
+
this.node = node;
|
|
152
|
+
this.dom = document.createElement('div');
|
|
153
|
+
this.dom.className = 'nile-wysiwyg__style-block';
|
|
154
|
+
this.dom.contentEditable = 'false';
|
|
155
|
+
// A <style> applies regardless of its (display:none) host, so the block is
|
|
156
|
+
// invisible yet its rules take effect on the content.
|
|
157
|
+
this.styleEl = document.createElement('style');
|
|
158
|
+
this.styleEl.textContent = scopeCss(node.attrs.css, CONTENT_SCOPE);
|
|
159
|
+
this.dom.append(this.styleEl);
|
|
160
|
+
}
|
|
161
|
+
update(node) {
|
|
162
|
+
if (node.type !== this.node.type)
|
|
163
|
+
return false;
|
|
164
|
+
if (node.attrs.css !== this.node.attrs.css) {
|
|
165
|
+
this.styleEl.textContent = scopeCss(node.attrs.css, CONTENT_SCOPE);
|
|
166
|
+
}
|
|
167
|
+
this.node = node;
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
ignoreMutation() {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=style-block-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style-block-view.js","sourceRoot":"","sources":["../../../../../src/nile-wysiwyg-editor/engine/plugins/style-block-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { Node as PMNode } from 'prosemirror-model';\nimport { NodeView } from 'prosemirror-view';\n\n/** Content root the editor mounts the document under. Author CSS is scoped to\n * it so a rule such as `div { background: green }` styles the content only and\n * can never paint the editor chrome (toolbar, panels, surface) that shares the\n * same shadow root. */\nconst CONTENT_SCOPE = '.ProseMirror';\n\n/** Advances past a quoted string starting at `i` (the opening quote), honoring\n * backslash escapes; returns the index just after the closing quote. */\nfunction skipString(css: string, i: number): number {\n const quote = css[i];\n i += 1;\n while (i < css.length) {\n if (css[i] === '\\\\') {\n i += 2;\n continue;\n }\n if (css[i] === quote) return i + 1;\n i += 1;\n }\n return i;\n}\n\n/** Splits on a top-level separator, ignoring ones inside (), [] or strings. */\nfunction splitTopLevel(str: string, sep: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let buf = '';\n for (let i = 0; i < str.length; i += 1) {\n const c = str[i];\n if (c === '\"' || c === \"'\") {\n const end = skipString(str, i);\n buf += str.slice(i, end);\n i = end - 1;\n continue;\n }\n if (c === '(' || c === '[') depth += 1;\n else if (c === ')' || c === ']') depth -= 1;\n if (c === sep && depth === 0) {\n parts.push(buf);\n buf = '';\n continue;\n }\n buf += c;\n }\n parts.push(buf);\n return parts;\n}\n\n/** Confines one complex selector to the content root. `html`/`body`/`:root`\n * map onto the root itself. */\nfunction scopeSelector(selector: string, scope: string): string {\n const sel = selector.trim();\n if (!sel) return sel;\n if (/^(?:html|body|:root)$/i.test(sel)) return scope;\n const root = sel.match(/^(?:html|body|:root)\\b\\s*/i);\n return root ? `${scope} ${sel.slice(root[0].length)}`.trim() : `${scope} ${sel}`;\n}\n\n/** Confines a comma-separated selector list to the content root. */\nfunction scopeSelectorList(list: string, scope: string): string {\n return splitTopLevel(list, ',')\n .map(part => scopeSelector(part, scope))\n .filter(Boolean)\n .join(', ');\n}\n\nconst NESTED_AT_RULES = new Set(['media', 'supports', 'container', 'layer', 'scope']);\n\n/**\n * Scopes every rule in a CSS block to the content root by prefixing selectors,\n * keeping each declaration block **verbatim**. Declarations are never re-parsed\n * (a CSSOM round-trip drops `var()` inside shorthands, e.g. gradient text), and\n * strings/nested braces are tracked so `content: \"}\"` can't derail it. Nested\n * conditionals (`@media`, `@supports`, …) recurse; other at-rules\n * (`@keyframes`, `@font-face`, …) pass through untouched.\n */\nfunction scopeBlock(css: string, scope: string): string {\n let out = '';\n let i = 0;\n const n = css.length;\n while (i < n) {\n let j = i;\n while (j < n) {\n const c = css[j];\n if (c === '\"' || c === \"'\") {\n j = skipString(css, j);\n continue;\n }\n if (c === '{' || c === ';') break;\n j += 1;\n }\n if (j >= n) {\n out += css.slice(i);\n break;\n }\n if (css[j] === ';') {\n // A statement at-rule (e.g. a stray @charset) — keep verbatim.\n out += css.slice(i, j + 1);\n i = j + 1;\n continue;\n }\n const prelude = css.slice(i, j).trim();\n let depth = 1;\n let k = j + 1;\n while (k < n && depth > 0) {\n const c = css[k];\n if (c === '\"' || c === \"'\") {\n k = skipString(css, k);\n continue;\n }\n if (c === '{') depth += 1;\n else if (c === '}') depth -= 1;\n k += 1;\n }\n const body = css.slice(j + 1, k - 1);\n if (prelude.startsWith('@')) {\n const name = (prelude.match(/^@([a-z-]+)/i)?.[1] ?? '').toLowerCase();\n out += NESTED_AT_RULES.has(name)\n ? `${prelude} {${scopeBlock(body, scope)}}`\n : `${prelude} {${body}}`;\n } else if (prelude) {\n out += `${scopeSelectorList(prelude, scope)} {${body}}`;\n }\n i = k;\n }\n return out;\n}\n\n/** Returns author CSS confined to the content root. */\nfunction scopeCss(css: string, scope: string): string {\n if (!css.trim()) return '';\n try {\n return scopeBlock(css, scope);\n } catch {\n return css;\n }\n}\n\n/**\n * Node view for an author `<style>` block. It injects the CSS live — scoped to\n * the content root — so the styles apply to the document as authored, with no\n * visible chip. The block itself renders nothing (a hidden `<style>` host); it\n * round-trips to `<style>` in the output via the schema's toDOM, and the same\n * raw CSS is what the HTML-source preview renders.\n */\nexport class StyleBlockView implements NodeView {\n dom: HTMLElement;\n\n private styleEl: HTMLStyleElement;\n\n constructor(private node: PMNode) {\n this.dom = document.createElement('div');\n this.dom.className = 'nile-wysiwyg__style-block';\n this.dom.contentEditable = 'false';\n\n // A <style> applies regardless of its (display:none) host, so the block is\n // invisible yet its rules take effect on the content.\n this.styleEl = document.createElement('style');\n this.styleEl.textContent = scopeCss(node.attrs.css, CONTENT_SCOPE);\n this.dom.append(this.styleEl);\n }\n\n update(node: PMNode): boolean {\n if (node.type !== this.node.type) return false;\n if (node.attrs.css !== this.node.attrs.css) {\n this.styleEl.textContent = scopeCss(node.attrs.css, CONTENT_SCOPE);\n }\n this.node = node;\n return true;\n }\n\n ignoreMutation(): boolean {\n return true;\n }\n}\n"]}
|
|
@@ -1,4 +1,32 @@
|
|
|
1
|
-
import { tableEditing, columnResizing } from 'prosemirror-tables';
|
|
1
|
+
import { tableEditing, columnResizing, TableView } from 'prosemirror-tables';
|
|
2
|
+
/**
|
|
3
|
+
* TableView that also reflects the author's `class`/`id` onto the inner
|
|
4
|
+
* `<table>` element (the base TableView leaves it bare and puts its own class on
|
|
5
|
+
* the wrapper div). The attributes round-trip via the schema's toDOM on export;
|
|
6
|
+
* this makes `table.grid …` style rules match in the live editor too. Passed to
|
|
7
|
+
* `columnResizing` as its `View`, so column resizing keeps working.
|
|
8
|
+
*/
|
|
9
|
+
class HtmlAttrTableView extends TableView {
|
|
10
|
+
constructor(node, defaultCellMinWidth) {
|
|
11
|
+
super(node, defaultCellMinWidth);
|
|
12
|
+
this.applyHtmlAttrs(node);
|
|
13
|
+
}
|
|
14
|
+
applyHtmlAttrs(node) {
|
|
15
|
+
const htmlClass = node.attrs.htmlClass;
|
|
16
|
+
const htmlId = node.attrs.htmlId;
|
|
17
|
+
this.table.className = htmlClass ?? '';
|
|
18
|
+
if (htmlId)
|
|
19
|
+
this.table.id = htmlId;
|
|
20
|
+
else
|
|
21
|
+
this.table.removeAttribute('id');
|
|
22
|
+
}
|
|
23
|
+
update(node) {
|
|
24
|
+
const updated = super.update(node);
|
|
25
|
+
if (updated)
|
|
26
|
+
this.applyHtmlAttrs(node);
|
|
27
|
+
return updated;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
2
30
|
/**
|
|
3
31
|
* prosemirror-tables wiring: cell selections, fix-tables normalization, and
|
|
4
32
|
* draggable column resizing (widths persist as `data-colwidth`). The CSS
|
|
@@ -6,6 +34,9 @@ import { tableEditing, columnResizing } from 'prosemirror-tables';
|
|
|
6
34
|
* is inlined in nile-wysiwyg-editor.css.ts.
|
|
7
35
|
*/
|
|
8
36
|
export function tablePlugins() {
|
|
9
|
-
return [
|
|
37
|
+
return [
|
|
38
|
+
columnResizing({ View: HtmlAttrTableView }),
|
|
39
|
+
tableEditing({ allowTableNodeSelection: true }),
|
|
40
|
+
];
|
|
10
41
|
}
|
|
11
42
|
//# sourceMappingURL=tables.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.js","sourceRoot":"","sources":["../../../../../src/nile-wysiwyg-editor/engine/plugins/tables.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tables.js","sourceRoot":"","sources":["../../../../../src/nile-wysiwyg-editor/engine/plugins/tables.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE7E;;;;;;GAMG;AACH,MAAM,iBAAkB,SAAQ,SAAS;IACvC,YAAY,IAAY,EAAE,mBAA2B;QACnD,KAAK,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;QACjC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAEO,cAAc,CAAC,IAAY;QACjC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAA0B,CAAC;QACxD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAuB,CAAC;QAClD,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,SAAS,IAAI,EAAE,CAAC;QACvC,IAAI,MAAM;YAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC;;YAC9B,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,CAAC,IAAY;QACjB,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,OAAO;YAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,cAAc,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;QAC3C,YAAY,CAAC,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC;KAChD,CAAC;AACJ,CAAC","sourcesContent":["import { Plugin } from 'prosemirror-state';\nimport { Node as PMNode } from 'prosemirror-model';\nimport { tableEditing, columnResizing, TableView } from 'prosemirror-tables';\n\n/**\n * TableView that also reflects the author's `class`/`id` onto the inner\n * `<table>` element (the base TableView leaves it bare and puts its own class on\n * the wrapper div). The attributes round-trip via the schema's toDOM on export;\n * this makes `table.grid …` style rules match in the live editor too. Passed to\n * `columnResizing` as its `View`, so column resizing keeps working.\n */\nclass HtmlAttrTableView extends TableView {\n constructor(node: PMNode, defaultCellMinWidth: number) {\n super(node, defaultCellMinWidth);\n this.applyHtmlAttrs(node);\n }\n\n private applyHtmlAttrs(node: PMNode): void {\n const htmlClass = node.attrs.htmlClass as string | null;\n const htmlId = node.attrs.htmlId as string | null;\n this.table.className = htmlClass ?? '';\n if (htmlId) this.table.id = htmlId;\n else this.table.removeAttribute('id');\n }\n\n update(node: PMNode): boolean {\n const updated = super.update(node);\n if (updated) this.applyHtmlAttrs(node);\n return updated;\n }\n}\n\n/**\n * prosemirror-tables wiring: cell selections, fix-tables normalization, and\n * draggable column resizing (widths persist as `data-colwidth`). The CSS\n * these plugins rely on (.selectedCell, .column-resize-handle, .resize-cursor)\n * is inlined in nile-wysiwyg-editor.css.ts.\n */\nexport function tablePlugins(): Plugin[] {\n return [\n columnResizing({ View: HtmlAttrTableView }),\n tableEditing({ allowTableNodeSelection: true }),\n ];\n}\n"]}
|
|
@@ -36,10 +36,26 @@ describe('sanitizeHtml', () => {
|
|
|
36
36
|
it('unwraps unknown elements but keeps their content', () => {
|
|
37
37
|
expect(sanitizeHtml('<article><p>kept</p></article>')).to.equal('<p>kept</p>');
|
|
38
38
|
});
|
|
39
|
-
it('drops iframes
|
|
40
|
-
const out = sanitizeHtml('<iframe src="x"></iframe><
|
|
39
|
+
it('drops non-https iframes and forms with content', () => {
|
|
40
|
+
const out = sanitizeHtml('<iframe src="x"></iframe><form><input></form><p>ok</p>');
|
|
41
41
|
expect(out).to.equal('<p>ok</p>');
|
|
42
42
|
});
|
|
43
|
+
it('keeps <style> blocks and sanitizes their CSS', () => {
|
|
44
|
+
const out = sanitizeHtml('<style>@import url("http://evil/x.css"); p{color:red} a{behavior:url(x.htc)} ' +
|
|
45
|
+
'b{width:expression(alert(1))} c{background:url(http://evil/x.png)}</style><p>ok</p>');
|
|
46
|
+
expect(out).to.include('<style>');
|
|
47
|
+
expect(out).to.include('color:red');
|
|
48
|
+
expect(out).to.include('<p>ok</p>');
|
|
49
|
+
expect(out).to.not.match(/@import/i);
|
|
50
|
+
expect(out).to.not.match(/expression\s*\(/i);
|
|
51
|
+
expect(out).to.not.match(/behavior\s*:/i);
|
|
52
|
+
expect(out).to.not.match(/url\(\s*['"]?http/i);
|
|
53
|
+
});
|
|
54
|
+
it('keeps safe data-image and #fragment url() in <style>', () => {
|
|
55
|
+
const out = sanitizeHtml('<style>a{background:url(data:image/png;base64,AAAA)} b{clip-path:url(#f)}</style>');
|
|
56
|
+
expect(out).to.include('url(data:image/png');
|
|
57
|
+
expect(out).to.include('url(#f)');
|
|
58
|
+
});
|
|
43
59
|
});
|
|
44
60
|
describe('serializer round-trip', () => {
|
|
45
61
|
const cases = [
|
|
@@ -67,6 +83,18 @@ describe('serializer round-trip', () => {
|
|
|
67
83
|
const doc = htmlToDoc(defaultSchema, '<p><span style="font-weight: 700">x</span></p>');
|
|
68
84
|
expect(docToHtml(doc)).to.include('<strong>');
|
|
69
85
|
});
|
|
86
|
+
it('round-trips an author <style> block through the schema', () => {
|
|
87
|
+
const doc = htmlToDoc(defaultSchema, '<style>p{color:red}</style><p>hi</p>');
|
|
88
|
+
expect(doc.firstChild.type.name).to.equal('styleBlock');
|
|
89
|
+
const out = docToHtml(doc);
|
|
90
|
+
expect(out).to.include('<style>');
|
|
91
|
+
expect(out).to.match(/color:\s*red/i);
|
|
92
|
+
expect(out).to.include('<p>hi</p>');
|
|
93
|
+
// Malicious CSS is neutralized on the round-trip, not just at the surface.
|
|
94
|
+
const evil = docToHtml(htmlToDoc(defaultSchema, '<style>@import url(http://evil);x{color:blue}</style><p>ok</p>'));
|
|
95
|
+
expect(evil).to.include('<style>');
|
|
96
|
+
expect(evil).to.not.match(/@import/i);
|
|
97
|
+
});
|
|
70
98
|
it('detects empty documents', () => {
|
|
71
99
|
expect(isDocEmpty(htmlToDoc(defaultSchema, ''))).to.equal(true);
|
|
72
100
|
expect(isDocEmpty(htmlToDoc(defaultSchema, '<p></p>'))).to.equal(true);
|
|
@@ -385,17 +413,30 @@ describe('embeds', () => {
|
|
|
385
413
|
});
|
|
386
414
|
expect(toEmbedUrl('https://www.loom.com/share/abcdef0123456789abcdef0123456789').provider).to.equal('loom');
|
|
387
415
|
});
|
|
388
|
-
it('
|
|
389
|
-
expect(toEmbedUrl('https://
|
|
416
|
+
it('accepts any https URL as a generic embed, rejects non-https', () => {
|
|
417
|
+
expect(toEmbedUrl('https://example.com/player/x')).to.deep.equal({
|
|
418
|
+
src: 'https://example.com/player/x',
|
|
419
|
+
provider: 'generic',
|
|
420
|
+
});
|
|
421
|
+
// Lookalike hosts are no longer canonicalized as a provider, just generic.
|
|
422
|
+
expect(toEmbedUrl('https://youtube.com.evil.com/watch?v=x').provider).to.equal('generic');
|
|
423
|
+
// Non-https is still refused.
|
|
390
424
|
expect(toEmbedUrl('http://www.youtube.com/watch?v=dQw4w9WgXcQ')).to.equal(null);
|
|
391
425
|
expect(toEmbedUrl('javascript:alert(1)')).to.equal(null);
|
|
392
|
-
expect(
|
|
393
|
-
expect(isAllowedEmbedSrc('
|
|
426
|
+
expect(isAllowedEmbedSrc('https://example.com/player/x')).to.equal(true);
|
|
427
|
+
expect(isAllowedEmbedSrc('http://example.com/x')).to.equal(false);
|
|
394
428
|
});
|
|
395
|
-
it('keeps
|
|
429
|
+
it('keeps any https iframe through sanitization but force-sandboxes it', () => {
|
|
396
430
|
const good = sanitizeHtml('<div data-embed-provider="youtube"><iframe src="https://www.youtube-nocookie.com/embed/abc123xyz"></iframe></div>');
|
|
397
431
|
expect(good).to.include('youtube-nocookie.com/embed');
|
|
398
|
-
|
|
432
|
+
expect(good).to.include('sandbox=');
|
|
433
|
+
// A non-provider https iframe is kept, but sandboxed and cannot navigate the top.
|
|
434
|
+
const generic = sanitizeHtml('<iframe src="https://example.com/player"></iframe><p>ok</p>');
|
|
435
|
+
expect(generic).to.include('example.com/player');
|
|
436
|
+
expect(generic).to.include('sandbox=');
|
|
437
|
+
expect(generic).to.not.include('allow-top-navigation');
|
|
438
|
+
// Non-https iframes are still dropped entirely.
|
|
439
|
+
const evil = sanitizeHtml('<iframe src="http://evil.example.com/steal"></iframe><p>ok</p>');
|
|
399
440
|
expect(evil).to.equal('<p>ok</p>');
|
|
400
441
|
});
|
|
401
442
|
it('round-trips embed nodes', () => {
|