web(memory): split Plate editor plugins into base + lazy heavy kits; idle-load heavy kits to reduce startup heap

Co-Authored-By: nate@buster.so <nate@buster.so>
This commit is contained in:
Devin AI 2025-08-08 05:38:35 +00:00
parent b979e208f7
commit 34515032ee
3 changed files with 149 additions and 16 deletions

View File

@ -0,0 +1,23 @@
'use client';
import { TrailingBlockPlugin } from 'platejs';
import { AlignKit } from './plugins/align-kit';
import { BasicBlocksKit } from './plugins/basic-blocks-kit';
import { BasicMarksKit } from './plugins/basic-marks-kit';
import { ExitBreakKit } from './plugins/exit-break-kit';
import { FixedToolbarKit } from './plugins/fixed-toolbar-kit';
import { LineHeightKit } from './plugins/line-height-kit';
import { LinkKit } from './plugins/link-kit';
import { ListKit } from './plugins/list-kit';
export const BaseEditorKit = [
...BasicBlocksKit,
...BasicMarksKit,
...ListKit,
...AlignKit,
...LineHeightKit,
...LinkKit,
...ExitBreakKit,
TrailingBlockPlugin,
...FixedToolbarKit
];

View File

@ -0,0 +1,88 @@
export const loadHeavyEditorKits = async () => {
const [
ai,
blockMenu,
codeBlock,
table,
toggle,
toc,
media,
callout,
column,
math,
date,
mention,
font,
discussion,
comment,
suggestion,
slash,
autoformat,
cursorOverlay,
dnd,
emoji,
docx,
markdown,
blockPlaceholder,
floatingToolbar,
busterStream
] = await Promise.all([
import('./plugins/ai-kit'),
import('./plugins/block-menu-kit'),
import('./plugins/code-block-kit'),
import('./plugins/table-kit'),
import('./plugins/toggle-kit'),
import('./plugins/toc-kit'),
import('./plugins/media-kit'),
import('./plugins/callout-kit'),
import('./plugins/column-kit'),
import('./plugins/math-kit'),
import('./plugins/date-kit'),
import('./plugins/mention-kit'),
import('./plugins/font-kit'),
import('./plugins/discussion-kit'),
import('./plugins/comment-kit'),
import('./plugins/suggestion-kit'),
import('./plugins/slash-kit'),
import('./plugins/autoformat-kit'),
import('./plugins/cursor-overlay-kit'),
import('./plugins/dnd-kit'),
import('./plugins/emoji-kit'),
import('./plugins/docx-kit'),
import('./plugins/markdown-kit'),
import('./plugins/block-placeholder-kit'),
import('./plugins/floating-toolbar-kit'),
import('./plugins/buster-stream-kit').catch(() => ({ BusterStreamKit: [] as any[] }))
]);
const kits = [
...(ai.AIKit || []),
...(blockMenu.BlockMenuKit || []),
...(codeBlock.CodeBlockKit || []),
...(table.TableKit || []),
...(toggle.ToggleKit || []),
...(toc.TocKit || []),
...(media.MediaKit || []),
...(callout.CalloutKit || []),
...(column.ColumnKit || []),
...(math.MathKit || []),
...(date.DateKit || []),
...(mention.MentionKit || []),
...(font.FontKit || []),
...(discussion.DiscussionKit || []),
...(comment.CommentKit || []),
...(suggestion.SuggestionKit || []),
...(slash.SlashKit || []),
...(autoformat.AutoformatKit || []),
...(cursorOverlay.CursorOverlayKit || []),
...(dnd.DndKit || []),
...(emoji.EmojiKit || []),
...(docx.DocxKit || []),
...(markdown.MarkdownKit || []),
...(blockPlaceholder.BlockPlaceholderKit || []),
...(floatingToolbar.FloatingToolbarKit || []),
...((busterStream as any).BusterStreamKit || [])
];
return kits;
};

View File

@ -1,9 +1,9 @@
import { type Value } from 'platejs';
import { useEditorRef, usePlateEditor, type TPlateEditor } from 'platejs/react';
import { useMemo } from 'react';
import { EditorKit } from './editor-kit';
import { useEffect, useMemo, useState } from 'react';
import { FIXED_TOOLBAR_KIT_KEY } from './plugins/fixed-toolbar-kit';
import { BaseEditorKit } from './base-editor-kit';
import { loadHeavyEditorKits } from './load-heavy-editor-kits';
import type { IReportEditor } from './ReportEditor';
export const useReportEditor = ({
@ -17,27 +17,49 @@ export const useReportEditor = ({
onReady?: (editor: IReportEditor) => void;
useFixedToolbarKit?: boolean;
}) => {
const plugins = useMemo(() => {
const filteredKeys: string[] = [];
if (!useFixedToolbarKit) {
filteredKeys.push(FIXED_TOOLBAR_KIT_KEY);
}
if (filteredKeys.length > 0) {
return EditorKit.filter((plugin) => !filteredKeys.includes(plugin.key));
}
return EditorKit;
const basePlugins = useMemo(() => {
if (useFixedToolbarKit) return BaseEditorKit;
return BaseEditorKit.filter((plugin) => plugin.key !== FIXED_TOOLBAR_KIT_KEY);
}, [useFixedToolbarKit]);
const [plugins, setPlugins] = useState(basePlugins);
useEffect(() => {
setPlugins(basePlugins);
}, [basePlugins]);
useEffect(() => {
const schedule = (cb: () => void) => {
if (typeof window !== 'undefined' && 'requestIdleCallback' in window) {
(window as any).requestIdleCallback(cb, { timeout: 3000 });
} else {
setTimeout(cb, 1500);
}
};
let cancelled = false;
schedule(async () => {
const heavy = await loadHeavyEditorKits();
if (!cancelled) {
setPlugins((prev) => {
const keys = new Set(prev.map((p) => p.key));
const toAdd = heavy.filter((p) => !keys.has(p.key));
return [...prev, ...toAdd];
});
}
});
return () => {
cancelled = true;
};
}, []);
return usePlateEditor({
plugins,
value,
readOnly: disabled,
onReady: ({ editor }) => onReady?.(editor)
}); // Pass dependencies to usePlateEditor
});
};
export type ReportEditor = TPlateEditor<Value, (typeof EditorKit)[number]>;
export type ReportEditor = TPlateEditor<Value, any>;
export const useEditor = () => useEditorRef<ReportEditor>();