import { LexicalComposer, type InitialConfigType } from "@lexical/react/LexicalComposer";import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";import { ContentEditable } from "@lexical/react/LexicalContentEditable";import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin";import { type ServerProviderSkill } from "@t3tools/contracts";import { serializeComposerFileLink, serializeComposerThreadLink,} from "@t3tools/shared/composerTrigger";import { MessagesSquareIcon } from "lucide-react";import { $applyNodeReplacement, $createRangeSelectionFromDom, $createRangeSelection, $getSelection, $setSelection, $isElementNode, $isLineBreakNode, $isRangeSelection, $isTextNode, $createLineBreakNode, $createParagraphNode, $createTextNode, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, KEY_BACKSPACE_COMMAND, BLUR_COMMAND, FOCUS_COMMAND, $getRoot, HISTORY_MERGE_TAG, DecoratorNode, type ElementNode, type LexicalNode, type SerializedLexicalNode, type EditorState, type NodeKey, type Spread,} from "lexical";import { createContext, use, useCallback, useEffect, useEffectEvent, useImperativeHandle, useLayoutEffect, useMemo, useRef,} from "react"; import { clampCollapsedComposerCursor, collapseExpandedComposerCursor, expandCollapsedComposerCursor, isCollapsedCursorAdjacentToInlineToken,} from "~/composer-logic";import { selectionTouchesMentionBoundary, splitPromptIntoComposerSegments,} from "~/composer-editor-mentions";import { INLINE_TERMINAL_CONTEXT_PLACEHOLDER, type TerminalContextDraft,} from "~/lib/terminalContext";import { cn, isMacPlatform } from "~/lib/utils";import { basenameOfPath } from "~/pierre-icons";import { COMPOSER_INLINE_CHIP_ICON_CLASS_NAME, COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME, COMPOSER_INLINE_SKILL_CHIP_CLASS_NAME, SKILL_CHIP_ICON_SVG,} from "./composerInlineChip";import { FILE_TAG_CHIP_CLASS_NAME, FileTagChipContent } from "./chat/FileTagChip";import { ComposerPendingTerminalContextChip } from "./chat/ComposerPendingTerminalContexts";import { formatProviderSkillDisplayName } from "~/providerSkillPresentation";import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";import { registerComposerInlineTokenPaste } from "./composerInlineTokenPaste"; const COMPOSER_EDITOR_HMR_KEY = `composer-editor-${Math.random().toString(36).slice(2)}`;const SURROUND_SYMBOLS: [string, string][] = [ ["(", ")"], ["[", "]"], ["{", "}"], ["'", "'"], ['"', '"'], ["“", "”"], ["`", "`"], ["<", ">"], ["«", "»"], ["*", "*"], ["_", "_"],];const SURROUND_SYMBOLS_MAP = new Map<string, string>(SURROUND_SYMBOLS);const BACKTICK_SURROUND_CLOSE_SYMBOL = SURROUND_SYMBOLS_MAP.get("`") ?? null; type SerializedComposerMentionNode = Spread< { path: string; type: "composer-mention"; version: 1; }, SerializedLexicalNode>; type SerializedComposerSkillNode = Spread< { skillName: string; skillLabel?: string; skillDescription?: string; type: "composer-skill"; version: 1; }, SerializedLexicalNode>; type SerializedComposerThreadNode = Spread< { environmentId: string; threadId: string; title: string; type: "composer-thread"; version: 1; }, SerializedLexicalNode>; type SerializedComposerTerminalContextNode = Spread< { context: TerminalContextDraft; type: "composer-terminal-context"; version: 1; }, SerializedLexicalNode>; const ComposerTerminalContextActionsContext = createContext<{ onRemoveTerminalContext: (contextId: string) => void;}>({ onRemoveTerminalContext: () => {},}); function ComposerMentionDecorator(props: { path: string }) { const theme = resolvedThemeFromDocument(); const chip = ( <span className={FILE_TAG_CHIP_CLASS_NAME} contentEditable={false} spellCheck={false} data-composer-mention-chip="true" > <FileTagChipContent path={props.path} label={basenameOfPath(props.path)} theme={theme} /> </span> ); return ( <Tooltip> <TooltipTrigger render={chip} /> <TooltipPopup side="top" className="max-w-120 whitespace-normal leading-tight wrap-anywhere"> {props.path} </TooltipPopup> </Tooltip> );} class ComposerMentionNode extends DecoratorNode<React.ReactElement> { __path: string; static override getType(): string { return "composer-mention"; } static override clone(node: ComposerMentionNode): ComposerMentionNode { return new ComposerMentionNode(node.__path, node.__key); } static override importJSON(serializedNode: SerializedComposerMentionNode): ComposerMentionNode { return $createComposerMentionNode(serializedNode.path).updateFromJSON(serializedNode); } constructor(path: string, key?: NodeKey) { super(key); this.__path = path; } override exportJSON(): SerializedComposerMentionNode { return { ...super.exportJSON(), path: this.__path, type: "composer-mention", version: 1, }; } override createDOM(): HTMLElement { const dom = document.createElement("span"); dom.className = "composer-inline-chip relative inline-flex align-middle leading-none"; return dom; } override updateDOM(): false { return false; } override getTextContent(): string { return serializeComposerFileLink(this.__path); } override isInline(): true { return true; } override decorate(): React.ReactElement { return <ComposerMentionDecorator path={this.__path} />; }} function $createComposerMentionNode(path: string): ComposerMentionNode { return $applyNodeReplacement(new ComposerMentionNode(path));} function resolveSkillDescription( skill: Pick<ServerProviderSkill, "shortDescription" | "description">,): string | null { const shortDescription = skill.shortDescription?.trim(); if (shortDescription) { return shortDescription; } const description = skill.description?.trim(); return description || null;} type ComposerSkillMetadata = { label: string; description: string | null;}; function skillMetadataByName( skills: ReadonlyArray<ServerProviderSkill>,): ReadonlyMap<string, ComposerSkillMetadata> { return new Map( skills.map((skill) => [ skill.name, { label: formatProviderSkillDisplayName(skill), description: resolveSkillDescription(skill), }, ]), );} function ComposerSkillDecorator(props: { skillLabel: string; skillDescription: string | null }) { const chip = ( <span className={COMPOSER_INLINE_SKILL_CHIP_CLASS_NAME} contentEditable={false} spellCheck={false} data-composer-skill-chip="true" > <span aria-hidden="true" className={COMPOSER_INLINE_CHIP_ICON_CLASS_NAME} dangerouslySetInnerHTML={{ __html: SKILL_CHIP_ICON_SVG }} /> <span className={COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME}>{props.skillLabel}</span> </span> ); if (!props.skillDescription) { return chip; } return ( <Tooltip> <TooltipTrigger render={chip} /> <TooltipPopup side="top" className="max-w-120 whitespace-normal leading-tight"> {props.skillDescription} </TooltipPopup> </Tooltip> );} class ComposerSkillNode extends DecoratorNode<React.ReactElement> { __skillName: string; __skillLabel: string; __skillDescription: string | null; static override getType(): string { return "composer-skill"; } static override clone(node: ComposerSkillNode): ComposerSkillNode { return new ComposerSkillNode( node.__skillName, node.__skillLabel, node.__skillDescription, node.__key, ); } static override importJSON(serializedNode: SerializedComposerSkillNode): ComposerSkillNode { return $createComposerSkillNode( serializedNode.skillName, serializedNode.skillLabel ?? serializedNode.skillName, serializedNode.skillDescription ?? null, ).updateFromJSON(serializedNode); } constructor( skillName: string, skillLabel: string, skillDescription: string | null, key?: NodeKey, ) { super(key); const normalizedSkillName = skillName.startsWith("$") ? skillName.slice(1) : skillName; this.__skillName = normalizedSkillName; this.__skillLabel = skillLabel; this.__skillDescription = skillDescription; } override exportJSON(): SerializedComposerSkillNode { return { ...super.exportJSON(), skillName: this.__skillName, skillLabel: this.__skillLabel, ...(this.__skillDescription ? { skillDescription: this.__skillDescription } : {}), type: "composer-skill", version: 1, }; } override createDOM(): HTMLElement { const dom = document.createElement("span"); dom.className = "composer-inline-chip relative inline-flex align-middle leading-none"; return dom; } override updateDOM(): false { return false; } override getTextContent(): string { return `$${this.__skillName}`; } override isInline(): true { return true; } override decorate(): React.ReactElement { return ( <ComposerSkillDecorator skillLabel={this.__skillLabel} skillDescription={this.__skillDescription} /> ); }} function $createComposerSkillNode( skillName: string, skillLabel: string, skillDescription: string | null,): ComposerSkillNode { return $applyNodeReplacement(new ComposerSkillNode(skillName, skillLabel, skillDescription));} function ComposerThreadDecorator(props: { title: string; threadId: string }) { const chip = ( <span className={COMPOSER_INLINE_SKILL_CHIP_CLASS_NAME} contentEditable={false} spellCheck={false} data-composer-thread-chip="true" > <MessagesSquareIcon className={COMPOSER_INLINE_CHIP_ICON_CLASS_NAME} aria-hidden="true" /> <span className={COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME}>{props.title}</span> </span> ); return ( <Tooltip> <TooltipTrigger render={chip} /> <TooltipPopup side="top" className="max-w-120 whitespace-normal leading-tight"> Thread reference · {props.threadId} </TooltipPopup> </Tooltip> );} class ComposerThreadNode extends DecoratorNode<React.ReactElement> { __environmentId: string; __threadId: string; __title: string; static override getType(): string { return "composer-thread"; } static override clone(node: ComposerThreadNode): ComposerThreadNode { return new ComposerThreadNode(node.__environmentId, node.__threadId, node.__title, node.__key); } static override importJSON(serializedNode: SerializedComposerThreadNode): ComposerThreadNode { return $createComposerThreadNode( serializedNode.environmentId, serializedNode.threadId, serializedNode.title, ).updateFromJSON(serializedNode); } constructor(environmentId: string, threadId: string, title: string, key?: NodeKey) { super(key); this.__environmentId = environmentId; this.__threadId = threadId; this.__title = title; } override exportJSON(): SerializedComposerThreadNode { return { ...super.exportJSON(), environmentId: this.__environmentId, threadId: this.__threadId, title: this.__title, type: "composer-thread", version: 1, }; } override createDOM(): HTMLElement { const dom = document.createElement("span"); dom.className = "inline-flex align-middle leading-none"; return dom; } override updateDOM(): false { return false; } override getTextContent(): string { return serializeComposerThreadLink({ environmentId: this.__environmentId, threadId: this.__threadId, title: this.__title, }); } override isInline(): true { return true; } override decorate(): React.ReactElement { return <ComposerThreadDecorator title={this.__title} threadId={this.__threadId} />; }} function $createComposerThreadNode( environmentId: string, threadId: string, title: string,): ComposerThreadNode { return $applyNodeReplacement(new ComposerThreadNode(environmentId, threadId, title));} function ComposerTerminalContextDecorator(props: { context: TerminalContextDraft }) { return <ComposerPendingTerminalContextChip context={props.context} />;} class ComposerTerminalContextNode extends DecoratorNode<React.ReactElement> { __context: TerminalContextDraft; static override getType(): string { return "composer-terminal-context"; } static override clone(node: ComposerTerminalContextNode): ComposerTerminalContextNode { return new ComposerTerminalContextNode(node.__context, node.__key); } static override importJSON( serializedNode: SerializedComposerTerminalContextNode, ): ComposerTerminalContextNode { return $createComposerTerminalContextNode(serializedNode.context); } constructor(context: TerminalContextDraft, key?: NodeKey) { super(key); this.__context = context; } Lines 1–500 of 1967. Load more
import { LexicalComposer, type InitialConfigType } from "@lexical/react/LexicalComposer"; import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; import { ContentEditable } from "@lexical/react/LexicalContentEditable"; import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary"; import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin"; import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin"; import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin"; import { type ServerProviderSkill } from "@t3tools/contracts"; import { serializeComposerFileLink, serializeComposerThreadLink, } from "@t3tools/shared/composerTrigger"; import { MessagesSquareIcon } from "lucide-react"; import { $applyNodeReplacement, $createRangeSelectionFromDom, $createRangeSelection, $getSelection, $setSelection, $isElementNode, $isLineBreakNode, $isRangeSelection, $isTextNode, $createLineBreakNode, $createParagraphNode, $createTextNode, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, KEY_BACKSPACE_COMMAND, BLUR_COMMAND, FOCUS_COMMAND, $getRoot, HISTORY_MERGE_TAG, DecoratorNode, type ElementNode, type LexicalNode, type SerializedLexicalNode, type EditorState, type NodeKey, type Spread, } from "lexical"; import { createContext, use, useCallback, useEffect, useEffectEvent, useImperativeHandle, useLayoutEffect, useMemo, useRef, } from "react"; import { clampCollapsedComposerCursor, collapseExpandedComposerCursor, expandCollapsedComposerCursor, isCollapsedCursorAdjacentToInlineToken, } from "~/composer-logic"; import { selectionTouchesMentionBoundary, splitPromptIntoComposerSegments, } from "~/composer-editor-mentions"; import { INLINE_TERMINAL_CONTEXT_PLACEHOLDER, type TerminalContextDraft, } from "~/lib/terminalContext"; import { cn, isMacPlatform } from "~/lib/utils"; import { basenameOfPath } from "~/pierre-icons"; import { COMPOSER_INLINE_CHIP_ICON_CLASS_NAME, COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME, COMPOSER_INLINE_SKILL_CHIP_CLASS_NAME, SKILL_CHIP_ICON_SVG, } from "./composerInlineChip"; import { FILE_TAG_CHIP_CLASS_NAME, FileTagChipContent } from "./chat/FileTagChip"; import { ComposerPendingTerminalContextChip } from "./chat/ComposerPendingTerminalContexts"; import { formatProviderSkillDisplayName } from "~/providerSkillPresentation"; import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip"; import { registerComposerInlineTokenPaste } from "./composerInlineTokenPaste"; const COMPOSER_EDITOR_HMR_KEY = `composer-editor-${Math.random().toString(36).slice(2)}`; const SURROUND_SYMBOLS: [string, string][] = [ ["(", ")"], ["[", "]"], ["{", "}"], ["'", "'"], ['"', '"'], ["“", "”"], ["`", "`"], ["<", ">"], ["«", "»"], ["*", "*"], ["_", "_"], ]; const SURROUND_SYMBOLS_MAP = new Map<string, string>(SURROUND_SYMBOLS); const BACKTICK_SURROUND_CLOSE_SYMBOL = SURROUND_SYMBOLS_MAP.get("`") ?? null; type SerializedComposerMentionNode = Spread< { path: string; type: "composer-mention"; version: 1; }, SerializedLexicalNode >; type SerializedComposerSkillNode = Spread< { skillName: string; skillLabel?: string; skillDescription?: string; type: "composer-skill"; version: 1; }, SerializedLexicalNode >; type SerializedComposerThreadNode = Spread< { environmentId: string; threadId: string; title: string; type: "composer-thread"; version: 1; }, SerializedLexicalNode >; type SerializedComposerTerminalContextNode = Spread< { context: TerminalContextDraft; type: "composer-terminal-context"; version: 1; }, SerializedLexicalNode >; const ComposerTerminalContextActionsContext = createContext<{ onRemoveTerminalContext: (contextId: string) => void; }>({ onRemoveTerminalContext: () => {}, }); function ComposerMentionDecorator(props: { path: string }) { const theme = resolvedThemeFromDocument(); const chip = ( <span className={FILE_TAG_CHIP_CLASS_NAME} contentEditable={false} spellCheck={false} data-composer-mention-chip="true" > <FileTagChipContent path={props.path} label={basenameOfPath(props.path)} theme={theme} /> </span> ); return ( <Tooltip> <TooltipTrigger render={chip} /> <TooltipPopup side="top" className="max-w-120 whitespace-normal leading-tight wrap-anywhere"> {props.path} </TooltipPopup> </Tooltip> ); } class ComposerMentionNode extends DecoratorNode<React.ReactElement> { __path: string; static override getType(): string { return "composer-mention"; } static override clone(node: ComposerMentionNode): ComposerMentionNode { return new ComposerMentionNode(node.__path, node.__key); } static override importJSON(serializedNode: SerializedComposerMentionNode): ComposerMentionNode { return $createComposerMentionNode(serializedNode.path).updateFromJSON(serializedNode); } constructor(path: string, key?: NodeKey) { super(key); this.__path = path; } override exportJSON(): SerializedComposerMentionNode { return { ...super.exportJSON(), path: this.__path, type: "composer-mention", version: 1, }; } override createDOM(): HTMLElement { const dom = document.createElement("span"); dom.className = "composer-inline-chip relative inline-flex align-middle leading-none"; return dom; } override updateDOM(): false { return false; } override getTextContent(): string { return serializeComposerFileLink(this.__path); } override isInline(): true { return true; } override decorate(): React.ReactElement { return <ComposerMentionDecorator path={this.__path} />; } } function $createComposerMentionNode(path: string): ComposerMentionNode { return $applyNodeReplacement(new ComposerMentionNode(path)); } function resolveSkillDescription( skill: Pick<ServerProviderSkill, "shortDescription" | "description">, ): string | null { const shortDescription = skill.shortDescription?.trim(); if (shortDescription) { return shortDescription; } const description = skill.description?.trim(); return description || null; } type ComposerSkillMetadata = { label: string; description: string | null; }; function skillMetadataByName( skills: ReadonlyArray<ServerProviderSkill>, ): ReadonlyMap<string, ComposerSkillMetadata> { return new Map( skills.map((skill) => [ skill.name, { label: formatProviderSkillDisplayName(skill), description: resolveSkillDescription(skill), }, ]), ); } function ComposerSkillDecorator(props: { skillLabel: string; skillDescription: string | null }) { const chip = ( <span className={COMPOSER_INLINE_SKILL_CHIP_CLASS_NAME} contentEditable={false} spellCheck={false} data-composer-skill-chip="true" > <span aria-hidden="true" className={COMPOSER_INLINE_CHIP_ICON_CLASS_NAME} dangerouslySetInnerHTML={{ __html: SKILL_CHIP_ICON_SVG }} /> <span className={COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME}>{props.skillLabel}</span> </span> ); if (!props.skillDescription) { return chip; } return ( <Tooltip> <TooltipTrigger render={chip} /> <TooltipPopup side="top" className="max-w-120 whitespace-normal leading-tight"> {props.skillDescription} </TooltipPopup> </Tooltip> ); } class ComposerSkillNode extends DecoratorNode<React.ReactElement> { __skillName: string; __skillLabel: string; __skillDescription: string | null; static override getType(): string { return "composer-skill"; } static override clone(node: ComposerSkillNode): ComposerSkillNode { return new ComposerSkillNode( node.__skillName, node.__skillLabel, node.__skillDescription, node.__key, ); } static override importJSON(serializedNode: SerializedComposerSkillNode): ComposerSkillNode { return $createComposerSkillNode( serializedNode.skillName, serializedNode.skillLabel ?? serializedNode.skillName, serializedNode.skillDescription ?? null, ).updateFromJSON(serializedNode); } constructor( skillName: string, skillLabel: string, skillDescription: string | null, key?: NodeKey, ) { super(key); const normalizedSkillName = skillName.startsWith("$") ? skillName.slice(1) : skillName; this.__skillName = normalizedSkillName; this.__skillLabel = skillLabel; this.__skillDescription = skillDescription; } override exportJSON(): SerializedComposerSkillNode { return { ...super.exportJSON(), skillName: this.__skillName, skillLabel: this.__skillLabel, ...(this.__skillDescription ? { skillDescription: this.__skillDescription } : {}), type: "composer-skill", version: 1, }; } override createDOM(): HTMLElement { const dom = document.createElement("span"); dom.className = "composer-inline-chip relative inline-flex align-middle leading-none"; return dom; } override updateDOM(): false { return false; } override getTextContent(): string { return `$${this.__skillName}`; } override isInline(): true { return true; } override decorate(): React.ReactElement { return ( <ComposerSkillDecorator skillLabel={this.__skillLabel} skillDescription={this.__skillDescription} /> ); } } function $createComposerSkillNode( skillName: string, skillLabel: string, skillDescription: string | null, ): ComposerSkillNode { return $applyNodeReplacement(new ComposerSkillNode(skillName, skillLabel, skillDescription)); } function ComposerThreadDecorator(props: { title: string; threadId: string }) { const chip = ( <span className={COMPOSER_INLINE_SKILL_CHIP_CLASS_NAME} contentEditable={false} spellCheck={false} data-composer-thread-chip="true" > <MessagesSquareIcon className={COMPOSER_INLINE_CHIP_ICON_CLASS_NAME} aria-hidden="true" /> <span className={COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME}>{props.title}</span> </span> ); return ( <Tooltip> <TooltipTrigger render={chip} /> <TooltipPopup side="top" className="max-w-120 whitespace-normal leading-tight"> Thread reference · {props.threadId} </TooltipPopup> </Tooltip> ); } class ComposerThreadNode extends DecoratorNode<React.ReactElement> { __environmentId: string; __threadId: string; __title: string; static override getType(): string { return "composer-thread"; } static override clone(node: ComposerThreadNode): ComposerThreadNode { return new ComposerThreadNode(node.__environmentId, node.__threadId, node.__title, node.__key); } static override importJSON(serializedNode: SerializedComposerThreadNode): ComposerThreadNode { return $createComposerThreadNode( serializedNode.environmentId, serializedNode.threadId, serializedNode.title, ).updateFromJSON(serializedNode); } constructor(environmentId: string, threadId: string, title: string, key?: NodeKey) { super(key); this.__environmentId = environmentId; this.__threadId = threadId; this.__title = title; } override exportJSON(): SerializedComposerThreadNode { return { ...super.exportJSON(), environmentId: this.__environmentId, threadId: this.__threadId, title: this.__title, type: "composer-thread", version: 1, }; } override createDOM(): HTMLElement { const dom = document.createElement("span"); dom.className = "inline-flex align-middle leading-none"; return dom; } override updateDOM(): false { return false; } override getTextContent(): string { return serializeComposerThreadLink({ environmentId: this.__environmentId, threadId: this.__threadId, title: this.__title, }); } override isInline(): true { return true; } override decorate(): React.ReactElement { return <ComposerThreadDecorator title={this.__title} threadId={this.__threadId} />; } } function $createComposerThreadNode( environmentId: string, threadId: string, title: string, ): ComposerThreadNode { return $applyNodeReplacement(new ComposerThreadNode(environmentId, threadId, title)); } function ComposerTerminalContextDecorator(props: { context: TerminalContextDraft }) { return <ComposerPendingTerminalContextChip context={props.context} />; } class ComposerTerminalContextNode extends DecoratorNode<React.ReactElement> { __context: TerminalContextDraft; static override getType(): string { return "composer-terminal-context"; } static override clone(node: ComposerTerminalContextNode): ComposerTerminalContextNode { return new ComposerTerminalContextNode(node.__context, node.__key); } static override importJSON( serializedNode: SerializedComposerTerminalContextNode, ): ComposerTerminalContextNode { return $createComposerTerminalContextNode(serializedNode.context); } constructor(context: TerminalContextDraft, key?: NodeKey) { super(key); this.__context = context; }