neiki-editor 3.0.1 → 3.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.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="logo.png" alt="Neiki's Editor" width="400">
2
+ <img src="assets/logo.png" alt="Neiki's Editor" width="400">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">Neiki's Editor</h1>
@@ -11,7 +11,7 @@
11
11
  <img src="https://img.shields.io/badge/css-%23663399.svg?style=for-the-badge&logo=css&logoColor=white" alt="CSS">
12
12
  <br>
13
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">
14
- <img src="https://img.shields.io/badge/Version-3.0.1-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.0.2-2563EB?style=for-the-badge&logo=semantic-release&logoColor=white&labelColor=000F15&logoWidth=20" alt="Version">
15
15
  </p>
16
16
 
17
17
  <p align="center">
@@ -33,12 +33,12 @@
33
33
 
34
34
  ---
35
35
  <p align="center">
36
- <img src="preview.png" alt="Neiki's Editor" width="900">
36
+ <img src="assets/preview.png" alt="Neiki's Editor" width="900">
37
37
  </p>
38
38
 
39
39
  ---
40
40
 
41
- **Live version:** [https://neikiri.dev/editor](https://neikiri.dev/editor)
41
+ **Live version:** [https://neikiri.dev/editor](https://neikiri.dev/editor) · **Documentation:** [Wiki](https://github.com/neikiri/neiki-editor/wiki)
42
42
 
43
43
  ---
44
44
 
@@ -62,7 +62,7 @@ Add this single line — CSS is included automatically, always the **latest vers
62
62
  #### Pin a specific version
63
63
 
64
64
  ```html
65
- <script src="https://cdn.neikiri.dev/neiki-editor/3.0.1/neiki-editor.min.js"></script>
65
+ <script src="https://cdn.neikiri.dev/neiki-editor/3.0.2/neiki-editor.min.js"></script>
66
66
  ```
67
67
 
68
68
  #### Load CSS and JS separately
@@ -73,8 +73,8 @@ Add this single line — CSS is included automatically, always the **latest vers
73
73
  <script src="https://cdn.neikiri.dev/neiki-editor/neiki-editor.js"></script>
74
74
 
75
75
  <!-- Or pinned -->
76
- <link rel="stylesheet" href="https://cdn.neikiri.dev/neiki-editor/3.0.1/neiki-editor.css">
77
- <script src="https://cdn.neikiri.dev/neiki-editor/3.0.1/neiki-editor.js"></script>
76
+ <link rel="stylesheet" href="https://cdn.neikiri.dev/neiki-editor/3.0.2/neiki-editor.css">
77
+ <script src="https://cdn.neikiri.dev/neiki-editor/3.0.2/neiki-editor.js"></script>
78
78
  ```
79
79
 
80
80
  #### Alternative CDN — jsDelivr
@@ -84,15 +84,15 @@ Add this single line — CSS is included automatically, always the **latest vers
84
84
  <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@latest/dist/neiki-editor.min.js"></script>
85
85
 
86
86
  <!-- Pinned -->
87
- <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.0.1/dist/neiki-editor.min.js"></script>
87
+ <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.0.2/dist/neiki-editor.min.js"></script>
88
88
 
89
89
  <!-- Separate files (latest) -->
90
90
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@latest/dist/neiki-editor.css">
91
91
  <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@latest/dist/neiki-editor.js"></script>
92
92
 
93
93
  <!-- Separate files (pinned) -->
94
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.0.1/dist/neiki-editor.css">
95
- <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.0.1/dist/neiki-editor.js"></script>
94
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.0.2/dist/neiki-editor.css">
95
+ <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.0.2/dist/neiki-editor.js"></script>
96
96
  ```
97
97
 
98
98
  #### Package Manager
@@ -747,24 +747,37 @@ function NeikiEditorComponent({ value, onChange }) {
747
747
 
748
748
  ```
749
749
  neiki-editor/
750
- ├── dist/
751
- ├── neiki-editor.min.js # Minified editor + embedded CSS (recommended)
752
- ├── neiki-editor.min.css # Minified styles
753
- ├── neiki-editor.js # Editor core (unminified)
754
- └── neiki-editor.css # Editor styles (unminified)
750
+ ├── .github/
751
+ └── workflows/
752
+ ├── ci.yml # Continuous integration workflow
753
+ ├── codeql.yml # CodeQL security analysis
754
+ └── publish.yml # NPM/CDN publish workflow
755
+ ├── assets/
756
+ │ ├── logo.png # Project logo
757
+ │ └── preview.png # Editor preview screenshot
755
758
  ├── demo/
756
- └── index.html # Interactive demo page
757
- │ └── logo.png # Demo logo
759
+ ├── index.html # Interactive demo page
760
+ │ └── logo.png # Demo page logo
761
+ ├── dist/
762
+ │ ├── neiki-editor.css # Editor styles (unminified)
763
+ │ ├── neiki-editor.js # Editor core (unminified)
764
+ │ ├── neiki-editor.min.css # Minified styles
765
+ │ └── neiki-editor.min.js # Minified editor + embedded CSS (recommended)
758
766
  ├── php/
759
- │ └── neiki-editor.php # PHP integration helper
760
- ├── logo.png
761
- ├── package.json
762
- ├── README.md
763
- ├── LICENSE
764
- ├── CHANGELOG.md
765
- ├── CONTRIBUTING.md
766
- ├── CODE_OF_CONDUCT.md
767
- └── SECURITY.md
767
+ │ └── neiki-editor.php # PHP integration helper
768
+ ├── src/
769
+ ├── neiki-editor.css # Source CSS styles
770
+ │ └── neiki-editor.js # Source JavaScript
771
+ ├── .gitattributes # Git attributes configuration
772
+ ├── .gitignore # Git ignore rules
773
+ ├── CHANGELOG.md # Project changelog
774
+ ├── CODE_OF_CONDUCT.md # Community code of conduct
775
+ ├── CONTRIBUTING.md # Contribution guidelines
776
+ ├── LICENSE # AGPL-3.0 license
777
+ ├── README.md # Project documentation
778
+ ├── SECURITY.md # Security policy
779
+ ├── package-lock.json # NPM lock file
780
+ └── package.json # NPM package configuration
768
781
  ```
769
782
 
770
783
  ---
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * NeikiEditor - Production-Ready WYSIWYG Rich Text Editor
3
3
  * CSS Stylesheet
4
- * Version: 3.0.1
4
+ * Version: 3.0.2
5
5
  */
6
6
 
7
7
  /* ============================================
@@ -2123,6 +2123,12 @@
2123
2123
  pointer-events: none;
2124
2124
  }
2125
2125
 
2126
+ @media (hover: none), (pointer: coarse) {
2127
+ .neiki-block-grip {
2128
+ display: none !important;
2129
+ }
2130
+ }
2131
+
2126
2132
  .neiki-block-ghost {
2127
2133
  position: fixed;
2128
2134
  z-index: 100000;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NeikiEditor - A Modern WYSIWYG Editor
3
- * Version: 3.0.1
3
+ * Version: 3.0.2
4
4
  *
5
5
  * A lightweight, feature-rich text editor with support for:
6
6
  * - Rich text formatting (bold, italic, underline, etc.)
@@ -196,6 +196,9 @@
196
196
  'imageToolbar.dragToMove': 'Drag to move',
197
197
  'videoToolbar.replaceVideo': 'Replace Video',
198
198
  'videoToolbar.deleteVideo': 'Delete Video',
199
+ 'blockToolbar.moveUp': 'Move block up',
200
+ 'blockToolbar.moveDown': 'Move block down',
201
+ 'blockToolbar.dragToReorder': 'Drag to reorder',
199
202
 
200
203
  // Placeholder
201
204
  'placeholder': 'Start typing...'
@@ -365,6 +368,9 @@
365
368
  'imageToolbar.dragToMove': 'Přetáhněte pro přesun',
366
369
  'videoToolbar.replaceVideo': 'Nahradit video',
367
370
  'videoToolbar.deleteVideo': 'Smazat video',
371
+ 'blockToolbar.moveUp': 'Přesunout blok nahoru',
372
+ 'blockToolbar.moveDown': 'Přesunout blok dolů',
373
+ 'blockToolbar.dragToReorder': 'Přetažením změnit pořadí',
368
374
 
369
375
  'placeholder': 'Začněte psát...'
370
376
  },
@@ -391,6 +397,7 @@
391
397
  'toolbar.outdent': '减少缩进',
392
398
  'toolbar.link': '插入链接 (Ctrl+K)',
393
399
  'toolbar.image': '插入图片',
400
+ 'toolbar.video': '插入视频',
394
401
  'toolbar.table': '插入表格',
395
402
  'toolbar.blockquote': '引用',
396
403
  'toolbar.viewCode': '查看代码 (HTML)',
@@ -423,6 +430,7 @@
423
430
  'font.cursive': '手写',
424
431
  'insert.link': '链接',
425
432
  'insert.image': '图片',
433
+ 'insert.video': '视频',
426
434
  'insert.table': '表格',
427
435
  'insert.emoji': '表情',
428
436
  'insert.symbol': '符号',
@@ -462,6 +470,15 @@
462
470
  'modal.describeImage': '描述图片',
463
471
  'modal.widthOptional': '宽度(可选)',
464
472
  'modal.invalidImageFile': '请选择有效的图片文件。',
473
+ 'modal.insertVideo': '插入视频',
474
+ 'modal.uploadVideo': '上传视频',
475
+ 'modal.convertedVideoToBase64': '将转换为base64',
476
+ 'modal.videoUrl': '视频URL',
477
+ 'modal.videoTitle': '视频标题',
478
+ 'modal.describeVideo': '描述视频',
479
+ 'modal.invalidVideoFile': '请选择有效的视频文件。',
480
+ 'modal.uploadingVideo': '上传中...',
481
+ 'modal.videoUploadError': '视频上传失败,请重试。',
465
482
  'modal.insertTable': '插入表格',
466
483
  'modal.rows': '行',
467
484
  'modal.columns': '列',
@@ -498,6 +515,9 @@
498
515
  'imageToolbar.dragToMove': '拖动移动',
499
516
  'videoToolbar.replaceVideo': '替换视频',
500
517
  'videoToolbar.deleteVideo': '删除视频',
518
+ 'blockToolbar.moveUp': '向上移动块',
519
+ 'blockToolbar.moveDown': '向下移动块',
520
+ 'blockToolbar.dragToReorder': '拖动以重新排序',
501
521
 
502
522
  'placeholder': '开始输入...'
503
523
  },
@@ -523,6 +543,7 @@
523
543
  'toolbar.outdent': 'Disminuir sangría',
524
544
  'toolbar.link': 'Insertar enlace (Ctrl+K)',
525
545
  'toolbar.image': 'Insertar imagen',
546
+ 'toolbar.video': 'Insertar video',
526
547
  'toolbar.table': 'Insertar tabla',
527
548
  'toolbar.blockquote': 'Cita',
528
549
  'toolbar.viewCode': 'Ver código (HTML)',
@@ -555,6 +576,7 @@
555
576
  'font.cursive': 'Cursiva',
556
577
  'insert.link': 'Enlace',
557
578
  'insert.image': 'Imagen',
579
+ 'insert.video': 'Video',
558
580
  'insert.table': 'Tabla',
559
581
  'insert.emoji': 'Emoji',
560
582
  'insert.symbol': 'Símbolo',
@@ -594,6 +616,15 @@
594
616
  'modal.describeImage': 'Describir la imagen',
595
617
  'modal.widthOptional': 'Ancho (opcional)',
596
618
  'modal.invalidImageFile': 'Por favor, seleccione un archivo de imagen válido.',
619
+ 'modal.insertVideo': 'Insertar video',
620
+ 'modal.uploadVideo': 'Subir video',
621
+ 'modal.convertedVideoToBase64': 'Se convertirá a base64',
622
+ 'modal.videoUrl': 'URL de video',
623
+ 'modal.videoTitle': 'Título del video',
624
+ 'modal.describeVideo': 'Describir el video',
625
+ 'modal.invalidVideoFile': 'Por favor, seleccione un archivo de video válido.',
626
+ 'modal.uploadingVideo': 'Subiendo...',
627
+ 'modal.videoUploadError': 'Error al subir el video. Inténtelo de nuevo.',
597
628
  'modal.insertTable': 'Insertar tabla',
598
629
  'modal.rows': 'Filas',
599
630
  'modal.columns': 'Columnas',
@@ -630,6 +661,9 @@
630
661
  'imageToolbar.dragToMove': 'Arrastrar para mover',
631
662
  'videoToolbar.replaceVideo': 'Reemplazar video',
632
663
  'videoToolbar.deleteVideo': 'Eliminar video',
664
+ 'blockToolbar.moveUp': 'Mover bloque arriba',
665
+ 'blockToolbar.moveDown': 'Mover bloque abajo',
666
+ 'blockToolbar.dragToReorder': 'Arrastrar para reordenar',
633
667
 
634
668
  'placeholder': 'Empiece a escribir...'
635
669
  },
@@ -655,6 +689,7 @@
655
689
  'toolbar.outdent': 'Einzug verkleinern',
656
690
  'toolbar.link': 'Link einfügen (Strg+K)',
657
691
  'toolbar.image': 'Bild einfügen',
692
+ 'toolbar.video': 'Video einfügen',
658
693
  'toolbar.table': 'Tabelle einfügen',
659
694
  'toolbar.blockquote': 'Zitat',
660
695
  'toolbar.viewCode': 'Code anzeigen (HTML)',
@@ -687,6 +722,7 @@
687
722
  'font.cursive': 'Schreibschrift',
688
723
  'insert.link': 'Link',
689
724
  'insert.image': 'Bild',
725
+ 'insert.video': 'Video',
690
726
  'insert.table': 'Tabelle',
691
727
  'insert.emoji': 'Emoji',
692
728
  'insert.symbol': 'Symbol',
@@ -726,6 +762,15 @@
726
762
  'modal.describeImage': 'Bild beschreiben',
727
763
  'modal.widthOptional': 'Breite (optional)',
728
764
  'modal.invalidImageFile': 'Bitte wählen Sie eine gültige Bilddatei.',
765
+ 'modal.insertVideo': 'Video einfügen',
766
+ 'modal.uploadVideo': 'Video hochladen',
767
+ 'modal.convertedVideoToBase64': 'Wird in Base64 konvertiert',
768
+ 'modal.videoUrl': 'Video-URL',
769
+ 'modal.videoTitle': 'Videotitel',
770
+ 'modal.describeVideo': 'Video beschreiben',
771
+ 'modal.invalidVideoFile': 'Bitte wählen Sie eine gültige Videodatei.',
772
+ 'modal.uploadingVideo': 'Hochladen...',
773
+ 'modal.videoUploadError': 'Video-Upload fehlgeschlagen. Bitte versuchen Sie es erneut.',
729
774
  'modal.insertTable': 'Tabelle einfügen',
730
775
  'modal.rows': 'Zeilen',
731
776
  'modal.columns': 'Spalten',
@@ -762,6 +807,9 @@
762
807
  'imageToolbar.dragToMove': 'Ziehen zum Verschieben',
763
808
  'videoToolbar.replaceVideo': 'Video ersetzen',
764
809
  'videoToolbar.deleteVideo': 'Video löschen',
810
+ 'blockToolbar.moveUp': 'Block nach oben verschieben',
811
+ 'blockToolbar.moveDown': 'Block nach unten verschieben',
812
+ 'blockToolbar.dragToReorder': 'Zum Neuordnen ziehen',
765
813
 
766
814
  'placeholder': 'Hier schreiben...'
767
815
  },
@@ -787,6 +835,7 @@
787
835
  'toolbar.outdent': 'Diminuer le retrait',
788
836
  'toolbar.link': 'Insérer un lien (Ctrl+K)',
789
837
  'toolbar.image': 'Insérer une image',
838
+ 'toolbar.video': 'Insérer une vidéo',
790
839
  'toolbar.table': 'Insérer un tableau',
791
840
  'toolbar.blockquote': 'Citation',
792
841
  'toolbar.viewCode': 'Voir le code (HTML)',
@@ -819,6 +868,7 @@
819
868
  'font.cursive': 'Cursive',
820
869
  'insert.link': 'Lien',
821
870
  'insert.image': 'Image',
871
+ 'insert.video': 'Vidéo',
822
872
  'insert.table': 'Tableau',
823
873
  'insert.emoji': 'Emoji',
824
874
  'insert.symbol': 'Symbole',
@@ -858,6 +908,15 @@
858
908
  'modal.describeImage': 'Décrire l\'image',
859
909
  'modal.widthOptional': 'Largeur (optionnel)',
860
910
  'modal.invalidImageFile': 'Veuillez sélectionner un fichier image valide.',
911
+ 'modal.insertVideo': 'Insérer une vidéo',
912
+ 'modal.uploadVideo': 'Téléverser une vidéo',
913
+ 'modal.convertedVideoToBase64': 'Sera converti en base64',
914
+ 'modal.videoUrl': 'URL de la vidéo',
915
+ 'modal.videoTitle': 'Titre de la vidéo',
916
+ 'modal.describeVideo': 'Décrire la vidéo',
917
+ 'modal.invalidVideoFile': 'Veuillez sélectionner un fichier vidéo valide.',
918
+ 'modal.uploadingVideo': 'Téléversement...',
919
+ 'modal.videoUploadError': 'Échec du téléversement de la vidéo. Veuillez réessayer.',
861
920
  'modal.insertTable': 'Insérer un tableau',
862
921
  'modal.rows': 'Lignes',
863
922
  'modal.columns': 'Colonnes',
@@ -894,6 +953,9 @@
894
953
  'imageToolbar.dragToMove': 'Glisser pour déplacer',
895
954
  'videoToolbar.replaceVideo': 'Remplacer la vidéo',
896
955
  'videoToolbar.deleteVideo': 'Supprimer la vidéo',
956
+ 'blockToolbar.moveUp': 'Déplacer le bloc vers le haut',
957
+ 'blockToolbar.moveDown': 'Déplacer le bloc vers le bas',
958
+ 'blockToolbar.dragToReorder': 'Glisser pour réorganiser',
897
959
 
898
960
  'placeholder': 'Commencez à écrire...'
899
961
  },
@@ -919,6 +981,7 @@
919
981
  'toolbar.outdent': 'Diminuir recuo',
920
982
  'toolbar.link': 'Inserir link (Ctrl+K)',
921
983
  'toolbar.image': 'Inserir imagem',
984
+ 'toolbar.video': 'Inserir vídeo',
922
985
  'toolbar.table': 'Inserir tabela',
923
986
  'toolbar.blockquote': 'Citação',
924
987
  'toolbar.viewCode': 'Ver código (HTML)',
@@ -951,6 +1014,7 @@
951
1014
  'font.cursive': 'Cursiva',
952
1015
  'insert.link': 'Link',
953
1016
  'insert.image': 'Imagem',
1017
+ 'insert.video': 'Vídeo',
954
1018
  'insert.table': 'Tabela',
955
1019
  'insert.emoji': 'Emoji',
956
1020
  'insert.symbol': 'Símbolo',
@@ -990,6 +1054,15 @@
990
1054
  'modal.describeImage': 'Descrever a imagem',
991
1055
  'modal.widthOptional': 'Largura (opcional)',
992
1056
  'modal.invalidImageFile': 'Por favor, selecione um arquivo de imagem válido.',
1057
+ 'modal.insertVideo': 'Inserir vídeo',
1058
+ 'modal.uploadVideo': 'Enviar vídeo',
1059
+ 'modal.convertedVideoToBase64': 'Será convertido para base64',
1060
+ 'modal.videoUrl': 'URL do vídeo',
1061
+ 'modal.videoTitle': 'Título do vídeo',
1062
+ 'modal.describeVideo': 'Descrever o vídeo',
1063
+ 'modal.invalidVideoFile': 'Por favor, selecione um arquivo de vídeo válido.',
1064
+ 'modal.uploadingVideo': 'Enviando...',
1065
+ 'modal.videoUploadError': 'Falha no envio do vídeo. Tente novamente.',
993
1066
  'modal.insertTable': 'Inserir tabela',
994
1067
  'modal.rows': 'Linhas',
995
1068
  'modal.columns': 'Colunas',
@@ -1026,6 +1099,9 @@
1026
1099
  'imageToolbar.dragToMove': 'Arraste para mover',
1027
1100
  'videoToolbar.replaceVideo': 'Substituir vídeo',
1028
1101
  'videoToolbar.deleteVideo': 'Excluir vídeo',
1102
+ 'blockToolbar.moveUp': 'Mover bloco para cima',
1103
+ 'blockToolbar.moveDown': 'Mover bloco para baixo',
1104
+ 'blockToolbar.dragToReorder': 'Arraste para reordenar',
1029
1105
 
1030
1106
  'placeholder': 'Comece a digitar...'
1031
1107
  },
@@ -1051,6 +1127,7 @@
1051
1127
  'toolbar.outdent': 'インデント減',
1052
1128
  'toolbar.link': 'リンク挿入 (Ctrl+K)',
1053
1129
  'toolbar.image': '画像挿入',
1130
+ 'toolbar.video': '動画挿入',
1054
1131
  'toolbar.table': '表挿入',
1055
1132
  'toolbar.blockquote': '引用',
1056
1133
  'toolbar.viewCode': 'コード表示 (HTML)',
@@ -1083,6 +1160,7 @@
1083
1160
  'font.cursive': '手書き',
1084
1161
  'insert.link': 'リンク',
1085
1162
  'insert.image': '画像',
1163
+ 'insert.video': '動画',
1086
1164
  'insert.table': '表',
1087
1165
  'insert.emoji': '絵文字',
1088
1166
  'insert.symbol': '記号',
@@ -1122,6 +1200,15 @@
1122
1200
  'modal.describeImage': '画像の説明',
1123
1201
  'modal.widthOptional': '幅(任意)',
1124
1202
  'modal.invalidImageFile': '有効な画像ファイルを選択してください。',
1203
+ 'modal.insertVideo': '動画挿入',
1204
+ 'modal.uploadVideo': '動画アップロード',
1205
+ 'modal.convertedVideoToBase64': 'Base64に変換されます',
1206
+ 'modal.videoUrl': '動画URL',
1207
+ 'modal.videoTitle': '動画タイトル',
1208
+ 'modal.describeVideo': '動画の説明',
1209
+ 'modal.invalidVideoFile': '有効な動画ファイルを選択してください。',
1210
+ 'modal.uploadingVideo': 'アップロード中...',
1211
+ 'modal.videoUploadError': '動画のアップロードに失敗しました。もう一度お試しください。',
1125
1212
  'modal.insertTable': '表挿入',
1126
1213
  'modal.rows': '行',
1127
1214
  'modal.columns': '列',
@@ -1158,6 +1245,9 @@
1158
1245
  'imageToolbar.dragToMove': 'ドラッグで移動',
1159
1246
  'videoToolbar.replaceVideo': '動画を置換',
1160
1247
  'videoToolbar.deleteVideo': '動画を削除',
1248
+ 'blockToolbar.moveUp': 'ブロックを上へ移動',
1249
+ 'blockToolbar.moveDown': 'ブロックを下へ移動',
1250
+ 'blockToolbar.dragToReorder': 'ドラッグして並べ替え',
1161
1251
 
1162
1252
  'placeholder': '入力してください...'
1163
1253
  }
@@ -2798,10 +2888,10 @@
2798
2888
  <button class="neiki-modal-close" type="button">${Icons.close}</button>
2799
2889
  </div>
2800
2890
  <div class="neiki-modal-body" style="text-align: center; padding: 24px 20px;">
2801
- <img src="https://github.com/neikiri/neiki-editor/raw/main/logo.png" alt="Neiki's Editor" style="width: 120px; height: auto; margin: 0 auto 16px; display: block;">
2891
+ <img src="https://github.com/neikiri/neiki-editor/raw/main/assets/logo.png" alt="Neiki's Editor" style="width: 240px; height: auto; margin: 0 auto 16px; display: block;">
2802
2892
  <div style="font-size: 14px; line-height: 2; color: var(--neiki-text-primary);">
2803
2893
  <div><strong>${Utils.escapeHTML(t('help.author'))}:</strong> neikiri (Jindřich Stoklasa)</div>
2804
- <div><strong>${Utils.escapeHTML(t('help.version'))}:</strong> 3.0.1</div>
2894
+ <div><strong>${Utils.escapeHTML(t('help.version'))}:</strong> 3.0.2</div>
2805
2895
  <div><strong>${Utils.escapeHTML(t('help.github'))}:</strong> <a href="https://github.com/neikiri/neiki-editor" target="_blank" rel="noopener noreferrer" style="color: var(--neiki-accent);">github.com/neikiri/neiki-editor</a></div>
2806
2896
  <div><strong>${Utils.escapeHTML(t('help.documentation'))}:</strong> <a href="https://github.com/neikiri/neiki-editor/wiki" target="_blank" rel="noopener noreferrer" style="color: var(--neiki-accent);">Wiki</a></div>
2807
2897
  </div>
@@ -3382,15 +3472,18 @@
3382
3472
  insertHorizontalRule() { this.exec('insertHorizontalRule'); }
3383
3473
 
3384
3474
  formatBlock(tag) {
3385
- // Toggle blockquote: if already inside one, revert to paragraph
3386
3475
  if (tag === 'blockquote') {
3387
3476
  const sel = window.getSelection();
3388
3477
  if (sel && sel.rangeCount) {
3389
- let node = sel.getRangeAt(0).startContainer;
3478
+ const range = sel.getRangeAt(0);
3479
+ let node = range.startContainer;
3390
3480
  if (node.nodeType === Node.TEXT_NODE) node = node.parentNode;
3391
3481
  const bq = node.closest ? node.closest('blockquote') : null;
3392
3482
  if (bq && this.editor.contentArea.contains(bq)) {
3393
- this.exec('formatBlock', '<p>');
3483
+ this._unwrapBlockquote(bq, range);
3484
+ this.editor.history.record();
3485
+ this.editor.updateToolbar();
3486
+ this.editor.triggerChange();
3394
3487
  return;
3395
3488
  }
3396
3489
  }
@@ -3398,6 +3491,65 @@
3398
3491
  this.exec('formatBlock', `<${tag}>`);
3399
3492
  }
3400
3493
 
3494
+ _unwrapBlockquote(blockquote, range) {
3495
+ const parent = blockquote.parentNode;
3496
+ const marker = document.createElement('span');
3497
+ marker.setAttribute('data-neiki-selection-marker', 'true');
3498
+ marker.style.display = 'none';
3499
+
3500
+ if (range && range.collapsed) {
3501
+ const markerRange = range.cloneRange();
3502
+ markerRange.insertNode(marker);
3503
+ }
3504
+
3505
+ let inlineParagraph = null;
3506
+ const insertedNodes = [];
3507
+ const flushInlineParagraph = () => {
3508
+ if (inlineParagraph) {
3509
+ parent.insertBefore(inlineParagraph, blockquote);
3510
+ insertedNodes.push(inlineParagraph);
3511
+ inlineParagraph = null;
3512
+ }
3513
+ };
3514
+
3515
+ while (blockquote.firstChild) {
3516
+ const child = blockquote.firstChild;
3517
+ if (this._isTopLevelBlock(child)) {
3518
+ flushInlineParagraph();
3519
+ parent.insertBefore(child, blockquote);
3520
+ insertedNodes.push(child);
3521
+ } else {
3522
+ if (!inlineParagraph) inlineParagraph = document.createElement('p');
3523
+ inlineParagraph.appendChild(child);
3524
+ }
3525
+ }
3526
+
3527
+ flushInlineParagraph();
3528
+ parent.removeChild(blockquote);
3529
+
3530
+ const sel = window.getSelection();
3531
+ const restoredMarker = parent.querySelector('[data-neiki-selection-marker="true"]');
3532
+ if (sel && restoredMarker) {
3533
+ const restoredRange = document.createRange();
3534
+ restoredRange.setStartBefore(restoredMarker);
3535
+ restoredRange.collapse(true);
3536
+ restoredMarker.remove();
3537
+ sel.removeAllRanges();
3538
+ sel.addRange(restoredRange);
3539
+ } else if (sel && insertedNodes.length > 0) {
3540
+ const fallbackRange = document.createRange();
3541
+ fallbackRange.setStart(insertedNodes[0], 0);
3542
+ fallbackRange.collapse(true);
3543
+ sel.removeAllRanges();
3544
+ sel.addRange(fallbackRange);
3545
+ }
3546
+ }
3547
+
3548
+ _isTopLevelBlock(node) {
3549
+ if (!node || node.nodeType !== Node.ELEMENT_NODE) return false;
3550
+ return ['ADDRESS', 'ARTICLE', 'ASIDE', 'BLOCKQUOTE', 'DIV', 'DL', 'FIGURE', 'FOOTER', 'FORM', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEADER', 'HR', 'MAIN', 'OL', 'P', 'PRE', 'SECTION', 'TABLE', 'UL'].includes(node.tagName);
3551
+ }
3552
+
3401
3553
  fontSize(sizeStr) {
3402
3554
  this.editor.focus();
3403
3555
  this._expandToWordIfCollapsed();
@@ -5958,6 +6110,8 @@
5958
6110
  const moveUpBtn = document.createElement('button');
5959
6111
  moveUpBtn.className = 'neiki-img-toolbar-btn';
5960
6112
  moveUpBtn.type = 'button';
6113
+ moveUpBtn.title = t('blockToolbar.moveUp');
6114
+ moveUpBtn.setAttribute('aria-label', t('blockToolbar.moveUp'));
5961
6115
  moveUpBtn.innerHTML = Icons.moveUp;
5962
6116
  moveUpBtn.addEventListener('click', (e) => {
5963
6117
  e.preventDefault();
@@ -5972,6 +6126,8 @@
5972
6126
  const moveDownBtn = document.createElement('button');
5973
6127
  moveDownBtn.className = 'neiki-img-toolbar-btn';
5974
6128
  moveDownBtn.type = 'button';
6129
+ moveDownBtn.title = t('blockToolbar.moveDown');
6130
+ moveDownBtn.setAttribute('aria-label', t('blockToolbar.moveDown'));
5975
6131
  moveDownBtn.innerHTML = Icons.moveDown;
5976
6132
  moveDownBtn.addEventListener('click', (e) => {
5977
6133
  e.preventDefault();
@@ -6589,7 +6745,7 @@
6589
6745
  const grip = document.createElement('div');
6590
6746
  grip.className = 'neiki-block-grip';
6591
6747
  grip.innerHTML = Icons.grip;
6592
- grip.title = 'Drag to reorder';
6748
+ grip.title = t('blockToolbar.dragToReorder');
6593
6749
  grip.contentEditable = 'false';
6594
6750
  grip._block = block;
6595
6751
 
@@ -6745,8 +6901,8 @@
6745
6901
 
6746
6902
  // Move block buttons (left side)
6747
6903
  const moveButtons = [
6748
- { item: 'moveUp', icon: Icons.moveUp, title: 'Move block up' },
6749
- { item: 'moveDown', icon: Icons.moveDown, title: 'Move block down' }
6904
+ { item: 'moveUp', icon: Icons.moveUp, title: t('blockToolbar.moveUp') },
6905
+ { item: 'moveDown', icon: Icons.moveDown, title: t('blockToolbar.moveDown') }
6750
6906
  ];
6751
6907
 
6752
6908
  moveButtons.forEach(({ item, icon, title }) => {