fix(npcs): stop TipTap crash black screen in secondary windows
Guard TipTap getHTML under StrictMode, wrap secondary window roots in WindowErrorBoundary, and add stability regression tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -73,7 +73,7 @@ export function SceneDescriptionModal({ initialHtml, onClose, onSave }: SceneDes
|
||||
const editor = useEditor({
|
||||
extensions,
|
||||
content: initialHtml || '',
|
||||
immediatelyRender: true,
|
||||
immediatelyRender: false,
|
||||
shouldRerenderOnTransaction: true,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
@@ -94,21 +94,56 @@ export function SceneDescriptionModal({ initialHtml, onClose, onSave }: SceneDes
|
||||
const toolbarState = useEditorState({
|
||||
editor,
|
||||
selector: ({ editor: ed }) => ({
|
||||
bold: ed.isActive('bold'),
|
||||
italic: ed.isActive('italic'),
|
||||
underline: ed.isActive('underline'),
|
||||
bulletList: ed.isActive('bulletList'),
|
||||
orderedList: ed.isActive('orderedList'),
|
||||
h2: ed.isActive('heading', { level: 2 }),
|
||||
h3: ed.isActive('heading', { level: 3 }),
|
||||
blockquote: ed.isActive('blockquote'),
|
||||
bold: Boolean(ed && !ed.isDestroyed && ed.isActive('bold')),
|
||||
italic: Boolean(ed && !ed.isDestroyed && ed.isActive('italic')),
|
||||
underline: Boolean(ed && !ed.isDestroyed && ed.isActive('underline')),
|
||||
bulletList: Boolean(ed && !ed.isDestroyed && ed.isActive('bulletList')),
|
||||
orderedList: Boolean(ed && !ed.isDestroyed && ed.isActive('orderedList')),
|
||||
h2: Boolean(ed && !ed.isDestroyed && ed.isActive('heading', { level: 2 })),
|
||||
h3: Boolean(ed && !ed.isDestroyed && ed.isActive('heading', { level: 3 })),
|
||||
blockquote: Boolean(ed && !ed.isDestroyed && ed.isActive('blockquote')),
|
||||
}),
|
||||
});
|
||||
|
||||
const handleSave = () => {
|
||||
onSave(normalizeSceneDescriptionHtml(editor.getHTML()));
|
||||
if (!editor || editor.isDestroyed) return;
|
||||
let raw = '';
|
||||
try {
|
||||
raw = editor.getHTML();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
onSave(normalizeSceneDescriptionHtml(raw));
|
||||
};
|
||||
|
||||
if (!editor || editor.isDestroyed) {
|
||||
return createPortal(
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t('common.close')}
|
||||
onClick={onClose}
|
||||
className={styles.modalBackdrop}
|
||||
/>
|
||||
<div role="dialog" aria-modal="true" className={[styles.modalDialog, modalStyles.dialog].join(' ')}>
|
||||
<div className={styles.modalHeader}>
|
||||
<div className={styles.modalTitle}>{t('scene.descriptionModalTitle')}</div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t('common.close')}
|
||||
onClick={onClose}
|
||||
className={styles.modalClose}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div className={modalStyles.editorShell} />
|
||||
</div>
|
||||
</>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
|
||||
return createPortal(
|
||||
<>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user