|
1 | 1 | import { Editor, EditorContent, JSONContent } from "@tiptap/react";
|
2 | 2 | import { ContextProviderDescription, InputModifiers } from "core";
|
3 | 3 | import { modelSupportsImages } from "core/llm/autodetect";
|
4 |
| -import { useCallback, useContext, useEffect, useRef, useState } from "react"; |
| 4 | +import { |
| 5 | + memo, |
| 6 | + useCallback, |
| 7 | + useContext, |
| 8 | + useEffect, |
| 9 | + useRef, |
| 10 | + useState, |
| 11 | +} from "react"; |
5 | 12 | import { IdeMessengerContext } from "../../../context/IdeMessenger";
|
6 | 13 | import useIsOSREnabled from "../../../hooks/useIsOSREnabled";
|
7 | 14 | import useUpdatingRef from "../../../hooks/useUpdatingRef";
|
@@ -38,7 +45,7 @@ export interface TipTapEditorProps {
|
38 | 45 |
|
39 | 46 | export const TIPPY_DIV_ID = "tippy-js-div";
|
40 | 47 |
|
41 |
| -export function TipTapEditor(props: TipTapEditorProps) { |
| 48 | +function TipTapEditorInner(props: TipTapEditorProps) { |
42 | 49 | const dispatch = useAppDispatch();
|
43 | 50 | const mainEditorContext = useMainEditor();
|
44 | 51 |
|
@@ -72,12 +79,13 @@ export function TipTapEditor(props: TipTapEditorProps) {
|
72 | 79 | return;
|
73 | 80 | }
|
74 | 81 | const placeholder = getPlaceholderText(props.placeholder, historyLength);
|
75 |
| - |
76 |
| - editor.extensionManager.extensions.filter( |
77 |
| - (extension) => extension.name === "placeholder", |
78 |
| - )[0].options["placeholder"] = placeholder; |
79 |
| - |
80 |
| - editor.view.dispatch(editor.state.tr); |
| 82 | + const placeholderExt = editor.extensionManager.extensions.find( |
| 83 | + (e) => e.name === "placeholder", |
| 84 | + ) as any; |
| 85 | + if (placeholderExt) { |
| 86 | + placeholderExt.options["placeholder"] = placeholder; |
| 87 | + editor.view.dispatch(editor.state.tr); |
| 88 | + } |
81 | 89 | }, [editor, props.placeholder, historyLength]);
|
82 | 90 |
|
83 | 91 | useEffect(() => {
|
@@ -281,3 +289,37 @@ export function TipTapEditor(props: TipTapEditorProps) {
|
281 | 289 | </InputBoxDiv>
|
282 | 290 | );
|
283 | 291 | }
|
| 292 | + |
| 293 | +function toolbarOptionsEqual(a?: ToolbarOptions, b?: ToolbarOptions) { |
| 294 | + if (a === b) return true; |
| 295 | + if (!a || !b) return false; |
| 296 | + return ( |
| 297 | + a.hideAddContext === b.hideAddContext && |
| 298 | + a.hideImageUpload === b.hideImageUpload && |
| 299 | + a.hideUseCodebase === b.hideUseCodebase && |
| 300 | + a.hideSelectModel === b.hideSelectModel && |
| 301 | + a.enterText === b.enterText |
| 302 | + ); |
| 303 | +} |
| 304 | + |
| 305 | +const MemoInner = memo( |
| 306 | + TipTapEditorInner, |
| 307 | + (prev, next) => |
| 308 | + prev.isMainInput === next.isMainInput && |
| 309 | + prev.placeholder === next.placeholder && |
| 310 | + prev.historyKey === next.historyKey && |
| 311 | + prev.inputId === next.inputId && |
| 312 | + toolbarOptionsEqual(prev.toolbarOptions, next.toolbarOptions) && |
| 313 | + (prev.availableContextProviders?.length || 0) === |
| 314 | + (next.availableContextProviders?.length || 0) && |
| 315 | + (prev.availableSlashCommands?.length || 0) === |
| 316 | + (next.availableSlashCommands?.length || 0), |
| 317 | +); |
| 318 | + |
| 319 | +export function TipTapEditor(props: TipTapEditorProps) { |
| 320 | + return ( |
| 321 | + <div className="relative w-full"> |
| 322 | + <MemoInner {...props} /> |
| 323 | + </div> |
| 324 | + ); |
| 325 | +} |
0 commit comments