open-wysiwyg-editor 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +278 -14
  2. package/dist/{chunk-HKWFKMAC.cjs → chunk-BMUV6KRM.cjs} +5 -5
  3. package/dist/{chunk-HKWFKMAC.cjs.map → chunk-BMUV6KRM.cjs.map} +1 -1
  4. package/dist/{chunk-MARI3ZF6.cjs → chunk-GAOA25B3.cjs} +38 -19
  5. package/dist/chunk-GAOA25B3.cjs.map +1 -0
  6. package/dist/{chunk-F3DLZFDY.js → chunk-OD7OEERC.js} +105 -5
  7. package/dist/chunk-OD7OEERC.js.map +1 -0
  8. package/dist/{chunk-2OSX7R22.js → chunk-PFABID5F.js} +38 -19
  9. package/dist/chunk-PFABID5F.js.map +1 -0
  10. package/dist/{chunk-2WBWKTLE.js → chunk-V7KX4CBK.js} +3 -3
  11. package/dist/{chunk-2WBWKTLE.js.map → chunk-V7KX4CBK.js.map} +1 -1
  12. package/dist/{chunk-JZHAUAUJ.cjs → chunk-Z4MNC4WN.cjs} +214 -112
  13. package/dist/chunk-Z4MNC4WN.cjs.map +1 -0
  14. package/dist/content.css +420 -283
  15. package/dist/content.min.css +1 -1
  16. package/dist/{editor-BKYDbfo1.d.ts → editor-Cx1TcWt7.d.ts} +28 -4
  17. package/dist/{editor-CY40AOtO.d.cts → editor-QMoMMBL6.d.cts} +28 -4
  18. package/dist/element.cjs +22 -8
  19. package/dist/element.cjs.map +1 -1
  20. package/dist/element.d.cts +5 -3
  21. package/dist/element.d.ts +5 -3
  22. package/dist/element.js +19 -5
  23. package/dist/element.js.map +1 -1
  24. package/dist/headless.cjs +87 -87
  25. package/dist/headless.d.cts +1 -1
  26. package/dist/headless.d.ts +1 -1
  27. package/dist/headless.js +2 -2
  28. package/dist/index.cjs +108 -100
  29. package/dist/index.d.cts +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +3 -3
  32. package/dist/open-wysiwyg-editor.global.js +16 -16
  33. package/dist/style.css +1300 -1082
  34. package/dist/style.min.css +1 -1
  35. package/package.json +1 -1
  36. package/dist/chunk-2OSX7R22.js.map +0 -1
  37. package/dist/chunk-F3DLZFDY.js.map +0 -1
  38. package/dist/chunk-JZHAUAUJ.cjs.map +0 -1
  39. package/dist/chunk-MARI3ZF6.cjs.map +0 -1
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  An accessible rich text editor that works on **any website**: plain HTML/JS, React, Next.js, Preact, Vue, Nuxt, Angular, Svelte, Solid, Astro, WordPress and more. It's RTL-first, it runs under a strict Content Security Policy, and it ships its own TypeScript types. MIT licensed, with no license key, no telemetry and no cloud service.
12
12
 
13
- Built on [ProseMirror](https://prosemirror.net). Content is stored as HTML or JSON, and node and mark names match Tiptap's.
13
+ Built on [ProseMirror](https://prosemirror.net). Content is stored as HTML or JSON.
14
14
 
15
15
  ## Features
16
16
 
@@ -23,7 +23,7 @@ Built on [ProseMirror](https://prosemirror.net). Content is stored as HTML or JS
23
23
  | **Security** | DOMPurify on every input · URL allow-list · Trusted Types · No `unsafe-inline` needed |
24
24
  | **Languages** | English and Arabic built in (add your own) · Mirrored UI in RTL · `dir="auto"` on every block |
25
25
  | **Everywhere** | `<owe-editor>` web component for plain HTML and forms · Packages for React, Next.js, Preact, Vue, Nuxt, Angular, Svelte, Solid and Astro |
26
- | **Your way** | Full UI by default · Headless mode for your own UI · Custom toolbar buttons · Extensions API · Theme with CSS variables |
26
+ | **Your way** | Full UI by default · Headless mode for your own UI · Custom toolbar buttons · Extensions API · Brand it with one color or CSS variables |
27
27
 
28
28
  Every release is tested end to end in Chromium, Firefox and WebKit, under a strict CSP with Trusted Types and with axe-core accessibility checks.
29
29
 
@@ -40,10 +40,16 @@ yarn add open-wysiwyg-editor
40
40
  Or with no build step, from a CDN:
41
41
 
42
42
  ```html
43
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-wysiwyg-editor@1/dist/style.min.css" />
44
- <script src="https://cdn.jsdelivr.net/npm/open-wysiwyg-editor@1/dist/open-wysiwyg-editor.global.js"></script>
43
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-wysiwyg-editor@1.0.1/dist/style.min.css"
44
+ integrity="sha384-b7LA9M5in6uI0RSJJZ206o3E6yH2YJdquLqriMF+32aOQjNUNH7uitvYh2CebAip" crossorigin="anonymous" />
45
+ <script src="https://cdn.jsdelivr.net/npm/open-wysiwyg-editor@1.0.1/dist/open-wysiwyg-editor.global.js"
46
+ integrity="sha384-lgMQAQkEkLamVrDDDizcj9JBoeaPc6DUKFiL+F/fj7KczjRHePrgrfCGRCnXGr7D" crossorigin="anonymous"></script>
45
47
  ```
46
48
 
49
+ The URLs name an exact version and the `integrity` hashes make the browser refuse any other bytes, so a
50
+ new release or a compromised CDN can't change the code your page runs. To upgrade, change the version and
51
+ take the new hashes from the release notes, or compute them: `curl -s <url> | openssl dgst -sha384 -binary | openssl base64 -A`.
52
+
47
53
  ### Pick your framework
48
54
 
49
55
  The core package works everywhere. Each framework also has its own small package. It re-exports the whole core API, ships the stylesheets, and handles mounting, cleanup, server-side rendering and two-way binding for you. Install **one** of them (it brings the core with it):
@@ -76,8 +82,10 @@ The core is one function, `createEditor(options)`. It mounts the editor inside t
76
82
  The CDN script defines the `<owe-editor>` tag, so one tag is enough:
77
83
 
78
84
  ```html
79
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-wysiwyg-editor@1/dist/style.min.css" />
80
- <script src="https://cdn.jsdelivr.net/npm/open-wysiwyg-editor@1/dist/open-wysiwyg-editor.global.js"></script>
85
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-wysiwyg-editor@1.0.1/dist/style.min.css"
86
+ integrity="sha384-b7LA9M5in6uI0RSJJZ206o3E6yH2YJdquLqriMF+32aOQjNUNH7uitvYh2CebAip" crossorigin="anonymous" />
87
+ <script src="https://cdn.jsdelivr.net/npm/open-wysiwyg-editor@1.0.1/dist/open-wysiwyg-editor.global.js"
88
+ integrity="sha384-lgMQAQkEkLamVrDDDizcj9JBoeaPc6DUKFiL+F/fj7KczjRHePrgrfCGRCnXGr7D" crossorigin="anonymous"></script>
81
89
 
82
90
  <owe-editor id="editor" placeholder="Write something…">
83
91
  <template><p>Hello <strong>world</strong></p></template>
@@ -432,11 +440,13 @@ Still sanitize on the server. To display saved HTML on a page that has no editor
432
440
  | `dir` | Base direction of the content (`ltr`, `rtl`, `auto`) |
433
441
  | `content-lang` | `lang` of the content (spellcheck and screen readers) |
434
442
  | `label` | Accessible name. Or point a `<label for>` at the element's `id` |
443
+ | `theme` | `auto` (follows the system), `light` or `dark` |
444
+ | `brand` | Brand color (any CSS color): buttons, links, focus ring and selection follow it |
435
445
  | `value` | Initial content as HTML (read once, when the editor is created) |
436
446
  | `language` | UI language, e.g. `ar` (read once). Defaults to `<html lang>` |
437
447
  | `toolbar` | Toolbar items separated by spaces (`"bold italic \| link"`), or `none` (read once) |
438
448
 
439
- `placeholder`, `readonly`, `disabled`, `dir`, `content-lang` and `label` follow later changes. `value`, `language` and `toolbar` are only read when the editor starts.
449
+ `placeholder`, `readonly`, `disabled`, `dir`, `content-lang`, `label`, `theme` and `brand` follow later changes. `value`, `language` and `toolbar` are only read when the editor starts.
440
450
 
441
451
  | Property | Description |
442
452
  | --- | --- |
@@ -489,6 +499,8 @@ createEditor({
489
499
  items: { /* custom buttons, see below */ },
490
500
  statusbar: { elementPath: true, wordCount: true, characterCount: false }, // or false
491
501
  theme: "auto", // "light" | "dark"
502
+ brand: "#e11d48", // one color for buttons, links, focus and selection (see Styling)
503
+ tokens: { radius: "8px" }, // any design token, without the --owe- prefix (see Styling)
492
504
  stickyToolbar: true,
493
505
  shortcuts: { toolbar: "Alt-F10", help: "Alt-0", find: "Mod-f", link: "Mod-k" },
494
506
  },
@@ -520,7 +532,7 @@ editor.announce("Saved"); // send a message to screen readers
520
532
  editor.destroy();
521
533
  ```
522
534
 
523
- The main entry also exports `getUI(editor)`, which returns `focusToolbar()`, `openDialog(kind)`, `openFind()`, `toggleSource()`, `isSourceMode()` and `update()`.
535
+ The main entry also exports `getUI(editor)`, which returns `focusToolbar()`, `openDialog(kind)`, `openFind()`, `toggleSource()`, `isSourceMode()`, `update()` and `setTheme({ theme, brand, tokens })` (see [Styling](#styling)).
524
536
 
525
537
  ### Commands
526
538
 
@@ -708,16 +720,268 @@ Untranslated labels fall back to English, and plurals use `Intl.PluralRules`. Th
708
720
 
709
721
  Each framework package ships the same files under its own name, for example `@open-wysiwyg-editor/vue/style.css` and `@open-wysiwyg-editor/vue/content.css`, because package managers like pnpm don't expose the core package to your app.
710
722
 
711
- All rules are in `@layer owe`, so any of your own CSS outside a layer overrides them. To theme the editor, set the design tokens:
723
+ The editor's look comes from its own API: the `brand` and `theme` options and attributes, and the design tokens below. Your page's CSS doesn't change it. Resets such as Tailwind's preflight, element rules like `button {}` or `h2 {}`, and utility classes on a wrapper don't reach into the editor, so it looks the same in any app. Inside the editor, every rule is unlayered and two classes more specific than its selector reads, so only a more specific selector or `!important` overrides one. Use tokens to theme it instead: a token you set always wins, wherever you set it.
724
+
725
+ ### One color
726
+
727
+ Pick your brand color and the whole editor follows it, in light and dark: the pressed toolbar buttons, the dialog buttons, links, the focus ring, the caret, the text selection and the gradient edge of the card.
728
+
729
+ ```ts
730
+ createEditor({ element, ui: { brand: "#e11d48" } });
731
+ ```
732
+
733
+ ```html
734
+ <owe-editor brand="#e11d48" theme="dark"></owe-editor>
735
+ ```
736
+
737
+ ```css
738
+ .owe { --owe-brand: #e11d48; }
739
+ ```
740
+
741
+ All three do the same thing. `brand` and the attribute take any CSS color. They also pick black or white for the text on brand-colored buttons (`--owe-on-brand`), by contrast, when the color is a hex, `rgb()`, `hsl()` or basic named color. With plain CSS, set `--owe-on-brand` yourself if white doesn't read well on your color. `--owe-brand` can be set on `.owe`, on a wrapper or on `:root`.
742
+
743
+ Everything else is derived from the brand with `color-mix()`, so you only override what you want to change:
744
+
745
+ | Token | Derived as |
746
+ | --- | --- |
747
+ | `--owe-primary` | The brand. Pressed toolbar buttons, primary dialog buttons, the current find match |
748
+ | `--owe-primary-hover` | `--owe-primary` mixed 15% toward black |
749
+ | `--owe-accent` | Text and link color. The brand mixed 30% toward black in light, 40% toward white in dark |
750
+ | `--owe-accent-soft` | The brand at 18% over `--owe-bg` |
751
+ | `--owe-focus`, `--owe-caret` | `--owe-accent` |
752
+ | `--owe-selection` | The brand at 22% opacity in light, 45% in dark |
753
+ | `--owe-on-accent` | `--owe-on-brand` |
754
+ | `--owe-gradient` | `linear-gradient(90deg, var(--owe-brand), var(--owe-brand-2))` |
755
+
756
+ Setting one of these tokens yourself always wins over the derived value. `ui.brand` also sets `--owe-brand-2` to a lighter tint of the brand, so the card edge fades from your color. With plain CSS the second stop stays `#00b8ff`: set `--owe-brand-2` to the same color as `--owe-brand` for a solid edge.
757
+
758
+ The editor can't pick your colors for you. `--owe-accent` keeps 4.5:1 on white for typical brands, but a very pale brand (yellow, light cyan) or a very dark one in dark mode may need an explicit `--owe-accent`. Pressed buttons need 3:1 against the toolbar (WCAG 1.4.11): for a very dark brand in dark mode, set `--owe-btn-active-bg: var(--owe-accent)` and `--owe-btn-active-color` to a dark color.
759
+
760
+ ### Design tokens
761
+
762
+ Set any token in your own CSS, on `.owe`, on a wrapper or on `:root`, in or out of a cascade layer, or pass it as `ui.tokens` without the `--owe-` prefix (`tokens: { bg: "#0b0d10", radius: "8px" }`). Tokens set from JavaScript are applied through the CSSOM, so they work under a strict CSP. Unknown names are ignored, and so are values that contain `;`, `{`, `}`, `<` or `\`, or that call `url()`, `image()`, `image-set()` or `src()`. In TypeScript the names are the `ThemeToken` type.
763
+
764
+ | Token | Light | Dark | Styles |
765
+ | --- | --- | --- | --- |
766
+ | `--owe-brand` | `#0066ff` | same | The one brand color (see above) |
767
+ | `--owe-brand-2` | `#00b8ff` | same | Second color of the gradient edge |
768
+ | `--owe-on-brand` | `#ffffff` | same | Text and icons on brand fills |
769
+ | `--owe-primary` | brand | brand | Solid fills: pressed buttons, primary buttons |
770
+ | `--owe-primary-hover` | brand, 15% darker | same | Hover on those fills |
771
+ | `--owe-accent` | brand, 30% darker (`#003c9e`) | brand, 40% lighter (`#75a9ff`) | Links, checked menu items, the current path item |
772
+ | `--owe-accent-soft` | `#d1e3ff` | `#132744` | A brand tint for your own UI. The editor itself doesn't use it |
773
+ | `--owe-focus` | accent | accent | Focus ring, selected image and table outlines |
774
+ | `--owe-caret` | accent | accent | Text caret |
775
+ | `--owe-selection` | brand, 22% opacity | brand, 45% opacity | Selected text and table cells |
776
+ | `--owe-gradient` | brand to brand-2 | same | Bottom edge of the card |
777
+ | `--owe-bg` | `#ffffff` | `#17191b` | Card and editing area |
778
+ | `--owe-chrome` | `#fafbfc` | `#131517` | Toolbar, find bar and status bar |
779
+ | `--owe-surface` | `#f4f6f8` | `#1e2124` | Menus and dialogs |
780
+ | `--owe-surface-2` | `#f1f3f5` | `#25282b` | Hover backgrounds, keys in the shortcut list |
781
+ | `--owe-field-bg` | `#ffffff` | `#101214` | Inputs and selects |
782
+ | `--owe-code-bg` | `#f4f6f8` | `#0f1112` | Code, the source view |
783
+ | `--owe-text` | `#101112` | `#ffffff` | Headings and UI text |
784
+ | `--owe-body` | `#1b1f24` | `#dcdedf` | Paragraph text |
785
+ | `--owe-text-2` | `#3d434a` | `#c3c7cb` | Quotes |
786
+ | `--owe-muted` | `#54595f` | `#a1a7ad` | Placeholder, hints, shortcuts, status bar |
787
+ | `--owe-icon` | `#3a3f45` | `#d2d4d5` | Toolbar icons |
788
+ | `--owe-border` | `#8a929a` | `#6b7279` | Input and swatch borders (3:1 on the card) |
789
+ | `--owe-border-subtle` | `#e3e6ea` | `#25282b` | Card edge and dividers |
790
+ | `--owe-popup-border` | `#d5d9de` | `#33383d` | Menu and dialog edge |
791
+ | `--owe-danger` | `#b3261e` | `#f2b8b5` | Errors and destructive buttons |
792
+ | `--owe-warning` | `#8a5300` | `#f6c66b` | Find-match outline |
793
+ | `--owe-success` | `#146c2e` | `#8bd6a0` | For your own success messages. The editor itself doesn't use it |
794
+ | `--owe-mark` | `#fff2a8` | `#6b4e00` | `<mark>` and find matches |
795
+ | `--owe-shadow` | soft grey shadow | black shadow | Menus and dialogs |
796
+ | `--owe-backdrop` | `rgb(16 17 18 / 0.45)` | `rgb(4 5 16 / 0.65)` | Behind dialogs |
797
+ | `--owe-font` | `system-ui, "Segoe UI", "Noto Sans Arabic", …` | same | UI and content typeface. No web fonts are bundled |
798
+ | `--owe-font-mono` | `ui-monospace, Menlo, Consolas, …` | same | Code and the status bar |
799
+ | `--owe-font-size`, `--owe-line-height` | `1rem`, `1.65` | same | Content text |
800
+ | `--owe-radius` | `24px` | same | The card |
801
+ | `--owe-radius-sm`, `-md`, `-lg` | `10px`, `12px`, `16px` | same | Controls, menus, dialogs |
802
+ | `--owe-btn-size` | `40px` | same | Toolbar button size |
803
+ | `--owe-target` | `32px` | same | Minimum height of menu items, the status bar and small controls |
804
+ | `--owe-pad-x`, `--owe-pad-y` | `clamp(16px, 4vw, 32px)`, `28px` | same | Padding of the editing area |
805
+ | `--owe-z-popover` | `1000` | same | `z-index` of menus and tooltips |
806
+
807
+ In dark mode the editor uses the dark column when the system prefers dark, or when `theme` is `dark`. `theme: "light"` turns that off.
808
+
809
+ ### Button tokens
810
+
811
+ These restyle the buttons, the toolbar and the card without touching any selector. Like every token, set them on `.owe` or on any element around it, such as `:root` or your dashboard wrapper. The default in the table is what the editor uses when you don't set them.
812
+
813
+ | Token | Default | Styles |
814
+ | --- | --- | --- |
815
+ | `--owe-btn-color` | `--owe-icon` | Toolbar button icon and text |
816
+ | `--owe-btn-bg` | `transparent` | Toolbar button background |
817
+ | `--owe-btn-hover-color` | `--owe-text` | Button and path item text on hover |
818
+ | `--owe-btn-hover-bg` | `--owe-surface-2` | Hover background of buttons, menu items and path items |
819
+ | `--owe-btn-active-color` | `--owe-on-accent` | Text and icon of a pressed or toggled button |
820
+ | `--owe-btn-active-bg` | `--owe-primary` | Background of a pressed or toggled button |
821
+ | `--owe-btn-active-hover-bg` | `--owe-btn-active-bg`, 15% darker | Hover on a pressed button |
822
+ | `--owe-btn-radius` | `--owe-radius-sm` | Corners of buttons, menu items and path items |
823
+ | `--owe-btn-primary-bg` | `--owe-primary` | Primary button in dialogs (Insert, Apply) |
824
+ | `--owe-btn-primary-color` | `--owe-on-accent` | Text of the primary button |
825
+ | `--owe-btn-primary-hover-bg` | `--owe-primary-hover` | Primary button on hover |
826
+ | `--owe-toolbar-bg` | `--owe-chrome` | Toolbar, find bar and status bar background |
827
+ | `--owe-toolbar-border` | `--owe-border-subtle` | Lines between toolbar, content and status bar |
828
+ | `--owe-editor-border` | `--owe-border-subtle` | Border of the card |
829
+ | `--owe-editor-radius` | `--owe-radius` | Corners of the card, toolbar and status bar |
830
+
831
+ A dark toolbar over a light editing area, with smaller corners:
832
+
833
+ ```css
834
+ .owe {
835
+ --owe-toolbar-bg: #0f172a;
836
+ --owe-toolbar-border: #0f172a;
837
+ --owe-btn-color: #e2e8f0;
838
+ --owe-btn-hover-bg: #1e293b;
839
+ --owe-btn-hover-color: #ffffff;
840
+ --owe-btn-radius: 6px;
841
+ --owe-editor-radius: 8px;
842
+ }
843
+ ```
844
+
845
+ Windows High Contrast (`forced-colors`) and `prefers-contrast: more` still take over where they must: pressed buttons use the system highlight colors whatever you set.
846
+
847
+ ### Match your dashboard
848
+
849
+ Point the tokens at the variables your design system already defines. Put the CSS anywhere, in or out of a layer: the editor never declares a token itself, so yours always wins. Because the tokens read your variables when they are used, the colors follow your light and dark themes without any script.
850
+
851
+ Tell the editor when your dashboard is dark as well: set `theme` to `dark` (`<owe-editor theme="dark">`, `ui.theme` or `setTheme()`). That switches the parts that are derived from the brand for dark backgrounds (links, selection), the highlight color and `color-scheme`, so scrollbars and native controls match. With `theme` left on `auto`, the editor follows the system setting.
852
+
853
+ **shadcn/ui** (Tailwind v4 variables):
712
854
 
713
855
  ```css
714
856
  .owe {
715
- --owe-accent: #0b57d0;
716
- --owe-font: "IBM Plex Sans Arabic", system-ui, sans-serif;
717
- --owe-radius: 10px;
857
+ --owe-brand: var(--primary);
858
+ --owe-brand-2: var(--primary);
859
+ --owe-on-brand: var(--primary-foreground);
860
+ --owe-bg: var(--card);
861
+ --owe-text: var(--card-foreground);
862
+ --owe-body: var(--card-foreground);
863
+ --owe-chrome: var(--muted);
864
+ --owe-surface: var(--popover);
865
+ --owe-surface-2: var(--accent);
866
+ --owe-field-bg: var(--background);
867
+ --owe-muted: var(--muted-foreground);
868
+ --owe-icon: var(--foreground);
869
+ --owe-border-subtle: var(--border);
870
+ --owe-popup-border: var(--border);
871
+ /* --owe-border draws input outlines, which need 3:1; shadcn's --border is much lighter */
872
+ --owe-border: color-mix(in oklab, var(--foreground) 45%, var(--card));
873
+ --owe-radius: var(--radius);
874
+ --owe-radius-sm: calc(var(--radius) - 2px);
875
+ --owe-font: inherit;
718
876
  }
719
877
  ```
720
878
 
879
+ **Bootstrap 5.3** (`data-bs-theme` switches light and dark):
880
+
881
+ ```css
882
+ .owe {
883
+ --owe-brand: var(--bs-primary);
884
+ --owe-brand-2: var(--bs-primary);
885
+ --owe-accent: var(--bs-link-color);
886
+ --owe-bg: var(--bs-body-bg);
887
+ --owe-chrome: var(--bs-tertiary-bg);
888
+ --owe-surface: var(--bs-body-bg);
889
+ --owe-surface-2: var(--bs-secondary-bg);
890
+ --owe-field-bg: var(--bs-body-bg);
891
+ --owe-text: var(--bs-emphasis-color);
892
+ --owe-body: var(--bs-body-color);
893
+ --owe-muted: var(--bs-secondary-color);
894
+ --owe-icon: var(--bs-body-color);
895
+ --owe-border-subtle: var(--bs-border-color);
896
+ --owe-popup-border: var(--bs-border-color);
897
+ --owe-radius: var(--bs-border-radius-xl);
898
+ --owe-radius-sm: var(--bs-border-radius);
899
+ --owe-font: inherit;
900
+ }
901
+ ```
902
+
903
+ **Material UI** (v6 and later, with `createTheme({ cssVariables: true, colorSchemes: { dark: true } })`):
904
+
905
+ ```css
906
+ .owe {
907
+ --owe-brand: var(--mui-palette-primary-main);
908
+ --owe-brand-2: var(--mui-palette-primary-main);
909
+ --owe-on-brand: var(--mui-palette-primary-contrastText);
910
+ --owe-accent: var(--mui-palette-primary-main);
911
+ --owe-bg: var(--mui-palette-background-paper);
912
+ --owe-chrome: var(--mui-palette-background-default);
913
+ --owe-surface: var(--mui-palette-background-paper);
914
+ --owe-field-bg: var(--mui-palette-background-paper);
915
+ --owe-text: var(--mui-palette-text-primary);
916
+ --owe-body: var(--mui-palette-text-primary);
917
+ --owe-muted: var(--mui-palette-text-secondary);
918
+ --owe-icon: var(--mui-palette-text-secondary);
919
+ --owe-border: var(--mui-palette-text-secondary);
920
+ --owe-border-subtle: var(--mui-palette-divider);
921
+ --owe-popup-border: var(--mui-palette-divider);
922
+ --owe-btn-hover-bg: var(--mui-palette-action-hover);
923
+ --owe-radius: calc(var(--mui-shape-borderRadius) * 3);
924
+ --owe-radius-sm: var(--mui-shape-borderRadius);
925
+ --owe-font: inherit;
926
+ }
927
+ ```
928
+
929
+ **A plain dark dashboard** with its own palette:
930
+
931
+ ```css
932
+ .owe {
933
+ --owe-brand: #38bdf8;
934
+ --owe-on-brand: #0b1120; /* white is only 2:1 on this blue */
935
+ --owe-bg: #0f172a;
936
+ --owe-chrome: #0b1120;
937
+ --owe-surface: #1e293b;
938
+ --owe-surface-2: #263449;
939
+ --owe-field-bg: #0b1120;
940
+ --owe-text: #f8fafc;
941
+ --owe-body: #e2e8f0;
942
+ --owe-muted: #94a3b8;
943
+ --owe-icon: #cbd5e1;
944
+ --owe-border: #64748b;
945
+ --owe-border-subtle: #1e293b;
946
+ --owe-popup-border: #334155;
947
+ --owe-radius: 12px;
948
+ }
949
+ ```
950
+
951
+ Then switch the editor to its dark theme with `<owe-editor theme="dark">`, or the same from JavaScript, without any CSS. `brand` picks the dark text on this blue by itself:
952
+
953
+ ```ts
954
+ createEditor({
955
+ element,
956
+ ui: {
957
+ theme: "dark",
958
+ brand: "#38bdf8",
959
+ tokens: { bg: "#0f172a", chrome: "#0b1120", surface: "#1e293b", radius: "12px" },
960
+ },
961
+ });
962
+ ```
963
+
964
+ ### Changing the theme later
965
+
966
+ `<owe-editor>` follows changes to its `theme` and `brand` attributes. For an editor you created with `createEditor()`, call `setTheme()` on its UI. It replaces the whole theme, so pass everything you want to keep:
967
+
968
+ ```ts
969
+ import { getUI } from "open-wysiwyg-editor";
970
+
971
+ getUI(editor)?.setTheme({ theme: "dark", brand: "#e11d48", tokens: { radius: "8px" } });
972
+ ```
973
+
974
+ The framework components read `ui` once, when the editor starts, so call `setTheme()` when your own state changes:
975
+
976
+ ```tsx
977
+ const { ref, editor } = useEditor({ ui: { brand } });
978
+ useEffect(() => {
979
+ if (editor) getUI(editor)?.setTheme({ brand });
980
+ }, [editor, brand]);
981
+ ```
982
+
983
+ `applyTheme(element, { theme, brand, tokens })` is the function behind `setTheme()`. Use it on any element, for example the `.owe-content-root` that shows saved HTML. CSS variables also change at runtime: set `--owe-brand` on `.owe`, or on a wrapper, whenever you like.
984
+
721
985
  ## Extensions
722
986
 
723
987
  Everything is built from extensions. You can write your own the same way:
@@ -815,14 +1079,14 @@ npm install
815
1079
  npm run build # builds every package
816
1080
  npm run check # lint + build + typecheck + unit tests
817
1081
  npm run e2e # end-to-end + axe tests in Chromium, Firefox and WebKit (Playwright)
818
- npm run dev # serve the demo site locally
1082
+ npm run dev # serve the website locally (http://localhost:3000/open-wysiwyg-editor/)
819
1083
  ```
820
1084
 
821
1085
  Layout:
822
1086
 
823
1087
  ```
824
1088
  README.md CHANGELOG.md CONTRIBUTING.md SECURITY.md LICENSE
825
- examples/ the documentation website + live demo (deployed to GitHub Pages)
1089
+ examples/ the documentation website + live demo: one Next.js page in English and Arabic (deployed to GitHub Pages)
826
1090
  test/e2e/ Playwright end-to-end + axe tests
827
1091
  scripts/ serve-e2e, size-check, license-check, copy-styles, publish, render-banner
828
1092
  packages/<name>/ core, react, next, preact, vue, nuxt, angular, svelte, solid, astro
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkMARI3ZF6_cjs = require('./chunk-MARI3ZF6.cjs');
3
+ var chunkGAOA25B3_cjs = require('./chunk-GAOA25B3.cjs');
4
4
 
5
5
  /*! open-wysiwyg-editor | MIT License | https://github.com/alhassan73/open-wysiwyg-editor */
6
6
 
@@ -128,7 +128,7 @@ function createHtmlFilter(options, policy = {}) {
128
128
  }
129
129
  function validValue(name, value) {
130
130
  if (value.length > 2e3 || UNSAFE_VALUE.test(value)) return null;
131
- if (URL_ATTRS.has(name)) return chunkMARI3ZF6_cjs.sanitizeUrl(value, policy);
131
+ if (URL_ATTRS.has(name)) return chunkGAOA25B3_cjs.sanitizeUrl(value, policy);
132
132
  if (name === "id") {
133
133
  if (!ID.test(value)) return null;
134
134
  if (typeof window !== "undefined" && (value in window || value in document)) return null;
@@ -203,7 +203,7 @@ var NODE_TAG = {
203
203
  link: "a"
204
204
  };
205
205
  var tagOf = (owner) => owner.type.name === "heading" ? `h${owner.attrs.level}` : NODE_TAG[owner.type.name] ?? "div";
206
- var HtmlSupport = chunkMARI3ZF6_cjs.defineExtension({
206
+ var HtmlSupport = chunkGAOA25B3_cjs.defineExtension({
207
207
  name: "htmlSupport",
208
208
  priority: 90,
209
209
  defaultOptions: { allow: "safe", disallow: [] },
@@ -280,5 +280,5 @@ exports.HTML_SAFE_RULES = HTML_SAFE_RULES;
280
280
  exports.HTML_TEXTBLOCKS = HTML_TEXTBLOCKS;
281
281
  exports.HtmlSupport = HtmlSupport;
282
282
  exports.createHtmlFilter = createHtmlFilter;
283
- //# sourceMappingURL=chunk-HKWFKMAC.cjs.map
284
- //# sourceMappingURL=chunk-HKWFKMAC.cjs.map
283
+ //# sourceMappingURL=chunk-BMUV6KRM.cjs.map
284
+ //# sourceMappingURL=chunk-BMUV6KRM.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/extensions/html-support.ts"],"names":["sanitizeUrl","defineExtension"],"mappings":"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","file":"chunk-HKWFKMAC.cjs","sourcesContent":["import type { DOMOutputSpec, Mark, MarkSpec, Node as PMNode, NodeSpec } from \"prosemirror-model\";\nimport { defineExtension } from \"../core/extension\";\nimport { sanitizeUrl, type UrlPolicy } from \"../core/url\";\n\n/**\n * One allowlist rule. Strings support a trailing \"*\" wildcard (\"data-*\", \"aria-*\").\n *\n * ```ts\n * HtmlSupport.configure({ allow: [\n * { name: \"section\", classes: true, attributes: [\"data-*\", \"id\"] },\n * { name: /^(span|abbr)$/, attributes: [\"title\", \"lang\"], classes: [\"highlight\", /^tag-/] },\n * { name: \"p\", styles: [\"color\", \"background-color\"] },\n * ]})\n * ```\n */\nexport interface HtmlRule {\n name: string | RegExp;\n attributes?: true | Array<string | RegExp>;\n classes?: true | Array<string | RegExp>;\n /** CSS properties allowed in `style`. Note: inline styles in the live editor need\n * `style-src-attr 'unsafe-inline'` under CSP; leave this out to stay strict-CSP clean. */\n styles?: true | Array<string | RegExp>;\n}\n\nexport interface HtmlSupportOptions {\n /** Rules for markup to keep, or \"safe\" for a sensible preset (no styles, no scripts). */\n allow: HtmlRule[] | \"safe\";\n /** Rules that win over `allow`. */\n disallow: HtmlRule[];\n}\n\n/** Elements kept as generic block containers (content: blocks). */\nexport const HTML_CONTAINERS = [\n \"div\", \"section\", \"article\", \"aside\", \"header\", \"footer\", \"nav\", \"main\", \"address\", \"hgroup\",\n \"details\", \"dl\", \"dd\", \"center\", \"search\",\n];\n/** Elements kept as generic text blocks (content: inline). */\nexport const HTML_TEXTBLOCKS = [\"summary\", \"dt\"];\n/** Elements kept as generic inline wrappers. */\nexport const HTML_INLINE = [\n \"span\", \"abbr\", \"cite\", \"dfn\", \"kbd\", \"mark\", \"q\", \"small\", \"time\", \"var\", \"samp\", \"bdi\", \"bdo\",\n \"data\", \"big\", \"ruby\", \"rt\", \"rp\",\n];\n\n/** Element names of the built-in node/mark types that may receive extra attributes. */\nconst KNOWN_TYPES = [\n \"paragraph\", \"heading\", \"blockquote\", \"bulletList\", \"orderedList\", \"listItem\", \"taskList\", \"taskItem\",\n \"codeBlock\", \"horizontalRule\", \"image\", \"table\", \"tableRow\", \"tableCell\", \"tableHeader\",\n \"bold\", \"italic\", \"underline\", \"strike\", \"code\", \"subscript\", \"superscript\", \"link\",\n];\n\nexport const HTML_SAFE_RULES: HtmlRule[] = [\n {\n name: new RegExp(`^(${[...HTML_CONTAINERS, ...HTML_TEXTBLOCKS].join(\"|\")})$`),\n attributes: [\"data-*\", \"id\", \"title\", \"lang\", \"aria-label\", \"aria-labelledby\", \"aria-describedby\", \"role\"],\n classes: true,\n },\n {\n name: new RegExp(`^(${HTML_INLINE.join(\"|\")})$`),\n attributes: [\"data-*\", \"title\", \"lang\", \"dir\", \"datetime\", \"value\", \"cite\", \"translate\"],\n classes: true,\n },\n {\n name: /^(p|h[1-6]|blockquote|ul|ol|li|table|tr|td|th|pre|code|figure|img|a|strong|em|u|s|sub|sup|hr)$/,\n attributes: [\"data-*\", \"id\", \"lang\", \"translate\"],\n classes: true,\n },\n];\n\n/** Attributes managed by the schema itself, never through HtmlSupport. */\nconst RESERVED = new Set([\n \"src\", \"srcset\", \"href\", \"alt\", \"width\", \"height\", \"start\", \"type\", \"dir\", \"colspan\", \"rowspan\",\n \"colwidth\", \"scope\", \"title\", \"data-type\", \"data-checked\", \"data-colwidth\", \"data-language\", \"open\",\n]);\n/** Never kept, whatever the rules say. */\nconst DENIED = /^(on|xmlns|xlink)|^(srcdoc|formaction|action|ping|background|tabindex|accesskey|autofocus|contenteditable|draggable|hidden|inert|popover|popovertarget|is|slot|part|nonce|style|class)$|^data-(pm|owe)-/i;\nconst URL_ATTRS = new Set([\"cite\", \"longdesc\"]);\nconst ROLES = new Set([\"note\", \"region\", \"group\", \"figure\", \"doc-note\", \"doc-example\", \"doc-tip\", \"doc-pullquote\", \"doc-epigraph\", \"presentation\", \"none\"]);\nconst CLASS = /^-?[A-Za-z_][\\w-]{0,63}$/;\nconst ID = /^[A-Za-z][\\w:.-]{0,63}$/;\nconst LANG = /^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*$/;\n// eslint-disable-next-line no-control-regex\nconst UNSAFE_VALUE = /[\\u0000-\\u001f]/;\nconst UNSAFE_CSS = /url\\s*\\(|expression\\s*\\(|javascript:|@import|[<>\\\\]|behavior\\s*:|-moz-binding/i;\n\ntype Attrs = Record<string, string>;\n\nconst matchesName = (pattern: string | RegExp, name: string): boolean =>\n typeof pattern === \"string\"\n ? pattern.endsWith(\"*\")\n ? name.startsWith(pattern.slice(0, -1).toLowerCase())\n : pattern.toLowerCase() === name\n : pattern.test(name);\n\nconst allowedBy = (list: true | Array<string | RegExp> | undefined, name: string): boolean =>\n list === true || (!!list && list.some((p) => matchesName(p, name)));\n\nexport function createHtmlFilter(options: HtmlSupportOptions, policy: UrlPolicy = {}) {\n const allow = options.allow === \"safe\" ? HTML_SAFE_RULES : options.allow;\n const deny = options.disallow;\n const rulesFor = (tag: string, rules: HtmlRule[]) => rules.filter((r) => matchesName(r.name, tag));\n\n const tagAllowed = (tag: string) =>\n rulesFor(tag, allow).length > 0 && !rulesFor(tag, deny).some((r) => !r.attributes && !r.classes && !r.styles);\n\n function permits(tag: string, kind: \"attributes\" | \"classes\" | \"styles\", name: string): boolean {\n const yes = rulesFor(tag, allow).some((r) => allowedBy(r[kind], name));\n const no = rulesFor(tag, deny).some((r) => allowedBy(r[kind], name));\n return yes && !no;\n }\n\n function validValue(name: string, value: string): string | null {\n if (value.length > 2000 || UNSAFE_VALUE.test(value)) return null;\n if (URL_ATTRS.has(name)) return sanitizeUrl(value, policy);\n if (name === \"id\") {\n if (!ID.test(value)) return null;\n // Ids that shadow window/document properties enable DOM clobbering.\n if (typeof window !== \"undefined\" && (value in window || value in document)) return null;\n }\n if (name === \"lang\" && !LANG.test(value)) return null;\n if (name === \"role\" && !ROLES.has(value)) return null;\n if (name === \"dir\" && !/^(ltr|rtl|auto)$/.test(value)) return null;\n return value;\n }\n\n /** Filters an element's attributes. `reserved` excludes attributes the schema already owns. */\n function filter(tag: string, read: (name: string) => string | null, names: string[], reserved: boolean): Attrs | null {\n const out: Attrs = {};\n for (const raw of names) {\n const name = raw.toLowerCase();\n const value = read(raw);\n if (value === null) continue;\n if (name === \"class\") {\n const classes = value\n .split(/\\s+/)\n .filter((c) => c && CLASS.test(c) && !/^(owe-|ProseMirror)/.test(c) && permits(tag, \"classes\", c));\n if (classes.length) out.class = classes.join(\" \");\n continue;\n }\n if (name === \"style\") {\n const decls: string[] = [];\n for (const decl of value.split(\";\")) {\n const i = decl.indexOf(\":\");\n if (i < 1) continue;\n const prop = decl.slice(0, i).trim().toLowerCase();\n const val = decl.slice(i + 1).trim();\n if (!/^-?[a-z][a-z-]*$/.test(prop) || !val || UNSAFE_CSS.test(val) || val.length > 200) continue;\n if (permits(tag, \"styles\", prop)) decls.push(`${prop}: ${val}`);\n }\n if (decls.length) out.style = decls.join(\"; \");\n continue;\n }\n if (DENIED.test(name) || (reserved && RESERVED.has(name))) continue;\n if (!permits(tag, \"attributes\", name)) continue;\n const valid = validValue(name, value);\n if (valid !== null) out[name] = valid;\n }\n return Object.keys(out).length ? out : null;\n }\n\n const fromElement = (el: Element, reserved: boolean) =>\n filter(el.tagName.toLowerCase(), (n) => el.getAttribute(n), el.getAttributeNames(), reserved);\n\n /** Re-validates stored attributes at render time (JSON input bypasses parsing). */\n const revalidate = (tag: string, value: unknown, reserved: boolean): Attrs | null => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n const stored = value as Record<string, unknown>;\n const names = Object.keys(stored).filter((k) => Object.prototype.hasOwnProperty.call(stored, k));\n return filter(tag, (n) => (typeof stored[n] === \"string\" ? (stored[n] as string) : null), names, reserved);\n };\n\n return { tagAllowed, fromElement, revalidate };\n}\n\nconst NODE_TAG: Record<string, string> = {\n paragraph: \"p\", blockquote: \"blockquote\", bulletList: \"ul\", orderedList: \"ol\", listItem: \"li\",\n taskList: \"ul\", taskItem: \"li\", codeBlock: \"pre\", horizontalRule: \"hr\", image: \"img\", table: \"table\",\n tableRow: \"tr\", tableCell: \"td\", tableHeader: \"th\", bold: \"strong\", italic: \"em\", underline: \"u\",\n strike: \"s\", code: \"code\", subscript: \"sub\", superscript: \"sup\", link: \"a\",\n};\nconst tagOf = (owner: PMNode | Mark): string =>\n owner.type.name === \"heading\" ? `h${owner.attrs.level as number}` : (NODE_TAG[owner.type.name] ?? \"div\");\n\n/**\n * General HTML support: keeps markup the editor doesn't model natively — wrappers like\n * <section>/<div>, inline elements like <abbr title>/<span lang>, and extra classes, data-*, ids\n * and (opt-in) styles on known elements — within an allowlist. Everything still passes the\n * sanitizer, URL policy and output validation.\n */\nexport const HtmlSupport = defineExtension<HtmlSupportOptions>({\n name: \"htmlSupport\",\n priority: 90,\n defaultOptions: { allow: \"safe\", disallow: [] },\n nodes: (options, { urlPolicy }): Record<string, NodeSpec> => {\n const f = createHtmlFilter(options, urlPolicy);\n const containers = HTML_CONTAINERS.filter(f.tagAllowed);\n const textblocks = HTML_TEXTBLOCKS.filter(f.tagAllowed);\n const nodeSpec = (tags: string[], content: string): NodeSpec => ({\n attrs: { tag: { default: tags[0] ?? \"div\" }, attributes: { default: null } },\n content,\n group: \"block\",\n parseDOM: tags.map((tag) => ({\n tag,\n priority: 40,\n getAttrs: (el: HTMLElement) => ({ tag, attributes: f.fromElement(el, false) }),\n })),\n toDOM: (node: PMNode) => {\n const tag = tags.includes(node.attrs.tag as string) ? (node.attrs.tag as string) : \"div\";\n const attrs: Attrs = { ...(f.revalidate(tag, node.attrs.attributes, false) ?? {}) };\n if (tag === \"details\") attrs.open = \"\"; // keep collapsible content editable/visible\n return [tag, attrs, 0] as DOMOutputSpec;\n },\n });\n return {\n ...(containers.length ? { htmlContainer: nodeSpec(containers, \"block+\") } : {}),\n ...(textblocks.length ? { htmlTextblock: nodeSpec(textblocks, \"inline*\") } : {}),\n };\n },\n marks: (options, { urlPolicy }): Record<string, MarkSpec> => {\n const f = createHtmlFilter(options, urlPolicy);\n const tags = HTML_INLINE.filter(f.tagAllowed);\n if (!tags.length) return {};\n return {\n htmlInline: {\n attrs: { tag: { default: \"span\" }, attributes: { default: null } },\n excludes: \"\", // different wrappers (and nested spans) can coexist\n parseDOM: tags.map((tag) => ({\n tag,\n priority: 40,\n getAttrs: (el: HTMLElement) => {\n const attributes = f.fromElement(el, false);\n // A bare <span> carries no meaning — let its content parse normally.\n if (tag === \"span\" && !attributes) return false;\n return { tag, attributes };\n },\n })),\n toDOM: (mark: Mark) => {\n const tag = tags.includes(mark.attrs.tag as string) ? (mark.attrs.tag as string) : \"span\";\n return [tag, f.revalidate(tag, mark.attrs.attributes, false) ?? {}, 0] as DOMOutputSpec;\n },\n },\n };\n },\n globalAttributes: (options) => {\n const f = createHtmlFilter(options);\n return [\n {\n types: KNOWN_TYPES,\n attributes: {\n htmlAttributes: {\n default: null,\n parseHTML: (el) => f.fromElement(el, true),\n renderHTML: (value, owner) => f.revalidate(tagOf(owner), value, true),\n },\n },\n },\n ];\n },\n});\n"]}
1
+ {"version":3,"sources":["../src/extensions/html-support.ts"],"names":["sanitizeUrl","defineExtension"],"mappings":"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","file":"chunk-BMUV6KRM.cjs","sourcesContent":["import type { DOMOutputSpec, Mark, MarkSpec, Node as PMNode, NodeSpec } from \"prosemirror-model\";\nimport { defineExtension } from \"../core/extension\";\nimport { sanitizeUrl, type UrlPolicy } from \"../core/url\";\n\n/**\n * One allowlist rule. Strings support a trailing \"*\" wildcard (\"data-*\", \"aria-*\").\n *\n * ```ts\n * HtmlSupport.configure({ allow: [\n * { name: \"section\", classes: true, attributes: [\"data-*\", \"id\"] },\n * { name: /^(span|abbr)$/, attributes: [\"title\", \"lang\"], classes: [\"highlight\", /^tag-/] },\n * { name: \"p\", styles: [\"color\", \"background-color\"] },\n * ]})\n * ```\n */\nexport interface HtmlRule {\n name: string | RegExp;\n attributes?: true | Array<string | RegExp>;\n classes?: true | Array<string | RegExp>;\n /** CSS properties allowed in `style`. Note: inline styles in the live editor need\n * `style-src-attr 'unsafe-inline'` under CSP; leave this out to stay strict-CSP clean. */\n styles?: true | Array<string | RegExp>;\n}\n\nexport interface HtmlSupportOptions {\n /** Rules for markup to keep, or \"safe\" for a sensible preset (no styles, no scripts). */\n allow: HtmlRule[] | \"safe\";\n /** Rules that win over `allow`. */\n disallow: HtmlRule[];\n}\n\n/** Elements kept as generic block containers (content: blocks). */\nexport const HTML_CONTAINERS = [\n \"div\", \"section\", \"article\", \"aside\", \"header\", \"footer\", \"nav\", \"main\", \"address\", \"hgroup\",\n \"details\", \"dl\", \"dd\", \"center\", \"search\",\n];\n/** Elements kept as generic text blocks (content: inline). */\nexport const HTML_TEXTBLOCKS = [\"summary\", \"dt\"];\n/** Elements kept as generic inline wrappers. */\nexport const HTML_INLINE = [\n \"span\", \"abbr\", \"cite\", \"dfn\", \"kbd\", \"mark\", \"q\", \"small\", \"time\", \"var\", \"samp\", \"bdi\", \"bdo\",\n \"data\", \"big\", \"ruby\", \"rt\", \"rp\",\n];\n\n/** Element names of the built-in node/mark types that may receive extra attributes. */\nconst KNOWN_TYPES = [\n \"paragraph\", \"heading\", \"blockquote\", \"bulletList\", \"orderedList\", \"listItem\", \"taskList\", \"taskItem\",\n \"codeBlock\", \"horizontalRule\", \"image\", \"table\", \"tableRow\", \"tableCell\", \"tableHeader\",\n \"bold\", \"italic\", \"underline\", \"strike\", \"code\", \"subscript\", \"superscript\", \"link\",\n];\n\nexport const HTML_SAFE_RULES: HtmlRule[] = [\n {\n name: new RegExp(`^(${[...HTML_CONTAINERS, ...HTML_TEXTBLOCKS].join(\"|\")})$`),\n attributes: [\"data-*\", \"id\", \"title\", \"lang\", \"aria-label\", \"aria-labelledby\", \"aria-describedby\", \"role\"],\n classes: true,\n },\n {\n name: new RegExp(`^(${HTML_INLINE.join(\"|\")})$`),\n attributes: [\"data-*\", \"title\", \"lang\", \"dir\", \"datetime\", \"value\", \"cite\", \"translate\"],\n classes: true,\n },\n {\n name: /^(p|h[1-6]|blockquote|ul|ol|li|table|tr|td|th|pre|code|figure|img|a|strong|em|u|s|sub|sup|hr)$/,\n attributes: [\"data-*\", \"id\", \"lang\", \"translate\"],\n classes: true,\n },\n];\n\n/** Attributes managed by the schema itself, never through HtmlSupport. */\nconst RESERVED = new Set([\n \"src\", \"srcset\", \"href\", \"alt\", \"width\", \"height\", \"start\", \"type\", \"dir\", \"colspan\", \"rowspan\",\n \"colwidth\", \"scope\", \"title\", \"data-type\", \"data-checked\", \"data-colwidth\", \"data-language\", \"open\",\n]);\n/** Never kept, whatever the rules say. */\nconst DENIED = /^(on|xmlns|xlink)|^(srcdoc|formaction|action|ping|background|tabindex|accesskey|autofocus|contenteditable|draggable|hidden|inert|popover|popovertarget|is|slot|part|nonce|style|class)$|^data-(pm|owe)-/i;\nconst URL_ATTRS = new Set([\"cite\", \"longdesc\"]);\nconst ROLES = new Set([\"note\", \"region\", \"group\", \"figure\", \"doc-note\", \"doc-example\", \"doc-tip\", \"doc-pullquote\", \"doc-epigraph\", \"presentation\", \"none\"]);\nconst CLASS = /^-?[A-Za-z_][\\w-]{0,63}$/;\nconst ID = /^[A-Za-z][\\w:.-]{0,63}$/;\nconst LANG = /^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*$/;\n// eslint-disable-next-line no-control-regex\nconst UNSAFE_VALUE = /[\\u0000-\\u001f]/;\nconst UNSAFE_CSS = /url\\s*\\(|expression\\s*\\(|javascript:|@import|[<>\\\\]|behavior\\s*:|-moz-binding/i;\n\ntype Attrs = Record<string, string>;\n\nconst matchesName = (pattern: string | RegExp, name: string): boolean =>\n typeof pattern === \"string\"\n ? pattern.endsWith(\"*\")\n ? name.startsWith(pattern.slice(0, -1).toLowerCase())\n : pattern.toLowerCase() === name\n : pattern.test(name);\n\nconst allowedBy = (list: true | Array<string | RegExp> | undefined, name: string): boolean =>\n list === true || (!!list && list.some((p) => matchesName(p, name)));\n\nexport function createHtmlFilter(options: HtmlSupportOptions, policy: UrlPolicy = {}) {\n const allow = options.allow === \"safe\" ? HTML_SAFE_RULES : options.allow;\n const deny = options.disallow;\n const rulesFor = (tag: string, rules: HtmlRule[]) => rules.filter((r) => matchesName(r.name, tag));\n\n const tagAllowed = (tag: string) =>\n rulesFor(tag, allow).length > 0 && !rulesFor(tag, deny).some((r) => !r.attributes && !r.classes && !r.styles);\n\n function permits(tag: string, kind: \"attributes\" | \"classes\" | \"styles\", name: string): boolean {\n const yes = rulesFor(tag, allow).some((r) => allowedBy(r[kind], name));\n const no = rulesFor(tag, deny).some((r) => allowedBy(r[kind], name));\n return yes && !no;\n }\n\n function validValue(name: string, value: string): string | null {\n if (value.length > 2000 || UNSAFE_VALUE.test(value)) return null;\n if (URL_ATTRS.has(name)) return sanitizeUrl(value, policy);\n if (name === \"id\") {\n if (!ID.test(value)) return null;\n // Ids that shadow window/document properties enable DOM clobbering.\n if (typeof window !== \"undefined\" && (value in window || value in document)) return null;\n }\n if (name === \"lang\" && !LANG.test(value)) return null;\n if (name === \"role\" && !ROLES.has(value)) return null;\n if (name === \"dir\" && !/^(ltr|rtl|auto)$/.test(value)) return null;\n return value;\n }\n\n /** Filters an element's attributes. `reserved` excludes attributes the schema already owns. */\n function filter(tag: string, read: (name: string) => string | null, names: string[], reserved: boolean): Attrs | null {\n const out: Attrs = {};\n for (const raw of names) {\n const name = raw.toLowerCase();\n const value = read(raw);\n if (value === null) continue;\n if (name === \"class\") {\n const classes = value\n .split(/\\s+/)\n .filter((c) => c && CLASS.test(c) && !/^(owe-|ProseMirror)/.test(c) && permits(tag, \"classes\", c));\n if (classes.length) out.class = classes.join(\" \");\n continue;\n }\n if (name === \"style\") {\n const decls: string[] = [];\n for (const decl of value.split(\";\")) {\n const i = decl.indexOf(\":\");\n if (i < 1) continue;\n const prop = decl.slice(0, i).trim().toLowerCase();\n const val = decl.slice(i + 1).trim();\n if (!/^-?[a-z][a-z-]*$/.test(prop) || !val || UNSAFE_CSS.test(val) || val.length > 200) continue;\n if (permits(tag, \"styles\", prop)) decls.push(`${prop}: ${val}`);\n }\n if (decls.length) out.style = decls.join(\"; \");\n continue;\n }\n if (DENIED.test(name) || (reserved && RESERVED.has(name))) continue;\n if (!permits(tag, \"attributes\", name)) continue;\n const valid = validValue(name, value);\n if (valid !== null) out[name] = valid;\n }\n return Object.keys(out).length ? out : null;\n }\n\n const fromElement = (el: Element, reserved: boolean) =>\n filter(el.tagName.toLowerCase(), (n) => el.getAttribute(n), el.getAttributeNames(), reserved);\n\n /** Re-validates stored attributes at render time (JSON input bypasses parsing). */\n const revalidate = (tag: string, value: unknown, reserved: boolean): Attrs | null => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n const stored = value as Record<string, unknown>;\n const names = Object.keys(stored).filter((k) => Object.prototype.hasOwnProperty.call(stored, k));\n return filter(tag, (n) => (typeof stored[n] === \"string\" ? (stored[n] as string) : null), names, reserved);\n };\n\n return { tagAllowed, fromElement, revalidate };\n}\n\nconst NODE_TAG: Record<string, string> = {\n paragraph: \"p\", blockquote: \"blockquote\", bulletList: \"ul\", orderedList: \"ol\", listItem: \"li\",\n taskList: \"ul\", taskItem: \"li\", codeBlock: \"pre\", horizontalRule: \"hr\", image: \"img\", table: \"table\",\n tableRow: \"tr\", tableCell: \"td\", tableHeader: \"th\", bold: \"strong\", italic: \"em\", underline: \"u\",\n strike: \"s\", code: \"code\", subscript: \"sub\", superscript: \"sup\", link: \"a\",\n};\nconst tagOf = (owner: PMNode | Mark): string =>\n owner.type.name === \"heading\" ? `h${owner.attrs.level as number}` : (NODE_TAG[owner.type.name] ?? \"div\");\n\n/**\n * General HTML support: keeps markup the editor doesn't model natively — wrappers like\n * <section>/<div>, inline elements like <abbr title>/<span lang>, and extra classes, data-*, ids\n * and (opt-in) styles on known elements — within an allowlist. Everything still passes the\n * sanitizer, URL policy and output validation.\n */\nexport const HtmlSupport = defineExtension<HtmlSupportOptions>({\n name: \"htmlSupport\",\n priority: 90,\n defaultOptions: { allow: \"safe\", disallow: [] },\n nodes: (options, { urlPolicy }): Record<string, NodeSpec> => {\n const f = createHtmlFilter(options, urlPolicy);\n const containers = HTML_CONTAINERS.filter(f.tagAllowed);\n const textblocks = HTML_TEXTBLOCKS.filter(f.tagAllowed);\n const nodeSpec = (tags: string[], content: string): NodeSpec => ({\n attrs: { tag: { default: tags[0] ?? \"div\" }, attributes: { default: null } },\n content,\n group: \"block\",\n parseDOM: tags.map((tag) => ({\n tag,\n priority: 40,\n getAttrs: (el: HTMLElement) => ({ tag, attributes: f.fromElement(el, false) }),\n })),\n toDOM: (node: PMNode) => {\n const tag = tags.includes(node.attrs.tag as string) ? (node.attrs.tag as string) : \"div\";\n const attrs: Attrs = { ...(f.revalidate(tag, node.attrs.attributes, false) ?? {}) };\n if (tag === \"details\") attrs.open = \"\"; // keep collapsible content editable/visible\n return [tag, attrs, 0] as DOMOutputSpec;\n },\n });\n return {\n ...(containers.length ? { htmlContainer: nodeSpec(containers, \"block+\") } : {}),\n ...(textblocks.length ? { htmlTextblock: nodeSpec(textblocks, \"inline*\") } : {}),\n };\n },\n marks: (options, { urlPolicy }): Record<string, MarkSpec> => {\n const f = createHtmlFilter(options, urlPolicy);\n const tags = HTML_INLINE.filter(f.tagAllowed);\n if (!tags.length) return {};\n return {\n htmlInline: {\n attrs: { tag: { default: \"span\" }, attributes: { default: null } },\n excludes: \"\", // different wrappers (and nested spans) can coexist\n parseDOM: tags.map((tag) => ({\n tag,\n priority: 40,\n getAttrs: (el: HTMLElement) => {\n const attributes = f.fromElement(el, false);\n // A bare <span> carries no meaning — let its content parse normally.\n if (tag === \"span\" && !attributes) return false;\n return { tag, attributes };\n },\n })),\n toDOM: (mark: Mark) => {\n const tag = tags.includes(mark.attrs.tag as string) ? (mark.attrs.tag as string) : \"span\";\n return [tag, f.revalidate(tag, mark.attrs.attributes, false) ?? {}, 0] as DOMOutputSpec;\n },\n },\n };\n },\n globalAttributes: (options) => {\n const f = createHtmlFilter(options);\n return [\n {\n types: KNOWN_TYPES,\n attributes: {\n htmlAttributes: {\n default: null,\n parseHTML: (el) => f.fromElement(el, true),\n renderHTML: (value, owner) => f.revalidate(tagOf(owner), value, true),\n },\n },\n },\n ];\n },\n});\n"]}
@@ -149,9 +149,13 @@ var FORBIDDEN_OUTPUT = /* @__PURE__ */ new Set([
149
149
  "frameset",
150
150
  "template",
151
151
  "noscript",
152
- "form"
152
+ "form",
153
+ // Foreign content: SVG animation attributes (values/to/from) can set javascript: URLs.
154
+ "svg",
155
+ "math"
153
156
  ]);
154
157
  var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "cite", "poster", "action", "background"]);
158
+ var IMAGE_ATTRS = /* @__PURE__ */ new Set(["src", "poster"]);
155
159
  var DROP_ATTRS = /* @__PURE__ */ new Set(["srcdoc", "srcset", "formaction", "xlink:href", "ping"]);
156
160
  var NBSP = new RegExp(String.fromCharCode(160), "g");
157
161
  var escapeText = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(NBSP, "&nbsp;");
@@ -163,7 +167,7 @@ function renderAttrs(raw, policy) {
163
167
  const lower = name.toLowerCase();
164
168
  if (!ATTR_NAME.test(name) || lower.startsWith("on") || DROP_ATTRS.has(lower)) continue;
165
169
  if (URL_ATTRS.has(lower)) {
166
- const safe = sanitizeUrl(String(value), { ...policy, allowDataImages: true }, lower === "src" ? "image" : "link");
170
+ const safe = sanitizeUrl(String(value), policy, IMAGE_ATTRS.has(lower) ? "image" : "link");
167
171
  if (safe !== null) attrs += ` ${name}="${escapeAttr(safe)}"`;
168
172
  continue;
169
173
  }
@@ -1732,6 +1736,9 @@ function setBlockAttrs(types, attrs) {
1732
1736
 
1733
1737
  // src/extensions/attributes.ts
1734
1738
  var ALIGNMENTS = ["start", "center", "end", "justify"];
1739
+ var DIRECTIONS = ["ltr", "rtl", "auto"];
1740
+ var alignOf = (value) => value !== "start" && ALIGNMENTS.includes(value) ? value : null;
1741
+ var dirOf = (value) => DIRECTIONS.includes(value) ? value : null;
1735
1742
  var TextAlign = defineExtension({
1736
1743
  name: "textAlign",
1737
1744
  defaultOptions: { types: ["paragraph", "heading"], output: "style" },
@@ -1749,8 +1756,15 @@ var TextAlign = defineExtension({
1749
1756
  }
1750
1757
  return ALIGNMENTS.includes(raw) && raw !== "start" ? raw : null;
1751
1758
  },
1752
- renderHTML: (value) => value ? output === "class" ? { class: `owe-align-${value}` } : { style: `text-align: ${value}` } : null,
1753
- renderDOM: (value) => value ? { class: `owe-align-${String(value)}` } : null
1759
+ renderHTML: (value) => {
1760
+ const align = alignOf(value);
1761
+ if (!align) return null;
1762
+ return output === "class" ? { class: `owe-align-${align}` } : { style: `text-align: ${align}` };
1763
+ },
1764
+ renderDOM: (value) => {
1765
+ const align = alignOf(value);
1766
+ return align ? { class: `owe-align-${align}` } : null;
1767
+ }
1754
1768
  }
1755
1769
  }
1756
1770
  }
@@ -1778,12 +1792,12 @@ var TextDirection = defineExtension({
1778
1792
  const attr = (auto) => ({
1779
1793
  dir: {
1780
1794
  default: null,
1781
- parseHTML: (el) => {
1782
- const dir = el.getAttribute("dir")?.toLowerCase();
1783
- return dir === "ltr" || dir === "rtl" || dir === "auto" ? dir : null;
1784
- },
1795
+ parseHTML: (el) => dirOf(el.getAttribute("dir")?.toLowerCase()),
1785
1796
  // Empty blocks inherit the editor direction so the caret starts on the correct side.
1786
- renderHTML: (value, node) => value ? { dir: String(value) } : auto && "content" in node && node.content.size > 0 ? { dir: "auto" } : null
1797
+ renderHTML: (value, node) => {
1798
+ const dir = dirOf(value);
1799
+ return dir ? { dir } : auto && "content" in node && node.content.size > 0 ? { dir: "auto" } : null;
1800
+ }
1787
1801
  }
1788
1802
  });
1789
1803
  return [
@@ -1985,14 +1999,19 @@ var OrderedList = defineExtension({
1985
1999
  }
1986
2000
  }
1987
2001
  ],
1988
- toDOM: (node) => [
1989
- "ol",
1990
- {
1991
- start: node.attrs.start === 1 ? null : String(node.attrs.start),
1992
- type: node.attrs.type
1993
- },
1994
- 0
1995
- ]
2002
+ // Re-checked here: JSON content and clipboard slice context skip getAttrs.
2003
+ toDOM: (node) => {
2004
+ const start = Number(node.attrs.start);
2005
+ const type = node.attrs.type;
2006
+ return [
2007
+ "ol",
2008
+ {
2009
+ start: Number.isInteger(start) && start !== 1 ? String(start) : null,
2010
+ type: typeof type === "string" && /^[1aAiI]$/.test(type) ? type : null
2011
+ },
2012
+ 0
2013
+ ];
2014
+ }
1996
2015
  }
1997
2016
  }),
1998
2017
  commands: ({ schema }) => ({
@@ -3180,5 +3199,5 @@ exports.toggleList = toggleList;
3180
3199
  exports.toggleMarkCommand = toggleMarkCommand;
3181
3200
  exports.toggleWrap = toggleWrap;
3182
3201
  exports.uid = uid;
3183
- //# sourceMappingURL=chunk-MARI3ZF6.cjs.map
3184
- //# sourceMappingURL=chunk-MARI3ZF6.cjs.map
3202
+ //# sourceMappingURL=chunk-GAOA25B3.cjs.map
3203
+ //# sourceMappingURL=chunk-GAOA25B3.cjs.map