@open-wysiwyg-editor/solid 0.0.0-stage → 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 alhassan-ahmed and open-wysiwyg-editor contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,155 @@
1
- # Temporary Holding Version
1
+ <p align="center"><a href="https://alhassan73.github.io/open-wysiwyg-editor/"><img src="https://raw.githubusercontent.com/alhassan73/open-wysiwyg-editor/main/.github/assets/banner.png" alt="Open WYSIWYG Editor — accessible, RTL-first rich text editor for every framework" width="100%"></a></p>
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ # @open-wysiwyg-editor/solid
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@open-wysiwyg-editor/solid)](https://www.npmjs.com/package/@open-wysiwyg-editor/solid)
6
+ [![license](https://img.shields.io/npm/l/@open-wysiwyg-editor/solid)](https://github.com/alhassan73/open-wysiwyg-editor/blob/main/LICENSE)
7
+
8
+ **[Live demo →](https://alhassan73.github.io/open-wysiwyg-editor/)** · [GitHub](https://github.com/alhassan73/open-wysiwyg-editor) · [npm](https://www.npmjs.com/package/@open-wysiwyg-editor/solid)
9
+
10
+ A SolidJS directive that turns any element into the [Open WYSIWYG Editor](https://github.com/alhassan73/open-wysiwyg-editor#readme): an accessible (WCAG 2.2 AA), RTL-first rich text editor that runs under a strict Content Security Policy. It works with Solid 1.6+ and SolidStart, and ships its own TypeScript types.
11
+
12
+ ```tsx
13
+ <div use:richText={{ content: html(), onUpdate: (e) => setHtml(e.getHTML()) }} />
14
+ ```
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install @open-wysiwyg-editor/solid
20
+ ```
21
+
22
+ This one package gives you the whole editor API (`StarterKit`, `getUI`, types…): it re-exports everything from [`open-wysiwyg-editor`](https://www.npmjs.com/package/open-wysiwyg-editor).
23
+
24
+ ## Usage
25
+
26
+ ```tsx
27
+ import { createSignal } from "solid-js";
28
+ import { richText } from "@open-wysiwyg-editor/solid";
29
+ import "@open-wysiwyg-editor/solid/style.css";
30
+
31
+ // Keep this line. TypeScript and Babel drop imports that look unused,
32
+ // and `use:richText` is only a reference to the function.
33
+ richText;
34
+
35
+ export default function App() {
36
+ const [html, setHtml] = createSignal("<p>Hello <strong>world</strong></p>");
37
+
38
+ return (
39
+ <>
40
+ <div
41
+ use:richText={{
42
+ content: html(),
43
+ placeholder: "Write something…",
44
+ onUpdate: (editor) => setHtml(editor.getHTML()),
45
+ }}
46
+ />
47
+ <pre>{html()}</pre>
48
+ </>
49
+ );
50
+ }
51
+ ```
52
+
53
+ The directive creates the editor when the element mounts and destroys it when its owner is disposed. The object you pass is read reactively, so signals used inside it (like `html()` above) are tracked.
54
+
55
+ In TypeScript, `use:richText` is typed for you: this package augments `JSX.Directives` in `solid-js`.
56
+
57
+ ### Two-way binding
58
+
59
+ `content` goes in, `onUpdate` sends the HTML back.
60
+
61
+ - When the signal changes from outside (a reset button, a loaded record), the editor loads it.
62
+ - When it changed because the editor itself reported it, nothing is reloaded, so the cursor stays where it is.
63
+
64
+ ### Options and language
65
+
66
+ Pass any [editor option](https://github.com/alhassan73/open-wysiwyg-editor#options) except `element`.
67
+
68
+ ```tsx
69
+ <div
70
+ use:richText={{
71
+ content: html(),
72
+ language: "ar",
73
+ dir: "rtl",
74
+ contentLang: "ar",
75
+ ariaLabel: "Article body",
76
+ onUpdate: (e) => setHtml(e.getHTML()),
77
+ }}
78
+ />
79
+ ```
80
+
81
+ ### Toolbar
82
+
83
+ ```tsx
84
+ <div
85
+ use:richText={{
86
+ content: html(),
87
+ ui: { toolbar: ["bold", "italic", "|", "link", "bulletList", "orderedList"] },
88
+ onUpdate: (e) => setHtml(e.getHTML()),
89
+ }}
90
+ />
91
+ ```
92
+
93
+ ### Getting the editor
94
+
95
+ `onCreate` receives the editor instance.
96
+
97
+ ```tsx
98
+ import type { Editor } from "@open-wysiwyg-editor/solid";
99
+
100
+ let editor: Editor | undefined;
101
+
102
+ <div use:richText={{ onCreate: (e) => (editor = e) }} />
103
+ <button type="button" onClick={() => editor?.chain().focus().toggleBold().run()}>Bold</button>
104
+ ```
105
+
106
+ ### Forms
107
+
108
+ The editor does not submit by itself. Keep the HTML in a hidden input so a normal form post (or a SolidStart action) receives it.
109
+
110
+ ```tsx
111
+ <form method="post">
112
+ <div use:richText={{ content: html(), onUpdate: (e) => setHtml(e.getHTML()) }} />
113
+ <input type="hidden" name="body" value={html()} />
114
+ <button>Save</button>
115
+ </form>
116
+ ```
117
+
118
+ Always sanitize or escape the HTML again on the server before you render it for other users.
119
+
120
+ ### Updates after mount
121
+
122
+ | Changes after mount | What happens |
123
+ | --- | --- |
124
+ | `content` | Loaded into the editor (unless it is the editor's own HTML) |
125
+ | `editable`, `placeholder`, `ariaLabel`, `ariaLabelledBy`, `ariaDescribedBy`, `dir`, `contentLang` | Applied live |
126
+ | Callbacks (`onUpdate`, `onFocus`…) | The latest one is always called |
127
+ | Everything else (`extensions`, `ui`, `language`…) | Read once when the editor is created. Re-render it inside a keyed `<Show>` to recreate it |
128
+
129
+ ## Options
130
+
131
+ The directive takes the same options as `createEditor()` (without `element`). See the [full list](https://github.com/alhassan73/open-wysiwyg-editor#options) in the core docs. The editor API, commands and extensions are documented there too.
132
+
133
+ ## Styling
134
+
135
+ ```ts
136
+ import "@open-wysiwyg-editor/solid/style.css"; // editor UI + content (also style.min.css)
137
+ import "@open-wysiwyg-editor/solid/content.css"; // only the content styles, to show saved HTML (also content.min.css)
138
+ ```
139
+
140
+ Theme it with CSS variables; see [Styling](https://github.com/alhassan73/open-wysiwyg-editor#styling) in the core docs.
141
+
142
+ ## SSR and SolidStart
143
+
144
+ Directives only run in the browser. On the server the element is rendered empty, and the editor starts when the page hydrates, so there is nothing to guard with `isServer`. Import the CSS in `root.tsx` or `entry-client.tsx` to avoid a flash of unstyled content.
145
+
146
+ ## Links
147
+
148
+ - [Core docs](https://github.com/alhassan73/open-wysiwyg-editor#readme)
149
+ - [Live demo](https://alhassan73.github.io/open-wysiwyg-editor/)
150
+ - [Changelog](https://github.com/alhassan73/open-wysiwyg-editor/blob/main/CHANGELOG.md)
151
+ - [Report an issue](https://github.com/alhassan73/open-wysiwyg-editor/issues)
152
+
153
+ ## License
154
+
155
+ [MIT](https://github.com/alhassan73/open-wysiwyg-editor/blob/main/LICENSE) © alhassan-ahmed
@@ -0,0 +1,323 @@
1
+ /*! open-wysiwyg-editor | MIT License */
2
+ /*
3
+ * Design tokens. Everything lives in the `owe` cascade layer, so any unlayered host CSS wins
4
+ * without !important. Override tokens on .owe (or :root) to theme the editor.
5
+ * Contrast: text >= 4.5:1, borders/focus/states >= 3:1 against --owe-bg (WCAG 1.4.3 / 1.4.11).
6
+ *
7
+ * Tokens added in 1.0 (existing names keep working):
8
+ * --owe-radius card radius (24px) --owe-radius-sm inner controls (10px)
9
+ * --owe-radius-md menus (12px) --owe-radius-lg dialogs (16px)
10
+ * --owe-btn-size toolbar button (40px) --owe-pad-x/-y editing-area padding
11
+ * --owe-chrome toolbar / status bar surface
12
+ * --owe-field-bg inputs and source view --owe-popup-border menu / dialog edge
13
+ * --owe-body paragraph text --owe-text-2 secondary text (quotes)
14
+ * --owe-icon toolbar icon color --owe-caret caret color
15
+ * --owe-primary(-hover) solid fills: pressed buttons, primary buttons (white text, 4.8:1)
16
+ * --owe-gradient bottom edge of the card --owe-backdrop dialog backdrop
17
+ * --owe-accent is the TEXT/link/focus blue; --owe-primary is the FILL blue.
18
+ * Set --owe-font (e.g. "Almarai", system-ui) to change the typeface; no web fonts are bundled.
19
+ */
20
+ @layer owe {
21
+ .owe,
22
+ .owe-content-root {
23
+ --owe-font: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans Arabic", "Noto Sans", Tahoma, sans-serif;
24
+ --owe-font-mono: ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
25
+ --owe-font-size: 1rem;
26
+ --owe-line-height: 1.65;
27
+ --owe-radius: 24px;
28
+ --owe-radius-sm: 10px;
29
+ --owe-radius-md: 12px;
30
+ --owe-radius-lg: 16px;
31
+ --owe-space: 8px;
32
+ --owe-target: 32px; /* >= 24px (WCAG 2.5.8); 32px is comfortable */
33
+ --owe-btn-size: 40px;
34
+ --owe-pad-x: clamp(16px, 4vw, 32px);
35
+ --owe-pad-y: 28px;
36
+ --owe-gradient: linear-gradient(90deg, #0066ff, #9644ff, #af47c0);
37
+
38
+ --owe-bg: #ffffff;
39
+ --owe-chrome: #fafbfc;
40
+ --owe-surface: #f4f6f8;
41
+ --owe-surface-2: #f1f3f5;
42
+ --owe-field-bg: #ffffff;
43
+ --owe-text: #101112;
44
+ --owe-body: #1b1f24;
45
+ --owe-text-2: #3d434a;
46
+ --owe-muted: #54595f; /* 7:1 on white */
47
+ --owe-icon: #3a3f45;
48
+ --owe-border: #8a929a; /* 3.2:1 on white */
49
+ --owe-border-subtle: #e3e6ea;
50
+ --owe-popup-border: #d5d9de;
51
+ --owe-accent: #0057d9; /* 6.2:1 on white */
52
+ --owe-on-accent: #ffffff;
53
+ --owe-primary: #0066ff;
54
+ --owe-primary-hover: #0057d9;
55
+ --owe-accent-soft: #d3e3fd;
56
+ --owe-focus: #0066ff;
57
+ --owe-caret: #0066ff;
58
+ --owe-danger: #b3261e;
59
+ --owe-warning: #8a5300;
60
+ --owe-success: #146c2e;
61
+ --owe-selection: rgb(0 102 255 / 0.22);
62
+ --owe-code-bg: #f4f6f8;
63
+ --owe-mark: #fff2a8;
64
+ --owe-shadow: 0 16px 40px rgb(16 17 18 / 0.16), 0 2px 8px rgb(16 17 18 / 0.08);
65
+ --owe-backdrop: rgb(16 17 18 / 0.45);
66
+ --owe-z-popover: 1000;
67
+
68
+ color-scheme: light;
69
+ }
70
+
71
+ @media (prefers-color-scheme: dark) {
72
+ .owe:not([data-theme="light"]),
73
+ .owe-content-root:not([data-theme="light"]) {
74
+ --owe-bg: #17191b;
75
+ --owe-chrome: #131517;
76
+ --owe-surface: #1e2124;
77
+ --owe-surface-2: #25282b;
78
+ --owe-field-bg: #101214;
79
+ --owe-text: #ffffff;
80
+ --owe-body: #dcdedf;
81
+ --owe-text-2: #c3c7cb;
82
+ --owe-muted: #a1a7ad;
83
+ --owe-icon: #d2d4d5;
84
+ --owe-border: #6b7279;
85
+ --owe-border-subtle: #25282b;
86
+ --owe-popup-border: #33383d;
87
+ --owe-accent: #4d94ff;
88
+ --owe-on-accent: #ffffff;
89
+ --owe-primary: #0066ff;
90
+ --owe-primary-hover: #0057d9;
91
+ --owe-accent-soft: #14315e;
92
+ --owe-focus: #4d94ff;
93
+ --owe-caret: #5b9dff;
94
+ --owe-danger: #f2b8b5;
95
+ --owe-warning: #f6c66b;
96
+ --owe-success: #8bd6a0;
97
+ --owe-selection: rgb(0 102 255 / 0.55);
98
+ --owe-code-bg: #0f1112;
99
+ --owe-mark: #4a35b8;
100
+ --owe-shadow: 0 16px 40px rgb(0 0 0 / 0.55), 0 2px 8px rgb(0 0 0 / 0.4);
101
+ --owe-backdrop: rgb(4 5 16 / 0.65);
102
+ color-scheme: dark;
103
+ }
104
+ }
105
+
106
+ .owe[data-theme="dark"],
107
+ .owe-content-root[data-theme="dark"] {
108
+ --owe-bg: #17191b;
109
+ --owe-chrome: #131517;
110
+ --owe-surface: #1e2124;
111
+ --owe-surface-2: #25282b;
112
+ --owe-field-bg: #101214;
113
+ --owe-text: #ffffff;
114
+ --owe-body: #dcdedf;
115
+ --owe-text-2: #c3c7cb;
116
+ --owe-muted: #a1a7ad;
117
+ --owe-icon: #d2d4d5;
118
+ --owe-border: #6b7279;
119
+ --owe-border-subtle: #25282b;
120
+ --owe-popup-border: #33383d;
121
+ --owe-accent: #4d94ff;
122
+ --owe-on-accent: #ffffff;
123
+ --owe-primary: #0066ff;
124
+ --owe-primary-hover: #0057d9;
125
+ --owe-accent-soft: #14315e;
126
+ --owe-focus: #4d94ff;
127
+ --owe-caret: #5b9dff;
128
+ --owe-danger: #f2b8b5;
129
+ --owe-warning: #f6c66b;
130
+ --owe-success: #8bd6a0;
131
+ --owe-selection: rgb(0 102 255 / 0.55);
132
+ --owe-code-bg: #0f1112;
133
+ --owe-mark: #4a35b8;
134
+ --owe-shadow: 0 16px 40px rgb(0 0 0 / 0.55), 0 2px 8px rgb(0 0 0 / 0.4);
135
+ --owe-backdrop: rgb(4 5 16 / 0.65);
136
+ color-scheme: dark;
137
+ }
138
+
139
+ @media (prefers-contrast: more) {
140
+ .owe {
141
+ --owe-border: currentColor;
142
+ --owe-border-subtle: var(--owe-border);
143
+ --owe-popup-border: var(--owe-border);
144
+ --owe-muted: var(--owe-text);
145
+ --owe-text-2: var(--owe-text);
146
+ }
147
+ }
148
+ }
149
+
150
+ /*
151
+ * Content styles — shared by the editing area (.owe-content) and published pages
152
+ * (wrap saved HTML in <div class="owe-content-root">), so what authors see is what readers get.
153
+ */
154
+ @layer owe {
155
+ :is(.owe-content, .owe-content-root) {
156
+ font-family: var(--owe-font);
157
+ font-size: var(--owe-font-size);
158
+ line-height: var(--owe-line-height);
159
+ color: var(--owe-body);
160
+ overflow-wrap: break-word;
161
+ }
162
+
163
+ :is(.owe-content, .owe-content-root) > :first-child {
164
+ margin-block-start: 0;
165
+ }
166
+
167
+ :is(.owe-content, .owe-content-root) :is(p, ul, ol, blockquote, pre, table, figure, hr) {
168
+ margin-block: 0 0.85em;
169
+ }
170
+
171
+ :is(.owe-content, .owe-content-root) :is(h1, h2, h3, h4, h5, h6) {
172
+ line-height: 1.2;
173
+ margin-block: 1.3em 0.5em;
174
+ font-weight: 700;
175
+ letter-spacing: -0.02em;
176
+ color: var(--owe-text);
177
+ }
178
+ /* Negative tracking breaks Arabic joining: reset for RTL / Arabic-script content. */
179
+ :is(.owe-content, .owe-content-root) :is(h1, h2, h3, h4, h5, h6):is([dir="rtl"], :lang(ar), :lang(fa), :lang(ur), :lang(he)),
180
+ :is([dir="rtl"], :lang(ar), :lang(fa), :lang(ur), :lang(he)) :is(.owe-content, .owe-content-root) :is(h1, h2, h3, h4, h5, h6),
181
+ :is(.owe-content, .owe-content-root):is([dir="rtl"], :lang(ar), :lang(fa), :lang(ur), :lang(he)) :is(h1, h2, h3, h4, h5, h6) {
182
+ letter-spacing: 0;
183
+ }
184
+ :is(.owe-content, .owe-content-root) h1 { font-size: 2.1em; }
185
+ :is(.owe-content, .owe-content-root) h2 { font-size: 1.5em; }
186
+ :is(.owe-content, .owe-content-root) h3 { font-size: 1.25em; }
187
+ :is(.owe-content, .owe-content-root) h4 { font-size: 1.1em; }
188
+ :is(.owe-content, .owe-content-root) h5 { font-size: 1em; }
189
+ :is(.owe-content, .owe-content-root) h6 { font-size: 0.9em; }
190
+
191
+ :is(.owe-content, .owe-content-root) a {
192
+ color: var(--owe-accent);
193
+ text-decoration: underline; /* never colour alone (WCAG 1.4.1) */
194
+ text-underline-offset: 0.15em;
195
+ }
196
+
197
+ :is(.owe-content, .owe-content-root) :is(ul, ol) {
198
+ padding-inline-start: 1.6em;
199
+ }
200
+ :is(.owe-content, .owe-content-root) li > :is(p, ul, ol) {
201
+ margin-block: 0.15em;
202
+ }
203
+
204
+ :is(.owe-content, .owe-content-root) ul[data-type="taskList"] {
205
+ list-style: none;
206
+ padding-inline-start: 0.25em;
207
+ }
208
+ :is(.owe-content, .owe-content-root) li[data-type="taskItem"] {
209
+ display: flex;
210
+ align-items: flex-start;
211
+ gap: 0.5em;
212
+ }
213
+ :is(.owe-content, .owe-content-root) li[data-type="taskItem"] > input,
214
+ .owe-task-control > input {
215
+ inline-size: 1.1em;
216
+ block-size: 1.1em;
217
+ margin-block-start: 0.3em;
218
+ accent-color: var(--owe-primary);
219
+ flex: none;
220
+ }
221
+ :is(.owe-content, .owe-content-root) li[data-type="taskItem"] > div {
222
+ flex: 1;
223
+ min-inline-size: 0;
224
+ }
225
+ :is(.owe-content, .owe-content-root) li[data-checked="true"] > div {
226
+ text-decoration: line-through;
227
+ color: var(--owe-muted);
228
+ }
229
+
230
+ :is(.owe-content, .owe-content-root) blockquote {
231
+ margin-inline: 0;
232
+ padding-inline-start: 1em;
233
+ border-inline-start: 3px solid var(--owe-accent);
234
+ border-start-start-radius: 2px;
235
+ border-end-start-radius: 2px;
236
+ color: var(--owe-text-2);
237
+ }
238
+
239
+ :is(.owe-content, .owe-content-root) code {
240
+ font-family: var(--owe-font-mono);
241
+ font-size: 0.9em;
242
+ background: var(--owe-code-bg);
243
+ border: 1px solid var(--owe-border-subtle);
244
+ border-radius: 6px;
245
+ padding: 0.1em 0.35em;
246
+ }
247
+ :is(.owe-content, .owe-content-root) pre {
248
+ font-family: var(--owe-font-mono);
249
+ background: var(--owe-code-bg);
250
+ border: 1px solid var(--owe-border-subtle);
251
+ border-radius: var(--owe-radius-md);
252
+ padding: 1em 1.25em;
253
+ overflow-x: auto;
254
+ direction: ltr; /* code is left-to-right even in RTL documents */
255
+ text-align: start;
256
+ tab-size: 2;
257
+ }
258
+ :is(.owe-content, .owe-content-root) pre code {
259
+ background: none;
260
+ border: 0;
261
+ padding: 0;
262
+ font-size: 0.9em;
263
+ }
264
+
265
+ :is(.owe-content, .owe-content-root) hr {
266
+ border: none;
267
+ border-block-start: 2px solid var(--owe-border-subtle);
268
+ margin-block: 1.5em;
269
+ }
270
+
271
+ :is(.owe-content, .owe-content-root) img {
272
+ max-inline-size: 100%;
273
+ block-size: auto;
274
+ display: block;
275
+ }
276
+ :is(.owe-content, .owe-content-root) figure {
277
+ margin-inline: 0;
278
+ }
279
+ :is(.owe-content, .owe-content-root) figcaption {
280
+ color: var(--owe-muted);
281
+ font-size: 0.9em;
282
+ margin-block-start: 0.4em;
283
+ }
284
+
285
+ :is(.owe-content, .owe-content-root) table {
286
+ border-collapse: collapse;
287
+ inline-size: 100%;
288
+ table-layout: fixed;
289
+ }
290
+ :is(.owe-content, .owe-content-root) caption {
291
+ caption-side: top;
292
+ text-align: start;
293
+ font-weight: 600;
294
+ padding-block-end: 0.4em;
295
+ }
296
+ :is(.owe-content, .owe-content-root) :is(td, th) {
297
+ border: 1px solid var(--owe-border-subtle);
298
+ padding: 0.55em 0.8em;
299
+ vertical-align: top;
300
+ text-align: start;
301
+ min-inline-size: 3em;
302
+ }
303
+ :is(.owe-content, .owe-content-root) th {
304
+ background: var(--owe-surface);
305
+ color: var(--owe-text);
306
+ font-weight: 700;
307
+ }
308
+ :is(.owe-content, .owe-content-root) :is(td, th) > :last-child {
309
+ margin-block-end: 0;
310
+ }
311
+
312
+ :is(.owe-content, .owe-content-root) mark {
313
+ background-color: var(--owe-mark);
314
+ color: inherit;
315
+ border-radius: 0.2em;
316
+ padding-inline: 0.15em;
317
+ }
318
+
319
+ /* Alignment classes (used by the editor always, and by output when TextAlign output: "class"). */
320
+ .owe-align-center { text-align: center; }
321
+ .owe-align-end { text-align: end; }
322
+ .owe-align-justify { text-align: justify; }
323
+ }
@@ -0,0 +1,2 @@
1
+ /*! open-wysiwyg-editor | MIT License */
2
+ /*! open-wysiwyg-editor | MIT License */ @layer owe{.owe,.owe-content-root{--owe-font:system-ui,-apple-system,"Segoe UI",Roboto,"Noto Sans Arabic","Noto Sans",Tahoma,sans-serif;--owe-font-mono:ui-monospace,"JetBrains Mono","SFMono-Regular",Menlo,Consolas,"Liberation Mono",monospace;--owe-font-size:1rem;--owe-line-height:1.65;--owe-radius:24px;--owe-radius-sm:10px;--owe-radius-md:12px;--owe-radius-lg:16px;--owe-space:8px;--owe-target:32px;--owe-btn-size:40px;--owe-pad-x:clamp(16px,4vw,32px);--owe-pad-y:28px;--owe-gradient:linear-gradient(90deg,#0066ff,#9644ff,#af47c0);--owe-bg:#ffffff;--owe-chrome:#fafbfc;--owe-surface:#f4f6f8;--owe-surface-2:#f1f3f5;--owe-field-bg:#ffffff;--owe-text:#101112;--owe-body:#1b1f24;--owe-text-2:#3d434a;--owe-muted:#54595f;--owe-icon:#3a3f45;--owe-border:#8a929a;--owe-border-subtle:#e3e6ea;--owe-popup-border:#d5d9de;--owe-accent:#0057d9;--owe-on-accent:#ffffff;--owe-primary:#0066ff;--owe-primary-hover:#0057d9;--owe-accent-soft:#d3e3fd;--owe-focus:#0066ff;--owe-caret:#0066ff;--owe-danger:#b3261e;--owe-warning:#8a5300;--owe-success:#146c2e;--owe-selection:rgb(0 102 255 / 0.22);--owe-code-bg:#f4f6f8;--owe-mark:#fff2a8;--owe-shadow:0 16px 40px rgb(16 17 18 / 0.16),0 2px 8px rgb(16 17 18 / 0.08);--owe-backdrop:rgb(16 17 18 / 0.45);--owe-z-popover:1000;color-scheme:light}@media (prefers-color-scheme:dark){.owe:not([data-theme="light"]),.owe-content-root:not([data-theme="light"]){--owe-bg:#17191b;--owe-chrome:#131517;--owe-surface:#1e2124;--owe-surface-2:#25282b;--owe-field-bg:#101214;--owe-text:#ffffff;--owe-body:#dcdedf;--owe-text-2:#c3c7cb;--owe-muted:#a1a7ad;--owe-icon:#d2d4d5;--owe-border:#6b7279;--owe-border-subtle:#25282b;--owe-popup-border:#33383d;--owe-accent:#4d94ff;--owe-on-accent:#ffffff;--owe-primary:#0066ff;--owe-primary-hover:#0057d9;--owe-accent-soft:#14315e;--owe-focus:#4d94ff;--owe-caret:#5b9dff;--owe-danger:#f2b8b5;--owe-warning:#f6c66b;--owe-success:#8bd6a0;--owe-selection:rgb(0 102 255 / 0.55);--owe-code-bg:#0f1112;--owe-mark:#4a35b8;--owe-shadow:0 16px 40px rgb(0 0 0 / 0.55),0 2px 8px rgb(0 0 0 / 0.4);--owe-backdrop:rgb(4 5 16 / 0.65);color-scheme:dark}}.owe[data-theme="dark"],.owe-content-root[data-theme="dark"]{--owe-bg:#17191b;--owe-chrome:#131517;--owe-surface:#1e2124;--owe-surface-2:#25282b;--owe-field-bg:#101214;--owe-text:#ffffff;--owe-body:#dcdedf;--owe-text-2:#c3c7cb;--owe-muted:#a1a7ad;--owe-icon:#d2d4d5;--owe-border:#6b7279;--owe-border-subtle:#25282b;--owe-popup-border:#33383d;--owe-accent:#4d94ff;--owe-on-accent:#ffffff;--owe-primary:#0066ff;--owe-primary-hover:#0057d9;--owe-accent-soft:#14315e;--owe-focus:#4d94ff;--owe-caret:#5b9dff;--owe-danger:#f2b8b5;--owe-warning:#f6c66b;--owe-success:#8bd6a0;--owe-selection:rgb(0 102 255 / 0.55);--owe-code-bg:#0f1112;--owe-mark:#4a35b8;--owe-shadow:0 16px 40px rgb(0 0 0 / 0.55),0 2px 8px rgb(0 0 0 / 0.4);--owe-backdrop:rgb(4 5 16 / 0.65);color-scheme:dark}@media (prefers-contrast:more){.owe{--owe-border:currentColor;--owe-border-subtle:var(--owe-border);--owe-popup-border:var(--owe-border);--owe-muted:var(--owe-text);--owe-text-2:var(--owe-text)}}}@layer owe{:is(.owe-content,.owe-content-root){font-family:var(--owe-font);font-size:var(--owe-font-size);line-height:var(--owe-line-height);color:var(--owe-body);overflow-wrap:break-word}:is(.owe-content,.owe-content-root)>:first-child{margin-block-start:0}:is(.owe-content,.owe-content-root) :is(p,ul,ol,blockquote,pre,table,figure,hr){margin-block:0 0.85em}:is(.owe-content,.owe-content-root) :is(h1,h2,h3,h4,h5,h6){line-height:1.2;margin-block:1.3em 0.5em;font-weight:700;letter-spacing:-0.02em;color:var(--owe-text)}:is(.owe-content,.owe-content-root) :is(h1,h2,h3,h4,h5,h6):is([dir="rtl"],:lang(ar),:lang(fa),:lang(ur),:lang(he)),:is([dir="rtl"],:lang(ar),:lang(fa),:lang(ur),:lang(he)) :is(.owe-content,.owe-content-root) :is(h1,h2,h3,h4,h5,h6),:is(.owe-content,.owe-content-root):is([dir="rtl"],:lang(ar),:lang(fa),:lang(ur),:lang(he)) :is(h1,h2,h3,h4,h5,h6){letter-spacing:0}:is(.owe-content,.owe-content-root) h1{font-size:2.1em}:is(.owe-content,.owe-content-root) h2{font-size:1.5em}:is(.owe-content,.owe-content-root) h3{font-size:1.25em}:is(.owe-content,.owe-content-root) h4{font-size:1.1em}:is(.owe-content,.owe-content-root) h5{font-size:1em}:is(.owe-content,.owe-content-root) h6{font-size:0.9em}:is(.owe-content,.owe-content-root) a{color:var(--owe-accent);text-decoration:underline;text-underline-offset:0.15em}:is(.owe-content,.owe-content-root) :is(ul,ol){padding-inline-start:1.6em}:is(.owe-content,.owe-content-root) li>:is(p,ul,ol){margin-block:0.15em}:is(.owe-content,.owe-content-root) ul[data-type="taskList"]{list-style:none;padding-inline-start:0.25em}:is(.owe-content,.owe-content-root) li[data-type="taskItem"]{display:flex;align-items:flex-start;gap:0.5em}:is(.owe-content,.owe-content-root) li[data-type="taskItem"]>input,.owe-task-control>input{inline-size:1.1em;block-size:1.1em;margin-block-start:0.3em;accent-color:var(--owe-primary);flex:none}:is(.owe-content,.owe-content-root) li[data-type="taskItem"]>div{flex:1;min-inline-size:0}:is(.owe-content,.owe-content-root) li[data-checked="true"]>div{text-decoration:line-through;color:var(--owe-muted)}:is(.owe-content,.owe-content-root) blockquote{margin-inline:0;padding-inline-start:1em;border-inline-start:3px solid var(--owe-accent);border-start-start-radius:2px;border-end-start-radius:2px;color:var(--owe-text-2)}:is(.owe-content,.owe-content-root) code{font-family:var(--owe-font-mono);font-size:0.9em;background:var(--owe-code-bg);border:1px solid var(--owe-border-subtle);border-radius:6px;padding:0.1em 0.35em}:is(.owe-content,.owe-content-root) pre{font-family:var(--owe-font-mono);background:var(--owe-code-bg);border:1px solid var(--owe-border-subtle);border-radius:var(--owe-radius-md);padding:1em 1.25em;overflow-x:auto;direction:ltr;text-align:start;tab-size:2}:is(.owe-content,.owe-content-root) pre code{background:none;border:0;padding:0;font-size:0.9em}:is(.owe-content,.owe-content-root) hr{border:none;border-block-start:2px solid var(--owe-border-subtle);margin-block:1.5em}:is(.owe-content,.owe-content-root) img{max-inline-size:100%;block-size:auto;display:block}:is(.owe-content,.owe-content-root) figure{margin-inline:0}:is(.owe-content,.owe-content-root) figcaption{color:var(--owe-muted);font-size:0.9em;margin-block-start:0.4em}:is(.owe-content,.owe-content-root) table{border-collapse:collapse;inline-size:100%;table-layout:fixed}:is(.owe-content,.owe-content-root) caption{caption-side:top;text-align:start;font-weight:600;padding-block-end:0.4em}:is(.owe-content,.owe-content-root) :is(td,th){border:1px solid var(--owe-border-subtle);padding:0.55em 0.8em;vertical-align:top;text-align:start;min-inline-size:3em}:is(.owe-content,.owe-content-root) th{background:var(--owe-surface);color:var(--owe-text);font-weight:700}:is(.owe-content,.owe-content-root) :is(td,th)>:last-child{margin-block-end:0}:is(.owe-content,.owe-content-root) mark{background-color:var(--owe-mark);color:inherit;border-radius:0.2em;padding-inline:0.15em}.owe-align-center{text-align:center}.owe-align-end{text-align:end}.owe-align-justify{text-align:justify}}
package/dist/index.cjs ADDED
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var src_exports = {};
23
+ __export(src_exports, {
24
+ richText: () => richText
25
+ });
26
+ module.exports = __toCommonJS(src_exports);
27
+ var import_solid_js = require("solid-js");
28
+ var import_open_wysiwyg_editor = require("open-wysiwyg-editor");
29
+ __reExport(src_exports, require("open-wysiwyg-editor"), module.exports);
30
+ function richText(node, accessor) {
31
+ const current = () => accessor() ?? {};
32
+ const initial = (0, import_solid_js.untrack)(current);
33
+ let applied = initial.content;
34
+ let runtime = (0, import_open_wysiwyg_editor.pickRuntimeOptions)(initial);
35
+ const editor = (0, import_open_wysiwyg_editor.createEditor)({
36
+ ...initial,
37
+ element: node,
38
+ ...(0, import_open_wysiwyg_editor.forwardCallbacks)(current),
39
+ onUpdate: (e) => {
40
+ applied = e.getHTML();
41
+ current().onUpdate?.(e);
42
+ }
43
+ });
44
+ (0, import_solid_js.createEffect)(() => {
45
+ const next = current();
46
+ if (next.content !== applied) {
47
+ applied = next.content;
48
+ (0, import_open_wysiwyg_editor.syncContent)(editor, next.content);
49
+ }
50
+ const nextRuntime = (0, import_open_wysiwyg_editor.pickRuntimeOptions)(next);
51
+ if (!(0, import_open_wysiwyg_editor.sameRuntimeOptions)(runtime, nextRuntime)) {
52
+ runtime = nextRuntime;
53
+ editor.setOptions(nextRuntime);
54
+ }
55
+ });
56
+ (0, import_solid_js.onCleanup)(() => editor.destroy());
57
+ return editor;
58
+ }
59
+ // Annotate the CommonJS export names for ESM import in node:
60
+ 0 && (module.exports = {
61
+ richText,
62
+ ...require("open-wysiwyg-editor")
63
+ });
64
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// SolidJS directive (Solid 1.6+, SolidStart). Directives only run in the browser, so it is SSR-safe.\n//\n// import { richText } from \"@open-wysiwyg-editor/solid\";\n// <div use:richText={{ content: html(), onUpdate: (e) => setHtml(e.getHTML()) }} />\nimport { createEffect, onCleanup, untrack, type Accessor } from \"solid-js\";\nimport {\n createEditor,\n forwardCallbacks,\n pickRuntimeOptions,\n sameRuntimeOptions,\n syncContent,\n type Editor,\n type EditorOptions,\n} from \"open-wysiwyg-editor\";\n\n// One install gives the whole API: StarterKit, getUI, types…\nexport * from \"open-wysiwyg-editor\";\n\nexport type RichTextOptions = Omit<EditorOptions, \"element\">;\n\n/**\n * Mounts the full editor in the element it is used on, and destroys it with the owner.\n * When `content` changes, the editor loads it, unless it is the HTML the editor just produced.\n * Callbacks and the runtime options (`editable`, `placeholder`, `aria*`, `dir`, `contentLang`)\n * follow changes; the rest is read once. `onCreate` receives the editor instance.\n */\nexport function richText(node: HTMLElement, accessor: Accessor<RichTextOptions | undefined>): Editor {\n const current = () => accessor() ?? {};\n const initial = untrack(current);\n let applied = initial.content;\n let runtime = pickRuntimeOptions(initial);\n const editor = createEditor({\n ...initial,\n element: node,\n ...forwardCallbacks(current),\n onUpdate: (e) => {\n applied = e.getHTML();\n current().onUpdate?.(e);\n },\n });\n\n createEffect(() => {\n const next = current();\n if (next.content !== applied) {\n applied = next.content;\n syncContent(editor, next.content);\n }\n const nextRuntime = pickRuntimeOptions(next);\n if (!sameRuntimeOptions(runtime, nextRuntime)) {\n runtime = nextRuntime;\n editor.setOptions(nextRuntime);\n }\n });\n\n onCleanup(() => editor.destroy());\n return editor;\n}\n\n// Types `use:richText={…}` in JSX.\ndeclare module \"solid-js\" {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n interface Directives {\n richText: RichTextOptions;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAgE;AAChE,iCAQO;AAGP,wBAAc,gCAhBd;AA0BO,SAAS,SAAS,MAAmB,UAAyD;AACnG,QAAM,UAAU,MAAM,SAAS,KAAK,CAAC;AACrC,QAAM,cAAU,yBAAQ,OAAO;AAC/B,MAAI,UAAU,QAAQ;AACtB,MAAI,cAAU,+CAAmB,OAAO;AACxC,QAAM,aAAS,yCAAa;AAAA,IAC1B,GAAG;AAAA,IACH,SAAS;AAAA,IACT,OAAG,6CAAiB,OAAO;AAAA,IAC3B,UAAU,CAAC,MAAM;AACf,gBAAU,EAAE,QAAQ;AACpB,cAAQ,EAAE,WAAW,CAAC;AAAA,IACxB;AAAA,EACF,CAAC;AAED,oCAAa,MAAM;AACjB,UAAM,OAAO,QAAQ;AACrB,QAAI,KAAK,YAAY,SAAS;AAC5B,gBAAU,KAAK;AACf,kDAAY,QAAQ,KAAK,OAAO;AAAA,IAClC;AACA,UAAM,kBAAc,+CAAmB,IAAI;AAC3C,QAAI,KAAC,+CAAmB,SAAS,WAAW,GAAG;AAC7C,gBAAU;AACV,aAAO,WAAW,WAAW;AAAA,IAC/B;AAAA,EACF,CAAC;AAED,iCAAU,MAAM,OAAO,QAAQ,CAAC;AAChC,SAAO;AACT;","names":[]}
@@ -0,0 +1,21 @@
1
+ import { Accessor } from 'solid-js';
2
+ import { EditorOptions, Editor } from 'open-wysiwyg-editor';
3
+ export * from 'open-wysiwyg-editor';
4
+
5
+ type RichTextOptions = Omit<EditorOptions, "element">;
6
+ /**
7
+ * Mounts the full editor in the element it is used on, and destroys it with the owner.
8
+ * When `content` changes, the editor loads it, unless it is the HTML the editor just produced.
9
+ * Callbacks and the runtime options (`editable`, `placeholder`, `aria*`, `dir`, `contentLang`)
10
+ * follow changes; the rest is read once. `onCreate` receives the editor instance.
11
+ */
12
+ declare function richText(node: HTMLElement, accessor: Accessor<RichTextOptions | undefined>): Editor;
13
+ declare module "solid-js" {
14
+ namespace JSX {
15
+ interface Directives {
16
+ richText: RichTextOptions;
17
+ }
18
+ }
19
+ }
20
+
21
+ export { type RichTextOptions, richText };
@@ -0,0 +1,21 @@
1
+ import { Accessor } from 'solid-js';
2
+ import { EditorOptions, Editor } from 'open-wysiwyg-editor';
3
+ export * from 'open-wysiwyg-editor';
4
+
5
+ type RichTextOptions = Omit<EditorOptions, "element">;
6
+ /**
7
+ * Mounts the full editor in the element it is used on, and destroys it with the owner.
8
+ * When `content` changes, the editor loads it, unless it is the HTML the editor just produced.
9
+ * Callbacks and the runtime options (`editable`, `placeholder`, `aria*`, `dir`, `contentLang`)
10
+ * follow changes; the rest is read once. `onCreate` receives the editor instance.
11
+ */
12
+ declare function richText(node: HTMLElement, accessor: Accessor<RichTextOptions | undefined>): Editor;
13
+ declare module "solid-js" {
14
+ namespace JSX {
15
+ interface Directives {
16
+ richText: RichTextOptions;
17
+ }
18
+ }
19
+ }
20
+
21
+ export { type RichTextOptions, richText };
package/dist/index.js ADDED
@@ -0,0 +1,43 @@
1
+ // src/index.ts
2
+ import { createEffect, onCleanup, untrack } from "solid-js";
3
+ import {
4
+ createEditor,
5
+ forwardCallbacks,
6
+ pickRuntimeOptions,
7
+ sameRuntimeOptions,
8
+ syncContent
9
+ } from "open-wysiwyg-editor";
10
+ export * from "open-wysiwyg-editor";
11
+ function richText(node, accessor) {
12
+ const current = () => accessor() ?? {};
13
+ const initial = untrack(current);
14
+ let applied = initial.content;
15
+ let runtime = pickRuntimeOptions(initial);
16
+ const editor = createEditor({
17
+ ...initial,
18
+ element: node,
19
+ ...forwardCallbacks(current),
20
+ onUpdate: (e) => {
21
+ applied = e.getHTML();
22
+ current().onUpdate?.(e);
23
+ }
24
+ });
25
+ createEffect(() => {
26
+ const next = current();
27
+ if (next.content !== applied) {
28
+ applied = next.content;
29
+ syncContent(editor, next.content);
30
+ }
31
+ const nextRuntime = pickRuntimeOptions(next);
32
+ if (!sameRuntimeOptions(runtime, nextRuntime)) {
33
+ runtime = nextRuntime;
34
+ editor.setOptions(nextRuntime);
35
+ }
36
+ });
37
+ onCleanup(() => editor.destroy());
38
+ return editor;
39
+ }
40
+ export {
41
+ richText
42
+ };
43
+ //# sourceMappingURL=index.js.map