@questpie/admin 3.5.2 → 3.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/client/blocks/block-renderer.d.mts +2 -2
  2. package/dist/client/builder/types/collection-types.d.mts +9 -0
  3. package/dist/client/components/actions/action-dialog.mjs +5 -0
  4. package/dist/client/components/fields/rich-text-editor/bubble-menu.mjs +7 -0
  5. package/dist/client/components/fields/rich-text-editor/extensions.mjs +17 -1
  6. package/dist/client/components/fields/rich-text-editor/index.d.mts +2 -1
  7. package/dist/client/components/fields/rich-text-editor/index.mjs +35 -74
  8. package/dist/client/components/fields/rich-text-editor/slash-commands.mjs +30 -7
  9. package/dist/client/components/fields/rich-text-editor/toolbar.mjs +1 -312
  10. package/dist/client/components/fields/rich-text-editor/types.d.mts +4 -0
  11. package/dist/client/components/fields/rich-text-editor/types.mjs +1 -1
  12. package/dist/client/components/fields/rich-text-editor/utils.mjs +6 -12
  13. package/dist/client/components/filter-builder/filter-builder-sheet.mjs +75 -22
  14. package/dist/client/components/ui/dropdown-menu.mjs +1 -34
  15. package/dist/client/hooks/query-access.d.mts +9 -0
  16. package/dist/client/hooks/query-access.mjs +20 -0
  17. package/dist/client/hooks/typed-hooks.d.mts +4 -2
  18. package/dist/client/hooks/typed-hooks.mjs +30 -29
  19. package/dist/client/hooks/use-reactive-fields.d.mts +1 -0
  20. package/dist/client/hooks/use-reactive-fields.mjs +16 -1
  21. package/dist/client/hooks/use-server-actions.mjs +12 -1
  22. package/dist/client/hooks/use-view-state.mjs +15 -7
  23. package/dist/client/lib/view-filter-utils.mjs +30 -0
  24. package/dist/client/preview/block-scope-context.d.mts +2 -2
  25. package/dist/client/preview/preview-banner.d.mts +2 -2
  26. package/dist/client/preview/preview-field.d.mts +4 -4
  27. package/dist/client/styles/base.css +69 -77
  28. package/dist/client/utils/build-field-definitions-from-schema.mjs +1 -0
  29. package/dist/client/views/auth/accept-invite-form.d.mts +2 -2
  30. package/dist/client/views/auth/auth-layout.d.mts +3 -3
  31. package/dist/client/views/auth/login-form.d.mts +2 -2
  32. package/dist/client/views/auth/reset-password-form.d.mts +2 -2
  33. package/dist/client/views/collection/auto-form-fields.mjs +3 -2
  34. package/dist/client/views/collection/cells/primitive-cells.mjs +9 -6
  35. package/dist/client/views/collection/columns/build-columns.mjs +3 -1
  36. package/dist/client/views/collection/field-renderer.mjs +11 -3
  37. package/dist/client/views/collection/form-view.mjs +207 -202
  38. package/dist/client/views/collection/list-view.mjs +581 -183
  39. package/dist/client/views/collection/outline.mjs +44 -19
  40. package/dist/client/views/collection/quick-filter-bar.mjs +45 -0
  41. package/dist/client/views/collection/table-view.mjs +60 -16
  42. package/dist/client/views/globals/global-form-view.mjs +12 -9
  43. package/dist/client/views/layout/admin-layout.mjs +1 -1
  44. package/dist/client/views/layout/admin-sidebar.mjs +20 -14
  45. package/dist/client/views/layout/admin-theme.mjs +5 -4
  46. package/dist/client.mjs +1 -1
  47. package/dist/components/rich-text/rich-text-renderer.d.mts +5 -5
  48. package/dist/components/rich-text/rich-text-renderer.mjs +5 -2
  49. package/dist/index.mjs +1 -1
  50. package/dist/modules/admin.d.mts +1 -1
  51. package/dist/server/augmentation/actions.d.mts +4 -3
  52. package/dist/server/augmentation/dashboard.d.mts +11 -11
  53. package/dist/server/augmentation/form-layout.d.mts +11 -6
  54. package/dist/server/augmentation/index.d.mts +7 -0
  55. package/dist/server/augmentation/sidebar.d.mts +8 -8
  56. package/dist/server/codegen/admin-client-template.mjs +7 -6
  57. package/dist/server/fields/index.d.mts +1 -1
  58. package/dist/server/fields/rich-text.d.mts +16 -17
  59. package/dist/server/fields/rich-text.mjs +18 -7
  60. package/dist/server/i18n/messages/cs.mjs +2 -0
  61. package/dist/server/i18n/messages/de.mjs +2 -0
  62. package/dist/server/i18n/messages/en.mjs +4 -0
  63. package/dist/server/i18n/messages/es.mjs +2 -0
  64. package/dist/server/i18n/messages/fr.mjs +2 -0
  65. package/dist/server/i18n/messages/pl.mjs +2 -0
  66. package/dist/server/i18n/messages/pt.mjs +2 -0
  67. package/dist/server/i18n/messages/sk.mjs +2 -0
  68. package/dist/server/modules/admin/block/block-builder.d.mts +0 -8
  69. package/dist/server/modules/admin/block/introspection.d.mts +2 -2
  70. package/dist/server/modules/admin/collections/account.d.mts +53 -52
  71. package/dist/server/modules/admin/collections/admin-locks.d.mts +4 -3
  72. package/dist/server/modules/admin/collections/admin-preferences.d.mts +38 -37
  73. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +50 -49
  74. package/dist/server/modules/admin/collections/apikey.d.mts +72 -71
  75. package/dist/server/modules/admin/collections/assets.d.mts +42 -41
  76. package/dist/server/modules/admin/collections/session.d.mts +46 -45
  77. package/dist/server/modules/admin/collections/user.d.mts +67 -66
  78. package/dist/server/modules/admin/collections/verification.d.mts +39 -38
  79. package/dist/server/modules/admin/index.d.mts +3 -3
  80. package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
  81. package/dist/server/modules/admin/routes/admin-config.mjs +39 -23
  82. package/dist/server/modules/admin/routes/execute-action.mjs +28 -8
  83. package/dist/server/modules/admin/routes/locales.d.mts +2 -2
  84. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  85. package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
  86. package/dist/server/modules/admin/routes/reactive.mjs +2 -2
  87. package/dist/server/modules/admin/routes/route-helpers.d.mts +11 -7
  88. package/dist/server/modules/admin/routes/setup.d.mts +7 -7
  89. package/dist/server/modules/admin/routes/translations.d.mts +4 -4
  90. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  91. package/dist/server/modules/admin/routes/widget-data.mjs +12 -4
  92. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +27 -27
  93. package/dist/server/modules/audit/.generated/module.d.mts +6 -6
  94. package/dist/server/modules/audit/collections/audit-log.d.mts +40 -39
  95. package/dist/server/plugin.mjs +3 -3
  96. package/dist/server.d.mts +1 -1
  97. package/dist/shared/types/index.d.mts +1 -0
  98. package/dist/shared/types/saved-views.types.d.mts +14 -7
  99. package/dist/shared.d.mts +3 -2
  100. package/package.json +4 -3
@@ -1,6 +1,6 @@
1
1
  import { BlockContent } from "./types.mjs";
2
2
  import * as React from "react";
3
- import * as react_jsx_runtime25 from "react/jsx-runtime";
3
+ import * as react_jsx_runtime19 from "react/jsx-runtime";
4
4
 
5
5
  //#region src/client/blocks/block-renderer.d.ts
6
6
 
@@ -50,6 +50,6 @@ declare function BlockRenderer({
50
50
  onBlockClick,
51
51
  onBlockInsert,
52
52
  className
53
- }: BlockRendererProps): react_jsx_runtime25.JSX.Element | null;
53
+ }: BlockRendererProps): react_jsx_runtime19.JSX.Element | null;
54
54
  //#endregion
55
55
  export { BlockRenderer, BlockRendererProps };
@@ -1,5 +1,6 @@
1
1
  import "../../i18n/types.mjs";
2
2
  import { MaybeLazyComponent } from "./common.mjs";
3
+ import { FilterRule, QuickFilterConfig } from "../../../shared/types/saved-views.types.mjs";
3
4
  import "../../../server/augmentation.mjs";
4
5
  import "../field/field.mjs";
5
6
  import "../admin.mjs";
@@ -124,6 +125,14 @@ interface ListViewConfig<TFieldNames extends string = string> {
124
125
  field: TFieldNames;
125
126
  direction: "asc" | "desc";
126
127
  };
128
+ /**
129
+ * Initial filters used when the user has no saved view state.
130
+ */
131
+ defaultFilters?: FilterRule[];
132
+ /**
133
+ * Header-level quick filter presets.
134
+ */
135
+ quickFilters?: QuickFilterConfig[];
127
136
  /**
128
137
  * Enables reorder mode for this list.
129
138
  * Requires a numeric field named `order` on the collection.
@@ -92,6 +92,11 @@ function FormDialogContent({ action, ctx, onClose }) {
92
92
  return message || t("toast.actionSuccess");
93
93
  },
94
94
  error: (error) => {
95
+ const validationError = error;
96
+ if (validationError.fieldErrors) for (const [field, message] of Object.entries(validationError.fieldErrors)) form.setError(field, {
97
+ type: "server",
98
+ message
99
+ });
95
100
  return error instanceof Error ? error.message : t("toast.actionFailed");
96
101
  },
97
102
  finally: () => {
@@ -283,6 +283,13 @@ function RichTextBubbleMenu({ editor, features, disabled, linkOpen, onImageClick
283
283
  shortcut: "⌘U",
284
284
  onClick: () => editor.chain().focus().toggleUnderline().run()
285
285
  }),
286
+ features.strike && /* @__PURE__ */ jsx(ToolbarButton, {
287
+ icon: EDITOR_ICONS.strikethrough,
288
+ active: editor.isActive("strike"),
289
+ disabled: !isEditable,
290
+ title: t("editor.strikethrough"),
291
+ onClick: () => editor.chain().focus().toggleStrike().run()
292
+ }),
286
293
  features.code && /* @__PURE__ */ jsx(ToolbarButton, {
287
294
  icon: EDITOR_ICONS.code,
288
295
  active: editor.isActive("code"),
@@ -10,6 +10,7 @@ import TableRow from "@tiptap/extension-table-row";
10
10
  import TextAlign from "@tiptap/extension-text-align";
11
11
  import Underline from "@tiptap/extension-underline";
12
12
  import StarterKit from "@tiptap/starter-kit";
13
+ import { Markdown } from "tiptap-markdown";
13
14
 
14
15
  //#region src/client/components/fields/rich-text-editor/extensions.tsx
15
16
  let codeBlockExtension = null;
@@ -67,7 +68,7 @@ function buildExtensions({ features, labels, placeholder, maxCharacters, customE
67
68
  if (!(codeBlock instanceof Promise)) return [...base, codeBlock];
68
69
  return codeBlock.then((ext) => [...base, ext]);
69
70
  }
70
- function buildBaseExtensions({ features, labels = defaultLabels, placeholder, maxCharacters, customExtensions }) {
71
+ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, maxCharacters, outputMode, customExtensions }) {
71
72
  const starterKitConfig = { codeBlock: false };
72
73
  if (!features.bold) starterKitConfig.bold = false;
73
74
  if (!features.italic) starterKitConfig.italic = false;
@@ -97,18 +98,21 @@ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, ma
97
98
  title: labels.heading(1),
98
99
  description: labels.heading1Description,
99
100
  icon: "ph:text-h-one",
101
+ group: "Headings",
100
102
  keywords: ["h1"],
101
103
  command: (cmdEditor) => cmdEditor.chain().focus().toggleHeading({ level: 1 }).run()
102
104
  }, {
103
105
  title: labels.heading(2),
104
106
  description: labels.heading2Description,
105
107
  icon: "ph:text-h-two",
108
+ group: "Headings",
106
109
  keywords: ["h2"],
107
110
  command: (cmdEditor) => cmdEditor.chain().focus().toggleHeading({ level: 2 }).run()
108
111
  }, {
109
112
  title: labels.heading(3),
110
113
  description: labels.heading3Description,
111
114
  icon: "ph:text-h-three",
115
+ group: "Headings",
112
116
  keywords: ["h3"],
113
117
  command: (cmdEditor) => cmdEditor.chain().focus().toggleHeading({ level: 3 }).run()
114
118
  });
@@ -116,6 +120,7 @@ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, ma
116
120
  title: labels.paragraph,
117
121
  description: labels.paragraphDescription,
118
122
  icon: "ph:text-align-left",
123
+ group: "Text",
119
124
  keywords: ["text"],
120
125
  command: (cmdEditor) => cmdEditor.chain().focus().setParagraph().run()
121
126
  });
@@ -123,6 +128,7 @@ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, ma
123
128
  title: labels.bulletList,
124
129
  description: labels.bulletListDescription,
125
130
  icon: "ph:list-bullets",
131
+ group: "Lists",
126
132
  keywords: ["list", "ul"],
127
133
  command: (cmdEditor) => cmdEditor.chain().focus().toggleBulletList().run()
128
134
  });
@@ -130,6 +136,7 @@ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, ma
130
136
  title: labels.orderedList,
131
137
  description: labels.orderedListDescription,
132
138
  icon: "ph:list-numbers",
139
+ group: "Lists",
133
140
  keywords: ["list", "ol"],
134
141
  command: (cmdEditor) => cmdEditor.chain().focus().toggleOrderedList().run()
135
142
  });
@@ -137,6 +144,7 @@ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, ma
137
144
  title: labels.quote,
138
145
  description: labels.quoteDescription,
139
146
  icon: "ph:quotes",
147
+ group: "Blocks",
140
148
  keywords: ["blockquote"],
141
149
  command: (cmdEditor) => cmdEditor.chain().focus().toggleBlockquote().run()
142
150
  });
@@ -144,6 +152,7 @@ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, ma
144
152
  title: labels.codeBlock,
145
153
  description: labels.codeBlockDescription,
146
154
  icon: "ph:code-block",
155
+ group: "Blocks",
147
156
  keywords: ["code"],
148
157
  command: (cmdEditor) => cmdEditor.chain().focus().toggleCodeBlock().run()
149
158
  });
@@ -151,6 +160,7 @@ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, ma
151
160
  title: labels.divider,
152
161
  description: labels.dividerDescription,
153
162
  icon: "ph:minus",
163
+ group: "Blocks",
154
164
  keywords: ["hr"],
155
165
  command: (cmdEditor) => cmdEditor.chain().focus().setHorizontalRule().run()
156
166
  });
@@ -158,6 +168,7 @@ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, ma
158
168
  title: labels.table,
159
169
  description: labels.tableDescription,
160
170
  icon: "ph:table",
171
+ group: "Blocks",
161
172
  keywords: ["grid"],
162
173
  command: (cmdEditor) => cmdEditor.chain().focus().insertTable({
163
174
  rows: 3,
@@ -167,6 +178,11 @@ function buildBaseExtensions({ features, labels = defaultLabels, placeholder, ma
167
178
  });
168
179
  return commands;
169
180
  }));
181
+ if (outputMode === "markdown") extensions.push(Markdown.configure({
182
+ html: true,
183
+ transformCopiedText: true,
184
+ transformPastedText: true
185
+ }));
170
186
  if (customExtensions?.length) extensions.push(...customExtensions);
171
187
  return extensions;
172
188
  }
@@ -32,7 +32,8 @@ declare function RichTextEditor({
32
32
  enableImages,
33
33
  onImageUpload,
34
34
  imageCollection,
35
- enableMediaLibrary
35
+ enableMediaLibrary,
36
+ outputMode
36
37
  }: RichTextEditorProps): react_jsx_runtime0.JSX.Element;
37
38
  //#endregion
38
39
  export { RichTextEditor };
@@ -5,48 +5,24 @@ import { Skeleton } from "../../ui/skeleton.mjs";
5
5
  import { isModifierShortcut } from "../../../utils/keyboard-shortcuts.mjs";
6
6
  import { LocaleBadge } from "../locale-badge.mjs";
7
7
  import { createLinkAttributes, isLikelyLinkHref } from "./link-utils.mjs";
8
- import { RichTextToolbar } from "./toolbar.mjs";
8
+ import { TableControls } from "./table-controls.mjs";
9
+ import { ToolbarButton } from "./toolbar.mjs";
9
10
  import { RichTextBubbleMenu } from "./bubble-menu.mjs";
10
11
  import { buildExtensions } from "./extensions.mjs";
11
12
  import { getImageAltFromFile, getImageFilesFromDataTransfer, useRichTextImageUpload } from "./image-upload.mjs";
12
13
  import { ImagePopover } from "./image-popover.mjs";
13
14
  import { mergePresetFeatures } from "./presets.mjs";
14
15
  import { defaultFeatures } from "./types.mjs";
15
- import { getCharacterCount, getHeadingLevel, getOutput, isSameValue } from "./utils.mjs";
16
+ import { getCharacterCount, getOutput, isSameValue } from "./utils.mjs";
16
17
  import * as React from "react";
17
18
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
18
19
  import { toast } from "sonner";
19
20
  import { EditorContent, useEditor } from "@tiptap/react";
20
21
 
21
22
  //#region src/client/components/fields/rich-text-editor/index.tsx
22
- function RichTextToolbarSkeleton() {
23
- return /* @__PURE__ */ jsxs("div", {
24
- className: "qp-rich-text-editor__toolbar border-border-subtle bg-surface-low flex flex-nowrap items-center gap-1.5 overflow-hidden border-b p-1.5",
25
- "aria-hidden": "true",
26
- children: [
27
- /* @__PURE__ */ jsxs("div", {
28
- className: "flex items-center gap-1",
29
- role: "presentation",
30
- children: [/* @__PURE__ */ jsx(Skeleton, { className: "size-8" }), /* @__PURE__ */ jsx(Skeleton, { className: "size-8" })]
31
- }),
32
- /* @__PURE__ */ jsx(Skeleton, { className: "h-8 min-w-[136px]" }),
33
- /* @__PURE__ */ jsxs("div", {
34
- className: "flex items-center gap-1",
35
- role: "presentation",
36
- children: [
37
- /* @__PURE__ */ jsx(Skeleton, { className: "size-8" }),
38
- /* @__PURE__ */ jsx(Skeleton, { className: "size-8" }),
39
- /* @__PURE__ */ jsx(Skeleton, { className: "size-8" })
40
- ]
41
- }),
42
- /* @__PURE__ */ jsx(Skeleton, { className: "size-8" }),
43
- /* @__PURE__ */ jsx(Skeleton, { className: "size-8" })
44
- ]
45
- });
46
- }
47
23
  function RichTextEditorContentSkeleton() {
48
24
  return /* @__PURE__ */ jsxs("div", {
49
- className: "qp-rich-text-editor__content space-y-3",
25
+ className: "qp-rich-text-editor__content space-y-4",
50
26
  "aria-hidden": "true",
51
27
  children: [
52
28
  /* @__PURE__ */ jsx(Skeleton, {
@@ -62,7 +38,7 @@ function RichTextEditorContentSkeleton() {
62
38
  className: "h-4 w-full max-w-[86%]"
63
39
  }),
64
40
  /* @__PURE__ */ jsx("div", {
65
- className: "pt-3",
41
+ className: "pt-2",
66
42
  children: /* @__PURE__ */ jsx(Skeleton, {
67
43
  variant: "text",
68
44
  className: "h-4 w-full max-w-[58%]"
@@ -71,19 +47,15 @@ function RichTextEditorContentSkeleton() {
71
47
  ]
72
48
  });
73
49
  }
74
- function RichTextEditorLoadingSkeleton({ disabled, readOnly, error, showToolbar }) {
50
+ function RichTextEditorLoadingSkeleton({ disabled, readOnly }) {
75
51
  return /* @__PURE__ */ jsxs("div", {
76
- className: cn("qp-rich-text-editor control-surface h-auto min-h-[160px] overflow-hidden", disabled || readOnly ? "opacity-60" : "", error ? "border-destructive" : "border-border"),
52
+ className: cn("qp-rich-text-editor", disabled || readOnly ? "opacity-60" : ""),
77
53
  "data-admin-rich-text-editor": "root",
78
54
  "aria-busy": "true",
79
- children: [
80
- /* @__PURE__ */ jsx("span", {
81
- className: "sr-only",
82
- children: "Loading editor"
83
- }),
84
- showToolbar && /* @__PURE__ */ jsx(RichTextToolbarSkeleton, {}),
85
- /* @__PURE__ */ jsx(RichTextEditorContentSkeleton, {})
86
- ]
55
+ children: [/* @__PURE__ */ jsx("span", {
56
+ className: "sr-only",
57
+ children: "Loading editor"
58
+ }), /* @__PURE__ */ jsx(RichTextEditorContentSkeleton, {})]
87
59
  });
88
60
  }
89
61
  /**
@@ -94,7 +66,7 @@ function RichTextEditorLoadingSkeleton({ disabled, readOnly, error, showToolbar
94
66
  * `useEditor` with an empty extension list which causes the ProseMirror
95
67
  * "Schema is missing its top node type" error.
96
68
  */
97
- function RichTextEditor({ name, value, onChange, disabled, readOnly, label, description, placeholder, required, error, localized, locale, extensions, preset, features, showCharacterCount, maxCharacters, enableImages, onImageUpload, imageCollection, enableMediaLibrary }) {
69
+ function RichTextEditor({ name, value, onChange, disabled, readOnly, label, description, placeholder, required, error, localized, locale, extensions, preset, features, showCharacterCount, maxCharacters, enableImages, onImageUpload, imageCollection, enableMediaLibrary, outputMode }) {
98
70
  const { t } = useTranslation();
99
71
  const resolveText = useResolveText();
100
72
  const resolvedLabel = label ? resolveText(label) : void 0;
@@ -134,12 +106,14 @@ function RichTextEditor({ name, value, onChange, disabled, readOnly, label, desc
134
106
  labels: extensionLabels,
135
107
  placeholder: placeholder || t("editor.startWriting"),
136
108
  maxCharacters,
109
+ outputMode,
137
110
  customExtensions: extensions
138
111
  }), [
139
112
  resolvedFeatures,
140
113
  extensionLabels,
141
114
  placeholder,
142
115
  maxCharacters,
116
+ outputMode,
143
117
  extensions,
144
118
  t
145
119
  ]);
@@ -190,12 +164,11 @@ function RichTextEditor({ name, value, onChange, disabled, readOnly, label, desc
190
164
  enableImages,
191
165
  onImageUpload,
192
166
  imageCollection,
193
- enableMediaLibrary
167
+ enableMediaLibrary,
168
+ outputMode
194
169
  }) : /* @__PURE__ */ jsx(RichTextEditorLoadingSkeleton, {
195
170
  disabled,
196
- readOnly,
197
- error,
198
- showToolbar: resolvedFeatures.toolbar
171
+ readOnly
199
172
  }),
200
173
  resolvedDescription && /* @__PURE__ */ jsx("p", {
201
174
  id: descriptionId,
@@ -215,7 +188,7 @@ function RichTextEditor({ name, value, onChange, disabled, readOnly, label, desc
215
188
  * `useEditor` always receives a complete extension list, so the ProseMirror
216
189
  * schema always has its `doc` node.
217
190
  */
218
- function RichTextEditorCore({ name, ariaLabel, ariaDescribedBy, value, onChange, disabled, readOnly, error, locale, features, resolvedExtensions, showCharacterCount, maxCharacters, enableImages, onImageUpload, imageCollection, enableMediaLibrary }) {
191
+ function RichTextEditorCore({ name, ariaLabel, ariaDescribedBy, value, onChange, disabled, readOnly, error, locale, features, resolvedExtensions, showCharacterCount, maxCharacters, enableImages, onImageUpload, imageCollection, enableMediaLibrary, outputMode }) {
219
192
  const { t } = useTranslation();
220
193
  const [linkOpen, setLinkOpen] = React.useState(false);
221
194
  const [imageOpen, setImageOpen] = React.useState(false);
@@ -225,7 +198,6 @@ function RichTextEditorCore({ name, ariaLabel, ariaDescribedBy, value, onChange,
225
198
  const allowImages = features.image && (enableImages ?? true);
226
199
  const allowLinks = features.link;
227
200
  const allowBubbleMenu = features.bubbleMenu;
228
- const allowToolbar = features.toolbar;
229
201
  const allowCharacterCount = features.characterCount && (showCharacterCount || maxCharacters);
230
202
  const isEditable = !disabled && !readOnly;
231
203
  const editorStateRef = React.useRef({
@@ -339,7 +311,7 @@ function RichTextEditorCore({ name, ariaLabel, ariaDescribedBy, value, onChange,
339
311
  editable: isEditable,
340
312
  onUpdate: ({ editor: currentEditor }) => {
341
313
  if (disabled || readOnly) return;
342
- const nextValue = getOutput(currentEditor);
314
+ const nextValue = getOutput(currentEditor, outputMode);
343
315
  lastEmittedValueRef.current = nextValue;
344
316
  onChange?.(nextValue);
345
317
  }
@@ -350,7 +322,6 @@ function RichTextEditorCore({ name, ariaLabel, ariaDescribedBy, value, onChange,
350
322
  if (editorRef.current === editor) editorRef.current = null;
351
323
  };
352
324
  }, [editor]);
353
- const headingValue = getHeadingLevel(editor);
354
325
  const inTable = editor?.isActive("table") ?? false;
355
326
  React.useEffect(() => {
356
327
  if (!editor) return;
@@ -372,33 +343,9 @@ function RichTextEditorCore({ name, ariaLabel, ariaDescribedBy, value, onChange,
372
343
  }, [editor, value]);
373
344
  const characterCount = getCharacterCount(editor);
374
345
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
375
- className: cn("qp-rich-text-editor control-surface h-auto min-h-[160px] overflow-hidden", disabled || readOnly ? "opacity-60" : "", error ? "border-destructive" : "border-border"),
346
+ className: cn("qp-rich-text-editor", disabled || readOnly ? "opacity-60" : ""),
376
347
  "data-admin-rich-text-editor": "root",
377
348
  children: [
378
- editor && allowToolbar && /* @__PURE__ */ jsx(RichTextToolbar, {
379
- editor,
380
- features,
381
- disabled: !isEditable,
382
- headingValue,
383
- onHeadingChange: (value$1) => {
384
- if (!editor) return;
385
- if (value$1 === "paragraph") {
386
- editor.chain().focus().setParagraph().run();
387
- return;
388
- }
389
- editor.chain().focus().toggleHeading({ level: Number(value$1) }).run();
390
- },
391
- onLinkClick: () => setLinkOpen(true),
392
- onImageClick: () => setImageOpen(true),
393
- onTableClick: () => {
394
- if (!inTable) editor.chain().focus().insertTable({
395
- rows: 3,
396
- cols: 3,
397
- withHeaderRow: true
398
- }).run();
399
- },
400
- inTable
401
- }),
402
349
  editor && (allowBubbleMenu || linkOpen) && /* @__PURE__ */ jsx(RichTextBubbleMenu, {
403
350
  editor,
404
351
  features,
@@ -412,6 +359,20 @@ function RichTextEditorCore({ name, ariaLabel, ariaDescribedBy, value, onChange,
412
359
  editor,
413
360
  id: name
414
361
  }) : /* @__PURE__ */ jsx(RichTextEditorContentSkeleton, {}),
362
+ editor && features.table && features.tableControls && inTable && /* @__PURE__ */ jsx("div", {
363
+ className: "flex justify-end px-2 py-1",
364
+ children: /* @__PURE__ */ jsx(TableControls, {
365
+ editor,
366
+ disabled: !isEditable,
367
+ inTable,
368
+ triggerButton: /* @__PURE__ */ jsx(ToolbarButton, {
369
+ icon: "ph:table",
370
+ active: inTable,
371
+ disabled: !isEditable,
372
+ title: t("editor.table")
373
+ })
374
+ })
375
+ }),
415
376
  uploadingInlineImage && /* @__PURE__ */ jsx("div", {
416
377
  className: "qp-rich-text-editor__upload-status",
417
378
  role: "status",
@@ -419,7 +380,7 @@ function RichTextEditorCore({ name, ariaLabel, ariaDescribedBy, value, onChange,
419
380
  children: t("editor.uploading")
420
381
  }),
421
382
  allowCharacterCount && showCharacterCount && /* @__PURE__ */ jsxs("div", {
422
- className: "bg-surface-low text-muted-foreground border-border-subtle flex items-center justify-between border-t px-2 py-1 text-xs",
383
+ className: "text-muted-foreground flex items-center justify-between px-1.5 pt-2 text-xs",
423
384
  children: [/* @__PURE__ */ jsxs("span", { children: [
424
385
  characterCount.words,
425
386
  " word",
@@ -47,21 +47,44 @@ const SlashCommandList = React.forwardRef(function SlashCommandList$1({ items, c
47
47
  }
48
48
  return false;
49
49
  } }));
50
+ const grouped = React.useMemo(() => {
51
+ const groups = [];
52
+ let currentLabel;
53
+ for (let i = 0; i < items.length; i++) {
54
+ const item = items[i];
55
+ if (item.group !== currentLabel || i === 0) {
56
+ currentLabel = item.group;
57
+ groups.push({
58
+ label: currentLabel,
59
+ items: []
60
+ });
61
+ }
62
+ groups[groups.length - 1].items.push({
63
+ item,
64
+ globalIndex: i
65
+ });
66
+ }
67
+ return groups;
68
+ }, [items]);
50
69
  return /* @__PURE__ */ jsxs("div", {
51
70
  className: "qp-rich-text-editor__slash",
52
71
  role: "listbox",
53
72
  children: [items.length === 0 && /* @__PURE__ */ jsx("div", {
54
73
  className: "qp-rich-text-editor__slash-empty",
55
74
  children: "No results"
56
- }), items.map((item, index) => /* @__PURE__ */ jsxs("button", {
75
+ }), grouped.map((group) => /* @__PURE__ */ jsxs("div", { children: [group.label && /* @__PURE__ */ jsx("div", {
76
+ className: "qp-rich-text-editor__slash-group",
77
+ "aria-hidden": "true",
78
+ children: group.label
79
+ }), group.items.map(({ item, globalIndex }) => /* @__PURE__ */ jsxs("button", {
57
80
  type: "button",
58
- "aria-selected": index === safeIndex,
81
+ "aria-selected": globalIndex === safeIndex,
59
82
  role: "option",
60
83
  tabIndex: -1,
61
- className: cn("qp-rich-text-editor__slash-item", index === safeIndex ? "qp-rich-text-editor__slash-item--active" : ""),
62
- onMouseEnter: () => setSelectedIndex(index),
84
+ className: cn("qp-rich-text-editor__slash-item", globalIndex === safeIndex ? "qp-rich-text-editor__slash-item--active" : ""),
85
+ onMouseEnter: () => setSelectedIndex(globalIndex),
63
86
  onMouseDown: (event) => event.preventDefault(),
64
- onClick: () => selectItem(index),
87
+ onClick: () => selectItem(globalIndex),
65
88
  children: [item.icon && /* @__PURE__ */ jsx("span", {
66
89
  className: "qp-rich-text-editor__slash-icon",
67
90
  "aria-hidden": "true",
@@ -80,7 +103,7 @@ const SlashCommandList = React.forwardRef(function SlashCommandList$1({ items, c
80
103
  children: item.description
81
104
  })]
82
105
  })]
83
- }, item.title))]
106
+ }, item.title))] }, group.label ?? "ungrouped"))]
84
107
  });
85
108
  });
86
109
  /**
@@ -92,7 +115,7 @@ function createSlashCommandExtension(getItems) {
92
115
  addOptions() {
93
116
  return { suggestion: {
94
117
  char: "/",
95
- startOfLine: true,
118
+ startOfLine: false,
96
119
  command: ({ editor, range, props }) => {
97
120
  editor.chain().focus().deleteRange(range).run();
98
121
  props.command(editor);