@adminforth/rich-editor 2.1.1 → 3.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/README.md CHANGED
@@ -4,6 +4,9 @@
4
4
 
5
5
  [![Ask AI](https://tluma.ai/badge)](https://tluma.ai/ask-ai/devforth/adminforth)
6
6
 
7
+ > [!WARNING]
8
+ > **This plugin is not recommended for new projects.** Use the [Markdown plugin](https://adminforth.dev/docs/tutorial/Plugins/markdown/) (`@adminforth/markdown`) for editing formatted text instead. It stores content as Markdown, which suits the agentic era much better than HTML: AI agents read and write Markdown reliably, it stays readable as plain text and gives clean diffs. RichEditor keeps working for apps which already store HTML.
9
+
7
10
  Allows to add a rich text editor to your AdminForth text columns.
8
11
 
9
12
  ## Features
package/build.log CHANGED
@@ -10,5 +10,5 @@ custom/package.json
10
10
  custom/quillEditor.vue
11
11
  custom/tsconfig.json
12
12
 
13
- sent 35,541 bytes received 115 bytes 71,312.00 bytes/sec
14
- total size is 35,118 speedup is 0.98
13
+ sent 38,509 bytes received 115 bytes 77,248.00 bytes/sec
14
+ total size is 38,086 speedup is 0.99
@@ -1,10 +1,11 @@
1
1
  <template>
2
2
  <div
3
3
  v-if="wasQuillInitializedSuccessfully"
4
- class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500
5
- focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
6
- dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 whitespace-normal af-quill-editor"
7
- :class="'bg-lightInputBackground placeholder-lightInputPlaceholderText text-lightInputText border-lightInputBorder dark:!bg-darkInputBackground dark:!placeholder-darkInputPlaceholderText dark:!text-darkInputText dark:!border-darkInputBorder'"
4
+ class="af-quill-editor block w-full whitespace-normal rounded-default border shadow-customLight transition-colors
5
+ bg-lightInputBackground text-lightInputText border-lightInputBorder hover:border-lightInputBorderHover
6
+ focus-within:border-lightInputFocusBorder focus-within:ring-[3px] focus-within:ring-lightInputFocusRing
7
+ dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder dark:hover:border-darkInputBorderHover
8
+ dark:focus-within:border-darkInputFocusBorder dark:focus-within:ring-darkInputFocusRing"
8
9
  >
9
10
  <div
10
11
  ref="editor"
@@ -28,7 +29,7 @@
28
29
  </template>
29
30
 
30
31
  <script setup lang="ts">
31
- import { onMounted, ref, onUnmounted, watch, type Ref } from "vue";
32
+ import { onMounted, ref, onUnmounted, watch, h, render, type Ref, type FunctionalComponent } from "vue";
32
33
  import { callAdminForthApi } from '@/utils';
33
34
  import { AdminForthColumnCommon } from '@/types/Common';
34
35
  import adminforth from '@/adminforth';
@@ -39,6 +40,11 @@ import "quill/dist/quill.snow.css";
39
40
  import QuillTableBetter from 'quill-table-better-yaroslav8765';
40
41
  import 'quill-table-better-yaroslav8765/dist/quill-table-better.css';
41
42
  import { Textarea } from '@/afcl'
43
+ import {
44
+ IconAlignCenter, IconAlignJustify, IconAlignLeft, IconAlignRight, IconBold, IconCode, IconHeading2, IconHeading3,
45
+ IconImage, IconItalic, IconLink, IconList, IconListChecks, IconListOrdered, IconRemoveFormatting, IconStrikethrough,
46
+ IconTable, IconTextQuote, IconUnderline,
47
+ } from '@iconify-prerendered/vue-lucide';
42
48
 
43
49
  import { useI18n } from 'vue-i18n';
44
50
  import { ok } from "assert";
@@ -131,6 +137,31 @@ Quill.register({
131
137
  'modules/table-better': QuillTableBetter
132
138
  }, true);
133
139
 
140
+ // the toolbar draws Lucide icons like the rest of the UI; Quill takes icons as SVG strings,
141
+ // so they are set after the table module registered its own one
142
+ function iconSvg(icon: FunctionalComponent) {
143
+ const holder = document.createElement('div');
144
+ render(h(icon), holder);
145
+ return holder.innerHTML;
146
+ }
147
+
148
+ const quillIcons = Quill.import('ui/icons') as Record<string, any>;
149
+ Object.assign(quillIcons, {
150
+ bold: iconSvg(IconBold),
151
+ italic: iconSvg(IconItalic),
152
+ underline: iconSvg(IconUnderline),
153
+ strike: iconSvg(IconStrikethrough),
154
+ 'table-better': iconSvg(IconTable),
155
+ blockquote: iconSvg(IconTextQuote),
156
+ 'code-block': iconSvg(IconCode),
157
+ link: iconSvg(IconLink),
158
+ image: iconSvg(IconImage),
159
+ clean: iconSvg(IconRemoveFormatting),
160
+ header: { ...quillIcons.header, 2: iconSvg(IconHeading2), 3: iconSvg(IconHeading3) },
161
+ list: { ...quillIcons.list, ordered: iconSvg(IconListOrdered), bullet: iconSvg(IconList), check: iconSvg(IconListChecks) },
162
+ align: { ...quillIcons.align, '': iconSvg(IconAlignLeft), center: iconSvg(IconAlignCenter), right: iconSvg(IconAlignRight), justify: iconSvg(IconAlignJustify) },
163
+ });
164
+
134
165
 
135
166
  const updaterQueue = new AsyncQueue();
136
167
 
@@ -630,34 +661,61 @@ function removeCompletionOnBlur() {
630
661
 
631
662
  .af-quill-editor {
632
663
 
664
+ // the toolbar sits on a hairline over the text, its buttons are quiet like AFCL ghost icon buttons
633
665
  .ql-toolbar.ql-snow[class] {
634
- border: none;
635
- padding: 0 0 1rem 0;
636
- .ql-picker-label{
637
- padding-left: 0;
666
+ @apply flex flex-wrap items-center gap-0.5 border-0 border-b border-lightInputBorder px-1.5 py-1 dark:border-darkInputBorder;
667
+ font-family: inherit;
668
+
669
+ .ql-formats {
670
+ @apply inline-flex items-center gap-0.5 mr-2;
671
+ }
672
+
673
+ button, .ql-picker-label {
674
+ @apply w-7 h-7 p-1.5 rounded-default border-0 text-lightInputIconColor transition-colors dark:text-darkInputIconColor;
675
+ }
676
+
677
+ button svg, .ql-picker-label svg {
678
+ @apply w-4 h-4;
679
+ }
680
+
681
+ button:hover, .ql-picker-label:hover {
682
+ @apply bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText;
683
+ }
684
+
685
+ button.ql-active, .ql-picker-label.ql-active, .ql-picker-item.ql-selected {
686
+ @apply text-lightPrimary dark:text-darkPrimary;
687
+ }
688
+
689
+ .ql-picker.ql-icon-picker {
690
+ @apply w-7 h-7;
691
+ }
692
+
693
+ .ql-picker-options {
694
+ @apply p-1 rounded-lg border-0 bg-lightDropdownOptionsBackground shadow-popover dark:bg-darkDropdownOptionsBackground dark:shadow-darkPopover;
695
+ }
696
+
697
+ .ql-picker-item {
698
+ @apply w-7 h-7 p-1.5 rounded-md text-lightInputIconColor hover:bg-lightDropdownOptionsHoverBackground hover:text-lightInputText dark:text-darkInputIconColor dark:hover:bg-darkDropdownOptionsHoverBackground dark:hover:text-darkInputText;
638
699
  }
639
700
  }
640
701
 
641
702
  .ql-container {
642
703
  border: 0;
704
+ font-family: inherit;
705
+ font-size: inherit;
706
+
643
707
  .ql-editor {
644
708
  position: relative;
645
- padding: 0;
709
+ @apply px-3 py-2.5 text-sm;
646
710
  min-height: 100px;
647
711
  &.ql-blank::before {
648
- left: 0px;
649
- font-style: normal;
712
+ @apply left-3 right-3 not-italic text-lightInputPlaceholderText dark:text-darkInputPlaceholderText;
650
713
  }
651
714
  }
652
715
  }
653
716
 
654
- // .ql-editor:not(:focus) [completer] {
655
- // display: none;
656
- // }
657
-
658
717
  .ql-editor [completer] {
659
- color: gray;
660
- font-style: italic;
718
+ @apply italic text-lightInputPlaceholderText dark:text-darkInputPlaceholderText;
661
719
  }
662
720
 
663
721
  // basic typography
@@ -690,23 +748,21 @@ function removeCompletionOnBlur() {
690
748
  /* Horizontal rule */
691
749
  hr {
692
750
  margin: 0.8rem 0;
693
- border: none;
694
- border-top: 1px solid #ddd;
751
+ @apply border-0 border-t border-lightInputBorder dark:border-darkInputBorder;
695
752
  }
696
753
 
697
754
  /* Blockquotes */
698
755
  blockquote {
699
756
  margin: 0.8rem 0;
700
757
  padding-left: 0.8rem;
701
- border-left: 2px solid #ccc;
758
+ @apply border-0 border-l-2 border-lightInputBorder text-lightInputIconColor dark:border-darkInputBorder dark:text-darkInputIconColor;
702
759
  }
703
760
 
704
761
  /* Preformatted text */
705
762
  pre {
706
763
  margin: 0.8rem 0;
707
764
  padding: 0.4rem;
708
- background: #f8f9fa;
709
- border-radius: 3px;
765
+ @apply rounded bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText;
710
766
  }
711
767
 
712
768
  /* Tables */
@@ -717,11 +773,11 @@ function removeCompletionOnBlur() {
717
773
 
718
774
  th, td {
719
775
  padding: 0.3rem;
720
- border: 1px solid #ddd;
776
+ @apply border border-lightInputBorder dark:border-darkInputBorder;
721
777
  }
722
778
 
723
779
  thead th {
724
- border-bottom: 1.5px solid #aaa;
780
+ @apply border-b-2;
725
781
  }
726
782
 
727
783
  /* Images */
@@ -732,27 +788,21 @@ function removeCompletionOnBlur() {
732
788
  }
733
789
  }
734
790
 
791
+ // Quill's own icons which are left (table menus) follow the muted icon color too
735
792
  .ql-snow .ql-stroke {
736
- @apply dark:stroke-darkPrimary;
737
- @apply stroke-lightPrimary;
738
-
739
- }
740
- .ql-snow button:hover .ql-stroke,
741
- .ql-snow [role="button"]:hover .ql-stroke {
742
- @apply dark:stroke-darkPrimary;
743
- @apply stroke-lightPrimary;
744
- filter: brightness(1.3);
793
+ @apply stroke-lightInputIconColor dark:stroke-darkInputIconColor;
745
794
  }
746
795
 
747
796
  .ql-snow .ql-fill {
748
- @apply dark:fill-darkPrimary;
749
- @apply fill-lightPrimary;
797
+ @apply fill-lightInputIconColor dark:fill-darkInputIconColor;
750
798
  }
751
799
 
752
- .ql-snow button:hover .ql-fill {
753
- @apply dark:fill-darkPrimary;
754
- @apply fill-lightPrimary;
755
- filter: brightness(1.3);
800
+ .ql-snow .ql-active .ql-stroke {
801
+ @apply stroke-lightPrimary dark:stroke-darkPrimary;
802
+ }
803
+
804
+ .ql-snow .ql-active .ql-fill {
805
+ @apply fill-lightPrimary dark:fill-darkPrimary;
756
806
  }
757
807
 
758
808
  }
@@ -779,11 +829,6 @@ function removeCompletionOnBlur() {
779
829
  }
780
830
  }
781
831
 
782
- .ql-table-better svg path {
783
- @apply dark:fill-darkPrimary;
784
- @apply fill-lightPrimary;
785
- }
786
-
787
832
  .ql-table-select-container {
788
833
  @apply dark:bg-darkForm;
789
834
  @apply bg-lightForm;
@@ -1,10 +1,11 @@
1
1
  <template>
2
2
  <div
3
3
  v-if="wasQuillInitializedSuccessfully"
4
- class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500
5
- focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
6
- dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 whitespace-normal af-quill-editor"
7
- :class="'bg-lightInputBackground placeholder-lightInputPlaceholderText text-lightInputText border-lightInputBorder dark:!bg-darkInputBackground dark:!placeholder-darkInputPlaceholderText dark:!text-darkInputText dark:!border-darkInputBorder'"
4
+ class="af-quill-editor block w-full whitespace-normal rounded-default border shadow-customLight transition-colors
5
+ bg-lightInputBackground text-lightInputText border-lightInputBorder hover:border-lightInputBorderHover
6
+ focus-within:border-lightInputFocusBorder focus-within:ring-[3px] focus-within:ring-lightInputFocusRing
7
+ dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder dark:hover:border-darkInputBorderHover
8
+ dark:focus-within:border-darkInputFocusBorder dark:focus-within:ring-darkInputFocusRing"
8
9
  >
9
10
  <div
10
11
  ref="editor"
@@ -28,7 +29,7 @@
28
29
  </template>
29
30
 
30
31
  <script setup lang="ts">
31
- import { onMounted, ref, onUnmounted, watch, type Ref } from "vue";
32
+ import { onMounted, ref, onUnmounted, watch, h, render, type Ref, type FunctionalComponent } from "vue";
32
33
  import { callAdminForthApi } from '@/utils';
33
34
  import { AdminForthColumnCommon } from '@/types/Common';
34
35
  import adminforth from '@/adminforth';
@@ -39,6 +40,11 @@ import "quill/dist/quill.snow.css";
39
40
  import QuillTableBetter from 'quill-table-better-yaroslav8765';
40
41
  import 'quill-table-better-yaroslav8765/dist/quill-table-better.css';
41
42
  import { Textarea } from '@/afcl'
43
+ import {
44
+ IconAlignCenter, IconAlignJustify, IconAlignLeft, IconAlignRight, IconBold, IconCode, IconHeading2, IconHeading3,
45
+ IconImage, IconItalic, IconLink, IconList, IconListChecks, IconListOrdered, IconRemoveFormatting, IconStrikethrough,
46
+ IconTable, IconTextQuote, IconUnderline,
47
+ } from '@iconify-prerendered/vue-lucide';
42
48
 
43
49
  import { useI18n } from 'vue-i18n';
44
50
  import { ok } from "assert";
@@ -131,6 +137,31 @@ Quill.register({
131
137
  'modules/table-better': QuillTableBetter
132
138
  }, true);
133
139
 
140
+ // the toolbar draws Lucide icons like the rest of the UI; Quill takes icons as SVG strings,
141
+ // so they are set after the table module registered its own one
142
+ function iconSvg(icon: FunctionalComponent) {
143
+ const holder = document.createElement('div');
144
+ render(h(icon), holder);
145
+ return holder.innerHTML;
146
+ }
147
+
148
+ const quillIcons = Quill.import('ui/icons') as Record<string, any>;
149
+ Object.assign(quillIcons, {
150
+ bold: iconSvg(IconBold),
151
+ italic: iconSvg(IconItalic),
152
+ underline: iconSvg(IconUnderline),
153
+ strike: iconSvg(IconStrikethrough),
154
+ 'table-better': iconSvg(IconTable),
155
+ blockquote: iconSvg(IconTextQuote),
156
+ 'code-block': iconSvg(IconCode),
157
+ link: iconSvg(IconLink),
158
+ image: iconSvg(IconImage),
159
+ clean: iconSvg(IconRemoveFormatting),
160
+ header: { ...quillIcons.header, 2: iconSvg(IconHeading2), 3: iconSvg(IconHeading3) },
161
+ list: { ...quillIcons.list, ordered: iconSvg(IconListOrdered), bullet: iconSvg(IconList), check: iconSvg(IconListChecks) },
162
+ align: { ...quillIcons.align, '': iconSvg(IconAlignLeft), center: iconSvg(IconAlignCenter), right: iconSvg(IconAlignRight), justify: iconSvg(IconAlignJustify) },
163
+ });
164
+
134
165
 
135
166
  const updaterQueue = new AsyncQueue();
136
167
 
@@ -630,34 +661,61 @@ function removeCompletionOnBlur() {
630
661
 
631
662
  .af-quill-editor {
632
663
 
664
+ // the toolbar sits on a hairline over the text, its buttons are quiet like AFCL ghost icon buttons
633
665
  .ql-toolbar.ql-snow[class] {
634
- border: none;
635
- padding: 0 0 1rem 0;
636
- .ql-picker-label{
637
- padding-left: 0;
666
+ @apply flex flex-wrap items-center gap-0.5 border-0 border-b border-lightInputBorder px-1.5 py-1 dark:border-darkInputBorder;
667
+ font-family: inherit;
668
+
669
+ .ql-formats {
670
+ @apply inline-flex items-center gap-0.5 mr-2;
671
+ }
672
+
673
+ button, .ql-picker-label {
674
+ @apply w-7 h-7 p-1.5 rounded-default border-0 text-lightInputIconColor transition-colors dark:text-darkInputIconColor;
675
+ }
676
+
677
+ button svg, .ql-picker-label svg {
678
+ @apply w-4 h-4;
679
+ }
680
+
681
+ button:hover, .ql-picker-label:hover {
682
+ @apply bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText;
683
+ }
684
+
685
+ button.ql-active, .ql-picker-label.ql-active, .ql-picker-item.ql-selected {
686
+ @apply text-lightPrimary dark:text-darkPrimary;
687
+ }
688
+
689
+ .ql-picker.ql-icon-picker {
690
+ @apply w-7 h-7;
691
+ }
692
+
693
+ .ql-picker-options {
694
+ @apply p-1 rounded-lg border-0 bg-lightDropdownOptionsBackground shadow-popover dark:bg-darkDropdownOptionsBackground dark:shadow-darkPopover;
695
+ }
696
+
697
+ .ql-picker-item {
698
+ @apply w-7 h-7 p-1.5 rounded-md text-lightInputIconColor hover:bg-lightDropdownOptionsHoverBackground hover:text-lightInputText dark:text-darkInputIconColor dark:hover:bg-darkDropdownOptionsHoverBackground dark:hover:text-darkInputText;
638
699
  }
639
700
  }
640
701
 
641
702
  .ql-container {
642
703
  border: 0;
704
+ font-family: inherit;
705
+ font-size: inherit;
706
+
643
707
  .ql-editor {
644
708
  position: relative;
645
- padding: 0;
709
+ @apply px-3 py-2.5 text-sm;
646
710
  min-height: 100px;
647
711
  &.ql-blank::before {
648
- left: 0px;
649
- font-style: normal;
712
+ @apply left-3 right-3 not-italic text-lightInputPlaceholderText dark:text-darkInputPlaceholderText;
650
713
  }
651
714
  }
652
715
  }
653
716
 
654
- // .ql-editor:not(:focus) [completer] {
655
- // display: none;
656
- // }
657
-
658
717
  .ql-editor [completer] {
659
- color: gray;
660
- font-style: italic;
718
+ @apply italic text-lightInputPlaceholderText dark:text-darkInputPlaceholderText;
661
719
  }
662
720
 
663
721
  // basic typography
@@ -690,23 +748,21 @@ function removeCompletionOnBlur() {
690
748
  /* Horizontal rule */
691
749
  hr {
692
750
  margin: 0.8rem 0;
693
- border: none;
694
- border-top: 1px solid #ddd;
751
+ @apply border-0 border-t border-lightInputBorder dark:border-darkInputBorder;
695
752
  }
696
753
 
697
754
  /* Blockquotes */
698
755
  blockquote {
699
756
  margin: 0.8rem 0;
700
757
  padding-left: 0.8rem;
701
- border-left: 2px solid #ccc;
758
+ @apply border-0 border-l-2 border-lightInputBorder text-lightInputIconColor dark:border-darkInputBorder dark:text-darkInputIconColor;
702
759
  }
703
760
 
704
761
  /* Preformatted text */
705
762
  pre {
706
763
  margin: 0.8rem 0;
707
764
  padding: 0.4rem;
708
- background: #f8f9fa;
709
- border-radius: 3px;
765
+ @apply rounded bg-lightSecondaryDarken text-lightInputText dark:bg-darkSecondaryLighten dark:text-darkInputText;
710
766
  }
711
767
 
712
768
  /* Tables */
@@ -717,11 +773,11 @@ function removeCompletionOnBlur() {
717
773
 
718
774
  th, td {
719
775
  padding: 0.3rem;
720
- border: 1px solid #ddd;
776
+ @apply border border-lightInputBorder dark:border-darkInputBorder;
721
777
  }
722
778
 
723
779
  thead th {
724
- border-bottom: 1.5px solid #aaa;
780
+ @apply border-b-2;
725
781
  }
726
782
 
727
783
  /* Images */
@@ -732,27 +788,21 @@ function removeCompletionOnBlur() {
732
788
  }
733
789
  }
734
790
 
791
+ // Quill's own icons which are left (table menus) follow the muted icon color too
735
792
  .ql-snow .ql-stroke {
736
- @apply dark:stroke-darkPrimary;
737
- @apply stroke-lightPrimary;
738
-
739
- }
740
- .ql-snow button:hover .ql-stroke,
741
- .ql-snow [role="button"]:hover .ql-stroke {
742
- @apply dark:stroke-darkPrimary;
743
- @apply stroke-lightPrimary;
744
- filter: brightness(1.3);
793
+ @apply stroke-lightInputIconColor dark:stroke-darkInputIconColor;
745
794
  }
746
795
 
747
796
  .ql-snow .ql-fill {
748
- @apply dark:fill-darkPrimary;
749
- @apply fill-lightPrimary;
797
+ @apply fill-lightInputIconColor dark:fill-darkInputIconColor;
750
798
  }
751
799
 
752
- .ql-snow button:hover .ql-fill {
753
- @apply dark:fill-darkPrimary;
754
- @apply fill-lightPrimary;
755
- filter: brightness(1.3);
800
+ .ql-snow .ql-active .ql-stroke {
801
+ @apply stroke-lightPrimary dark:stroke-darkPrimary;
802
+ }
803
+
804
+ .ql-snow .ql-active .ql-fill {
805
+ @apply fill-lightPrimary dark:fill-darkPrimary;
756
806
  }
757
807
 
758
808
  }
@@ -779,11 +829,6 @@ function removeCompletionOnBlur() {
779
829
  }
780
830
  }
781
831
 
782
- .ql-table-better svg path {
783
- @apply dark:fill-darkPrimary;
784
- @apply fill-lightPrimary;
785
- }
786
-
787
832
  .ql-table-select-container {
788
833
  @apply dark:bg-darkForm;
789
834
  @apply bg-lightForm;
package/dist/index.d.ts CHANGED
@@ -14,6 +14,5 @@ export default class RichEditorPlugin extends AdminForthPlugin {
14
14
  validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
15
15
  instanceUniqueRepresentation(pluginOptions: any): string;
16
16
  generateRecordContext(record: any, maxFields: number, inputFieldLimit: number, partsCount: number): string;
17
- private parseBody;
18
17
  setupEndpoints(server: IHttpServer): void;
19
18
  }
package/dist/index.js CHANGED
@@ -218,27 +218,14 @@ export default class RichEditorPlugin extends AdminForthPlugin {
218
218
  });
219
219
  return JSON.stringify(Object.fromEntries(fields));
220
220
  }
221
- parseBody(schema, body, response) {
222
- const parsed = schema.safeParse(body !== null && body !== void 0 ? body : {});
223
- if (!parsed.success) {
224
- response.setStatus(400, '');
225
- return {
226
- ok: false,
227
- error: { error: 'Request body validation failed', details: parsed.error.issues },
228
- };
229
- }
230
- return { ok: true, data: parsed.data };
231
- }
232
221
  setupEndpoints(server) {
233
222
  server.endpoint({
234
223
  method: 'POST',
235
224
  path: `/plugin/${this.pluginInstanceId}/doComplete`,
225
+ request_schema: doCompleteBodySchema,
236
226
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers, response }) {
237
227
  var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
238
- const parsed = this.parseBody(doCompleteBodySchema, body, response);
239
- if ('error' in parsed)
240
- return parsed.error;
241
- const data = parsed.data;
228
+ const data = body;
242
229
  const { record } = data;
243
230
  if (!record) {
244
231
  return { completion: [] };
package/index.ts CHANGED
@@ -275,30 +275,13 @@ export default class RichEditorPlugin extends AdminForthPlugin {
275
275
  return JSON.stringify(Object.fromEntries(fields));
276
276
  }
277
277
 
278
- private parseBody<T>(
279
- schema: z.ZodType<T>,
280
- body: unknown,
281
- response: { setStatus: (code: number, message: string) => void },
282
- ): { ok: true; data: T } | { ok: false; error: { error: string; details: unknown } } {
283
- const parsed = schema.safeParse(body ?? {});
284
- if (!parsed.success) {
285
- response.setStatus(400, '');
286
- return {
287
- ok: false,
288
- error: { error: 'Request body validation failed', details: parsed.error.issues },
289
- };
290
- }
291
- return { ok: true, data: parsed.data };
292
- }
293
-
294
278
  setupEndpoints(server: IHttpServer) {
295
279
  server.endpoint({
296
280
  method: 'POST',
297
281
  path: `/plugin/${this.pluginInstanceId}/doComplete`,
282
+ request_schema: doCompleteBodySchema,
298
283
  handler: async ({ body, headers, response }) => {
299
- const parsed = this.parseBody(doCompleteBodySchema, body, response);
300
- if ('error' in parsed) return parsed.error;
301
- const data = parsed.data;
284
+ const data = body as z.infer<typeof doCompleteBodySchema>;
302
285
  const { record } = data;
303
286
 
304
287
  if (!record) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/rich-editor",
3
- "version": "2.1.1",
3
+ "version": "3.0.0",
4
4
  "description": "Rich editor plugin for adminforth",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -27,11 +27,11 @@
27
27
  "zod": "^4.3.6"
28
28
  },
29
29
  "peerDependencies": {
30
- "adminforth": "^3.7.1"
30
+ "adminforth": "^4.4.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^22.10.7",
34
- "adminforth": "^3.7.1",
34
+ "adminforth": "^3.8.2",
35
35
  "semantic-release": "^24.2.1",
36
36
  "semantic-release-slack-bot": "^4.0.2",
37
37
  "typescript": "^5.7.3"