diff --git a/surfsense_web/app/dashboard/[search_space_id]/editor/[documentId]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/editor/[documentId]/page.tsx
index dd65ae56c..765bbf098 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/editor/[documentId]/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/editor/[documentId]/page.tsx
@@ -466,6 +466,7 @@ export default function EditorPage() {
void;
/** Whether there are unsaved changes */
hasUnsavedChanges?: boolean;
@@ -52,6 +40,20 @@ interface PlateEditorProps {
isSaving?: boolean;
/** Start the editor in editing mode instead of viewing mode. Ignored when readOnly is true. */
defaultEditing?: boolean;
+ /**
+ * Plugin preset to use. Controls which plugin kits are loaded.
+ * - "full" – all plugins (toolbars, slash commands, DnD, etc.)
+ * - "minimal" – core formatting only (no fixed toolbar, slash commands, DnD, block selection)
+ * - "readonly" – rendering support for all rich content, no editing UI
+ * @default "full"
+ */
+ preset?: EditorPreset;
+ /**
+ * Additional plugins to append after the preset plugins.
+ * Use this to inject feature-specific plugins (e.g. approve/reject blocks)
+ * without modifying the core editor component.
+ */
+ extraPlugins?: AnyPluginConfig[];
}
export function PlateEditor({
@@ -66,6 +68,8 @@ export function PlateEditor({
hasUnsavedChanges = false,
isSaving = false,
defaultEditing = false,
+ preset = "full",
+ extraPlugins = [],
}: PlateEditorProps) {
const lastMarkdownRef = useRef(markdown);
@@ -76,7 +80,8 @@ export function PlateEditor({
onSaveRef.current = onSave;
}, [onSave]);
- // Stable Plate plugin for ⌘+S / Ctrl+S save shortcut
+ // Stable Plate plugin for ⌘+S / Ctrl+S save shortcut.
+ // Only included when onSave is provided.
const SaveShortcutPlugin = useMemo(
() =>
createPlatePlugin({
@@ -94,27 +99,20 @@ export function PlateEditor({
[]
);
+ // Resolve the plugin set from the chosen preset
+ const presetPlugins = presetMap[preset];
+
// When readOnly is forced, always start in readOnly.
// Otherwise, respect defaultEditing to decide initial mode.
// The user can still toggle between editing/viewing via ModeToolbarButton.
const editor = usePlateEditor({
readOnly: readOnly || !defaultEditing,
plugins: [
- ...BasicNodesKit,
- ...TableKit,
- ...ListKit,
- ...CodeBlockKit,
- ...LinkKit,
- ...CalloutKit,
- ...ToggleKit,
- ...MathKit,
- ...SelectionKit,
- ...SlashCommandKit,
- ...FixedToolbarKit,
- ...FloatingToolbarKit,
- ...AutoformatKit,
- ...DndKit,
- SaveShortcutPlugin,
+ ...presetPlugins,
+ // Only register save shortcut when a save handler is provided
+ ...(onSave ? [SaveShortcutPlugin] : []),
+ // Consumer-provided extra plugins
+ ...extraPlugins,
MarkdownPlugin.configure({
options: {
remarkPlugins: [remarkGfm, remarkMath, remarkMdx],
diff --git a/surfsense_web/components/editor/presets.ts b/surfsense_web/components/editor/presets.ts
new file mode 100644
index 000000000..7800f7c7d
--- /dev/null
+++ b/surfsense_web/components/editor/presets.ts
@@ -0,0 +1,79 @@
+"use client";
+
+import type { AnyPluginConfig } from "platejs";
+
+import { AutoformatKit } from "@/components/editor/plugins/autoformat-kit";
+import { BasicNodesKit } from "@/components/editor/plugins/basic-nodes-kit";
+import { CalloutKit } from "@/components/editor/plugins/callout-kit";
+import { CodeBlockKit } from "@/components/editor/plugins/code-block-kit";
+import { DndKit } from "@/components/editor/plugins/dnd-kit";
+import { FixedToolbarKit } from "@/components/editor/plugins/fixed-toolbar-kit";
+import { FloatingToolbarKit } from "@/components/editor/plugins/floating-toolbar-kit";
+import { LinkKit } from "@/components/editor/plugins/link-kit";
+import { ListKit } from "@/components/editor/plugins/list-kit";
+import { MathKit } from "@/components/editor/plugins/math-kit";
+import { SelectionKit } from "@/components/editor/plugins/selection-kit";
+import { SlashCommandKit } from "@/components/editor/plugins/slash-command-kit";
+import { TableKit } from "@/components/editor/plugins/table-kit";
+import { ToggleKit } from "@/components/editor/plugins/toggle-kit";
+
+/**
+ * Full preset – every plugin kit enabled.
+ * Used by the Documents editor and Reports editor (rich editing experience).
+ */
+export const fullPreset: AnyPluginConfig[] = [
+ ...BasicNodesKit,
+ ...TableKit,
+ ...ListKit,
+ ...CodeBlockKit,
+ ...LinkKit,
+ ...CalloutKit,
+ ...ToggleKit,
+ ...MathKit,
+ ...SelectionKit,
+ ...SlashCommandKit,
+ ...FixedToolbarKit,
+ ...FloatingToolbarKit,
+ ...AutoformatKit,
+ ...DndKit,
+];
+
+/**
+ * Minimal preset – lightweight editing with core formatting only.
+ * No fixed toolbar, no slash commands, no DnD, no block selection.
+ * Ideal for inline editors like human-in-the-loop agent actions.
+ */
+export const minimalPreset: AnyPluginConfig[] = [
+ ...BasicNodesKit,
+ ...ListKit,
+ ...CodeBlockKit,
+ ...LinkKit,
+ ...FloatingToolbarKit,
+ ...AutoformatKit,
+];
+
+/**
+ * Read-only preset – rendering support for all rich content, but no editing UI.
+ * No toolbars, no autoformat, no DnD, no slash commands, no block selection.
+ * Ideal for pure display / viewer contexts.
+ */
+export const readonlyPreset: AnyPluginConfig[] = [
+ ...BasicNodesKit,
+ ...TableKit,
+ ...ListKit,
+ ...CodeBlockKit,
+ ...LinkKit,
+ ...CalloutKit,
+ ...ToggleKit,
+ ...MathKit,
+];
+
+/** All available preset names */
+export type EditorPreset = "full" | "minimal" | "readonly";
+
+/** Map from preset name to plugin array */
+export const presetMap: Record = {
+ full: fullPreset,
+ minimal: minimalPreset,
+ readonly: readonlyPreset,
+};
diff --git a/surfsense_web/components/report-panel/report-panel.tsx b/surfsense_web/components/report-panel/report-panel.tsx
index a6621f135..300920951 100644
--- a/surfsense_web/components/report-panel/report-panel.tsx
+++ b/surfsense_web/components/report-panel/report-panel.tsx
@@ -438,6 +438,7 @@ function ReportPanelContent({
) : (