neiki-editor 3.3.0 → 3.5.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/README.md CHANGED
@@ -1,18 +1,17 @@
1
1
  <p align="center">
2
- <img src="assets/logo.png" alt="Neiki's Editor" width="400">
2
+ <img src="assets/logo.svg" alt="Neiki's Editor" width="400">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">Neiki's Editor</h1>
6
6
 
7
7
  <p align="center">
8
8
  <img src="https://img.shields.io/badge/javascript-%23323330.svg?style=for-the-badge&logo=javascript&logoColor=%23F7DF1E" alt="JavaScript">
9
- <img src="https://img.shields.io/badge/php-%23777BB4.svg?style=for-the-badge&logo=php&logoColor=white" alt="PHP">
10
9
  <img src="https://img.shields.io/badge/html5-%23E34F26.svg?style=for-the-badge&logo=html5&logoColor=white" alt="HTML5">
11
10
  <img src="https://img.shields.io/badge/css-%23663399.svg?style=for-the-badge&logo=css&logoColor=white" alt="CSS">
12
11
  <img src="https://img.shields.io/badge/web%20components-29ABE2.svg?style=for-the-badge&logo=webcomponentsdotorg&logoColor=white" alt="Web Components">
13
12
  <br>
14
13
  <img src="https://img.shields.io/badge/License-AGPL--3.0-2563EB?style=for-the-badge&logo=open-source-initiative&logoColor=white&labelColor=000F15&logoWidth=20" alt="License">
15
- <img src="https://img.shields.io/badge/Version-3.3.0-2563EB?style=for-the-badge&logo=semantic-release&logoColor=white&labelColor=000F15&logoWidth=20" alt="Version">
14
+ <img src="https://img.shields.io/badge/Version-3.5.0-2563EB?style=for-the-badge&logo=semantic-release&logoColor=white&labelColor=000F15&logoWidth=20" alt="Version">
16
15
  </p>
17
16
 
18
17
  <p align="center">
@@ -32,8 +31,19 @@
32
31
  </p>
33
32
 
34
33
  ---
34
+
35
35
  <p align="center">
36
- <img src="assets/preview.png" alt="Neiki's Editor" width="900">
36
+ <img src="assets/neiki-editor-light.png" alt="Neiki's Editor" width="900">
37
+ </p>
38
+
39
+ <p align="center">
40
+ <img src="assets/neiki-editor-blue.png" alt="Blue Theme" width="260">
41
+ <img src="assets/neiki-editor-dark.png" alt="Dark Theme" width="260">
42
+ <img src="assets/neiki-editor-dark-blue.png" alt="Dark Blue Theme" width="260">
43
+ <img src="assets/neiki-editor-midnight.png" alt="Midnight Theme" width="260">
44
+ <img src="assets/neiki-editor-void.png" alt="Void Theme" width="260">
45
+ <img src="assets/neiki-editor-autumn.png" alt="Autumn Theme" width="260">
46
+ <img src="assets/neiki-editor-dracula.png" alt="Dracula Theme" width="260">
37
47
  </p>
38
48
 
39
49
  ---
@@ -110,7 +120,7 @@ If you want a content editor that you can read, host, and reason about as a sing
110
120
  - Plugin API for custom toolbar buttons and init hooks
111
121
  - PHP integration helper with asset loading, rendering, and HTML sanitization
112
122
  - Eight built-in UI languages: `en`, `cs`, `zh`, `es`, `de`, `fr`, `pt`, `ja`
113
- - Seven built-in themes: Light, Dark, Blue, Dark Blue, Midnight, Void, Autumn
123
+ - Eight built-in themes: Light, Dark, Blue, Dark Blue, Midnight, Void, Autumn, Dracula
114
124
  - Lifecycle callbacks: `onReady`, `onChange`, `onSave`, `onFocus`, `onBlur`
115
125
 
116
126
  ---
@@ -130,7 +140,7 @@ The recommended install is the single bundled script from the CDN. CSS is includ
130
140
  **Pin a specific version**
131
141
 
132
142
  ```html
133
- <script src="https://cdn.neikiri.dev/neiki-editor/3.3.0/neiki-editor.min.js"></script>
143
+ <script src="https://cdn.neikiri.dev/neiki-editor/3.5.0/neiki-editor.min.js"></script>
134
144
  ```
135
145
 
136
146
  **Load CSS and JS separately**
@@ -141,8 +151,8 @@ The recommended install is the single bundled script from the CDN. CSS is includ
141
151
  <script src="https://cdn.neikiri.dev/neiki-editor/neiki-editor.js"></script>
142
152
 
143
153
  <!-- Or pinned -->
144
- <link rel="stylesheet" href="https://cdn.neikiri.dev/neiki-editor/3.3.0/neiki-editor.css">
145
- <script src="https://cdn.neikiri.dev/neiki-editor/3.3.0/neiki-editor.js"></script>
154
+ <link rel="stylesheet" href="https://cdn.neikiri.dev/neiki-editor/3.5.0/neiki-editor.css">
155
+ <script src="https://cdn.neikiri.dev/neiki-editor/3.5.0/neiki-editor.js"></script>
146
156
  ```
147
157
 
148
158
  **Alternative CDN — jsDelivr**
@@ -150,7 +160,7 @@ The recommended install is the single bundled script from the CDN. CSS is includ
150
160
  ```html
151
161
  <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@latest/dist/neiki-editor.min.js"></script>
152
162
  <!-- Pinned -->
153
- <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.3.0/dist/neiki-editor.min.js"></script>
163
+ <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.5.0/dist/neiki-editor.min.js"></script>
154
164
  ```
155
165
 
156
166
  **Package manager**
@@ -230,7 +240,7 @@ const editor = new NeikiEditor('#editor', {
230
240
  placeholder: 'Start typing...',
231
241
  minHeight: 300,
232
242
  maxHeight: 600,
233
- theme: 'light', // 'light' | 'dark' | 'blue' | 'dark-blue' | 'midnight' | 'void' | 'autumn'
243
+ theme: 'light', // 'light' | 'dark' | 'blue' | 'dark-blue' | 'midnight' | 'void' | 'autumn' | 'dracula'
234
244
  language: 'en', // 'en' | 'cs' | 'zh' | 'es' | 'de' | 'fr' | 'pt' | 'ja'
235
245
  onChange: function (content, editor) {
236
246
  console.log('Content changed:', content);
@@ -248,12 +258,12 @@ const editor = new NeikiEditor('#editor', {
248
258
  | `autofocus` | `boolean` | `false` | Focus the editor on initialization |
249
259
  | `spellcheck` | `boolean` | `true` | Enable browser spellcheck |
250
260
  | `readonly` | `boolean` | `false` | Make the editor read-only |
251
- | `theme` | `string` | `'light'` | `'light'`, `'dark'`, `'blue'`, `'dark-blue'`, `'midnight'`, `'void'`, or `'autumn'` |
261
+ | `theme` | `string` | `'light'` | `'light'`, `'dark'`, `'blue'`, `'dark-blue'`, `'midnight'`, `'void'`, `'autumn'`, or `'dracula'` |
252
262
  | `language` | `string` | `'en'` | UI language (see list above) |
253
263
  | `translations` | `object \| null` | `null` | Custom translation keys, merged with built-ins |
254
264
  | `contextMenu` | `boolean` | `true` | Enable the custom right-click context menu. Set to `false` to fall back to the browser's native menu |
255
265
  | `autosaveKey` | `string \| null` | `null` | Custom `localStorage` scope for autosave |
256
- | `customClass` | `string \| null` | `null` | Extra CSS class appended to the content area (`neiki-content`) |
266
+ | `customClass` | `string \| null` | `null` | Custom content style class that replaces the default `neiki-content-default-style` class |
257
267
  | `toolbar` | `array` | *(full set)* | Toolbar button configuration |
258
268
  | `showHelp` | `boolean` | `true` | Show the Help item in the More menu (⋯) |
259
269
  | `imageUploadHandler` | `function \| null` | `null` | Async `(file) => Promise<url>` for server/CDN image uploads instead of base64 |
@@ -286,25 +296,27 @@ Available identifiers include text formatting (`bold`, `italic`, `underline`, `s
286
296
 
287
297
  ### Themes
288
298
 
289
- Seven themes ship by default: `light`, `dark`, `blue`, `dark-blue`, `midnight`, `void` (a dark purple cyberpunk theme with neon-purple glow accents), and `autumn` (a warm retro theme with a brown background and orange accents). Set one at init or change it at runtime:
299
+ Eight themes ship by default: `light`, `dark`, `blue`, `dark-blue`, `midnight`, `void` (a dark purple cyberpunk theme with neon-purple glow accents), `autumn` (a warm retro theme with a brown background and orange accents), and `dracula` (the official [Dracula theme](https://draculatheme.com) — a dark purple-blue palette with pink, purple, green, and yellow accents). Set one at init or change it at runtime:
290
300
 
291
301
  ```javascript
292
302
  const editor = new NeikiEditor('#editor', { theme: 'dark' });
293
303
 
294
- editor.setTheme('void'); // set a specific theme
295
- editor.toggleTheme(); // cycle: light → dark → blue → dark-blue → midnight → void → autumn → light
304
+ editor.setTheme('dracula'); // set a specific theme
305
+ editor.toggleTheme(); // cycle: light → dark → blue → dark-blue → midnight → void → autumn → dracula → light
296
306
  ```
297
307
 
298
308
  > The selected theme is persisted to `localStorage` as a **global** setting. It applies to all editor instances on the page and persists across reloads. If a user has already chosen a theme, that saved preference takes priority over the `theme` config value — call `setTheme()` after init if you need to override it.
299
309
 
300
310
  ### Custom content styling
301
311
 
302
- Use `customClass` to add your own class to the content area without overriding the defaults:
312
+ By default, the editable area uses the internal `neiki-content` class plus the default typography class `neiki-content-default-style`. Use `customClass` to replace only the default typography class while keeping the internal editor class:
303
313
 
304
314
  ```javascript
305
315
  new NeikiEditor('#editor', { customClass: 'article-content' });
306
316
  ```
307
317
 
318
+ That renders the content area with `class="neiki-content article-content"` instead of `class="neiki-content neiki-content-default-style"`.
319
+
308
320
  ```css
309
321
  .article-content {
310
322
  font-family: Georgia, serif;
@@ -341,6 +353,18 @@ Autosave is toggled from the More menu (⋯). When enabled, content is written t
341
353
  new NeikiEditor('#editor', { autosaveKey: 'article-42' });
342
354
  ```
343
355
 
356
+ You can clear saved autosave data for the current editor, remove a specific scoped storage key, or remove all scoped keys matching a prefix:
357
+
358
+ ```javascript
359
+ editor.clearAutosaveStorage(); // current editor's autosave flags/content
360
+ editor.removeStorage('autosave_content'); // current editor storage key
361
+ editor.removeStorageByPrefix('autosave_'); // current editor storage keys by prefix
362
+
363
+ NeikiEditor.removeStorageKey('neiki_autosave_article-42_autosave_content');
364
+ NeikiEditor.removeStorageByPrefix('neiki_autosave_');
365
+ NeikiEditor.clearAutosaveStorage(); // shortcut for the default autosave prefix
366
+ ```
367
+
344
368
  > Autosave is intended for drafts and recovery. For production persistence, use the `onSave` or `onChange` callbacks to save to your backend.
345
369
 
346
370
  ---
@@ -376,6 +400,11 @@ editor.triggerSave(); // trigger onSave
376
400
  editor.previewContent(); // open preview modal
377
401
  editor.downloadContent(); // download as .html
378
402
 
403
+ // Storage cleanup
404
+ editor.clearAutosaveStorage();
405
+ editor.removeStorage('autosave_content');
406
+ editor.removeStorageByPrefix('autosave_');
407
+
379
408
  // Theme
380
409
  editor.setTheme('dark');
381
410
  editor.toggleTheme();
@@ -388,6 +417,9 @@ editor.execCommand('foreColor', '#ff0000');
388
417
  NeikiEditor.registerPlugin({ /* ... */ });
389
418
  NeikiEditor.getPlugins(); // array of registered plugins
390
419
  NeikiEditor.addTranslation('de', { /* ... */ });
420
+ NeikiEditor.removeStorageKey('neiki_autosave_article-42_autosave_content');
421
+ NeikiEditor.removeStorageByPrefix('neiki_autosave_');
422
+ NeikiEditor.clearAutosaveStorage();
391
423
  ```
392
424
 
393
425
  > In SPA frameworks, always call `editor.destroy()` when the component unmounts to clean up listeners and avoid memory leaks.
@@ -608,45 +640,6 @@ Neiki's Editor uses `contentEditable` and standard DOM APIs and targets current
608
640
 
609
641
  ---
610
642
 
611
- ## File structure
612
-
613
- ```
614
- neiki-editor/
615
- ├── .github/
616
- │ └── workflows/
617
- │ ├── ci.yml # Continuous integration workflow
618
- │ ├── codeql.yml # CodeQL security analysis
619
- │ └── publish.yml # NPM/CDN publish workflow
620
- ├── assets/
621
- │ ├── logo.png # Project logo
622
- │ └── preview.png # Editor preview screenshot
623
- ├── demo/
624
- │ ├── index.html # Interactive demo page
625
- │ └── logo.png # Demo page logo
626
- ├── dist/
627
- │ ├── neiki-editor.css # Editor styles (unminified)
628
- │ ├── neiki-editor.js # Editor core (unminified)
629
- │ ├── neiki-editor.min.css # Minified styles
630
- │ └── neiki-editor.min.js # Minified editor + embedded CSS (recommended)
631
- ├── php/
632
- │ └── neiki-editor.php # PHP integration helper
633
- ├── src/
634
- │ ├── neiki-editor.css # Source CSS styles
635
- │ └── neiki-editor.js # Source JavaScript
636
- ├── .gitattributes
637
- ├── .gitignore
638
- ├── CHANGELOG.md
639
- ├── CODE_OF_CONDUCT.md
640
- ├── CONTRIBUTING.md
641
- ├── LICENSE
642
- ├── README.md
643
- ├── SECURITY.md
644
- ├── package-lock.json
645
- └── package.json
646
- ```
647
-
648
- ---
649
-
650
643
  ## Contributing
651
644
 
652
645
  Contributions are welcome. Please review [CONTRIBUTING.md](CONTRIBUTING.md) and the [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before opening an issue or pull request. Security-related reports should follow [SECURITY.md](SECURITY.md).
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * NeikiEditor - Production-Ready WYSIWYG Rich Text Editor
3
3
  * CSS Stylesheet
4
- * Version: 3.3.0
4
+ * Version: 3.5.0
5
5
  */
6
6
 
7
7
  /* ============================================
@@ -268,6 +268,61 @@
268
268
  color: var(--neiki-text-secondary);
269
269
  }
270
270
 
271
+ /* Dracula Theme — https://draculatheme.com */
272
+ .neiki-editor.neiki-theme-dracula,
273
+ .neiki-modal-overlay.neiki-theme-dracula,
274
+ .neiki-context-menu.neiki-theme-dracula {
275
+ --neiki-bg-primary: #282a36;
276
+ --neiki-bg-secondary: #21222c;
277
+ --neiki-bg-tertiary: #343746;
278
+ --neiki-bg-hover: #44475a;
279
+ --neiki-bg-active: #6272a4;
280
+ --neiki-border-color: #44475a;
281
+ --neiki-border-color-dark: #6272a4;
282
+ --neiki-text-primary: #f8f8f2;
283
+ --neiki-text-secondary: #bd93f9;
284
+ --neiki-text-muted: #6272a4;
285
+ --neiki-accent-color: #bd93f9;
286
+ --neiki-accent-hover: #ff79c6;
287
+ --neiki-danger-color: #ff5555;
288
+ --neiki-success-color: #50fa7b;
289
+ --neiki-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.25);
290
+ --neiki-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35);
291
+ --neiki-shadow-lg: 0 16px 32px rgba(0, 0, 0, 0.45);
292
+ --neiki-editor-bg: #282a36;
293
+ --neiki-toolbar-bg: #21222c;
294
+ --neiki-statusbar-bg: #21222c;
295
+ --neiki-modal-overlay: rgba(0, 0, 0, 0.75);
296
+ --neiki-scrollbar-thumb: #6272a4;
297
+ --neiki-scrollbar-track: #343746;
298
+ --neiki-selection-bg: #44475a;
299
+ --neiki-highlight-color: #6b6423;
300
+ --neiki-table-border: #44475a;
301
+ }
302
+
303
+ /* Dracula — toolbar selects and fontsize input text color */
304
+ .neiki-editor.neiki-theme-dracula .neiki-select,
305
+ .neiki-editor.neiki-theme-dracula .neiki-fontsize-input {
306
+ color: var(--neiki-text-secondary);
307
+ }
308
+
309
+ /* Dracula — code view syntax highlighting */
310
+ .neiki-editor.neiki-theme-dracula .neiki-html-tag {
311
+ color: #ff79c6;
312
+ }
313
+
314
+ .neiki-editor.neiki-theme-dracula .neiki-html-attr {
315
+ color: #50fa7b;
316
+ }
317
+
318
+ .neiki-editor.neiki-theme-dracula .neiki-html-string {
319
+ color: #f1fa8c;
320
+ }
321
+
322
+ .neiki-editor.neiki-theme-dracula .neiki-html-comment {
323
+ color: #6272a4;
324
+ }
325
+
271
326
  /* ============================================
272
327
  Base Editor Container
273
328
  ============================================ */
@@ -613,58 +668,58 @@
613
668
  background: var(--neiki-border-color-dark);
614
669
  }
615
670
 
616
- /* Content Typography */
617
- .neiki-content p {
671
+ /* Default Content Typography */
672
+ .neiki-content-default-style p {
618
673
  margin: 0 0 1em 0;
619
674
  }
620
675
 
621
- .neiki-content h1 {
676
+ .neiki-content-default-style h1 {
622
677
  font-size: 2em;
623
678
  font-weight: 700;
624
679
  margin: 0 0 0.5em 0;
625
680
  }
626
681
 
627
- .neiki-content h2 {
682
+ .neiki-content-default-style h2 {
628
683
  font-size: 1.5em;
629
684
  font-weight: 600;
630
685
  margin: 0 0 0.5em 0;
631
686
  }
632
687
 
633
- .neiki-content h3 {
688
+ .neiki-content-default-style h3 {
634
689
  font-size: 1.25em;
635
690
  font-weight: 600;
636
691
  margin: 0 0 0.5em 0;
637
692
  }
638
693
 
639
- .neiki-content h4 {
694
+ .neiki-content-default-style h4 {
640
695
  font-size: 1.1em;
641
696
  font-weight: 600;
642
697
  margin: 0 0 0.5em 0;
643
698
  }
644
699
 
645
- .neiki-content h5 {
700
+ .neiki-content-default-style h5 {
646
701
  font-size: 1em;
647
702
  font-weight: 600;
648
703
  margin: 0 0 0.5em 0;
649
704
  }
650
705
 
651
- .neiki-content h6 {
706
+ .neiki-content-default-style h6 {
652
707
  font-size: 0.9em;
653
708
  font-weight: 600;
654
709
  margin: 0 0 0.5em 0;
655
710
  }
656
711
 
657
- .neiki-content ul,
658
- .neiki-content ol {
712
+ .neiki-content-default-style ul,
713
+ .neiki-content-default-style ol {
659
714
  margin: 0 0 1em 0;
660
715
  padding-left: 2em;
661
716
  }
662
717
 
663
- .neiki-content li {
718
+ .neiki-content-default-style li {
664
719
  margin-bottom: 0.25em;
665
720
  }
666
721
 
667
- .neiki-content blockquote {
722
+ .neiki-content-default-style blockquote {
668
723
  margin: 0 0 1em 0;
669
724
  padding: 10px 20px;
670
725
  border-left: 4px solid var(--neiki-accent-color);
@@ -672,7 +727,7 @@
672
727
  font-style: italic;
673
728
  }
674
729
 
675
- .neiki-content pre {
730
+ .neiki-content-default-style pre {
676
731
  margin: 0 0 1em 0;
677
732
  padding: 16px;
678
733
  background: var(--neiki-bg-tertiary);
@@ -682,7 +737,7 @@
682
737
  font-size: 13px;
683
738
  }
684
739
 
685
- .neiki-content code {
740
+ .neiki-content-default-style code {
686
741
  padding: 2px 6px;
687
742
  background: var(--neiki-bg-tertiary);
688
743
  border-radius: 3px;
@@ -690,16 +745,16 @@
690
745
  font-size: 0.9em;
691
746
  }
692
747
 
693
- .neiki-content a {
748
+ .neiki-content-default-style a {
694
749
  color: var(--neiki-accent-color);
695
750
  text-decoration: underline;
696
751
  }
697
752
 
698
- .neiki-content a:hover {
753
+ .neiki-content-default-style a:hover {
699
754
  color: var(--neiki-accent-hover);
700
755
  }
701
756
 
702
- .neiki-content img {
757
+ .neiki-content-default-style img {
703
758
  display: inline-block;
704
759
  max-width: 100%;
705
760
  height: auto;
@@ -707,12 +762,12 @@
707
762
  cursor: pointer;
708
763
  }
709
764
 
710
- .neiki-content img.neiki-selected {
765
+ .neiki-content-default-style img.neiki-selected {
711
766
  outline: 2px solid var(--neiki-accent-color);
712
767
  outline-offset: 2px;
713
768
  }
714
769
 
715
- .neiki-content video {
770
+ .neiki-content-default-style video {
716
771
  display: block;
717
772
  max-width: 100%;
718
773
  height: auto;
@@ -735,7 +790,7 @@
735
790
  /* ============================================
736
791
  Tables
737
792
  ============================================ */
738
- .neiki-content table,
793
+ .neiki-content-default-style table,
739
794
  .neiki-table {
740
795
  width: 100%;
741
796
  border-collapse: collapse;
@@ -743,8 +798,8 @@
743
798
  table-layout: fixed;
744
799
  }
745
800
 
746
- .neiki-content table td,
747
- .neiki-content table th {
801
+ .neiki-content-default-style table td,
802
+ .neiki-content-default-style table th {
748
803
  border: 1px solid var(--neiki-table-border);
749
804
  padding: 8px 12px;
750
805
  min-width: 50px;
@@ -752,18 +807,18 @@
752
807
  position: relative;
753
808
  }
754
809
 
755
- .neiki-content table th {
810
+ .neiki-content-default-style table th {
756
811
  background: var(--neiki-bg-secondary);
757
812
  font-weight: 600;
758
813
  }
759
814
 
760
- .neiki-content table td:hover,
761
- .neiki-content table th:hover {
815
+ .neiki-content-default-style table td:hover,
816
+ .neiki-content-default-style table th:hover {
762
817
  background: var(--neiki-bg-hover);
763
818
  }
764
819
 
765
- .neiki-content table td.neiki-cell-selected,
766
- .neiki-content table th.neiki-cell-selected {
820
+ .neiki-content-default-style table td.neiki-cell-selected,
821
+ .neiki-content-default-style table th.neiki-cell-selected {
767
822
  background: var(--neiki-selection-bg);
768
823
  }
769
824