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({
) : (