@blocknote/xl-ai 0.51.2 → 0.51.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"blocknote-xl-ai.cjs","names":[],"sources":["../src/plugins/AgentCursorPlugin.ts","../src/AIExtension.ts","../src/hooks/useAIDictionary.ts","../src/components/AIMenu/PromptSuggestionMenu.tsx","../src/components/AIMenu/getDefaultAIMenuItems.tsx","../src/components/AIMenu/AIMenu.tsx","../src/components/AIMenu/AIMenuController.tsx","../src/components/FormattingToolbar/AIToolbarButton.tsx","../src/components/SuggestionMenu/getAISlashMenuItems.tsx"],"sourcesContent":["import { Plugin, PluginKey } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport { defaultSelectionBuilder } from \"y-prosemirror\";\n\ntype AgentCursorState = {\n selection: { anchor: number; head: number } | undefined;\n};\nconst PLUGIN_KEY = new PluginKey<AgentCursorState>(`blocknote-agent-cursor`);\n\n/**\n * Plugin that renders an \"AI\" cursor\n */\nexport function createAgentCursorPlugin(agentCursor: {\n name: string;\n color: string;\n}) {\n return new Plugin<AgentCursorState>({\n key: PLUGIN_KEY,\n view: (_view) => {\n return {};\n },\n state: {\n init: () => {\n return {\n selection: undefined,\n };\n },\n apply: (tr, _oldState) => {\n const meta = tr.getMeta(\"aiAgent\");\n\n if (!meta) {\n return {\n selection: undefined,\n };\n }\n\n return {\n selection: meta.selection,\n };\n },\n },\n props: {\n decorations: (state) => {\n const { doc } = state;\n\n const { selection } = PLUGIN_KEY.getState(state)!;\n\n const decs = [];\n\n if (!selection) {\n return DecorationSet.create(doc, []);\n }\n\n decs.push(\n Decoration.widget(selection.head, () => renderCursor(agentCursor), {\n key: \"agent-cursor\",\n side: 10,\n }),\n );\n\n const from = Math.min(selection.anchor, selection.head);\n const to = Math.max(selection.anchor, selection.head);\n\n decs.push(\n Decoration.inline(from, to, defaultSelectionBuilder(agentCursor), {\n inclusiveEnd: true,\n inclusiveStart: false,\n }),\n );\n\n return DecorationSet.create(doc, decs);\n },\n },\n });\n}\n\nconst renderCursor = (user: { name: string; color: string }) => {\n const cursorElement = document.createElement(\"span\");\n\n cursorElement.classList.add(\"bn-collaboration-cursor__base\");\n cursorElement.setAttribute(\"data-active\", \"true\");\n\n const caretElement = document.createElement(\"span\");\n caretElement.setAttribute(\"contentedEditable\", \"false\");\n caretElement.classList.add(\"bn-collaboration-cursor__caret\");\n caretElement.setAttribute(\"style\", `background-color: ${user.color}`);\n\n const labelElement = document.createElement(\"span\");\n\n labelElement.classList.add(\"bn-collaboration-cursor__label\");\n labelElement.setAttribute(\"style\", `background-color: ${user.color}`);\n labelElement.insertBefore(document.createTextNode(user.name), null);\n\n caretElement.insertBefore(labelElement, null);\n\n cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n cursorElement.insertBefore(caretElement, null);\n cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n\n return cursorElement;\n};\n","import { Chat } from \"@ai-sdk/react\";\nimport {\n createExtension,\n createStore,\n ExtensionOptions,\n getNodeById,\n UnreachableCaseError,\n} from \"@blocknote/core\";\nimport {\n ForkYDocExtension,\n ShowSelectionExtension,\n} from \"@blocknote/core/extensions\";\nimport {\n applySuggestions,\n revertSuggestions,\n suggestChanges,\n} from \"@handlewithcare/prosemirror-suggest-changes\";\nimport { UIMessage } from \"ai\";\nimport { Fragment, Slice } from \"prosemirror-model\";\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport { fixTablesKey } from \"prosemirror-tables\";\nimport { buildAIRequest, sendMessageWithAIRequest } from \"./api/index.js\";\nimport { createAgentCursorPlugin } from \"./plugins/AgentCursorPlugin.js\";\nimport { AIRequestHelpers, InvokeAIOptions } from \"./types.js\";\n\ntype AIPluginState = {\n aiMenuState:\n | ({\n /**\n * The ID of the block that the AI menu is opened at.\n * This changes as the AI is making changes to the document\n */\n blockId: string;\n } & (\n | {\n status: \"error\";\n error: any;\n }\n | {\n // fix: it might be nice to derive this from the Chat status and Tool call status\n status: \"user-input\" | \"thinking\" | \"ai-writing\" | \"user-reviewing\";\n }\n ))\n | \"closed\";\n};\n\nconst PLUGIN_KEY = new PluginKey(`blocknote-ai-plugin`);\n\nexport const AIExtension = createExtension(\n ({\n editor,\n options: editorOptions,\n }: ExtensionOptions<\n | (AIRequestHelpers & {\n /**\n * The name and color of the agent cursor\n *\n * @default { name: \"AI\", color: \"#8bc6ff\" }\n */\n agentCursor?: { name: string; color: string };\n })\n | undefined\n >) => {\n // TODO should we really expose it like this?\n const options = createStore<AIRequestHelpers>(editorOptions ?? {});\n const store = createStore<AIPluginState>({\n aiMenuState: \"closed\",\n });\n let chatSession:\n | {\n previousRequestOptions: InvokeAIOptions;\n chat: Chat<UIMessage>;\n abortController: AbortController;\n }\n | undefined;\n let autoScroll = false;\n\n const suggestChangesPlugin = suggestChanges();\n // disable decorations for suggest changes, not needed\n // (and the pilcrows are ugly)\n suggestChangesPlugin.props.decorations = undefined;\n return {\n key: \"ai\",\n options,\n store,\n mount({ signal }: { signal: AbortSignal }) {\n let scrollInProgress = false;\n // Listens for `scroll` and `scrollend` events to see if a new scroll was\n // started before an existing one ended. This is the most reliable way we\n // have of checking if a scroll event was caused by the user and not by\n // `scrollIntoView`, as the events are otherwise indistinguishable. If a\n // scroll was started before an existing one finished (meaning the user has\n // scrolled), auto scrolling is disabled.\n document.addEventListener(\n \"scroll\",\n () => {\n if (scrollInProgress) {\n autoScroll = false;\n }\n\n scrollInProgress = true;\n },\n {\n capture: true,\n signal,\n },\n );\n document.addEventListener(\n \"scrollend\",\n () => {\n scrollInProgress = false;\n },\n {\n capture: true,\n signal,\n },\n );\n },\n prosemirrorPlugins: [\n new Plugin({\n key: PLUGIN_KEY,\n filterTransaction: (tr) => {\n const menuState = store.state.aiMenuState;\n\n if (menuState !== \"closed\" && menuState.status === \"ai-writing\") {\n if (tr.getMeta(fixTablesKey)?.fixTables) {\n // the fixtables plugin causes the steps between of the AI Agent to become invalid\n // so we need to prevent it from running\n // (we might need to filter out other / or maybe any transactions during the writing phase)\n return false;\n }\n }\n return true;\n },\n }),\n suggestChangesPlugin,\n createAgentCursorPlugin(\n editorOptions?.agentCursor || { name: \"AI\", color: \"#8bc6ff\" },\n ),\n ],\n\n /**\n * Open the AI menu at a specific block\n */\n openAIMenuAtBlock(blockID: string) {\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(true, \"aiMenu\");\n editor.isEditable = false;\n store.setState({\n aiMenuState: {\n blockId: blockID,\n status: \"user-input\",\n },\n });\n\n // Scrolls to the block when the menu opens.\n const blockElement = editor.domElement?.querySelector(\n `[data-node-type=\"blockContainer\"][data-id=\"${blockID}\"]`,\n );\n blockElement?.scrollIntoView({ block: \"center\" });\n },\n\n /**\n * Close the AI menu\n */\n closeAIMenu() {\n store.setState({\n aiMenuState: \"closed\",\n });\n chatSession = undefined;\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(false, \"aiMenu\");\n editor.isEditable = true;\n editor.focus();\n },\n\n /**\n * Accept the changes made by the LLM\n */\n acceptChanges() {\n // This is slightly convoluted, to try to maintain the undo history as much as possible\n // The idea is that the LLM call has appended a number of updates to the document, moving the document from state `A` to state `C`\n // But we want the undo history to skip all of the intermediate states and go straight from `C` to `A`\n // To do this, we capture the document state `C` (post-LLM call), and then reject the suggestions to recover the original document state `A`\n // Then we create an intermediate state `B` that captures the diff between `A` and `C`\n // Then we apply the suggestions to `B` to get the final state `C`\n // This causes the undo history to skip `B` and go straight from `C` back to `A`\n\n // Capture the document state `C'` (post-LLM call with all suggestions still in the document)\n const markedUpDocument = editor.prosemirrorState.doc;\n\n // revert the suggestions to get back to the original document state `A`\n editor.exec((state, dispatch) => {\n return revertSuggestions(state, (tr) => {\n dispatch?.(tr.setMeta(\"addToHistory\", false));\n });\n });\n\n // Create an intermediate state `B` that captures the diff between the original document and the marked up document\n editor.exec((state, dispatch) => {\n const tr = state.tr;\n tr.replace(\n 0,\n tr.doc.content.size,\n new Slice(Fragment.from(markedUpDocument), 0, 0),\n );\n const nextState = state.apply(tr);\n // Apply the suggestions to the intermediate state `B` to get the final state `C`\n return applySuggestions(nextState, (resultTr) => {\n dispatch?.(\n tr.replace(\n 0,\n tr.doc.content.size,\n new Slice(Fragment.from(resultTr.doc), 0, 0),\n ),\n );\n });\n });\n\n // If in collaboration mode, merge the changes back into the original yDoc\n editor.getExtension(ForkYDocExtension)?.merge({ keepChanges: true });\n\n this.closeAIMenu();\n },\n\n /**\n * Reject the changes made by the LLM\n */\n rejectChanges() {\n // Revert the suggestions to get back to the original document\n editor.exec((state, dispatch) => {\n return revertSuggestions(state, (tr) => {\n // Do so without adding to history (so the last undo step is just prior to the LLM call)\n dispatch?.(tr.setMeta(\"addToHistory\", false));\n });\n });\n\n // If in collaboration mode, discard the changes and revert to the original yDoc\n editor.getExtension(ForkYDocExtension)?.merge({ keepChanges: false });\n this.closeAIMenu();\n },\n\n /**\n * Abort the current LLM request.\n *\n * This will stop the ongoing request and revert any changes made by the AI.\n * Only valid when there is an active AI request in progress.\n */\n async abort(reason?: any) {\n const { aiMenuState } = store.state;\n if (aiMenuState === \"closed\" || !chatSession) {\n return;\n }\n\n // Only abort if the request is in progress (thinking or ai-writing)\n if (\n aiMenuState.status !== \"thinking\" &&\n aiMenuState.status !== \"ai-writing\"\n ) {\n return;\n }\n\n const chat = chatSession.chat;\n const abortController = chatSession.abortController;\n\n // Abort the tool call operations\n abortController.abort(reason);\n\n // Stop the chat request\n await chat.stop();\n },\n\n /**\n * Retry the previous LLM call.\n *\n * Only valid if the current status is \"error\"\n */\n async retry() {\n const { aiMenuState } = store.state;\n if (\n aiMenuState === \"closed\" ||\n aiMenuState.status !== \"error\" ||\n !chatSession\n ) {\n throw new Error(\n \"retry() is only valid when a previous response failed\",\n );\n }\n\n /*\n Design decisions:\n - we cannot use chat.regenerate() because the document might have been updated already by toolcalls that failed mid-way\n - we also cannot revert the document changes and use chat.regenerate(), \n because we might want to retry a subsequent user-direction that failed, not the original one\n - this means we always need to send the new document state to the LLM\n - an alternative would be to take a snapshot of the document before the LLM call, and revert to that specific state\n when a call fails\n */\n\n if (chatSession?.chat.status === \"error\") {\n // the LLM call failed (i.e. a network error)\n // console.log(\"retry failed LLM call\", this.chatSession.chat.error);\n return this.invokeAI({\n ...chatSession.previousRequestOptions,\n userPrompt: `An error occured in the previous request. Please retry to accomplish the last user prompt.`,\n });\n } else {\n // an error occurred while parsing / executing the previous LLM call\n // give the LLM a chance to fix the error\n // console.log(\"retry failed tool execution\");\n return this.invokeAI({\n ...chatSession.previousRequestOptions,\n userPrompt: `An error occured while executing the previous tool call. Please retry to accomplish the last user prompt.`,\n });\n }\n },\n\n /**\n * Update the status of a call to an LLM\n *\n * @warning This method should usually only be used for advanced use-cases\n * if you want to implement how an LLM call is executed. Usually, you should\n * use {@link executeLLMRequest} instead which will handle the status updates for you.\n */\n setAIResponseStatus(\n status:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"user-reviewing\"\n | {\n status: \"error\";\n error: any;\n },\n ) {\n const aiMenuState = store.state.aiMenuState;\n if (aiMenuState === \"closed\") {\n return;\n }\n\n if (status === \"ai-writing\") {\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(false, \"aiMenu\");\n }\n\n if (typeof status === \"object\") {\n if (status.status !== \"error\") {\n throw new UnreachableCaseError(status.status);\n }\n this.store.setState({\n aiMenuState: {\n status: status.status,\n error: status.error,\n blockId: aiMenuState.blockId,\n },\n });\n } else {\n this.store.setState({\n aiMenuState: {\n status: status,\n blockId: aiMenuState.blockId,\n },\n });\n }\n },\n\n /**\n * @deprecated Use {@link invokeAI} instead\n */\n async callLLM(opts: InvokeAIOptions) {\n return this.invokeAI(opts);\n },\n\n /**\n * Execute a call to an LLM and apply the result to the editor\n */\n async invokeAI(opts: InvokeAIOptions) {\n this.setAIResponseStatus(\"thinking\");\n editor.getExtension(ForkYDocExtension)?.fork();\n\n try {\n // Create a new AbortController for this request\n const abortController = new AbortController();\n\n if (!chatSession) {\n // note: in the current implementation opts.transport is only used when creating a new chat\n // (so changing transport for a subsequent call in the same chat-session is not supported)\n chatSession = {\n previousRequestOptions: opts,\n chat:\n opts.chatProvider?.() ||\n this.options.state.chatProvider?.() ||\n new Chat<UIMessage>({\n sendAutomaticallyWhen: () => false,\n transport: opts.transport || this.options.state.transport,\n }),\n abortController,\n };\n } else {\n chatSession.previousRequestOptions = opts;\n chatSession.abortController = abortController;\n }\n const chat = chatSession.chat;\n\n // merge the global options with the local options\n const globalOpts = options.state;\n opts = {\n ...globalOpts,\n ...opts,\n } as InvokeAIOptions;\n\n const aiRequest = await buildAIRequest({\n editor,\n useSelection: opts.useSelection,\n deleteEmptyCursorBlock: opts.deleteEmptyCursorBlock,\n streamToolsProvider:\n opts.streamToolsProvider ??\n this.options.state.streamToolsProvider,\n documentStateBuilder:\n opts.documentStateBuilder ??\n this.options.state.documentStateBuilder,\n onBlockUpdated: (blockId) => {\n const aiMenuState = store.state.aiMenuState;\n const aiMenuOpenState =\n aiMenuState === \"closed\" ? undefined : aiMenuState;\n if (!aiMenuOpenState || aiMenuOpenState.status !== \"ai-writing\") {\n return;\n }\n\n // TODO: Sometimes, the updated block doesn't actually exist in\n // the editor. I don't know why this happens, seems like a bug?\n const nodeInfo = getNodeById(\n blockId,\n editor.prosemirrorState.doc,\n );\n if (!nodeInfo) {\n return;\n }\n\n // NOTE: does this setState with an anon object trigger unnecessary re-renders?\n store.setState({\n aiMenuState: {\n blockId,\n status: \"ai-writing\",\n },\n });\n\n if (autoScroll) {\n const blockElement = editor.prosemirrorView.domAtPos(\n nodeInfo.posBeforeNode + 1,\n );\n (blockElement.node as HTMLElement).scrollIntoView({\n block: \"center\",\n });\n }\n },\n onStart: () => {\n autoScroll = true;\n this.setAIResponseStatus(\"ai-writing\");\n\n if (\n aiRequest.emptyCursorBlockToDelete &&\n aiRequest.editor.getBlock(aiRequest.emptyCursorBlockToDelete)\n ) {\n aiRequest.editor.removeBlocks([\n aiRequest.emptyCursorBlockToDelete,\n ]);\n }\n },\n });\n\n const result = await sendMessageWithAIRequest(\n chat,\n aiRequest,\n {\n role: \"user\",\n parts: [\n {\n type: \"text\",\n text: opts.userPrompt,\n },\n ],\n },\n opts.chatRequestOptions || this.options.state.chatRequestOptions,\n chatSession.abortController.signal,\n );\n\n if (\n (result.ok && chat.status !== \"error\") ||\n abortController.signal.aborted\n ) {\n this.setAIResponseStatus(\"user-reviewing\");\n } else {\n // eslint-disable-next-line no-console\n console.warn(\"Error calling LLM\", {\n result,\n chatStatus: chat.status,\n chatError: chat.error,\n });\n this.setAIResponseStatus({\n status: \"error\",\n error: result.ok ? chat.error : result.error,\n });\n }\n } catch (e) {\n this.setAIResponseStatus({\n status: \"error\",\n error: e,\n });\n // eslint-disable-next-line no-console\n console.error(\n \"Unexpected error calling LLM\",\n e,\n chatSession?.chat.messages,\n );\n }\n },\n };\n },\n);\n","import { useBlockNoteContext } from \"@blocknote/react\";\n\nimport { getAIDictionary } from \"../i18n/dictionary.js\";\n\nexport function useAIDictionary() {\n const ctx = useBlockNoteContext();\n return getAIDictionary(ctx!.editor!);\n}\n","import { mergeCSSClasses } from \"@blocknote/core\";\nimport { filterSuggestionItems } from \"@blocknote/core/extensions\";\nimport {\n DefaultReactSuggestionItem,\n useComponentsContext,\n useSuggestionMenuKeyboardHandler,\n} from \"@blocknote/react\";\nimport {\n ChangeEvent,\n KeyboardEvent,\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport type PromptSuggestionMenuProps = {\n items: DefaultReactSuggestionItem[];\n onManualPromptSubmit: (userPrompt: string) => void;\n promptText?: string;\n onPromptTextChange?: (userPrompt: string) => void;\n icon?: ReactNode;\n rightSection?: ReactNode;\n placeholder?: string;\n disabled?: boolean;\n};\n\nexport const PromptSuggestionMenu = (props: PromptSuggestionMenuProps) => {\n // const dict = useAIDictionary();\n const Components = useComponentsContext()!;\n\n const { onManualPromptSubmit, promptText, onPromptTextChange, disabled } =\n props;\n\n // Only used internal state when `props.prompText` is undefined (i.e., uncontrolled mode)\n const [internalPromptText, setInternalPromptText] = useState<string>(\"\");\n const promptTextToUse = promptText || internalPromptText;\n\n const handleEnter = useCallback(\n async (event: KeyboardEvent) => {\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing) {\n // console.log(\"ENTER\", currentEditingPrompt);\n onManualPromptSubmit(promptTextToUse);\n }\n },\n [promptTextToUse, onManualPromptSubmit],\n );\n\n const handleChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const newValue = event.currentTarget.value;\n if (onPromptTextChange) {\n onPromptTextChange(newValue);\n }\n\n // Only update internal state if it's uncontrolled\n if (promptText === undefined) {\n setInternalPromptText(newValue);\n }\n },\n [onPromptTextChange, setInternalPromptText, promptText],\n );\n\n const items: DefaultReactSuggestionItem[] = useMemo(() => {\n return filterSuggestionItems(props.items, promptTextToUse);\n }, [promptTextToUse, props.items]);\n\n const { selectedIndex, setSelectedIndex, handler } =\n useSuggestionMenuKeyboardHandler(items, (item) => item.onItemClick());\n\n const activeDescendantId =\n items.length > 0 && selectedIndex >= 0 && selectedIndex < items.length\n ? `bn-suggestion-menu-item-${selectedIndex}`\n : undefined;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n // TODO: handle backspace to close\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing) {\n if (items.length > 0) {\n handler(event);\n } else {\n // TODO: check focus?\n handleEnter(event);\n }\n } else {\n handler(event);\n }\n },\n [handleEnter, handler, items.length],\n );\n\n // Resets index when items change\n useEffect(() => {\n setSelectedIndex(0);\n }, [promptTextToUse, setSelectedIndex]);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const hasBeenDisabled = useRef(disabled);\n\n useEffect(() => {\n // This effect is used so that after the input has been disabled (for example, when AI results are loaded),\n // the input is focused again.\n if (inputRef.current && hasBeenDisabled.current && !disabled) {\n inputRef.current.focus();\n }\n\n if (disabled) {\n hasBeenDisabled.current = true;\n }\n }, [disabled]);\n\n return (\n <div className={\"bn-combobox\"}>\n <Components.Generic.Form.Root>\n <Components.Generic.Form.TextInput\n ref={inputRef}\n className={\"bn-combobox-input\"}\n name={\"ai-prompt\"}\n variant={\"large\"}\n icon={props.icon}\n value={promptTextToUse || \"\"}\n placeholder={props.placeholder}\n disabled={props.disabled}\n onKeyDown={handleKeyDown}\n onChange={handleChange}\n autoComplete={\"off\"}\n rightSection={props.rightSection}\n aria-activedescendant={activeDescendantId}\n />\n </Components.Generic.Form.Root>\n {items.length > 0 && (\n <Components.SuggestionMenu.Root\n className={\"bn-combobox-items\"}\n id={\"ai-suggestion-menu\"}\n >\n {items.map((item, i) => (\n <Components.SuggestionMenu.Item\n key={item.title}\n className={mergeCSSClasses(\n \"bn-suggestion-menu-item\",\n item.size === \"small\" ? \"bn-suggestion-menu-item-small\" : \"\",\n )}\n id={`bn-suggestion-menu-item-${i}`}\n isSelected={i === selectedIndex}\n onClick={item.onItemClick}\n item={item}\n />\n ))}\n </Components.SuggestionMenu.Root>\n )}\n </div>\n );\n};\n","import {\n BlockNoteEditor,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"@blocknote/core\";\nimport { DefaultReactSuggestionItem } from \"@blocknote/react\";\nimport {\n RiArrowGoBackFill,\n RiBallPenLine,\n RiCheckFill,\n RiCheckLine,\n RiEarthLine,\n RiListCheck3,\n RiLoopLeftFill,\n RiMagicLine,\n RiText,\n RiTextWrap,\n} from \"react-icons/ri\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { aiDocumentFormats } from \"../../api/index.js\";\nimport { getAIDictionary } from \"../../i18n/dictionary.js\";\n\nexport type AIMenuSuggestionItem = Omit<\n DefaultReactSuggestionItem,\n \"onItemClick\"\n> & {\n onItemClick: (setPrompt: (userPrompt: string) => void) => void;\n key: string;\n};\n\n/**\n * Default items we show in the AI Menu when there is no selection active.\n * For example, when the AI menu is triggered via the slash menu\n */\nfunction getDefaultAIMenuItemsWithoutSelection<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n return [\n {\n key: \"continue_writing\",\n title: dict.ai_default_commands.continue_writing.title,\n aliases: dict.ai_default_commands.continue_writing.aliases,\n icon: <RiBallPenLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt:\n \"Continue writing at the current cursor position related to the previous text. Add multiple blocks if needed. If the document looks like a template / draft, follow the template. Be extensive if needed.\",\n // By default, LLM will be able to add / update / delete blocks. For \"continue writing\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n\n {\n key: \"summarize\",\n title: dict.ai_default_commands.summarize.title,\n aliases: dict.ai_default_commands.summarize.aliases,\n icon: <RiTextWrap size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt: \"Summarize\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"action_items\",\n title: dict.ai_default_commands.add_action_items.title,\n aliases: dict.ai_default_commands.add_action_items.aliases,\n icon: <RiListCheck3 size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt: \"Add action items\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"write_anything\",\n title: dict.ai_default_commands.write_anything.title,\n aliases: dict.ai_default_commands.write_anything.aliases,\n icon: <RiBallPenLine size={18} />,\n onItemClick: (setPrompt) => {\n setPrompt(dict.ai_default_commands.write_anything.prompt_placeholder);\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when there is a selection active.\n * For example, when the AI menu is triggered via formatting toolbar (AIToolbarButton)\n */\nfunction getDefaultAIMenuItemsWithSelection<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"improve_writing\",\n title: dict.ai_default_commands.improve_writing.title,\n aliases: dict.ai_default_commands.improve_writing.aliases,\n icon: <RiText size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Improve writing\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"fix_spelling\",\n title: dict.ai_default_commands.fix_spelling.title,\n aliases: dict.ai_default_commands.fix_spelling.aliases,\n icon: <RiCheckLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Fix spelling\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"translate\",\n title: dict.ai_default_commands.translate.title,\n aliases: dict.ai_default_commands.translate.aliases,\n icon: <RiEarthLine size={18} />,\n onItemClick: (setPrompt) => {\n setPrompt(dict.ai_default_commands.translate.prompt_placeholder);\n },\n size: \"small\",\n },\n {\n key: \"simplify\",\n title: dict.ai_default_commands.simplify.title,\n aliases: dict.ai_default_commands.simplify.aliases,\n icon: <RiMagicLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Simplify\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when the AI response is done.\n */\nfunction getDefaultAIMenuItemsForReview<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"accept\",\n title: dict.ai_menu.actions.accept.title,\n aliases: dict.ai_menu.actions.accept.aliases,\n icon: <RiCheckFill size={18} />,\n onItemClick: () => {\n ai.acceptChanges();\n },\n size: \"small\",\n },\n {\n key: \"revert\",\n title: dict.ai_menu.actions.revert.title,\n aliases: dict.ai_menu.actions.revert.aliases,\n icon: <RiArrowGoBackFill size={18} />,\n onItemClick: () => {\n ai.rejectChanges();\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when the AI response is done.\n */\nfunction getDefaultAIMenuItemsForError<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"retry\",\n title: dict.ai_menu.actions.retry.title,\n aliases: dict.ai_menu.actions.retry.aliases,\n icon: <RiLoopLeftFill size={18} />,\n onItemClick: async () => {\n await ai.retry();\n },\n size: \"small\",\n },\n {\n key: \"cancel\",\n title: dict.ai_menu.actions.cancel.title,\n aliases: dict.ai_menu.actions.cancel.aliases,\n icon: <RiArrowGoBackFill size={18} />,\n onItemClick: () => {\n ai.rejectChanges();\n },\n size: \"small\",\n },\n ];\n}\n\nexport function getDefaultAIMenuItems(\n editor: BlockNoteEditor<any, any, any>,\n aiResponseStatus:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"error\"\n | \"user-reviewing\"\n | \"closed\",\n): AIMenuSuggestionItem[] {\n if (aiResponseStatus === \"user-input\") {\n return editor.getSelection()\n ? getDefaultAIMenuItemsWithSelection(editor)\n : getDefaultAIMenuItemsWithoutSelection(editor);\n } else if (aiResponseStatus === \"user-reviewing\") {\n return getDefaultAIMenuItemsForReview(editor);\n } else if (aiResponseStatus === \"error\") {\n return getDefaultAIMenuItemsForError(editor);\n } else {\n return [];\n }\n}\n","import { BlockNoteEditor } from \"@blocknote/core\";\nimport {\n useBlockNoteEditor,\n useComponentsContext,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { useAIDictionary } from \"../../hooks/useAIDictionary.js\";\nimport { PromptSuggestionMenu } from \"./PromptSuggestionMenu.js\";\nimport {\n AIMenuSuggestionItem,\n getDefaultAIMenuItems,\n} from \"./getDefaultAIMenuItems.js\";\n\nexport type AIMenuProps = {\n items?: (\n editor: BlockNoteEditor<any, any, any>,\n aiResponseStatus:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"error\"\n | \"user-reviewing\"\n | \"closed\",\n ) => AIMenuSuggestionItem[];\n onManualPromptSubmit?: (userPrompt: string) => void;\n};\n\nexport const AIMenu = (props: AIMenuProps) => {\n const editor = useBlockNoteEditor();\n const [prompt, setPrompt] = useState(\"\");\n const dict = useAIDictionary();\n\n const Components = useComponentsContext()!;\n\n const ai = useExtension(AIExtension);\n\n const aiResponseStatus = useExtensionState(AIExtension, {\n selector: (state) =>\n state.aiMenuState !== \"closed\" ? state.aiMenuState.status : \"closed\",\n });\n\n const { items: externalItems } = props;\n // note, technically there might be a bug with this useMemo when quickly changing the selection and opening the menu\n // would not call getDefaultAIMenuItems with the correct selection, because the component is reused and the memo not retriggered\n // practically this should not happen (you can test it by using a high transition duration in useUIElementPositioning)\n const items = useMemo(() => {\n let items: AIMenuSuggestionItem[] = [];\n if (externalItems) {\n items = externalItems(editor, aiResponseStatus);\n } else {\n items = getDefaultAIMenuItems(editor, aiResponseStatus);\n }\n\n // map from AI items to React Items required by PromptSuggestionMenu\n return items.map((item) => {\n return {\n ...item,\n onItemClick: () => {\n item.onItemClick(setPrompt);\n },\n };\n });\n }, [externalItems, aiResponseStatus, editor]);\n\n const onManualPromptSubmitDefault = useCallback(\n async (userPrompt: string) => {\n await ai.invokeAI({\n userPrompt,\n useSelection: editor.getSelection() !== undefined,\n });\n },\n [ai, editor],\n );\n\n useEffect(() => {\n // this is a bit hacky to run a useeffect to reset the prompt when the AI response is done\n if (\n aiResponseStatus === \"ai-writing\" ||\n aiResponseStatus === \"user-reviewing\" ||\n aiResponseStatus === \"error\"\n ) {\n setPrompt(\"\");\n }\n }, [aiResponseStatus]);\n\n const placeholder = useMemo(() => {\n if (aiResponseStatus === \"thinking\") {\n return dict.ai_menu.status.thinking;\n } else if (aiResponseStatus === \"ai-writing\") {\n return dict.ai_menu.status.editing;\n } else if (aiResponseStatus === \"error\") {\n return dict.ai_menu.status.error;\n }\n\n return dict.ai_menu.input_placeholder;\n }, [aiResponseStatus, dict]);\n\n const rightSection = useMemo(() => {\n if (aiResponseStatus === \"thinking\" || aiResponseStatus === \"ai-writing\") {\n return (\n <Components.SuggestionMenu.Loader\n className={\"bn-suggestion-menu-loader bn-combobox-right-section\"}\n />\n );\n } else if (aiResponseStatus === \"error\") {\n return (\n <div className={\"bn-combobox-right-section bn-combobox-error\"}>\n {/* Taken from Google Material Icons */}\n {/* https://fonts.google.com/icons?selected=Material+Symbols+Rounded:error:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=error&icon.size=24&icon.color=%23e8eaed&icon.set=Material+Symbols&icon.style=Rounded&icon.platform=web */}\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height=\"1em\"\n viewBox=\"0 -960 960 960\"\n width=\"1em\"\n fill=\"currentColor\"\n >\n <path d=\"M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm0-160q17 0 28.5-11.5T520-480v-160q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640v160q0 17 11.5 28.5T480-440Zm0 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z\" />\n </svg>\n </div>\n );\n }\n\n return undefined;\n }, [Components, aiResponseStatus]);\n\n return (\n <PromptSuggestionMenu\n onManualPromptSubmit={\n props.onManualPromptSubmit || onManualPromptSubmitDefault\n }\n items={items}\n promptText={prompt}\n onPromptTextChange={setPrompt}\n placeholder={placeholder}\n disabled={\n aiResponseStatus === \"thinking\" || aiResponseStatus === \"ai-writing\"\n }\n icon={\n <div className=\"bn-combobox-icon\">\n <RiSparkling2Fill />\n </div>\n }\n rightSection={rightSection}\n />\n );\n};\n","import {\n BlockPopover,\n FloatingUIOptions,\n useBlockNoteEditor,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { autoUpdate, flip, offset, size } from \"@floating-ui/react\";\nimport { FC, useMemo } from \"react\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { AIMenu, AIMenuProps } from \"./AIMenu.js\";\n\nexport const AIMenuController = (props: {\n aiMenu?: FC<AIMenuProps>;\n floatingUIOptions?: FloatingUIOptions;\n}) => {\n const editor = useBlockNoteEditor();\n const ai = useExtension(AIExtension);\n\n const aiMenuState = useExtensionState(AIExtension, {\n editor,\n selector: (state) => state.aiMenuState,\n });\n\n const blockId = aiMenuState === \"closed\" ? undefined : aiMenuState.blockId;\n\n const floatingUIOptions = useMemo<FloatingUIOptions>(\n () =>\n ({\n ...props.floatingUIOptions,\n useFloatingOptions: {\n open: aiMenuState !== \"closed\",\n placement: \"bottom\",\n middleware: [\n offset(10),\n flip(),\n size({\n apply({ rects, elements }) {\n Object.assign(elements.floating.style, {\n width: `${rects.reference.width}px`,\n });\n },\n }),\n ],\n onOpenChange: (open) => {\n if (open || aiMenuState === \"closed\") {\n return;\n }\n\n if (aiMenuState.status === \"user-input\") {\n ai.closeAIMenu();\n } else if (\n aiMenuState.status === \"user-reviewing\" ||\n aiMenuState.status === \"error\"\n ) {\n ai.rejectChanges();\n }\n },\n whileElementsMounted(reference, floating, update) {\n return autoUpdate(reference, floating, update, {\n animationFrame: true,\n });\n },\n ...props.floatingUIOptions?.useFloatingOptions,\n },\n useDismissProps: {\n enabled:\n aiMenuState === \"closed\" || aiMenuState.status === \"user-input\",\n // We should just be able to set `referencePress: true` instead of\n // using this listener, but this doesn't seem to trigger.\n // (probably because we don't assign the referenceProps to the reference element)\n outsidePress: (event) => {\n if (event.target instanceof Element) {\n const blockElement = event.target.closest(\".bn-block\");\n if (\n blockElement &&\n blockElement.getAttribute(\"data-id\") === blockId\n ) {\n ai.closeAIMenu();\n }\n }\n\n return true;\n },\n ...props.floatingUIOptions?.useDismissProps,\n },\n elementProps: {\n style: {\n zIndex: 100,\n },\n ...props.floatingUIOptions?.elementProps,\n },\n // we use the focus manager instead of `autoFocus={true}` to prevent \"page-scrolls-to-top-when-opening-the-floating-element\"\n // see https://floating-ui.com/docs/floatingfocusmanager#page-scrolls-to-top-when-opening-the-floating-element\n focusManagerProps: {\n disabled: false,\n // needed for https://github.com/floating-ui/floating-ui/pull/3202\n // (related: https://github.com/mui/base-ui/issues/3950)\n getInsideElements: () => {\n if (!editor.domElement) {\n return [];\n }\n return [editor.domElement];\n },\n ...props.floatingUIOptions?.focusManagerProps,\n },\n }) satisfies FloatingUIOptions,\n [ai, aiMenuState, blockId, props.floatingUIOptions, editor.domElement],\n );\n\n const Component = props.aiMenu || AIMenu;\n\n return (\n <BlockPopover blockId={blockId} {...floatingUIOptions}>\n {aiMenuState !== \"closed\" && <Component />}\n </BlockPopover>\n );\n};\n","import { BlockSchema, InlineContentSchema, StyleSchema } from \"@blocknote/core\";\nimport { FormattingToolbarExtension } from \"@blocknote/core/extensions\";\nimport {\n useBlockNoteEditor,\n useComponentsContext,\n useExtension,\n} from \"@blocknote/react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { useAIDictionary } from \"../../hooks/useAIDictionary.js\";\n\nexport const AIToolbarButton = () => {\n const dict = useAIDictionary();\n const Components = useComponentsContext()!;\n\n const editor = useBlockNoteEditor<\n BlockSchema,\n InlineContentSchema,\n StyleSchema\n >();\n\n const ai = useExtension(AIExtension);\n const formattingToolbar = useExtension(FormattingToolbarExtension);\n\n const onClick = () => {\n const selection = editor.getSelection();\n if (!selection) {\n throw new Error(\"No selection\");\n }\n\n const position = selection.blocks[selection.blocks.length - 1].id;\n\n ai.openAIMenuAtBlock(position);\n formattingToolbar.store.setState(false);\n };\n\n if (!editor.isEditable) {\n return null;\n }\n\n return (\n <Components.Generic.Toolbar.Button\n className={\"bn-button\"}\n label={dict.formatting_toolbar.ai.tooltip}\n mainTooltip={dict.formatting_toolbar.ai.tooltip}\n icon={<RiSparkling2Fill />}\n onClick={onClick}\n />\n );\n};\n","import {\n BlockNoteEditor,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"@blocknote/core\";\nimport { DefaultReactSuggestionItem } from \"@blocknote/react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { getAIDictionary } from \"../../i18n/dictionary.js\";\nconst Icons = {\n AI: RiSparkling2Fill,\n};\n\n/**\n * Returns AI related items that can be added to the slash menu\n */\nexport function getAISlashMenuItems<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): DefaultReactSuggestionItem[] {\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n const items = [\n {\n key: \"ai\",\n onItemClick: () => {\n const cursor = editor.getTextCursorPosition();\n if (\n cursor.block.content &&\n Array.isArray(cursor.block.content) && // isarray check not ideal\n cursor.block.content.length === 0 &&\n cursor.prevBlock\n ) {\n ai.openAIMenuAtBlock(cursor.prevBlock.id);\n } else {\n ai.openAIMenuAtBlock(cursor.block.id);\n }\n },\n ...getAIDictionary(editor).slash_menu.ai,\n icon: <Icons.AI />,\n },\n ];\n\n return items;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"blocknote-xl-ai.cjs","names":[],"sources":["../src/plugins/AgentCursorPlugin.ts","../src/AIExtension.ts","../src/hooks/useAIDictionary.ts","../src/components/AIMenu/PromptSuggestionMenu.tsx","../src/components/AIMenu/getDefaultAIMenuItems.tsx","../src/components/AIMenu/AIMenu.tsx","../src/components/AIMenu/AIMenuController.tsx","../src/components/FormattingToolbar/AIToolbarButton.tsx","../src/components/SuggestionMenu/getAISlashMenuItems.tsx"],"sourcesContent":["import { Plugin, PluginKey } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport { defaultSelectionBuilder } from \"y-prosemirror\";\n\ntype AgentCursorState = {\n selection: { anchor: number; head: number } | undefined;\n};\nconst PLUGIN_KEY = new PluginKey<AgentCursorState>(`blocknote-agent-cursor`);\n\n/**\n * Plugin that renders an \"AI\" cursor\n */\nexport function createAgentCursorPlugin(agentCursor: {\n name: string;\n color: string;\n}) {\n return new Plugin<AgentCursorState>({\n key: PLUGIN_KEY,\n view: (_view) => {\n return {};\n },\n state: {\n init: () => {\n return {\n selection: undefined,\n };\n },\n apply: (tr, _oldState) => {\n const meta = tr.getMeta(\"aiAgent\");\n\n if (!meta) {\n return {\n selection: undefined,\n };\n }\n\n return {\n selection: meta.selection,\n };\n },\n },\n props: {\n decorations: (state) => {\n const { doc } = state;\n\n const { selection } = PLUGIN_KEY.getState(state)!;\n\n const decs = [];\n\n if (!selection) {\n return DecorationSet.create(doc, []);\n }\n\n decs.push(\n Decoration.widget(selection.head, () => renderCursor(agentCursor), {\n key: \"agent-cursor\",\n side: 10,\n }),\n );\n\n const from = Math.min(selection.anchor, selection.head);\n const to = Math.max(selection.anchor, selection.head);\n\n decs.push(\n Decoration.inline(from, to, defaultSelectionBuilder(agentCursor), {\n inclusiveEnd: true,\n inclusiveStart: false,\n }),\n );\n\n return DecorationSet.create(doc, decs);\n },\n },\n });\n}\n\nconst renderCursor = (user: { name: string; color: string }) => {\n const cursorElement = document.createElement(\"span\");\n\n cursorElement.classList.add(\"bn-collaboration-cursor__base\");\n cursorElement.setAttribute(\"data-active\", \"true\");\n\n const caretElement = document.createElement(\"span\");\n caretElement.setAttribute(\"contentedEditable\", \"false\");\n caretElement.classList.add(\"bn-collaboration-cursor__caret\");\n caretElement.setAttribute(\"style\", `background-color: ${user.color}`);\n\n const labelElement = document.createElement(\"span\");\n\n labelElement.classList.add(\"bn-collaboration-cursor__label\");\n labelElement.setAttribute(\"style\", `background-color: ${user.color}`);\n labelElement.insertBefore(document.createTextNode(user.name), null);\n\n caretElement.insertBefore(labelElement, null);\n\n cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n cursorElement.insertBefore(caretElement, null);\n cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n\n return cursorElement;\n};\n","import { Chat } from \"@ai-sdk/react\";\nimport {\n createExtension,\n createStore,\n ExtensionOptions,\n getNodeById,\n UnreachableCaseError,\n} from \"@blocknote/core\";\nimport {\n ForkYDocExtension,\n ShowSelectionExtension,\n} from \"@blocknote/core/extensions\";\nimport {\n applySuggestions,\n revertSuggestions,\n suggestChanges,\n} from \"@handlewithcare/prosemirror-suggest-changes\";\nimport { UIMessage } from \"ai\";\nimport { Fragment, Slice } from \"prosemirror-model\";\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport { fixTablesKey } from \"prosemirror-tables\";\nimport { buildAIRequest, sendMessageWithAIRequest } from \"./api/index.js\";\nimport { createAgentCursorPlugin } from \"./plugins/AgentCursorPlugin.js\";\nimport { AIRequestHelpers, InvokeAIOptions } from \"./types.js\";\n\ntype AIPluginState = {\n aiMenuState:\n | ({\n /**\n * The ID of the block that the AI menu is opened at.\n * This changes as the AI is making changes to the document\n */\n blockId: string;\n } & (\n | {\n status: \"error\";\n error: any;\n }\n | {\n // fix: it might be nice to derive this from the Chat status and Tool call status\n status: \"user-input\" | \"thinking\" | \"ai-writing\" | \"user-reviewing\";\n }\n ))\n | \"closed\";\n};\n\nconst PLUGIN_KEY = new PluginKey(`blocknote-ai-plugin`);\n\nexport const AIExtension = createExtension(\n ({\n editor,\n options: editorOptions,\n }: ExtensionOptions<\n | (AIRequestHelpers & {\n /**\n * The name and color of the agent cursor\n *\n * @default { name: \"AI\", color: \"#8bc6ff\" }\n */\n agentCursor?: { name: string; color: string };\n })\n | undefined\n >) => {\n // TODO should we really expose it like this?\n const options = createStore<AIRequestHelpers>(editorOptions ?? {});\n const store = createStore<AIPluginState>({\n aiMenuState: \"closed\",\n });\n let chatSession:\n | {\n previousRequestOptions: InvokeAIOptions;\n chat: Chat<UIMessage>;\n abortController: AbortController;\n }\n | undefined;\n let autoScroll = false;\n\n const suggestChangesPlugin = suggestChanges();\n // disable decorations for suggest changes, not needed\n // (and the pilcrows are ugly)\n suggestChangesPlugin.props.decorations = undefined;\n return {\n key: \"ai\",\n options,\n store,\n mount({ signal }: { signal: AbortSignal }) {\n let scrollInProgress = false;\n // Listens for `scroll` and `scrollend` events to see if a new scroll was\n // started before an existing one ended. This is the most reliable way we\n // have of checking if a scroll event was caused by the user and not by\n // `scrollIntoView`, as the events are otherwise indistinguishable. If a\n // scroll was started before an existing one finished (meaning the user has\n // scrolled), auto scrolling is disabled.\n document.addEventListener(\n \"scroll\",\n () => {\n if (scrollInProgress) {\n autoScroll = false;\n }\n\n scrollInProgress = true;\n },\n {\n capture: true,\n signal,\n },\n );\n document.addEventListener(\n \"scrollend\",\n () => {\n scrollInProgress = false;\n },\n {\n capture: true,\n signal,\n },\n );\n },\n prosemirrorPlugins: [\n new Plugin({\n key: PLUGIN_KEY,\n filterTransaction: (tr) => {\n const menuState = store.state.aiMenuState;\n\n if (menuState !== \"closed\" && menuState.status === \"ai-writing\") {\n if (tr.getMeta(fixTablesKey)?.fixTables) {\n // the fixtables plugin causes the steps between of the AI Agent to become invalid\n // so we need to prevent it from running\n // (we might need to filter out other / or maybe any transactions during the writing phase)\n return false;\n }\n }\n return true;\n },\n }),\n suggestChangesPlugin,\n createAgentCursorPlugin(\n editorOptions?.agentCursor || { name: \"AI\", color: \"#8bc6ff\" },\n ),\n ],\n\n /**\n * Open the AI menu at a specific block\n */\n openAIMenuAtBlock(blockID: string) {\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(true, \"aiMenu\");\n editor.isEditable = false;\n store.setState({\n aiMenuState: {\n blockId: blockID,\n status: \"user-input\",\n },\n });\n\n // Scrolls to the block when the menu opens.\n const blockElement = editor.domElement?.querySelector(\n `[data-node-type=\"blockContainer\"][data-id=\"${blockID}\"]`,\n );\n blockElement?.scrollIntoView({ block: \"center\" });\n },\n\n /**\n * Close the AI menu\n */\n closeAIMenu() {\n store.setState({\n aiMenuState: \"closed\",\n });\n chatSession = undefined;\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(false, \"aiMenu\");\n editor.isEditable = true;\n editor.focus();\n },\n\n /**\n * Accept the changes made by the LLM\n */\n acceptChanges() {\n // This is slightly convoluted, to try to maintain the undo history as much as possible\n // The idea is that the LLM call has appended a number of updates to the document, moving the document from state `A` to state `C`\n // But we want the undo history to skip all of the intermediate states and go straight from `C` to `A`\n // To do this, we capture the document state `C` (post-LLM call), and then reject the suggestions to recover the original document state `A`\n // Then we create an intermediate state `B` that captures the diff between `A` and `C`\n // Then we apply the suggestions to `B` to get the final state `C`\n // This causes the undo history to skip `B` and go straight from `C` back to `A`\n\n // Capture the document state `C'` (post-LLM call with all suggestions still in the document)\n const markedUpDocument = editor.prosemirrorState.doc;\n\n // revert the suggestions to get back to the original document state `A`\n editor.exec((state, dispatch) => {\n return revertSuggestions(state, (tr) => {\n dispatch?.(tr.setMeta(\"addToHistory\", false));\n });\n });\n\n // Create an intermediate state `B` that captures the diff between the original document and the marked up document\n editor.exec((state, dispatch) => {\n const tr = state.tr;\n tr.replace(\n 0,\n tr.doc.content.size,\n new Slice(Fragment.from(markedUpDocument), 0, 0),\n );\n const nextState = state.apply(tr);\n // Apply the suggestions to the intermediate state `B` to get the final state `C`\n return applySuggestions(nextState, (resultTr) => {\n dispatch?.(\n tr.replace(\n 0,\n tr.doc.content.size,\n new Slice(Fragment.from(resultTr.doc), 0, 0),\n ),\n );\n });\n });\n\n // If in collaboration mode, merge the changes back into the original yDoc\n editor.getExtension(ForkYDocExtension)?.merge({ keepChanges: true });\n\n this.closeAIMenu();\n },\n\n /**\n * Reject the changes made by the LLM\n */\n rejectChanges() {\n // Revert the suggestions to get back to the original document\n editor.exec((state, dispatch) => {\n return revertSuggestions(state, (tr) => {\n // Do so without adding to history (so the last undo step is just prior to the LLM call)\n dispatch?.(tr.setMeta(\"addToHistory\", false));\n });\n });\n\n // If in collaboration mode, discard the changes and revert to the original yDoc\n editor.getExtension(ForkYDocExtension)?.merge({ keepChanges: false });\n this.closeAIMenu();\n },\n\n /**\n * Abort the current LLM request.\n *\n * This will stop the ongoing request and revert any changes made by the AI.\n * Only valid when there is an active AI request in progress.\n */\n async abort(reason?: any) {\n const { aiMenuState } = store.state;\n if (aiMenuState === \"closed\" || !chatSession) {\n return;\n }\n\n // Only abort if the request is in progress (thinking or ai-writing)\n if (\n aiMenuState.status !== \"thinking\" &&\n aiMenuState.status !== \"ai-writing\"\n ) {\n return;\n }\n\n const chat = chatSession.chat;\n const abortController = chatSession.abortController;\n\n // Abort the tool call operations\n abortController.abort(reason);\n\n // Stop the chat request\n await chat.stop();\n },\n\n /**\n * Retry the previous LLM call.\n *\n * Only valid if the current status is \"error\"\n */\n async retry() {\n const { aiMenuState } = store.state;\n if (\n aiMenuState === \"closed\" ||\n aiMenuState.status !== \"error\" ||\n !chatSession\n ) {\n throw new Error(\n \"retry() is only valid when a previous response failed\",\n );\n }\n\n /*\n Design decisions:\n - we cannot use chat.regenerate() because the document might have been updated already by toolcalls that failed mid-way\n - we also cannot revert the document changes and use chat.regenerate(), \n because we might want to retry a subsequent user-direction that failed, not the original one\n - this means we always need to send the new document state to the LLM\n - an alternative would be to take a snapshot of the document before the LLM call, and revert to that specific state\n when a call fails\n */\n\n if (chatSession?.chat.status === \"error\") {\n // the LLM call failed (i.e. a network error)\n // console.log(\"retry failed LLM call\", this.chatSession.chat.error);\n return this.invokeAI({\n ...chatSession.previousRequestOptions,\n userPrompt: `An error occured in the previous request. Please retry to accomplish the last user prompt.`,\n });\n } else {\n // an error occurred while parsing / executing the previous LLM call\n // give the LLM a chance to fix the error\n // console.log(\"retry failed tool execution\");\n return this.invokeAI({\n ...chatSession.previousRequestOptions,\n userPrompt: `An error occured while executing the previous tool call. Please retry to accomplish the last user prompt.`,\n });\n }\n },\n\n /**\n * Update the status of a call to an LLM\n *\n * @warning This method should usually only be used for advanced use-cases\n * if you want to implement how an LLM call is executed. Usually, you should\n * use {@link executeLLMRequest} instead which will handle the status updates for you.\n */\n setAIResponseStatus(\n status:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"user-reviewing\"\n | {\n status: \"error\";\n error: any;\n },\n ) {\n const aiMenuState = store.state.aiMenuState;\n if (aiMenuState === \"closed\") {\n return;\n }\n\n if (status === \"ai-writing\") {\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(false, \"aiMenu\");\n }\n\n if (typeof status === \"object\") {\n if (status.status !== \"error\") {\n throw new UnreachableCaseError(status.status);\n }\n this.store.setState({\n aiMenuState: {\n status: status.status,\n error: status.error,\n blockId: aiMenuState.blockId,\n },\n });\n } else {\n this.store.setState({\n aiMenuState: {\n status: status,\n blockId: aiMenuState.blockId,\n },\n });\n }\n },\n\n /**\n * @deprecated Use {@link invokeAI} instead\n */\n async callLLM(opts: InvokeAIOptions) {\n return this.invokeAI(opts);\n },\n\n /**\n * Execute a call to an LLM and apply the result to the editor\n */\n async invokeAI(opts: InvokeAIOptions) {\n this.setAIResponseStatus(\"thinking\");\n editor.getExtension(ForkYDocExtension)?.fork();\n\n try {\n // Create a new AbortController for this request\n const abortController = new AbortController();\n\n if (!chatSession) {\n // note: in the current implementation opts.transport is only used when creating a new chat\n // (so changing transport for a subsequent call in the same chat-session is not supported)\n chatSession = {\n previousRequestOptions: opts,\n chat:\n opts.chatProvider?.() ||\n this.options.state.chatProvider?.() ||\n new Chat<UIMessage>({\n sendAutomaticallyWhen: () => false,\n transport: opts.transport || this.options.state.transport,\n }),\n abortController,\n };\n } else {\n chatSession.previousRequestOptions = opts;\n chatSession.abortController = abortController;\n }\n const chat = chatSession.chat;\n\n // merge the global options with the local options\n const globalOpts = options.state;\n opts = {\n ...globalOpts,\n ...opts,\n } as InvokeAIOptions;\n\n const aiRequest = await buildAIRequest({\n editor,\n useSelection: opts.useSelection,\n deleteEmptyCursorBlock: opts.deleteEmptyCursorBlock,\n streamToolsProvider:\n opts.streamToolsProvider ??\n this.options.state.streamToolsProvider,\n documentStateBuilder:\n opts.documentStateBuilder ??\n this.options.state.documentStateBuilder,\n onBlockUpdated: (blockId) => {\n const aiMenuState = store.state.aiMenuState;\n const aiMenuOpenState =\n aiMenuState === \"closed\" ? undefined : aiMenuState;\n if (!aiMenuOpenState || aiMenuOpenState.status !== \"ai-writing\") {\n return;\n }\n\n // TODO: Sometimes, the updated block doesn't actually exist in\n // the editor. I don't know why this happens, seems like a bug?\n const nodeInfo = getNodeById(\n blockId,\n editor.prosemirrorState.doc,\n );\n if (!nodeInfo) {\n return;\n }\n\n // NOTE: does this setState with an anon object trigger unnecessary re-renders?\n store.setState({\n aiMenuState: {\n blockId,\n status: \"ai-writing\",\n },\n });\n\n if (autoScroll) {\n const blockElement = editor.prosemirrorView.domAtPos(\n nodeInfo.posBeforeNode + 1,\n );\n (blockElement.node as HTMLElement).scrollIntoView({\n block: \"center\",\n });\n }\n },\n onStart: () => {\n autoScroll = true;\n this.setAIResponseStatus(\"ai-writing\");\n\n if (\n aiRequest.emptyCursorBlockToDelete &&\n aiRequest.editor.getBlock(aiRequest.emptyCursorBlockToDelete)\n ) {\n aiRequest.editor.removeBlocks([\n aiRequest.emptyCursorBlockToDelete,\n ]);\n }\n },\n });\n\n const result = await sendMessageWithAIRequest(\n chat,\n aiRequest,\n {\n role: \"user\",\n parts: [\n {\n type: \"text\",\n text: opts.userPrompt,\n },\n ],\n },\n opts.chatRequestOptions || this.options.state.chatRequestOptions,\n chatSession.abortController.signal,\n );\n\n if (\n (result.ok && chat.status !== \"error\") ||\n abortController.signal.aborted\n ) {\n this.setAIResponseStatus(\"user-reviewing\");\n } else {\n // eslint-disable-next-line no-console\n console.warn(\"Error calling LLM\", {\n result,\n chatStatus: chat.status,\n chatError: chat.error,\n });\n this.setAIResponseStatus({\n status: \"error\",\n error: result.ok ? chat.error : result.error,\n });\n }\n } catch (e) {\n this.setAIResponseStatus({\n status: \"error\",\n error: e,\n });\n // eslint-disable-next-line no-console\n console.error(\n \"Unexpected error calling LLM\",\n e,\n chatSession?.chat.messages,\n );\n }\n },\n };\n },\n);\n","import { useBlockNoteContext } from \"@blocknote/react\";\n\nimport { getAIDictionary } from \"../i18n/dictionary.js\";\n\nexport function useAIDictionary() {\n const ctx = useBlockNoteContext();\n return getAIDictionary(ctx!.editor!);\n}\n","import { mergeCSSClasses } from \"@blocknote/core\";\nimport { filterSuggestionItems } from \"@blocknote/core/extensions\";\nimport {\n DefaultReactSuggestionItem,\n useComponentsContext,\n useSuggestionMenuKeyboardHandler,\n} from \"@blocknote/react\";\nimport {\n ChangeEvent,\n KeyboardEvent,\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport type PromptSuggestionMenuProps = {\n items: DefaultReactSuggestionItem[];\n onManualPromptSubmit: (userPrompt: string) => void;\n promptText?: string;\n onPromptTextChange?: (userPrompt: string) => void;\n icon?: ReactNode;\n rightSection?: ReactNode;\n placeholder?: string;\n disabled?: boolean;\n};\n\nexport const PromptSuggestionMenu = (props: PromptSuggestionMenuProps) => {\n // const dict = useAIDictionary();\n const Components = useComponentsContext()!;\n\n const { onManualPromptSubmit, promptText, onPromptTextChange, disabled } =\n props;\n\n // Only used internal state when `props.prompText` is undefined (i.e., uncontrolled mode)\n const [internalPromptText, setInternalPromptText] = useState<string>(\"\");\n const promptTextToUse = promptText || internalPromptText;\n\n const handleEnter = useCallback(\n async (event: KeyboardEvent) => {\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing) {\n // console.log(\"ENTER\", currentEditingPrompt);\n onManualPromptSubmit(promptTextToUse);\n }\n },\n [promptTextToUse, onManualPromptSubmit],\n );\n\n const handleChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const newValue = event.currentTarget.value;\n if (onPromptTextChange) {\n onPromptTextChange(newValue);\n }\n\n // Only update internal state if it's uncontrolled\n if (promptText === undefined) {\n setInternalPromptText(newValue);\n }\n },\n [onPromptTextChange, setInternalPromptText, promptText],\n );\n\n const items: DefaultReactSuggestionItem[] = useMemo(() => {\n return filterSuggestionItems(props.items, promptTextToUse);\n }, [promptTextToUse, props.items]);\n\n const { selectedIndex, setSelectedIndex, handler } =\n useSuggestionMenuKeyboardHandler(items, (item) => item.onItemClick());\n\n const activeDescendantId =\n items.length > 0 && selectedIndex >= 0 && selectedIndex < items.length\n ? `bn-suggestion-menu-item-${selectedIndex}`\n : undefined;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n // TODO: handle backspace to close\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing) {\n if (items.length > 0) {\n handler(event);\n } else {\n // TODO: check focus?\n handleEnter(event);\n }\n } else {\n handler(event);\n }\n },\n [handleEnter, handler, items.length],\n );\n\n // Resets index when items change\n useEffect(() => {\n setSelectedIndex(0);\n }, [promptTextToUse, setSelectedIndex]);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const hasBeenDisabled = useRef(disabled);\n\n useEffect(() => {\n // This effect is used so that after the input has been disabled (for example, when AI results are loaded),\n // the input is focused again.\n if (inputRef.current && hasBeenDisabled.current && !disabled) {\n inputRef.current.focus();\n }\n\n if (disabled) {\n hasBeenDisabled.current = true;\n }\n }, [disabled]);\n\n return (\n <div className={\"bn-combobox\"}>\n <Components.Generic.Form.Root>\n <Components.Generic.Form.TextInput\n ref={inputRef}\n className={\"bn-combobox-input\"}\n name={\"ai-prompt\"}\n variant={\"large\"}\n icon={props.icon}\n value={promptTextToUse || \"\"}\n placeholder={props.placeholder}\n disabled={props.disabled}\n onKeyDown={handleKeyDown}\n onChange={handleChange}\n autoComplete={\"off\"}\n rightSection={props.rightSection}\n aria-activedescendant={activeDescendantId}\n />\n </Components.Generic.Form.Root>\n {items.length > 0 && (\n <Components.SuggestionMenu.Root\n className={\"bn-combobox-items\"}\n id={\"ai-suggestion-menu\"}\n >\n {items.map((item, i) => (\n <Components.SuggestionMenu.Item\n key={item.title}\n className={mergeCSSClasses(\n \"bn-suggestion-menu-item\",\n item.size === \"small\" ? \"bn-suggestion-menu-item-small\" : \"\",\n )}\n id={`bn-suggestion-menu-item-${i}`}\n isSelected={i === selectedIndex}\n onClick={item.onItemClick}\n item={item}\n />\n ))}\n </Components.SuggestionMenu.Root>\n )}\n </div>\n );\n};\n","import {\n BlockNoteEditor,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"@blocknote/core\";\nimport { DefaultReactSuggestionItem } from \"@blocknote/react\";\nimport {\n RiArrowGoBackFill,\n RiBallPenLine,\n RiCheckFill,\n RiCheckLine,\n RiEarthLine,\n RiListCheck3,\n RiLoopLeftFill,\n RiMagicLine,\n RiText,\n RiTextWrap,\n} from \"react-icons/ri\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { aiDocumentFormats } from \"../../api/index.js\";\nimport { getAIDictionary } from \"../../i18n/dictionary.js\";\n\nexport type AIMenuSuggestionItem = Omit<\n DefaultReactSuggestionItem,\n \"onItemClick\"\n> & {\n onItemClick: (setPrompt: (userPrompt: string) => void) => void;\n key: string;\n};\n\n/**\n * Default items we show in the AI Menu when there is no selection active.\n * For example, when the AI menu is triggered via the slash menu\n */\nfunction getDefaultAIMenuItemsWithoutSelection<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n return [\n {\n key: \"continue_writing\",\n title: dict.ai_default_commands.continue_writing.title,\n aliases: dict.ai_default_commands.continue_writing.aliases,\n icon: <RiBallPenLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt:\n \"Continue writing at the current cursor position related to the previous text. Add multiple blocks if needed. If the document looks like a template / draft, follow the template. Be extensive if needed.\",\n // By default, LLM will be able to add / update / delete blocks. For \"continue writing\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n\n {\n key: \"summarize\",\n title: dict.ai_default_commands.summarize.title,\n aliases: dict.ai_default_commands.summarize.aliases,\n icon: <RiTextWrap size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt: \"Summarize\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"action_items\",\n title: dict.ai_default_commands.add_action_items.title,\n aliases: dict.ai_default_commands.add_action_items.aliases,\n icon: <RiListCheck3 size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt: \"Add action items\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"write_anything\",\n title: dict.ai_default_commands.write_anything.title,\n aliases: dict.ai_default_commands.write_anything.aliases,\n icon: <RiBallPenLine size={18} />,\n onItemClick: (setPrompt) => {\n setPrompt(dict.ai_default_commands.write_anything.prompt_placeholder);\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when there is a selection active.\n * For example, when the AI menu is triggered via formatting toolbar (AIToolbarButton)\n */\nfunction getDefaultAIMenuItemsWithSelection<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"improve_writing\",\n title: dict.ai_default_commands.improve_writing.title,\n aliases: dict.ai_default_commands.improve_writing.aliases,\n icon: <RiText size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Improve writing\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"fix_spelling\",\n title: dict.ai_default_commands.fix_spelling.title,\n aliases: dict.ai_default_commands.fix_spelling.aliases,\n icon: <RiCheckLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Fix spelling\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"translate\",\n title: dict.ai_default_commands.translate.title,\n aliases: dict.ai_default_commands.translate.aliases,\n icon: <RiEarthLine size={18} />,\n onItemClick: (setPrompt) => {\n setPrompt(dict.ai_default_commands.translate.prompt_placeholder);\n },\n size: \"small\",\n },\n {\n key: \"simplify\",\n title: dict.ai_default_commands.simplify.title,\n aliases: dict.ai_default_commands.simplify.aliases,\n icon: <RiMagicLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Simplify\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when the AI response is done.\n */\nfunction getDefaultAIMenuItemsForReview<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"accept\",\n title: dict.ai_menu.actions.accept.title,\n aliases: dict.ai_menu.actions.accept.aliases,\n icon: <RiCheckFill size={18} />,\n onItemClick: () => {\n ai.acceptChanges();\n },\n size: \"small\",\n },\n {\n key: \"revert\",\n title: dict.ai_menu.actions.revert.title,\n aliases: dict.ai_menu.actions.revert.aliases,\n icon: <RiArrowGoBackFill size={18} />,\n onItemClick: () => {\n ai.rejectChanges();\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when the AI response is done.\n */\nfunction getDefaultAIMenuItemsForError<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"retry\",\n title: dict.ai_menu.actions.retry.title,\n aliases: dict.ai_menu.actions.retry.aliases,\n icon: <RiLoopLeftFill size={18} />,\n onItemClick: async () => {\n await ai.retry();\n },\n size: \"small\",\n },\n {\n key: \"cancel\",\n title: dict.ai_menu.actions.cancel.title,\n aliases: dict.ai_menu.actions.cancel.aliases,\n icon: <RiArrowGoBackFill size={18} />,\n onItemClick: () => {\n ai.rejectChanges();\n },\n size: \"small\",\n },\n ];\n}\n\nexport function getDefaultAIMenuItems(\n editor: BlockNoteEditor<any, any, any>,\n aiResponseStatus:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"error\"\n | \"user-reviewing\"\n | \"closed\",\n): AIMenuSuggestionItem[] {\n if (aiResponseStatus === \"user-input\") {\n return editor.getSelection()\n ? getDefaultAIMenuItemsWithSelection(editor)\n : getDefaultAIMenuItemsWithoutSelection(editor);\n } else if (aiResponseStatus === \"user-reviewing\") {\n return getDefaultAIMenuItemsForReview(editor);\n } else if (aiResponseStatus === \"error\") {\n return getDefaultAIMenuItemsForError(editor);\n } else {\n return [];\n }\n}\n","import { BlockNoteEditor } from \"@blocknote/core\";\nimport {\n useBlockNoteEditor,\n useComponentsContext,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { useAIDictionary } from \"../../hooks/useAIDictionary.js\";\nimport { PromptSuggestionMenu } from \"./PromptSuggestionMenu.js\";\nimport {\n AIMenuSuggestionItem,\n getDefaultAIMenuItems,\n} from \"./getDefaultAIMenuItems.js\";\n\nexport type AIMenuProps = {\n items?: (\n editor: BlockNoteEditor<any, any, any>,\n aiResponseStatus:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"error\"\n | \"user-reviewing\"\n | \"closed\",\n ) => AIMenuSuggestionItem[];\n onManualPromptSubmit?: (userPrompt: string) => void;\n};\n\nexport const AIMenu = (props: AIMenuProps) => {\n const editor = useBlockNoteEditor();\n const [prompt, setPrompt] = useState(\"\");\n const dict = useAIDictionary();\n\n const Components = useComponentsContext()!;\n\n const ai = useExtension(AIExtension);\n\n const aiResponseStatus = useExtensionState(AIExtension, {\n selector: (state) =>\n state.aiMenuState !== \"closed\" ? state.aiMenuState.status : \"closed\",\n });\n\n const { items: externalItems } = props;\n // note, technically there might be a bug with this useMemo when quickly changing the selection and opening the menu\n // would not call getDefaultAIMenuItems with the correct selection, because the component is reused and the memo not retriggered\n // practically this should not happen (you can test it by using a high transition duration in useUIElementPositioning)\n const items = useMemo(() => {\n let items: AIMenuSuggestionItem[] = [];\n if (externalItems) {\n items = externalItems(editor, aiResponseStatus);\n } else {\n items = getDefaultAIMenuItems(editor, aiResponseStatus);\n }\n\n // map from AI items to React Items required by PromptSuggestionMenu\n return items.map((item) => {\n return {\n ...item,\n onItemClick: () => {\n item.onItemClick(setPrompt);\n },\n };\n });\n }, [externalItems, aiResponseStatus, editor]);\n\n const onManualPromptSubmitDefault = useCallback(\n async (userPrompt: string) => {\n await ai.invokeAI({\n userPrompt,\n useSelection: editor.getSelection() !== undefined,\n });\n },\n [ai, editor],\n );\n\n useEffect(() => {\n // this is a bit hacky to run a useeffect to reset the prompt when the AI response is done\n if (\n aiResponseStatus === \"ai-writing\" ||\n aiResponseStatus === \"user-reviewing\" ||\n aiResponseStatus === \"error\"\n ) {\n setPrompt(\"\");\n }\n }, [aiResponseStatus]);\n\n const placeholder = useMemo(() => {\n if (aiResponseStatus === \"thinking\") {\n return dict.ai_menu.status.thinking;\n } else if (aiResponseStatus === \"ai-writing\") {\n return dict.ai_menu.status.editing;\n } else if (aiResponseStatus === \"error\") {\n return dict.ai_menu.status.error;\n }\n\n return dict.ai_menu.input_placeholder;\n }, [aiResponseStatus, dict]);\n\n const rightSection = useMemo(() => {\n if (aiResponseStatus === \"thinking\" || aiResponseStatus === \"ai-writing\") {\n return (\n <Components.SuggestionMenu.Loader\n className={\"bn-suggestion-menu-loader bn-combobox-right-section\"}\n />\n );\n } else if (aiResponseStatus === \"error\") {\n return (\n <div className={\"bn-combobox-right-section bn-combobox-error\"}>\n {/* Taken from Google Material Icons */}\n {/* https://fonts.google.com/icons?selected=Material+Symbols+Rounded:error:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=error&icon.size=24&icon.color=%23e8eaed&icon.set=Material+Symbols&icon.style=Rounded&icon.platform=web */}\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height=\"1em\"\n viewBox=\"0 -960 960 960\"\n width=\"1em\"\n fill=\"currentColor\"\n >\n <path d=\"M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm0-160q17 0 28.5-11.5T520-480v-160q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640v160q0 17 11.5 28.5T480-440Zm0 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z\" />\n </svg>\n </div>\n );\n }\n\n return undefined;\n }, [Components, aiResponseStatus]);\n\n return (\n <PromptSuggestionMenu\n onManualPromptSubmit={\n props.onManualPromptSubmit || onManualPromptSubmitDefault\n }\n items={items}\n promptText={prompt}\n onPromptTextChange={setPrompt}\n placeholder={placeholder}\n disabled={\n aiResponseStatus === \"thinking\" || aiResponseStatus === \"ai-writing\"\n }\n icon={\n <div className=\"bn-combobox-icon\">\n <RiSparkling2Fill />\n </div>\n }\n rightSection={rightSection}\n />\n );\n};\n","import {\n BlockPopover,\n FloatingUIOptions,\n useBlockNoteEditor,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { autoUpdate, flip, offset, size } from \"@floating-ui/react\";\nimport { FC, useMemo } from \"react\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { AIMenu, AIMenuProps } from \"./AIMenu.js\";\n\nexport const AIMenuController = (props: {\n aiMenu?: FC<AIMenuProps>;\n floatingUIOptions?: FloatingUIOptions;\n}) => {\n const editor = useBlockNoteEditor();\n const ai = useExtension(AIExtension);\n\n const aiMenuState = useExtensionState(AIExtension, {\n editor,\n selector: (state) => state.aiMenuState,\n });\n\n const blockId = aiMenuState === \"closed\" ? undefined : aiMenuState.blockId;\n\n const floatingUIOptions = useMemo<FloatingUIOptions>(\n () =>\n ({\n ...props.floatingUIOptions,\n useFloatingOptions: {\n open: aiMenuState !== \"closed\",\n placement: \"bottom\",\n middleware: [\n offset(10),\n flip(),\n size({\n apply({ rects, elements }) {\n Object.assign(elements.floating.style, {\n width: `${rects.reference.width}px`,\n });\n },\n }),\n ],\n onOpenChange: (open) => {\n if (open || aiMenuState === \"closed\") {\n return;\n }\n\n if (aiMenuState.status === \"user-input\") {\n ai.closeAIMenu();\n } else if (\n aiMenuState.status === \"user-reviewing\" ||\n aiMenuState.status === \"error\"\n ) {\n ai.rejectChanges();\n }\n },\n whileElementsMounted(reference, floating, update) {\n return autoUpdate(reference, floating, update, {\n animationFrame: true,\n });\n },\n ...props.floatingUIOptions?.useFloatingOptions,\n },\n useDismissProps: {\n enabled:\n aiMenuState === \"closed\" || aiMenuState.status === \"user-input\",\n // We should just be able to set `referencePress: true` instead of\n // using this listener, but this doesn't seem to trigger.\n // (probably because we don't assign the referenceProps to the reference element)\n outsidePress: (event: MouseEvent) => {\n if (event.target instanceof Element) {\n const blockElement = event.target.closest(\".bn-block\");\n if (\n blockElement &&\n blockElement.getAttribute(\"data-id\") === blockId\n ) {\n ai.closeAIMenu();\n }\n }\n\n return true;\n },\n ...props.floatingUIOptions?.useDismissProps,\n },\n elementProps: {\n style: {\n zIndex: 100,\n },\n ...props.floatingUIOptions?.elementProps,\n },\n // we use the focus manager instead of `autoFocus={true}` to prevent \"page-scrolls-to-top-when-opening-the-floating-element\"\n // see https://floating-ui.com/docs/floatingfocusmanager#page-scrolls-to-top-when-opening-the-floating-element\n focusManagerProps: {\n disabled: false,\n // needed for https://github.com/floating-ui/floating-ui/pull/3202\n // (related: https://github.com/mui/base-ui/issues/3950)\n getInsideElements: () => {\n if (!editor.domElement) {\n return [];\n }\n return [editor.domElement];\n },\n ...props.floatingUIOptions?.focusManagerProps,\n },\n }) satisfies FloatingUIOptions,\n [ai, aiMenuState, blockId, props.floatingUIOptions, editor.domElement],\n );\n\n const Component = props.aiMenu || AIMenu;\n\n return (\n <BlockPopover blockId={blockId} {...floatingUIOptions}>\n {aiMenuState !== \"closed\" && <Component />}\n </BlockPopover>\n );\n};\n","import { BlockSchema, InlineContentSchema, StyleSchema } from \"@blocknote/core\";\nimport { FormattingToolbarExtension } from \"@blocknote/core/extensions\";\nimport {\n useBlockNoteEditor,\n useComponentsContext,\n useExtension,\n} from \"@blocknote/react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { useAIDictionary } from \"../../hooks/useAIDictionary.js\";\n\nexport const AIToolbarButton = () => {\n const dict = useAIDictionary();\n const Components = useComponentsContext()!;\n\n const editor = useBlockNoteEditor<\n BlockSchema,\n InlineContentSchema,\n StyleSchema\n >();\n\n const ai = useExtension(AIExtension);\n const formattingToolbar = useExtension(FormattingToolbarExtension);\n\n const onClick = () => {\n const selection = editor.getSelection();\n if (!selection) {\n throw new Error(\"No selection\");\n }\n\n const position = selection.blocks[selection.blocks.length - 1].id;\n\n ai.openAIMenuAtBlock(position);\n formattingToolbar.store.setState(false);\n };\n\n if (!editor.isEditable) {\n return null;\n }\n\n return (\n <Components.Generic.Toolbar.Button\n className={\"bn-button\"}\n label={dict.formatting_toolbar.ai.tooltip}\n mainTooltip={dict.formatting_toolbar.ai.tooltip}\n icon={<RiSparkling2Fill />}\n onClick={onClick}\n />\n );\n};\n","import {\n BlockNoteEditor,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"@blocknote/core\";\nimport { DefaultReactSuggestionItem } from \"@blocknote/react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { getAIDictionary } from \"../../i18n/dictionary.js\";\nconst Icons = {\n AI: RiSparkling2Fill,\n};\n\n/**\n * Returns AI related items that can be added to the slash menu\n */\nexport function getAISlashMenuItems<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): DefaultReactSuggestionItem[] {\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n const items = [\n {\n key: \"ai\",\n onItemClick: () => {\n const cursor = editor.getTextCursorPosition();\n if (\n cursor.block.content &&\n Array.isArray(cursor.block.content) && // isarray check not ideal\n cursor.block.content.length === 0 &&\n cursor.prevBlock\n ) {\n ai.openAIMenuAtBlock(cursor.prevBlock.id);\n } else {\n ai.openAIMenuAtBlock(cursor.block.id);\n }\n },\n ...getAIDictionary(editor).slash_menu.ai,\n icon: <Icons.AI />,\n },\n ];\n\n return items;\n}\n"],"mappings":"4iBAOA,IAAM,EAAa,IAAI,EAAA,UAA4B,yBAAyB,CAK5E,SAAgB,EAAwB,EAGrC,CACD,OAAO,IAAI,EAAA,OAAyB,CAClC,IAAK,EACL,KAAO,IACE,EAAE,EAEX,MAAO,CACL,UACS,CACL,UAAW,IAAA,GACZ,EAEH,OAAQ,EAAI,IAAc,CACxB,IAAM,EAAO,EAAG,QAAQ,UAAU,CAQlC,OANK,EAME,CACL,UAAW,EAAK,UACjB,CAPQ,CACL,UAAW,IAAA,GACZ,EAON,CACD,MAAO,CACL,YAAc,GAAU,CACtB,GAAM,CAAE,OAAQ,EAEV,CAAE,aAAc,EAAW,SAAS,EAAM,CAE1C,EAAO,EAAE,CAEf,GAAI,CAAC,EACH,OAAO,EAAA,cAAc,OAAO,EAAK,EAAE,CAAC,CAGtC,EAAK,KACH,EAAA,WAAW,OAAO,EAAU,SAAY,EAAa,EAAY,CAAE,CACjE,IAAK,eACL,KAAM,GACP,CAAC,CACH,CAED,IAAM,EAAO,KAAK,IAAI,EAAU,OAAQ,EAAU,KAAK,CACjD,EAAK,KAAK,IAAI,EAAU,OAAQ,EAAU,KAAK,CASrD,OAPA,EAAK,KACH,EAAA,WAAW,OAAO,EAAM,GAAA,EAAA,EAAA,yBAA4B,EAAY,CAAE,CAChE,aAAc,GACd,eAAgB,GACjB,CAAC,CACH,CAEM,EAAA,cAAc,OAAO,EAAK,EAAK,EAEzC,CACF,CAAC,CAGJ,IAAM,EAAgB,GAA0C,CAC9D,IAAM,EAAgB,SAAS,cAAc,OAAO,CAEpD,EAAc,UAAU,IAAI,gCAAgC,CAC5D,EAAc,aAAa,cAAe,OAAO,CAEjD,IAAM,EAAe,SAAS,cAAc,OAAO,CACnD,EAAa,aAAa,oBAAqB,QAAQ,CACvD,EAAa,UAAU,IAAI,iCAAiC,CAC5D,EAAa,aAAa,QAAS,qBAAqB,EAAK,QAAQ,CAErE,IAAM,EAAe,SAAS,cAAc,OAAO,CAYnD,OAVA,EAAa,UAAU,IAAI,iCAAiC,CAC5D,EAAa,aAAa,QAAS,qBAAqB,EAAK,QAAQ,CACrE,EAAa,aAAa,SAAS,eAAe,EAAK,KAAK,CAAE,KAAK,CAEnE,EAAa,aAAa,EAAc,KAAK,CAE7C,EAAc,aAAa,SAAS,eAAe,IAAS,CAAE,KAAK,CACnE,EAAc,aAAa,EAAc,KAAK,CAC9C,EAAc,aAAa,SAAS,eAAe,IAAS,CAAE,KAAK,CAE5D,GCrDH,EAAa,IAAI,EAAA,UAAU,sBAAsB,CAE1C,GAAA,EAAA,EAAA,kBACV,CACC,SACA,QAAS,KAWL,CAEJ,IAAM,GAAA,EAAA,EAAA,aAAwC,GAAiB,EAAE,CAAC,CAC5D,GAAA,EAAA,EAAA,aAAmC,CACvC,YAAa,SACd,CAAC,CACE,EAOA,EAAa,GAEX,GAAA,EAAA,EAAA,iBAAuC,CAI7C,MADA,GAAqB,MAAM,YAAc,IAAA,GAClC,CACL,IAAK,KACL,UACA,QACA,MAAM,CAAE,UAAmC,CACzC,IAAI,EAAmB,GAOvB,SAAS,iBACP,aACM,CACA,IACF,EAAa,IAGf,EAAmB,IAErB,CACE,QAAS,GACT,SACD,CACF,CACD,SAAS,iBACP,gBACM,CACJ,EAAmB,IAErB,CACE,QAAS,GACT,SACD,CACF,EAEH,mBAAoB,CAClB,IAAI,EAAA,OAAO,CACT,IAAK,EACL,kBAAoB,GAAO,CACzB,IAAM,EAAY,EAAM,MAAM,YAU9B,MARA,EAAI,IAAc,UAAY,EAAU,SAAW,cAC7C,EAAG,QAAQ,EAAA,aAAa,EAAE,YASnC,CAAC,CACF,EACA,EACE,GAAe,aAAe,CAAE,KAAM,KAAM,MAAO,UAAW,CAC/D,CACF,CAKD,kBAAkB,EAAiB,CACjC,EACG,aAAa,EAAA,uBAAuB,EACnC,cAAc,GAAM,SAAS,CACjC,EAAO,WAAa,GACpB,EAAM,SAAS,CACb,YAAa,CACX,QAAS,EACT,OAAQ,aACT,CACF,CAAC,EAGmB,EAAO,YAAY,cACtC,8CAA8C,EAAQ,IACvD,GACa,eAAe,CAAE,MAAO,SAAU,CAAC,EAMnD,aAAc,CACZ,EAAM,SAAS,CACb,YAAa,SACd,CAAC,CACF,EAAc,IAAA,GACd,EACG,aAAa,EAAA,uBAAuB,EACnC,cAAc,GAAO,SAAS,CAClC,EAAO,WAAa,GACpB,EAAO,OAAO,EAMhB,eAAgB,CAUd,IAAM,EAAmB,EAAO,iBAAiB,IAGjD,EAAO,MAAM,EAAO,KAClB,EAAA,EAAA,mBAAyB,EAAQ,GAAO,CACtC,IAAW,EAAG,QAAQ,eAAgB,GAAM,CAAC,EAC7C,CACF,CAGF,EAAO,MAAM,EAAO,IAAa,CAC/B,IAAM,EAAK,EAAM,GAQjB,OAPA,EAAG,QACD,EACA,EAAG,IAAI,QAAQ,KACf,IAAI,EAAA,MAAM,EAAA,SAAS,KAAK,EAAiB,CAAE,EAAG,EAAE,CACjD,EAGD,EAAA,EAAA,kBAFkB,EAAM,MAAM,EAAG,CAEG,GAAa,CAC/C,IACE,EAAG,QACD,EACA,EAAG,IAAI,QAAQ,KACf,IAAI,EAAA,MAAM,EAAA,SAAS,KAAK,EAAS,IAAI,CAAE,EAAG,EAAE,CAC7C,CACF,EACD,EACF,CAGF,EAAO,aAAa,EAAA,kBAAkB,EAAE,MAAM,CAAE,YAAa,GAAM,CAAC,CAEpE,KAAK,aAAa,EAMpB,eAAgB,CAEd,EAAO,MAAM,EAAO,KAClB,EAAA,EAAA,mBAAyB,EAAQ,GAAO,CAEtC,IAAW,EAAG,QAAQ,eAAgB,GAAM,CAAC,EAC7C,CACF,CAGF,EAAO,aAAa,EAAA,kBAAkB,EAAE,MAAM,CAAE,YAAa,GAAO,CAAC,CACrE,KAAK,aAAa,EASpB,MAAM,MAAM,EAAc,CACxB,GAAM,CAAE,eAAgB,EAAM,MAM9B,GALI,IAAgB,UAAY,CAAC,GAM/B,EAAY,SAAW,YACvB,EAAY,SAAW,aAEvB,OAGF,IAAM,EAAO,EAAY,KACD,EAAY,gBAGpB,MAAM,EAAO,CAG7B,MAAM,EAAK,MAAM,EAQnB,MAAM,OAAQ,CACZ,GAAM,CAAE,eAAgB,EAAM,MAC9B,GACE,IAAgB,UAChB,EAAY,SAAW,SACvB,CAAC,EAED,MAAU,MACR,wDACD,CAwBD,OAXE,GAAa,KAAK,SAAW,QAGxB,KAAK,SAAS,CACnB,GAAG,EAAY,uBACf,WAAY,6FACb,CAAC,CAKK,KAAK,SAAS,CACnB,GAAG,EAAY,uBACf,WAAY,4GACb,CAAC,EAWN,oBACE,EASA,CACA,IAAM,EAAc,EAAM,MAAM,YAC5B,OAAgB,SAUpB,GANI,IAAW,cACb,EACG,aAAa,EAAA,uBAAuB,EACnC,cAAc,GAAO,SAAS,CAGhC,OAAO,GAAW,SAAU,CAC9B,GAAI,EAAO,SAAW,QACpB,MAAM,IAAI,EAAA,qBAAqB,EAAO,OAAO,CAE/C,KAAK,MAAM,SAAS,CAClB,YAAa,CACX,OAAQ,EAAO,OACf,MAAO,EAAO,MACd,QAAS,EAAY,QACtB,CACF,CAAC,MAEF,KAAK,MAAM,SAAS,CAClB,YAAa,CACH,SACR,QAAS,EAAY,QACtB,CACF,CAAC,EAON,MAAM,QAAQ,EAAuB,CACnC,OAAO,KAAK,SAAS,EAAK,EAM5B,MAAM,SAAS,EAAuB,CACpC,KAAK,oBAAoB,WAAW,CACpC,EAAO,aAAa,EAAA,kBAAkB,EAAE,MAAM,CAE9C,GAAI,CAEF,IAAM,EAAkB,IAAI,gBAEvB,GAeH,EAAY,uBAAyB,EACrC,EAAY,gBAAkB,GAb9B,EAAc,CACZ,uBAAwB,EACxB,KACE,EAAK,gBAAgB,EACrB,KAAK,QAAQ,MAAM,gBAAgB,EACnC,IAAI,EAAA,KAAgB,CAClB,0BAA6B,GAC7B,UAAW,EAAK,WAAa,KAAK,QAAQ,MAAM,UACjD,CAAC,CACJ,kBACD,CAKH,IAAM,EAAO,EAAY,KAIzB,EAAO,CACL,GAFiB,EAAQ,MAGzB,GAAG,EACJ,CAED,IAAM,EAAY,MAAM,EAAA,EAAe,CACrC,SACA,aAAc,EAAK,aACnB,uBAAwB,EAAK,uBAC7B,oBACE,EAAK,qBACL,KAAK,QAAQ,MAAM,oBACrB,qBACE,EAAK,sBACL,KAAK,QAAQ,MAAM,qBACrB,eAAiB,GAAY,CAC3B,IAAM,EAAc,EAAM,MAAM,YAC1B,EACJ,IAAgB,SAAW,IAAA,GAAY,EACzC,GAAI,CAAC,GAAmB,EAAgB,SAAW,aACjD,OAKF,IAAM,GAAA,EAAA,EAAA,aACJ,EACA,EAAO,iBAAiB,IACzB,CACI,IAKL,EAAM,SAAS,CACb,YAAa,CACX,UACA,OAAQ,aACT,CACF,CAAC,CAEE,GACmB,EAAO,gBAAgB,SAC1C,EAAS,cAAgB,EAC1B,CACa,KAAqB,eAAe,CAChD,MAAO,SACR,CAAC,GAGN,YAAe,CACb,EAAa,GACb,KAAK,oBAAoB,aAAa,CAGpC,EAAU,0BACV,EAAU,OAAO,SAAS,EAAU,yBAAyB,EAE7D,EAAU,OAAO,aAAa,CAC5B,EAAU,yBACX,CAAC,EAGP,CAAC,CAEI,EAAS,MAAM,EAAA,EACnB,EACA,EACA,CACE,KAAM,OACN,MAAO,CACL,CACE,KAAM,OACN,KAAM,EAAK,WACZ,CACF,CACF,CACD,EAAK,oBAAsB,KAAK,QAAQ,MAAM,mBAC9C,EAAY,gBAAgB,OAC7B,CAGE,EAAO,IAAM,EAAK,SAAW,SAC9B,EAAgB,OAAO,QAEvB,KAAK,oBAAoB,iBAAiB,EAG1C,QAAQ,KAAK,oBAAqB,CAChC,SACA,WAAY,EAAK,OACjB,UAAW,EAAK,MACjB,CAAC,CACF,KAAK,oBAAoB,CACvB,OAAQ,QACR,MAAO,EAAO,GAAK,EAAK,MAAQ,EAAO,MACxC,CAAC,QAEG,EAAG,CACV,KAAK,oBAAoB,CACvB,OAAQ,QACR,MAAO,EACR,CAAC,CAEF,QAAQ,MACN,+BACA,EACA,GAAa,KAAK,SACnB,GAGN,EAEJ,CCtgBD,SAAgB,GAAkB,CAEhC,OAAO,EAAA,GAAA,EAAA,EAAA,sBAD0B,CACL,OAAQ,CCuBtC,IAAa,EAAwB,GAAqC,CAExE,IAAM,GAAA,EAAA,EAAA,uBAAmC,CAEnC,CAAE,uBAAsB,aAAY,qBAAoB,YAC5D,EAGI,CAAC,EAAoB,IAAA,EAAA,EAAA,UAA0C,GAAG,CAClE,EAAkB,GAAc,EAEhC,GAAA,EAAA,EAAA,aACJ,KAAO,IAAyB,CAC1B,EAAM,MAAQ,SAAW,CAAC,EAAM,YAAY,aAE9C,EAAqB,EAAgB,EAGzC,CAAC,EAAiB,EAAqB,CACxC,CAEK,GAAA,EAAA,EAAA,aACH,GAAyC,CACxC,IAAM,EAAW,EAAM,cAAc,MACjC,GACF,EAAmB,EAAS,CAI1B,IAAe,IAAA,IACjB,EAAsB,EAAS,EAGnC,CAAC,EAAoB,EAAuB,EAAW,CACxD,CAEK,GAAA,EAAA,EAAA,cACJ,EAAA,EAAA,uBAA6B,EAAM,MAAO,EAAgB,CACzD,CAAC,EAAiB,EAAM,MAAM,CAAC,CAE5B,CAAE,gBAAe,mBAAkB,YAAA,EAAA,EAAA,kCACN,EAAQ,GAAS,EAAK,aAAa,CAAC,CAEjE,EACJ,EAAM,OAAS,GAAK,GAAiB,GAAK,EAAgB,EAAM,OAC5D,2BAA2B,IAC3B,IAAA,GAEA,GAAA,EAAA,EAAA,aACH,GAAyB,CAEpB,EAAM,MAAQ,SAAW,CAAC,EAAM,YAAY,YAC1C,EAAM,OAAS,EACjB,EAAQ,EAAM,CAGd,EAAY,EAAM,CAGpB,EAAQ,EAAM,EAGlB,CAAC,EAAa,EAAS,EAAM,OAAO,CACrC,EAGD,EAAA,EAAA,eAAgB,CACd,EAAiB,EAAE,EAClB,CAAC,EAAiB,EAAiB,CAAC,CAEvC,IAAM,GAAA,EAAA,EAAA,QAAoC,KAAK,CACzC,GAAA,EAAA,EAAA,QAAyB,EAAS,CAcxC,OAZA,EAAA,EAAA,eAAgB,CAGV,EAAS,SAAW,EAAgB,SAAW,CAAC,GAClD,EAAS,QAAQ,OAAO,CAGtB,IACF,EAAgB,QAAU,KAE3B,CAAC,EAAS,CAAC,EAGZ,EAAA,EAAA,MAAC,MAAD,CAAK,UAAW,uBAAhB,EACE,EAAA,EAAA,KAAC,EAAW,QAAQ,KAAK,KAAzB,CAAA,UACE,EAAA,EAAA,KAAC,EAAW,QAAQ,KAAK,UAAzB,CACE,IAAK,EACL,UAAW,oBACX,KAAM,YACN,QAAS,QACT,KAAM,EAAM,KACZ,MAAO,GAAmB,GAC1B,YAAa,EAAM,YACnB,SAAU,EAAM,SAChB,UAAW,EACX,SAAU,EACV,aAAc,MACd,aAAc,EAAM,aACpB,wBAAuB,EACvB,CAAA,CAC2B,CAAA,CAC9B,EAAM,OAAS,IACd,EAAA,EAAA,KAAC,EAAW,eAAe,KAA3B,CACE,UAAW,oBACX,GAAI,8BAEH,EAAM,KAAK,EAAM,KAChB,EAAA,EAAA,KAAC,EAAW,eAAe,KAA3B,CAEE,WAAA,EAAA,EAAA,iBACE,0BACA,EAAK,OAAS,QAAU,gCAAkC,GAC3D,CACD,GAAI,2BAA2B,IAC/B,WAAY,IAAM,EAClB,QAAS,EAAK,YACR,OACN,CATK,EAAK,MASV,CACF,CAC6B,CAAA,CAE/B,ICrHV,SAAS,EAIP,EAAgE,CAChE,IAAM,EAAO,EAAA,EAAgB,EAAO,CAC9B,EAAK,EAAO,aAAa,EAAY,CAI3C,OAHK,EAGE,CACL,CACE,IAAK,mBACL,MAAO,EAAK,oBAAoB,iBAAiB,MACjD,QAAS,EAAK,oBAAoB,iBAAiB,QACnD,MAAM,EAAA,EAAA,KAAC,EAAA,cAAD,CAAe,KAAM,GAAM,CAAA,CACjC,YAAa,SAAY,CACvB,MAAM,EAAG,SAAS,CAChB,WACE,2MAEF,oBAAqB,EAAA,EAAkB,KAAK,uBAAuB,CACjE,mBAAoB,CAClB,IAAK,GACL,OAAQ,GACR,OAAQ,GACT,CACF,CAAC,CACH,CAAC,EAEJ,KAAM,QACP,CAED,CACE,IAAK,YACL,MAAO,EAAK,oBAAoB,UAAU,MAC1C,QAAS,EAAK,oBAAoB,UAAU,QAC5C,MAAM,EAAA,EAAA,KAAC,EAAA,WAAD,CAAY,KAAM,GAAM,CAAA,CAC9B,YAAa,SAAY,CACvB,MAAM,EAAG,SAAS,CAChB,WAAY,YAEZ,oBAAqB,EAAA,EAAkB,KAAK,uBAAuB,CACjE,mBAAoB,CAClB,IAAK,GACL,OAAQ,GACR,OAAQ,GACT,CACF,CAAC,CACH,CAAC,EAEJ,KAAM,QACP,CACD,CACE,IAAK,eACL,MAAO,EAAK,oBAAoB,iBAAiB,MACjD,QAAS,EAAK,oBAAoB,iBAAiB,QACnD,MAAM,EAAA,EAAA,KAAC,EAAA,aAAD,CAAc,KAAM,GAAM,CAAA,CAChC,YAAa,SAAY,CACvB,MAAM,EAAG,SAAS,CAChB,WAAY,mBAEZ,oBAAqB,EAAA,EAAkB,KAAK,uBAAuB,CACjE,mBAAoB,CAClB,IAAK,GACL,OAAQ,GACR,OAAQ,GACT,CACF,CAAC,CACH,CAAC,EAEJ,KAAM,QACP,CACD,CACE,IAAK,iBACL,MAAO,EAAK,oBAAoB,eAAe,MAC/C,QAAS,EAAK,oBAAoB,eAAe,QACjD,MAAM,EAAA,EAAA,KAAC,EAAA,cAAD,CAAe,KAAM,GAAM,CAAA,CACjC,YAAc,GAAc,CAC1B,EAAU,EAAK,oBAAoB,eAAe,mBAAmB,EAEvE,KAAM,QACP,CACF,CA3EQ,EAAE,CAkFb,SAAS,EAIP,EAAgE,CAChE,IAAM,EAAO,EAAA,EAAgB,EAAO,CAE9B,EAAK,EAAO,aAAa,EAAY,CAK3C,OAJK,EAIE,CACL,CACE,IAAK,kBACL,MAAO,EAAK,oBAAoB,gBAAgB,MAChD,QAAS,EAAK,oBAAoB,gBAAgB,QAClD,MAAM,EAAA,EAAA,KAAC,EAAA,OAAD,CAAQ,KAAM,GAAM,CAAA,CAC1B,YAAa,SAAY,CACvB,MAAM,EAAG,SAAS,CAChB,aAAc,GACd,WAAY,kBAEZ,oBAAqB,EAAA,EAAkB,KAAK,uBAAuB,CACjE,mBAAoB,CAClB,IAAK,GACL,OAAQ,GACR,OAAQ,GACT,CACF,CAAC,CACH,CAAC,EAEJ,KAAM,QACP,CACD,CACE,IAAK,eACL,MAAO,EAAK,oBAAoB,aAAa,MAC7C,QAAS,EAAK,oBAAoB,aAAa,QAC/C,MAAM,EAAA,EAAA,KAAC,EAAA,YAAD,CAAa,KAAM,GAAM,CAAA,CAC/B,YAAa,SAAY,CACvB,MAAM,EAAG,SAAS,CAChB,aAAc,GACd,WAAY,eAEZ,oBAAqB,EAAA,EAAkB,KAAK,uBAAuB,CACjE,mBAAoB,CAClB,IAAK,GACL,OAAQ,GACR,OAAQ,GACT,CACF,CAAC,CACH,CAAC,EAEJ,KAAM,QACP,CACD,CACE,IAAK,YACL,MAAO,EAAK,oBAAoB,UAAU,MAC1C,QAAS,EAAK,oBAAoB,UAAU,QAC5C,MAAM,EAAA,EAAA,KAAC,EAAA,YAAD,CAAa,KAAM,GAAM,CAAA,CAC/B,YAAc,GAAc,CAC1B,EAAU,EAAK,oBAAoB,UAAU,mBAAmB,EAElE,KAAM,QACP,CACD,CACE,IAAK,WACL,MAAO,EAAK,oBAAoB,SAAS,MACzC,QAAS,EAAK,oBAAoB,SAAS,QAC3C,MAAM,EAAA,EAAA,KAAC,EAAA,YAAD,CAAa,KAAM,GAAM,CAAA,CAC/B,YAAa,SAAY,CACvB,MAAM,EAAG,SAAS,CAChB,aAAc,GACd,WAAY,WAEZ,oBAAqB,EAAA,EAAkB,KAAK,uBAAuB,CACjE,mBAAoB,CAClB,IAAK,GACL,OAAQ,GACR,OAAQ,GACT,CACF,CAAC,CACH,CAAC,EAEJ,KAAM,QACP,CACF,CA7EQ,EAAE,CAmFb,SAAS,EAIP,EAAgE,CAChE,IAAM,EAAO,EAAA,EAAgB,EAAO,CAC9B,EAAK,EAAO,aAAa,EAAY,CAK3C,OAJK,EAIE,CACL,CACE,IAAK,SACL,MAAO,EAAK,QAAQ,QAAQ,OAAO,MACnC,QAAS,EAAK,QAAQ,QAAQ,OAAO,QACrC,MAAM,EAAA,EAAA,KAAC,EAAA,YAAD,CAAa,KAAM,GAAM,CAAA,CAC/B,gBAAmB,CACjB,EAAG,eAAe,EAEpB,KAAM,QACP,CACD,CACE,IAAK,SACL,MAAO,EAAK,QAAQ,QAAQ,OAAO,MACnC,QAAS,EAAK,QAAQ,QAAQ,OAAO,QACrC,MAAM,EAAA,EAAA,KAAC,EAAA,kBAAD,CAAmB,KAAM,GAAM,CAAA,CACrC,gBAAmB,CACjB,EAAG,eAAe,EAEpB,KAAM,QACP,CACF,CAxBQ,EAAE,CA8Bb,SAAS,EAIP,EAAgE,CAChE,IAAM,EAAO,EAAA,EAAgB,EAAO,CAC9B,EAAK,EAAO,aAAa,EAAY,CAK3C,OAJK,EAIE,CACL,CACE,IAAK,QACL,MAAO,EAAK,QAAQ,QAAQ,MAAM,MAClC,QAAS,EAAK,QAAQ,QAAQ,MAAM,QACpC,MAAM,EAAA,EAAA,KAAC,EAAA,eAAD,CAAgB,KAAM,GAAM,CAAA,CAClC,YAAa,SAAY,CACvB,MAAM,EAAG,OAAO,EAElB,KAAM,QACP,CACD,CACE,IAAK,SACL,MAAO,EAAK,QAAQ,QAAQ,OAAO,MACnC,QAAS,EAAK,QAAQ,QAAQ,OAAO,QACrC,MAAM,EAAA,EAAA,KAAC,EAAA,kBAAD,CAAmB,KAAM,GAAM,CAAA,CACrC,gBAAmB,CACjB,EAAG,eAAe,EAEpB,KAAM,QACP,CACF,CAxBQ,EAAE,CA2Bb,SAAgB,EACd,EACA,EAOwB,CAUtB,OATE,IAAqB,aAChB,EAAO,cAAc,CACxB,EAAmC,EAAO,CAC1C,EAAsC,EAAO,CACxC,IAAqB,iBACvB,EAA+B,EAAO,CACpC,IAAqB,QACvB,EAA8B,EAAO,CAErC,EAAE,CCvRb,IAAa,EAAU,GAAuB,CAC5C,IAAM,GAAA,EAAA,EAAA,qBAA6B,CAC7B,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAsB,GAAG,CAClC,EAAO,GAAiB,CAExB,GAAA,EAAA,EAAA,uBAAmC,CAEnC,GAAA,EAAA,EAAA,cAAkB,EAAY,CAE9B,GAAA,EAAA,EAAA,mBAAqC,EAAa,CACtD,SAAW,GACT,EAAM,cAAgB,SAAsC,SAA3B,EAAM,YAAY,OACtD,CAAC,CAEI,CAAE,MAAO,GAAkB,EAI3B,GAAA,EAAA,EAAA,aAAsB,CAC1B,IAAI,EAAgC,EAAE,CAQtC,MAPA,CAGE,EAHE,EACM,EAAc,EAAQ,EAAiB,CAEvC,EAAsB,EAAQ,EAAiB,CAIlD,EAAM,IAAK,IACT,CACL,GAAG,EACH,gBAAmB,CACjB,EAAK,YAAY,EAAU,EAE9B,EACD,EACD,CAAC,EAAe,EAAkB,EAAO,CAAC,CAEvC,GAAA,EAAA,EAAA,aACJ,KAAO,IAAuB,CAC5B,MAAM,EAAG,SAAS,CAChB,aACA,aAAc,EAAO,cAAc,GAAK,IAAA,GACzC,CAAC,EAEJ,CAAC,EAAI,EAAO,CACb,EAED,EAAA,EAAA,eAAgB,EAGZ,IAAqB,cACrB,IAAqB,kBACrB,IAAqB,UAErB,EAAU,GAAG,EAEd,CAAC,EAAiB,CAAC,CAEtB,IAAM,GAAA,EAAA,EAAA,aACA,IAAqB,WAChB,EAAK,QAAQ,OAAO,SAClB,IAAqB,aACvB,EAAK,QAAQ,OAAO,QAClB,IAAqB,QACvB,EAAK,QAAQ,OAAO,MAGtB,EAAK,QAAQ,kBACnB,CAAC,EAAkB,EAAK,CAAC,CAEtB,GAAA,EAAA,EAAA,aAA6B,CACjC,GAAI,IAAqB,YAAc,IAAqB,aAC1D,OACE,EAAA,EAAA,KAAC,EAAW,eAAe,OAA3B,CACE,UAAW,sDACX,CAAA,IAEK,IAAqB,QAC9B,OACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAW,wDAGd,EAAA,EAAA,KAAC,MAAD,CACE,MAAM,6BACN,OAAO,MACP,QAAQ,iBACR,MAAM,MACN,KAAK,yBAEL,EAAA,EAAA,KAAC,OAAD,CAAM,EAAE,kfAAof,CAAA,CACxf,CAAA,CACF,CAAA,EAKT,CAAC,EAAY,EAAiB,CAAC,CAElC,OACE,EAAA,EAAA,KAAC,EAAD,CACE,qBACE,EAAM,sBAAwB,EAEzB,QACP,WAAY,EACZ,mBAAoB,EACP,cACb,SACE,IAAqB,YAAc,IAAqB,aAE1D,MACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,6BACb,EAAA,EAAA,KAAC,EAAA,iBAAD,EAAoB,CAAA,CAChB,CAAA,CAEM,eACd,CAAA,ECtIO,EAAoB,GAG3B,CACJ,IAAM,GAAA,EAAA,EAAA,qBAA6B,CAC7B,GAAA,EAAA,EAAA,cAAkB,EAAY,CAE9B,GAAA,EAAA,EAAA,mBAAgC,EAAa,CACjD,SACA,SAAW,GAAU,EAAM,YAC5B,CAAC,CAEI,EAAU,IAAgB,SAAW,IAAA,GAAY,EAAY,QAE7D,GAAA,EAAA,EAAA,cAED,CACC,GAAG,EAAM,kBACT,mBAAoB,CAClB,KAAM,IAAgB,SACtB,UAAW,SACX,WAAY,cACH,GAAG,aACJ,YACD,CACH,MAAM,CAAE,QAAO,YAAY,CACzB,OAAO,OAAO,EAAS,SAAS,MAAO,CACrC,MAAO,GAAG,EAAM,UAAU,MAAM,IACjC,CAAC,EAEL,CAAC,CACH,CACD,aAAe,GAAS,CAClB,GAAQ,IAAgB,WAIxB,EAAY,SAAW,aACzB,EAAG,aAAa,EAEhB,EAAY,SAAW,kBACvB,EAAY,SAAW,UAEvB,EAAG,eAAe,GAGtB,qBAAqB,EAAW,EAAU,EAAQ,CAChD,OAAA,EAAA,EAAA,YAAkB,EAAW,EAAU,EAAQ,CAC7C,eAAgB,GACjB,CAAC,EAEJ,GAAG,EAAM,mBAAmB,mBAC7B,CACD,gBAAiB,CACf,QACE,IAAgB,UAAY,EAAY,SAAW,aAIrD,aAAe,GAAsB,CACnC,GAAI,EAAM,kBAAkB,QAAS,CACnC,IAAM,EAAe,EAAM,OAAO,QAAQ,YAAY,CAEpD,GACA,EAAa,aAAa,UAAU,GAAK,GAEzC,EAAG,aAAa,CAIpB,MAAO,IAET,GAAG,EAAM,mBAAmB,gBAC7B,CACD,aAAc,CACZ,MAAO,CACL,OAAQ,IACT,CACD,GAAG,EAAM,mBAAmB,aAC7B,CAGD,kBAAmB,CACjB,SAAU,GAGV,sBACO,EAAO,WAGL,CAAC,EAAO,WAAW,CAFjB,EAAE,CAIb,GAAG,EAAM,mBAAmB,kBAC7B,CACF,EACH,CAAC,EAAI,EAAa,EAAS,EAAM,kBAAmB,EAAO,WAAW,CACvE,CAEK,EAAY,EAAM,QAAU,EAElC,OACE,EAAA,EAAA,KAAC,EAAA,aAAD,CAAuB,UAAS,GAAI,WACjC,IAAgB,WAAY,EAAA,EAAA,KAAC,EAAD,EAAa,CAAA,CAC7B,CAAA,ECzGN,MAAwB,CACnC,IAAM,EAAO,GAAiB,CACxB,GAAA,EAAA,EAAA,uBAAmC,CAEnC,GAAA,EAAA,EAAA,qBAIH,CAEG,GAAA,EAAA,EAAA,cAAkB,EAAY,CAC9B,GAAA,EAAA,EAAA,cAAiC,EAAA,2BAA2B,CAkBlE,OAJK,EAAO,YAKV,EAAA,EAAA,KAAC,EAAW,QAAQ,QAAQ,OAA5B,CACE,UAAW,YACX,MAAO,EAAK,mBAAmB,GAAG,QAClC,YAAa,EAAK,mBAAmB,GAAG,QACxC,MAAM,EAAA,EAAA,KAAC,EAAA,iBAAD,EAAoB,CAAA,CACjB,YAtBS,CACpB,IAAM,EAAY,EAAO,cAAc,CACvC,GAAI,CAAC,EACH,MAAU,MAAM,eAAe,CAGjC,IAAM,EAAW,EAAU,OAAO,EAAU,OAAO,OAAS,GAAG,GAE/D,EAAG,kBAAkB,EAAS,CAC9B,EAAkB,MAAM,SAAS,GAAM,EAcrC,CAAA,CAVK,MC1BL,EAAQ,CACZ,GAAI,EAAA,iBACL,CAKD,SAAgB,EAId,EAAsE,CACtE,IAAM,EAAK,EAAO,aAAa,EAAY,CAyB3C,OAxBK,EAGS,CACZ,CACE,IAAK,KACL,gBAAmB,CACjB,IAAM,EAAS,EAAO,uBAAuB,CAE3C,EAAO,MAAM,SACb,MAAM,QAAQ,EAAO,MAAM,QAAQ,EACnC,EAAO,MAAM,QAAQ,SAAW,GAChC,EAAO,UAEP,EAAG,kBAAkB,EAAO,UAAU,GAAG,CAEzC,EAAG,kBAAkB,EAAO,MAAM,GAAG,EAGzC,GAAG,EAAA,EAAgB,EAAO,CAAC,WAAW,GACtC,MAAM,EAAA,EAAA,KAAC,EAAM,GAAP,EAAY,CAAA,CACnB,CACF,CArBQ,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"blocknote-xl-ai.js","names":[],"sources":["../src/plugins/AgentCursorPlugin.ts","../src/AIExtension.ts","../src/hooks/useAIDictionary.ts","../src/components/AIMenu/PromptSuggestionMenu.tsx","../src/components/AIMenu/getDefaultAIMenuItems.tsx","../src/components/AIMenu/AIMenu.tsx","../src/components/AIMenu/AIMenuController.tsx","../src/components/FormattingToolbar/AIToolbarButton.tsx","../src/components/SuggestionMenu/getAISlashMenuItems.tsx"],"sourcesContent":["import { Plugin, PluginKey } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport { defaultSelectionBuilder } from \"y-prosemirror\";\n\ntype AgentCursorState = {\n selection: { anchor: number; head: number } | undefined;\n};\nconst PLUGIN_KEY = new PluginKey<AgentCursorState>(`blocknote-agent-cursor`);\n\n/**\n * Plugin that renders an \"AI\" cursor\n */\nexport function createAgentCursorPlugin(agentCursor: {\n name: string;\n color: string;\n}) {\n return new Plugin<AgentCursorState>({\n key: PLUGIN_KEY,\n view: (_view) => {\n return {};\n },\n state: {\n init: () => {\n return {\n selection: undefined,\n };\n },\n apply: (tr, _oldState) => {\n const meta = tr.getMeta(\"aiAgent\");\n\n if (!meta) {\n return {\n selection: undefined,\n };\n }\n\n return {\n selection: meta.selection,\n };\n },\n },\n props: {\n decorations: (state) => {\n const { doc } = state;\n\n const { selection } = PLUGIN_KEY.getState(state)!;\n\n const decs = [];\n\n if (!selection) {\n return DecorationSet.create(doc, []);\n }\n\n decs.push(\n Decoration.widget(selection.head, () => renderCursor(agentCursor), {\n key: \"agent-cursor\",\n side: 10,\n }),\n );\n\n const from = Math.min(selection.anchor, selection.head);\n const to = Math.max(selection.anchor, selection.head);\n\n decs.push(\n Decoration.inline(from, to, defaultSelectionBuilder(agentCursor), {\n inclusiveEnd: true,\n inclusiveStart: false,\n }),\n );\n\n return DecorationSet.create(doc, decs);\n },\n },\n });\n}\n\nconst renderCursor = (user: { name: string; color: string }) => {\n const cursorElement = document.createElement(\"span\");\n\n cursorElement.classList.add(\"bn-collaboration-cursor__base\");\n cursorElement.setAttribute(\"data-active\", \"true\");\n\n const caretElement = document.createElement(\"span\");\n caretElement.setAttribute(\"contentedEditable\", \"false\");\n caretElement.classList.add(\"bn-collaboration-cursor__caret\");\n caretElement.setAttribute(\"style\", `background-color: ${user.color}`);\n\n const labelElement = document.createElement(\"span\");\n\n labelElement.classList.add(\"bn-collaboration-cursor__label\");\n labelElement.setAttribute(\"style\", `background-color: ${user.color}`);\n labelElement.insertBefore(document.createTextNode(user.name), null);\n\n caretElement.insertBefore(labelElement, null);\n\n cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n cursorElement.insertBefore(caretElement, null);\n cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n\n return cursorElement;\n};\n","import { Chat } from \"@ai-sdk/react\";\nimport {\n createExtension,\n createStore,\n ExtensionOptions,\n getNodeById,\n UnreachableCaseError,\n} from \"@blocknote/core\";\nimport {\n ForkYDocExtension,\n ShowSelectionExtension,\n} from \"@blocknote/core/extensions\";\nimport {\n applySuggestions,\n revertSuggestions,\n suggestChanges,\n} from \"@handlewithcare/prosemirror-suggest-changes\";\nimport { UIMessage } from \"ai\";\nimport { Fragment, Slice } from \"prosemirror-model\";\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport { fixTablesKey } from \"prosemirror-tables\";\nimport { buildAIRequest, sendMessageWithAIRequest } from \"./api/index.js\";\nimport { createAgentCursorPlugin } from \"./plugins/AgentCursorPlugin.js\";\nimport { AIRequestHelpers, InvokeAIOptions } from \"./types.js\";\n\ntype AIPluginState = {\n aiMenuState:\n | ({\n /**\n * The ID of the block that the AI menu is opened at.\n * This changes as the AI is making changes to the document\n */\n blockId: string;\n } & (\n | {\n status: \"error\";\n error: any;\n }\n | {\n // fix: it might be nice to derive this from the Chat status and Tool call status\n status: \"user-input\" | \"thinking\" | \"ai-writing\" | \"user-reviewing\";\n }\n ))\n | \"closed\";\n};\n\nconst PLUGIN_KEY = new PluginKey(`blocknote-ai-plugin`);\n\nexport const AIExtension = createExtension(\n ({\n editor,\n options: editorOptions,\n }: ExtensionOptions<\n | (AIRequestHelpers & {\n /**\n * The name and color of the agent cursor\n *\n * @default { name: \"AI\", color: \"#8bc6ff\" }\n */\n agentCursor?: { name: string; color: string };\n })\n | undefined\n >) => {\n // TODO should we really expose it like this?\n const options = createStore<AIRequestHelpers>(editorOptions ?? {});\n const store = createStore<AIPluginState>({\n aiMenuState: \"closed\",\n });\n let chatSession:\n | {\n previousRequestOptions: InvokeAIOptions;\n chat: Chat<UIMessage>;\n abortController: AbortController;\n }\n | undefined;\n let autoScroll = false;\n\n const suggestChangesPlugin = suggestChanges();\n // disable decorations for suggest changes, not needed\n // (and the pilcrows are ugly)\n suggestChangesPlugin.props.decorations = undefined;\n return {\n key: \"ai\",\n options,\n store,\n mount({ signal }: { signal: AbortSignal }) {\n let scrollInProgress = false;\n // Listens for `scroll` and `scrollend` events to see if a new scroll was\n // started before an existing one ended. This is the most reliable way we\n // have of checking if a scroll event was caused by the user and not by\n // `scrollIntoView`, as the events are otherwise indistinguishable. If a\n // scroll was started before an existing one finished (meaning the user has\n // scrolled), auto scrolling is disabled.\n document.addEventListener(\n \"scroll\",\n () => {\n if (scrollInProgress) {\n autoScroll = false;\n }\n\n scrollInProgress = true;\n },\n {\n capture: true,\n signal,\n },\n );\n document.addEventListener(\n \"scrollend\",\n () => {\n scrollInProgress = false;\n },\n {\n capture: true,\n signal,\n },\n );\n },\n prosemirrorPlugins: [\n new Plugin({\n key: PLUGIN_KEY,\n filterTransaction: (tr) => {\n const menuState = store.state.aiMenuState;\n\n if (menuState !== \"closed\" && menuState.status === \"ai-writing\") {\n if (tr.getMeta(fixTablesKey)?.fixTables) {\n // the fixtables plugin causes the steps between of the AI Agent to become invalid\n // so we need to prevent it from running\n // (we might need to filter out other / or maybe any transactions during the writing phase)\n return false;\n }\n }\n return true;\n },\n }),\n suggestChangesPlugin,\n createAgentCursorPlugin(\n editorOptions?.agentCursor || { name: \"AI\", color: \"#8bc6ff\" },\n ),\n ],\n\n /**\n * Open the AI menu at a specific block\n */\n openAIMenuAtBlock(blockID: string) {\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(true, \"aiMenu\");\n editor.isEditable = false;\n store.setState({\n aiMenuState: {\n blockId: blockID,\n status: \"user-input\",\n },\n });\n\n // Scrolls to the block when the menu opens.\n const blockElement = editor.domElement?.querySelector(\n `[data-node-type=\"blockContainer\"][data-id=\"${blockID}\"]`,\n );\n blockElement?.scrollIntoView({ block: \"center\" });\n },\n\n /**\n * Close the AI menu\n */\n closeAIMenu() {\n store.setState({\n aiMenuState: \"closed\",\n });\n chatSession = undefined;\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(false, \"aiMenu\");\n editor.isEditable = true;\n editor.focus();\n },\n\n /**\n * Accept the changes made by the LLM\n */\n acceptChanges() {\n // This is slightly convoluted, to try to maintain the undo history as much as possible\n // The idea is that the LLM call has appended a number of updates to the document, moving the document from state `A` to state `C`\n // But we want the undo history to skip all of the intermediate states and go straight from `C` to `A`\n // To do this, we capture the document state `C` (post-LLM call), and then reject the suggestions to recover the original document state `A`\n // Then we create an intermediate state `B` that captures the diff between `A` and `C`\n // Then we apply the suggestions to `B` to get the final state `C`\n // This causes the undo history to skip `B` and go straight from `C` back to `A`\n\n // Capture the document state `C'` (post-LLM call with all suggestions still in the document)\n const markedUpDocument = editor.prosemirrorState.doc;\n\n // revert the suggestions to get back to the original document state `A`\n editor.exec((state, dispatch) => {\n return revertSuggestions(state, (tr) => {\n dispatch?.(tr.setMeta(\"addToHistory\", false));\n });\n });\n\n // Create an intermediate state `B` that captures the diff between the original document and the marked up document\n editor.exec((state, dispatch) => {\n const tr = state.tr;\n tr.replace(\n 0,\n tr.doc.content.size,\n new Slice(Fragment.from(markedUpDocument), 0, 0),\n );\n const nextState = state.apply(tr);\n // Apply the suggestions to the intermediate state `B` to get the final state `C`\n return applySuggestions(nextState, (resultTr) => {\n dispatch?.(\n tr.replace(\n 0,\n tr.doc.content.size,\n new Slice(Fragment.from(resultTr.doc), 0, 0),\n ),\n );\n });\n });\n\n // If in collaboration mode, merge the changes back into the original yDoc\n editor.getExtension(ForkYDocExtension)?.merge({ keepChanges: true });\n\n this.closeAIMenu();\n },\n\n /**\n * Reject the changes made by the LLM\n */\n rejectChanges() {\n // Revert the suggestions to get back to the original document\n editor.exec((state, dispatch) => {\n return revertSuggestions(state, (tr) => {\n // Do so without adding to history (so the last undo step is just prior to the LLM call)\n dispatch?.(tr.setMeta(\"addToHistory\", false));\n });\n });\n\n // If in collaboration mode, discard the changes and revert to the original yDoc\n editor.getExtension(ForkYDocExtension)?.merge({ keepChanges: false });\n this.closeAIMenu();\n },\n\n /**\n * Abort the current LLM request.\n *\n * This will stop the ongoing request and revert any changes made by the AI.\n * Only valid when there is an active AI request in progress.\n */\n async abort(reason?: any) {\n const { aiMenuState } = store.state;\n if (aiMenuState === \"closed\" || !chatSession) {\n return;\n }\n\n // Only abort if the request is in progress (thinking or ai-writing)\n if (\n aiMenuState.status !== \"thinking\" &&\n aiMenuState.status !== \"ai-writing\"\n ) {\n return;\n }\n\n const chat = chatSession.chat;\n const abortController = chatSession.abortController;\n\n // Abort the tool call operations\n abortController.abort(reason);\n\n // Stop the chat request\n await chat.stop();\n },\n\n /**\n * Retry the previous LLM call.\n *\n * Only valid if the current status is \"error\"\n */\n async retry() {\n const { aiMenuState } = store.state;\n if (\n aiMenuState === \"closed\" ||\n aiMenuState.status !== \"error\" ||\n !chatSession\n ) {\n throw new Error(\n \"retry() is only valid when a previous response failed\",\n );\n }\n\n /*\n Design decisions:\n - we cannot use chat.regenerate() because the document might have been updated already by toolcalls that failed mid-way\n - we also cannot revert the document changes and use chat.regenerate(), \n because we might want to retry a subsequent user-direction that failed, not the original one\n - this means we always need to send the new document state to the LLM\n - an alternative would be to take a snapshot of the document before the LLM call, and revert to that specific state\n when a call fails\n */\n\n if (chatSession?.chat.status === \"error\") {\n // the LLM call failed (i.e. a network error)\n // console.log(\"retry failed LLM call\", this.chatSession.chat.error);\n return this.invokeAI({\n ...chatSession.previousRequestOptions,\n userPrompt: `An error occured in the previous request. Please retry to accomplish the last user prompt.`,\n });\n } else {\n // an error occurred while parsing / executing the previous LLM call\n // give the LLM a chance to fix the error\n // console.log(\"retry failed tool execution\");\n return this.invokeAI({\n ...chatSession.previousRequestOptions,\n userPrompt: `An error occured while executing the previous tool call. Please retry to accomplish the last user prompt.`,\n });\n }\n },\n\n /**\n * Update the status of a call to an LLM\n *\n * @warning This method should usually only be used for advanced use-cases\n * if you want to implement how an LLM call is executed. Usually, you should\n * use {@link executeLLMRequest} instead which will handle the status updates for you.\n */\n setAIResponseStatus(\n status:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"user-reviewing\"\n | {\n status: \"error\";\n error: any;\n },\n ) {\n const aiMenuState = store.state.aiMenuState;\n if (aiMenuState === \"closed\") {\n return;\n }\n\n if (status === \"ai-writing\") {\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(false, \"aiMenu\");\n }\n\n if (typeof status === \"object\") {\n if (status.status !== \"error\") {\n throw new UnreachableCaseError(status.status);\n }\n this.store.setState({\n aiMenuState: {\n status: status.status,\n error: status.error,\n blockId: aiMenuState.blockId,\n },\n });\n } else {\n this.store.setState({\n aiMenuState: {\n status: status,\n blockId: aiMenuState.blockId,\n },\n });\n }\n },\n\n /**\n * @deprecated Use {@link invokeAI} instead\n */\n async callLLM(opts: InvokeAIOptions) {\n return this.invokeAI(opts);\n },\n\n /**\n * Execute a call to an LLM and apply the result to the editor\n */\n async invokeAI(opts: InvokeAIOptions) {\n this.setAIResponseStatus(\"thinking\");\n editor.getExtension(ForkYDocExtension)?.fork();\n\n try {\n // Create a new AbortController for this request\n const abortController = new AbortController();\n\n if (!chatSession) {\n // note: in the current implementation opts.transport is only used when creating a new chat\n // (so changing transport for a subsequent call in the same chat-session is not supported)\n chatSession = {\n previousRequestOptions: opts,\n chat:\n opts.chatProvider?.() ||\n this.options.state.chatProvider?.() ||\n new Chat<UIMessage>({\n sendAutomaticallyWhen: () => false,\n transport: opts.transport || this.options.state.transport,\n }),\n abortController,\n };\n } else {\n chatSession.previousRequestOptions = opts;\n chatSession.abortController = abortController;\n }\n const chat = chatSession.chat;\n\n // merge the global options with the local options\n const globalOpts = options.state;\n opts = {\n ...globalOpts,\n ...opts,\n } as InvokeAIOptions;\n\n const aiRequest = await buildAIRequest({\n editor,\n useSelection: opts.useSelection,\n deleteEmptyCursorBlock: opts.deleteEmptyCursorBlock,\n streamToolsProvider:\n opts.streamToolsProvider ??\n this.options.state.streamToolsProvider,\n documentStateBuilder:\n opts.documentStateBuilder ??\n this.options.state.documentStateBuilder,\n onBlockUpdated: (blockId) => {\n const aiMenuState = store.state.aiMenuState;\n const aiMenuOpenState =\n aiMenuState === \"closed\" ? undefined : aiMenuState;\n if (!aiMenuOpenState || aiMenuOpenState.status !== \"ai-writing\") {\n return;\n }\n\n // TODO: Sometimes, the updated block doesn't actually exist in\n // the editor. I don't know why this happens, seems like a bug?\n const nodeInfo = getNodeById(\n blockId,\n editor.prosemirrorState.doc,\n );\n if (!nodeInfo) {\n return;\n }\n\n // NOTE: does this setState with an anon object trigger unnecessary re-renders?\n store.setState({\n aiMenuState: {\n blockId,\n status: \"ai-writing\",\n },\n });\n\n if (autoScroll) {\n const blockElement = editor.prosemirrorView.domAtPos(\n nodeInfo.posBeforeNode + 1,\n );\n (blockElement.node as HTMLElement).scrollIntoView({\n block: \"center\",\n });\n }\n },\n onStart: () => {\n autoScroll = true;\n this.setAIResponseStatus(\"ai-writing\");\n\n if (\n aiRequest.emptyCursorBlockToDelete &&\n aiRequest.editor.getBlock(aiRequest.emptyCursorBlockToDelete)\n ) {\n aiRequest.editor.removeBlocks([\n aiRequest.emptyCursorBlockToDelete,\n ]);\n }\n },\n });\n\n const result = await sendMessageWithAIRequest(\n chat,\n aiRequest,\n {\n role: \"user\",\n parts: [\n {\n type: \"text\",\n text: opts.userPrompt,\n },\n ],\n },\n opts.chatRequestOptions || this.options.state.chatRequestOptions,\n chatSession.abortController.signal,\n );\n\n if (\n (result.ok && chat.status !== \"error\") ||\n abortController.signal.aborted\n ) {\n this.setAIResponseStatus(\"user-reviewing\");\n } else {\n // eslint-disable-next-line no-console\n console.warn(\"Error calling LLM\", {\n result,\n chatStatus: chat.status,\n chatError: chat.error,\n });\n this.setAIResponseStatus({\n status: \"error\",\n error: result.ok ? chat.error : result.error,\n });\n }\n } catch (e) {\n this.setAIResponseStatus({\n status: \"error\",\n error: e,\n });\n // eslint-disable-next-line no-console\n console.error(\n \"Unexpected error calling LLM\",\n e,\n chatSession?.chat.messages,\n );\n }\n },\n };\n },\n);\n","import { useBlockNoteContext } from \"@blocknote/react\";\n\nimport { getAIDictionary } from \"../i18n/dictionary.js\";\n\nexport function useAIDictionary() {\n const ctx = useBlockNoteContext();\n return getAIDictionary(ctx!.editor!);\n}\n","import { mergeCSSClasses } from \"@blocknote/core\";\nimport { filterSuggestionItems } from \"@blocknote/core/extensions\";\nimport {\n DefaultReactSuggestionItem,\n useComponentsContext,\n useSuggestionMenuKeyboardHandler,\n} from \"@blocknote/react\";\nimport {\n ChangeEvent,\n KeyboardEvent,\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport type PromptSuggestionMenuProps = {\n items: DefaultReactSuggestionItem[];\n onManualPromptSubmit: (userPrompt: string) => void;\n promptText?: string;\n onPromptTextChange?: (userPrompt: string) => void;\n icon?: ReactNode;\n rightSection?: ReactNode;\n placeholder?: string;\n disabled?: boolean;\n};\n\nexport const PromptSuggestionMenu = (props: PromptSuggestionMenuProps) => {\n // const dict = useAIDictionary();\n const Components = useComponentsContext()!;\n\n const { onManualPromptSubmit, promptText, onPromptTextChange, disabled } =\n props;\n\n // Only used internal state when `props.prompText` is undefined (i.e., uncontrolled mode)\n const [internalPromptText, setInternalPromptText] = useState<string>(\"\");\n const promptTextToUse = promptText || internalPromptText;\n\n const handleEnter = useCallback(\n async (event: KeyboardEvent) => {\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing) {\n // console.log(\"ENTER\", currentEditingPrompt);\n onManualPromptSubmit(promptTextToUse);\n }\n },\n [promptTextToUse, onManualPromptSubmit],\n );\n\n const handleChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const newValue = event.currentTarget.value;\n if (onPromptTextChange) {\n onPromptTextChange(newValue);\n }\n\n // Only update internal state if it's uncontrolled\n if (promptText === undefined) {\n setInternalPromptText(newValue);\n }\n },\n [onPromptTextChange, setInternalPromptText, promptText],\n );\n\n const items: DefaultReactSuggestionItem[] = useMemo(() => {\n return filterSuggestionItems(props.items, promptTextToUse);\n }, [promptTextToUse, props.items]);\n\n const { selectedIndex, setSelectedIndex, handler } =\n useSuggestionMenuKeyboardHandler(items, (item) => item.onItemClick());\n\n const activeDescendantId =\n items.length > 0 && selectedIndex >= 0 && selectedIndex < items.length\n ? `bn-suggestion-menu-item-${selectedIndex}`\n : undefined;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n // TODO: handle backspace to close\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing) {\n if (items.length > 0) {\n handler(event);\n } else {\n // TODO: check focus?\n handleEnter(event);\n }\n } else {\n handler(event);\n }\n },\n [handleEnter, handler, items.length],\n );\n\n // Resets index when items change\n useEffect(() => {\n setSelectedIndex(0);\n }, [promptTextToUse, setSelectedIndex]);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const hasBeenDisabled = useRef(disabled);\n\n useEffect(() => {\n // This effect is used so that after the input has been disabled (for example, when AI results are loaded),\n // the input is focused again.\n if (inputRef.current && hasBeenDisabled.current && !disabled) {\n inputRef.current.focus();\n }\n\n if (disabled) {\n hasBeenDisabled.current = true;\n }\n }, [disabled]);\n\n return (\n <div className={\"bn-combobox\"}>\n <Components.Generic.Form.Root>\n <Components.Generic.Form.TextInput\n ref={inputRef}\n className={\"bn-combobox-input\"}\n name={\"ai-prompt\"}\n variant={\"large\"}\n icon={props.icon}\n value={promptTextToUse || \"\"}\n placeholder={props.placeholder}\n disabled={props.disabled}\n onKeyDown={handleKeyDown}\n onChange={handleChange}\n autoComplete={\"off\"}\n rightSection={props.rightSection}\n aria-activedescendant={activeDescendantId}\n />\n </Components.Generic.Form.Root>\n {items.length > 0 && (\n <Components.SuggestionMenu.Root\n className={\"bn-combobox-items\"}\n id={\"ai-suggestion-menu\"}\n >\n {items.map((item, i) => (\n <Components.SuggestionMenu.Item\n key={item.title}\n className={mergeCSSClasses(\n \"bn-suggestion-menu-item\",\n item.size === \"small\" ? \"bn-suggestion-menu-item-small\" : \"\",\n )}\n id={`bn-suggestion-menu-item-${i}`}\n isSelected={i === selectedIndex}\n onClick={item.onItemClick}\n item={item}\n />\n ))}\n </Components.SuggestionMenu.Root>\n )}\n </div>\n );\n};\n","import {\n BlockNoteEditor,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"@blocknote/core\";\nimport { DefaultReactSuggestionItem } from \"@blocknote/react\";\nimport {\n RiArrowGoBackFill,\n RiBallPenLine,\n RiCheckFill,\n RiCheckLine,\n RiEarthLine,\n RiListCheck3,\n RiLoopLeftFill,\n RiMagicLine,\n RiText,\n RiTextWrap,\n} from \"react-icons/ri\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { aiDocumentFormats } from \"../../api/index.js\";\nimport { getAIDictionary } from \"../../i18n/dictionary.js\";\n\nexport type AIMenuSuggestionItem = Omit<\n DefaultReactSuggestionItem,\n \"onItemClick\"\n> & {\n onItemClick: (setPrompt: (userPrompt: string) => void) => void;\n key: string;\n};\n\n/**\n * Default items we show in the AI Menu when there is no selection active.\n * For example, when the AI menu is triggered via the slash menu\n */\nfunction getDefaultAIMenuItemsWithoutSelection<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n return [\n {\n key: \"continue_writing\",\n title: dict.ai_default_commands.continue_writing.title,\n aliases: dict.ai_default_commands.continue_writing.aliases,\n icon: <RiBallPenLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt:\n \"Continue writing at the current cursor position related to the previous text. Add multiple blocks if needed. If the document looks like a template / draft, follow the template. Be extensive if needed.\",\n // By default, LLM will be able to add / update / delete blocks. For \"continue writing\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n\n {\n key: \"summarize\",\n title: dict.ai_default_commands.summarize.title,\n aliases: dict.ai_default_commands.summarize.aliases,\n icon: <RiTextWrap size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt: \"Summarize\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"action_items\",\n title: dict.ai_default_commands.add_action_items.title,\n aliases: dict.ai_default_commands.add_action_items.aliases,\n icon: <RiListCheck3 size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt: \"Add action items\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"write_anything\",\n title: dict.ai_default_commands.write_anything.title,\n aliases: dict.ai_default_commands.write_anything.aliases,\n icon: <RiBallPenLine size={18} />,\n onItemClick: (setPrompt) => {\n setPrompt(dict.ai_default_commands.write_anything.prompt_placeholder);\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when there is a selection active.\n * For example, when the AI menu is triggered via formatting toolbar (AIToolbarButton)\n */\nfunction getDefaultAIMenuItemsWithSelection<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"improve_writing\",\n title: dict.ai_default_commands.improve_writing.title,\n aliases: dict.ai_default_commands.improve_writing.aliases,\n icon: <RiText size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Improve writing\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"fix_spelling\",\n title: dict.ai_default_commands.fix_spelling.title,\n aliases: dict.ai_default_commands.fix_spelling.aliases,\n icon: <RiCheckLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Fix spelling\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"translate\",\n title: dict.ai_default_commands.translate.title,\n aliases: dict.ai_default_commands.translate.aliases,\n icon: <RiEarthLine size={18} />,\n onItemClick: (setPrompt) => {\n setPrompt(dict.ai_default_commands.translate.prompt_placeholder);\n },\n size: \"small\",\n },\n {\n key: \"simplify\",\n title: dict.ai_default_commands.simplify.title,\n aliases: dict.ai_default_commands.simplify.aliases,\n icon: <RiMagicLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Simplify\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when the AI response is done.\n */\nfunction getDefaultAIMenuItemsForReview<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"accept\",\n title: dict.ai_menu.actions.accept.title,\n aliases: dict.ai_menu.actions.accept.aliases,\n icon: <RiCheckFill size={18} />,\n onItemClick: () => {\n ai.acceptChanges();\n },\n size: \"small\",\n },\n {\n key: \"revert\",\n title: dict.ai_menu.actions.revert.title,\n aliases: dict.ai_menu.actions.revert.aliases,\n icon: <RiArrowGoBackFill size={18} />,\n onItemClick: () => {\n ai.rejectChanges();\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when the AI response is done.\n */\nfunction getDefaultAIMenuItemsForError<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"retry\",\n title: dict.ai_menu.actions.retry.title,\n aliases: dict.ai_menu.actions.retry.aliases,\n icon: <RiLoopLeftFill size={18} />,\n onItemClick: async () => {\n await ai.retry();\n },\n size: \"small\",\n },\n {\n key: \"cancel\",\n title: dict.ai_menu.actions.cancel.title,\n aliases: dict.ai_menu.actions.cancel.aliases,\n icon: <RiArrowGoBackFill size={18} />,\n onItemClick: () => {\n ai.rejectChanges();\n },\n size: \"small\",\n },\n ];\n}\n\nexport function getDefaultAIMenuItems(\n editor: BlockNoteEditor<any, any, any>,\n aiResponseStatus:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"error\"\n | \"user-reviewing\"\n | \"closed\",\n): AIMenuSuggestionItem[] {\n if (aiResponseStatus === \"user-input\") {\n return editor.getSelection()\n ? getDefaultAIMenuItemsWithSelection(editor)\n : getDefaultAIMenuItemsWithoutSelection(editor);\n } else if (aiResponseStatus === \"user-reviewing\") {\n return getDefaultAIMenuItemsForReview(editor);\n } else if (aiResponseStatus === \"error\") {\n return getDefaultAIMenuItemsForError(editor);\n } else {\n return [];\n }\n}\n","import { BlockNoteEditor } from \"@blocknote/core\";\nimport {\n useBlockNoteEditor,\n useComponentsContext,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { useAIDictionary } from \"../../hooks/useAIDictionary.js\";\nimport { PromptSuggestionMenu } from \"./PromptSuggestionMenu.js\";\nimport {\n AIMenuSuggestionItem,\n getDefaultAIMenuItems,\n} from \"./getDefaultAIMenuItems.js\";\n\nexport type AIMenuProps = {\n items?: (\n editor: BlockNoteEditor<any, any, any>,\n aiResponseStatus:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"error\"\n | \"user-reviewing\"\n | \"closed\",\n ) => AIMenuSuggestionItem[];\n onManualPromptSubmit?: (userPrompt: string) => void;\n};\n\nexport const AIMenu = (props: AIMenuProps) => {\n const editor = useBlockNoteEditor();\n const [prompt, setPrompt] = useState(\"\");\n const dict = useAIDictionary();\n\n const Components = useComponentsContext()!;\n\n const ai = useExtension(AIExtension);\n\n const aiResponseStatus = useExtensionState(AIExtension, {\n selector: (state) =>\n state.aiMenuState !== \"closed\" ? state.aiMenuState.status : \"closed\",\n });\n\n const { items: externalItems } = props;\n // note, technically there might be a bug with this useMemo when quickly changing the selection and opening the menu\n // would not call getDefaultAIMenuItems with the correct selection, because the component is reused and the memo not retriggered\n // practically this should not happen (you can test it by using a high transition duration in useUIElementPositioning)\n const items = useMemo(() => {\n let items: AIMenuSuggestionItem[] = [];\n if (externalItems) {\n items = externalItems(editor, aiResponseStatus);\n } else {\n items = getDefaultAIMenuItems(editor, aiResponseStatus);\n }\n\n // map from AI items to React Items required by PromptSuggestionMenu\n return items.map((item) => {\n return {\n ...item,\n onItemClick: () => {\n item.onItemClick(setPrompt);\n },\n };\n });\n }, [externalItems, aiResponseStatus, editor]);\n\n const onManualPromptSubmitDefault = useCallback(\n async (userPrompt: string) => {\n await ai.invokeAI({\n userPrompt,\n useSelection: editor.getSelection() !== undefined,\n });\n },\n [ai, editor],\n );\n\n useEffect(() => {\n // this is a bit hacky to run a useeffect to reset the prompt when the AI response is done\n if (\n aiResponseStatus === \"ai-writing\" ||\n aiResponseStatus === \"user-reviewing\" ||\n aiResponseStatus === \"error\"\n ) {\n setPrompt(\"\");\n }\n }, [aiResponseStatus]);\n\n const placeholder = useMemo(() => {\n if (aiResponseStatus === \"thinking\") {\n return dict.ai_menu.status.thinking;\n } else if (aiResponseStatus === \"ai-writing\") {\n return dict.ai_menu.status.editing;\n } else if (aiResponseStatus === \"error\") {\n return dict.ai_menu.status.error;\n }\n\n return dict.ai_menu.input_placeholder;\n }, [aiResponseStatus, dict]);\n\n const rightSection = useMemo(() => {\n if (aiResponseStatus === \"thinking\" || aiResponseStatus === \"ai-writing\") {\n return (\n <Components.SuggestionMenu.Loader\n className={\"bn-suggestion-menu-loader bn-combobox-right-section\"}\n />\n );\n } else if (aiResponseStatus === \"error\") {\n return (\n <div className={\"bn-combobox-right-section bn-combobox-error\"}>\n {/* Taken from Google Material Icons */}\n {/* https://fonts.google.com/icons?selected=Material+Symbols+Rounded:error:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=error&icon.size=24&icon.color=%23e8eaed&icon.set=Material+Symbols&icon.style=Rounded&icon.platform=web */}\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height=\"1em\"\n viewBox=\"0 -960 960 960\"\n width=\"1em\"\n fill=\"currentColor\"\n >\n <path d=\"M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm0-160q17 0 28.5-11.5T520-480v-160q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640v160q0 17 11.5 28.5T480-440Zm0 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z\" />\n </svg>\n </div>\n );\n }\n\n return undefined;\n }, [Components, aiResponseStatus]);\n\n return (\n <PromptSuggestionMenu\n onManualPromptSubmit={\n props.onManualPromptSubmit || onManualPromptSubmitDefault\n }\n items={items}\n promptText={prompt}\n onPromptTextChange={setPrompt}\n placeholder={placeholder}\n disabled={\n aiResponseStatus === \"thinking\" || aiResponseStatus === \"ai-writing\"\n }\n icon={\n <div className=\"bn-combobox-icon\">\n <RiSparkling2Fill />\n </div>\n }\n rightSection={rightSection}\n />\n );\n};\n","import {\n BlockPopover,\n FloatingUIOptions,\n useBlockNoteEditor,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { autoUpdate, flip, offset, size } from \"@floating-ui/react\";\nimport { FC, useMemo } from \"react\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { AIMenu, AIMenuProps } from \"./AIMenu.js\";\n\nexport const AIMenuController = (props: {\n aiMenu?: FC<AIMenuProps>;\n floatingUIOptions?: FloatingUIOptions;\n}) => {\n const editor = useBlockNoteEditor();\n const ai = useExtension(AIExtension);\n\n const aiMenuState = useExtensionState(AIExtension, {\n editor,\n selector: (state) => state.aiMenuState,\n });\n\n const blockId = aiMenuState === \"closed\" ? undefined : aiMenuState.blockId;\n\n const floatingUIOptions = useMemo<FloatingUIOptions>(\n () =>\n ({\n ...props.floatingUIOptions,\n useFloatingOptions: {\n open: aiMenuState !== \"closed\",\n placement: \"bottom\",\n middleware: [\n offset(10),\n flip(),\n size({\n apply({ rects, elements }) {\n Object.assign(elements.floating.style, {\n width: `${rects.reference.width}px`,\n });\n },\n }),\n ],\n onOpenChange: (open) => {\n if (open || aiMenuState === \"closed\") {\n return;\n }\n\n if (aiMenuState.status === \"user-input\") {\n ai.closeAIMenu();\n } else if (\n aiMenuState.status === \"user-reviewing\" ||\n aiMenuState.status === \"error\"\n ) {\n ai.rejectChanges();\n }\n },\n whileElementsMounted(reference, floating, update) {\n return autoUpdate(reference, floating, update, {\n animationFrame: true,\n });\n },\n ...props.floatingUIOptions?.useFloatingOptions,\n },\n useDismissProps: {\n enabled:\n aiMenuState === \"closed\" || aiMenuState.status === \"user-input\",\n // We should just be able to set `referencePress: true` instead of\n // using this listener, but this doesn't seem to trigger.\n // (probably because we don't assign the referenceProps to the reference element)\n outsidePress: (event) => {\n if (event.target instanceof Element) {\n const blockElement = event.target.closest(\".bn-block\");\n if (\n blockElement &&\n blockElement.getAttribute(\"data-id\") === blockId\n ) {\n ai.closeAIMenu();\n }\n }\n\n return true;\n },\n ...props.floatingUIOptions?.useDismissProps,\n },\n elementProps: {\n style: {\n zIndex: 100,\n },\n ...props.floatingUIOptions?.elementProps,\n },\n // we use the focus manager instead of `autoFocus={true}` to prevent \"page-scrolls-to-top-when-opening-the-floating-element\"\n // see https://floating-ui.com/docs/floatingfocusmanager#page-scrolls-to-top-when-opening-the-floating-element\n focusManagerProps: {\n disabled: false,\n // needed for https://github.com/floating-ui/floating-ui/pull/3202\n // (related: https://github.com/mui/base-ui/issues/3950)\n getInsideElements: () => {\n if (!editor.domElement) {\n return [];\n }\n return [editor.domElement];\n },\n ...props.floatingUIOptions?.focusManagerProps,\n },\n }) satisfies FloatingUIOptions,\n [ai, aiMenuState, blockId, props.floatingUIOptions, editor.domElement],\n );\n\n const Component = props.aiMenu || AIMenu;\n\n return (\n <BlockPopover blockId={blockId} {...floatingUIOptions}>\n {aiMenuState !== \"closed\" && <Component />}\n </BlockPopover>\n );\n};\n","import { BlockSchema, InlineContentSchema, StyleSchema } from \"@blocknote/core\";\nimport { FormattingToolbarExtension } from \"@blocknote/core/extensions\";\nimport {\n useBlockNoteEditor,\n useComponentsContext,\n useExtension,\n} from \"@blocknote/react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { useAIDictionary } from \"../../hooks/useAIDictionary.js\";\n\nexport const AIToolbarButton = () => {\n const dict = useAIDictionary();\n const Components = useComponentsContext()!;\n\n const editor = useBlockNoteEditor<\n BlockSchema,\n InlineContentSchema,\n StyleSchema\n >();\n\n const ai = useExtension(AIExtension);\n const formattingToolbar = useExtension(FormattingToolbarExtension);\n\n const onClick = () => {\n const selection = editor.getSelection();\n if (!selection) {\n throw new Error(\"No selection\");\n }\n\n const position = selection.blocks[selection.blocks.length - 1].id;\n\n ai.openAIMenuAtBlock(position);\n formattingToolbar.store.setState(false);\n };\n\n if (!editor.isEditable) {\n return null;\n }\n\n return (\n <Components.Generic.Toolbar.Button\n className={\"bn-button\"}\n label={dict.formatting_toolbar.ai.tooltip}\n mainTooltip={dict.formatting_toolbar.ai.tooltip}\n icon={<RiSparkling2Fill />}\n onClick={onClick}\n />\n );\n};\n","import {\n BlockNoteEditor,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"@blocknote/core\";\nimport { DefaultReactSuggestionItem } from \"@blocknote/react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { getAIDictionary } from \"../../i18n/dictionary.js\";\nconst Icons = {\n AI: RiSparkling2Fill,\n};\n\n/**\n * Returns AI related items that can be added to the slash menu\n */\nexport function getAISlashMenuItems<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): DefaultReactSuggestionItem[] {\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n const items = [\n {\n key: \"ai\",\n onItemClick: () => {\n const cursor = editor.getTextCursorPosition();\n if (\n cursor.block.content &&\n Array.isArray(cursor.block.content) && // isarray check not ideal\n cursor.block.content.length === 0 &&\n cursor.prevBlock\n ) {\n ai.openAIMenuAtBlock(cursor.prevBlock.id);\n } else {\n ai.openAIMenuAtBlock(cursor.block.id);\n }\n },\n ...getAIDictionary(editor).slash_menu.ai,\n icon: <Icons.AI />,\n },\n ];\n\n return items;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"blocknote-xl-ai.js","names":[],"sources":["../src/plugins/AgentCursorPlugin.ts","../src/AIExtension.ts","../src/hooks/useAIDictionary.ts","../src/components/AIMenu/PromptSuggestionMenu.tsx","../src/components/AIMenu/getDefaultAIMenuItems.tsx","../src/components/AIMenu/AIMenu.tsx","../src/components/AIMenu/AIMenuController.tsx","../src/components/FormattingToolbar/AIToolbarButton.tsx","../src/components/SuggestionMenu/getAISlashMenuItems.tsx"],"sourcesContent":["import { Plugin, PluginKey } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport { defaultSelectionBuilder } from \"y-prosemirror\";\n\ntype AgentCursorState = {\n selection: { anchor: number; head: number } | undefined;\n};\nconst PLUGIN_KEY = new PluginKey<AgentCursorState>(`blocknote-agent-cursor`);\n\n/**\n * Plugin that renders an \"AI\" cursor\n */\nexport function createAgentCursorPlugin(agentCursor: {\n name: string;\n color: string;\n}) {\n return new Plugin<AgentCursorState>({\n key: PLUGIN_KEY,\n view: (_view) => {\n return {};\n },\n state: {\n init: () => {\n return {\n selection: undefined,\n };\n },\n apply: (tr, _oldState) => {\n const meta = tr.getMeta(\"aiAgent\");\n\n if (!meta) {\n return {\n selection: undefined,\n };\n }\n\n return {\n selection: meta.selection,\n };\n },\n },\n props: {\n decorations: (state) => {\n const { doc } = state;\n\n const { selection } = PLUGIN_KEY.getState(state)!;\n\n const decs = [];\n\n if (!selection) {\n return DecorationSet.create(doc, []);\n }\n\n decs.push(\n Decoration.widget(selection.head, () => renderCursor(agentCursor), {\n key: \"agent-cursor\",\n side: 10,\n }),\n );\n\n const from = Math.min(selection.anchor, selection.head);\n const to = Math.max(selection.anchor, selection.head);\n\n decs.push(\n Decoration.inline(from, to, defaultSelectionBuilder(agentCursor), {\n inclusiveEnd: true,\n inclusiveStart: false,\n }),\n );\n\n return DecorationSet.create(doc, decs);\n },\n },\n });\n}\n\nconst renderCursor = (user: { name: string; color: string }) => {\n const cursorElement = document.createElement(\"span\");\n\n cursorElement.classList.add(\"bn-collaboration-cursor__base\");\n cursorElement.setAttribute(\"data-active\", \"true\");\n\n const caretElement = document.createElement(\"span\");\n caretElement.setAttribute(\"contentedEditable\", \"false\");\n caretElement.classList.add(\"bn-collaboration-cursor__caret\");\n caretElement.setAttribute(\"style\", `background-color: ${user.color}`);\n\n const labelElement = document.createElement(\"span\");\n\n labelElement.classList.add(\"bn-collaboration-cursor__label\");\n labelElement.setAttribute(\"style\", `background-color: ${user.color}`);\n labelElement.insertBefore(document.createTextNode(user.name), null);\n\n caretElement.insertBefore(labelElement, null);\n\n cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n cursorElement.insertBefore(caretElement, null);\n cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n\n return cursorElement;\n};\n","import { Chat } from \"@ai-sdk/react\";\nimport {\n createExtension,\n createStore,\n ExtensionOptions,\n getNodeById,\n UnreachableCaseError,\n} from \"@blocknote/core\";\nimport {\n ForkYDocExtension,\n ShowSelectionExtension,\n} from \"@blocknote/core/extensions\";\nimport {\n applySuggestions,\n revertSuggestions,\n suggestChanges,\n} from \"@handlewithcare/prosemirror-suggest-changes\";\nimport { UIMessage } from \"ai\";\nimport { Fragment, Slice } from \"prosemirror-model\";\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport { fixTablesKey } from \"prosemirror-tables\";\nimport { buildAIRequest, sendMessageWithAIRequest } from \"./api/index.js\";\nimport { createAgentCursorPlugin } from \"./plugins/AgentCursorPlugin.js\";\nimport { AIRequestHelpers, InvokeAIOptions } from \"./types.js\";\n\ntype AIPluginState = {\n aiMenuState:\n | ({\n /**\n * The ID of the block that the AI menu is opened at.\n * This changes as the AI is making changes to the document\n */\n blockId: string;\n } & (\n | {\n status: \"error\";\n error: any;\n }\n | {\n // fix: it might be nice to derive this from the Chat status and Tool call status\n status: \"user-input\" | \"thinking\" | \"ai-writing\" | \"user-reviewing\";\n }\n ))\n | \"closed\";\n};\n\nconst PLUGIN_KEY = new PluginKey(`blocknote-ai-plugin`);\n\nexport const AIExtension = createExtension(\n ({\n editor,\n options: editorOptions,\n }: ExtensionOptions<\n | (AIRequestHelpers & {\n /**\n * The name and color of the agent cursor\n *\n * @default { name: \"AI\", color: \"#8bc6ff\" }\n */\n agentCursor?: { name: string; color: string };\n })\n | undefined\n >) => {\n // TODO should we really expose it like this?\n const options = createStore<AIRequestHelpers>(editorOptions ?? {});\n const store = createStore<AIPluginState>({\n aiMenuState: \"closed\",\n });\n let chatSession:\n | {\n previousRequestOptions: InvokeAIOptions;\n chat: Chat<UIMessage>;\n abortController: AbortController;\n }\n | undefined;\n let autoScroll = false;\n\n const suggestChangesPlugin = suggestChanges();\n // disable decorations for suggest changes, not needed\n // (and the pilcrows are ugly)\n suggestChangesPlugin.props.decorations = undefined;\n return {\n key: \"ai\",\n options,\n store,\n mount({ signal }: { signal: AbortSignal }) {\n let scrollInProgress = false;\n // Listens for `scroll` and `scrollend` events to see if a new scroll was\n // started before an existing one ended. This is the most reliable way we\n // have of checking if a scroll event was caused by the user and not by\n // `scrollIntoView`, as the events are otherwise indistinguishable. If a\n // scroll was started before an existing one finished (meaning the user has\n // scrolled), auto scrolling is disabled.\n document.addEventListener(\n \"scroll\",\n () => {\n if (scrollInProgress) {\n autoScroll = false;\n }\n\n scrollInProgress = true;\n },\n {\n capture: true,\n signal,\n },\n );\n document.addEventListener(\n \"scrollend\",\n () => {\n scrollInProgress = false;\n },\n {\n capture: true,\n signal,\n },\n );\n },\n prosemirrorPlugins: [\n new Plugin({\n key: PLUGIN_KEY,\n filterTransaction: (tr) => {\n const menuState = store.state.aiMenuState;\n\n if (menuState !== \"closed\" && menuState.status === \"ai-writing\") {\n if (tr.getMeta(fixTablesKey)?.fixTables) {\n // the fixtables plugin causes the steps between of the AI Agent to become invalid\n // so we need to prevent it from running\n // (we might need to filter out other / or maybe any transactions during the writing phase)\n return false;\n }\n }\n return true;\n },\n }),\n suggestChangesPlugin,\n createAgentCursorPlugin(\n editorOptions?.agentCursor || { name: \"AI\", color: \"#8bc6ff\" },\n ),\n ],\n\n /**\n * Open the AI menu at a specific block\n */\n openAIMenuAtBlock(blockID: string) {\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(true, \"aiMenu\");\n editor.isEditable = false;\n store.setState({\n aiMenuState: {\n blockId: blockID,\n status: \"user-input\",\n },\n });\n\n // Scrolls to the block when the menu opens.\n const blockElement = editor.domElement?.querySelector(\n `[data-node-type=\"blockContainer\"][data-id=\"${blockID}\"]`,\n );\n blockElement?.scrollIntoView({ block: \"center\" });\n },\n\n /**\n * Close the AI menu\n */\n closeAIMenu() {\n store.setState({\n aiMenuState: \"closed\",\n });\n chatSession = undefined;\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(false, \"aiMenu\");\n editor.isEditable = true;\n editor.focus();\n },\n\n /**\n * Accept the changes made by the LLM\n */\n acceptChanges() {\n // This is slightly convoluted, to try to maintain the undo history as much as possible\n // The idea is that the LLM call has appended a number of updates to the document, moving the document from state `A` to state `C`\n // But we want the undo history to skip all of the intermediate states and go straight from `C` to `A`\n // To do this, we capture the document state `C` (post-LLM call), and then reject the suggestions to recover the original document state `A`\n // Then we create an intermediate state `B` that captures the diff between `A` and `C`\n // Then we apply the suggestions to `B` to get the final state `C`\n // This causes the undo history to skip `B` and go straight from `C` back to `A`\n\n // Capture the document state `C'` (post-LLM call with all suggestions still in the document)\n const markedUpDocument = editor.prosemirrorState.doc;\n\n // revert the suggestions to get back to the original document state `A`\n editor.exec((state, dispatch) => {\n return revertSuggestions(state, (tr) => {\n dispatch?.(tr.setMeta(\"addToHistory\", false));\n });\n });\n\n // Create an intermediate state `B` that captures the diff between the original document and the marked up document\n editor.exec((state, dispatch) => {\n const tr = state.tr;\n tr.replace(\n 0,\n tr.doc.content.size,\n new Slice(Fragment.from(markedUpDocument), 0, 0),\n );\n const nextState = state.apply(tr);\n // Apply the suggestions to the intermediate state `B` to get the final state `C`\n return applySuggestions(nextState, (resultTr) => {\n dispatch?.(\n tr.replace(\n 0,\n tr.doc.content.size,\n new Slice(Fragment.from(resultTr.doc), 0, 0),\n ),\n );\n });\n });\n\n // If in collaboration mode, merge the changes back into the original yDoc\n editor.getExtension(ForkYDocExtension)?.merge({ keepChanges: true });\n\n this.closeAIMenu();\n },\n\n /**\n * Reject the changes made by the LLM\n */\n rejectChanges() {\n // Revert the suggestions to get back to the original document\n editor.exec((state, dispatch) => {\n return revertSuggestions(state, (tr) => {\n // Do so without adding to history (so the last undo step is just prior to the LLM call)\n dispatch?.(tr.setMeta(\"addToHistory\", false));\n });\n });\n\n // If in collaboration mode, discard the changes and revert to the original yDoc\n editor.getExtension(ForkYDocExtension)?.merge({ keepChanges: false });\n this.closeAIMenu();\n },\n\n /**\n * Abort the current LLM request.\n *\n * This will stop the ongoing request and revert any changes made by the AI.\n * Only valid when there is an active AI request in progress.\n */\n async abort(reason?: any) {\n const { aiMenuState } = store.state;\n if (aiMenuState === \"closed\" || !chatSession) {\n return;\n }\n\n // Only abort if the request is in progress (thinking or ai-writing)\n if (\n aiMenuState.status !== \"thinking\" &&\n aiMenuState.status !== \"ai-writing\"\n ) {\n return;\n }\n\n const chat = chatSession.chat;\n const abortController = chatSession.abortController;\n\n // Abort the tool call operations\n abortController.abort(reason);\n\n // Stop the chat request\n await chat.stop();\n },\n\n /**\n * Retry the previous LLM call.\n *\n * Only valid if the current status is \"error\"\n */\n async retry() {\n const { aiMenuState } = store.state;\n if (\n aiMenuState === \"closed\" ||\n aiMenuState.status !== \"error\" ||\n !chatSession\n ) {\n throw new Error(\n \"retry() is only valid when a previous response failed\",\n );\n }\n\n /*\n Design decisions:\n - we cannot use chat.regenerate() because the document might have been updated already by toolcalls that failed mid-way\n - we also cannot revert the document changes and use chat.regenerate(), \n because we might want to retry a subsequent user-direction that failed, not the original one\n - this means we always need to send the new document state to the LLM\n - an alternative would be to take a snapshot of the document before the LLM call, and revert to that specific state\n when a call fails\n */\n\n if (chatSession?.chat.status === \"error\") {\n // the LLM call failed (i.e. a network error)\n // console.log(\"retry failed LLM call\", this.chatSession.chat.error);\n return this.invokeAI({\n ...chatSession.previousRequestOptions,\n userPrompt: `An error occured in the previous request. Please retry to accomplish the last user prompt.`,\n });\n } else {\n // an error occurred while parsing / executing the previous LLM call\n // give the LLM a chance to fix the error\n // console.log(\"retry failed tool execution\");\n return this.invokeAI({\n ...chatSession.previousRequestOptions,\n userPrompt: `An error occured while executing the previous tool call. Please retry to accomplish the last user prompt.`,\n });\n }\n },\n\n /**\n * Update the status of a call to an LLM\n *\n * @warning This method should usually only be used for advanced use-cases\n * if you want to implement how an LLM call is executed. Usually, you should\n * use {@link executeLLMRequest} instead which will handle the status updates for you.\n */\n setAIResponseStatus(\n status:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"user-reviewing\"\n | {\n status: \"error\";\n error: any;\n },\n ) {\n const aiMenuState = store.state.aiMenuState;\n if (aiMenuState === \"closed\") {\n return;\n }\n\n if (status === \"ai-writing\") {\n editor\n .getExtension(ShowSelectionExtension)\n ?.showSelection(false, \"aiMenu\");\n }\n\n if (typeof status === \"object\") {\n if (status.status !== \"error\") {\n throw new UnreachableCaseError(status.status);\n }\n this.store.setState({\n aiMenuState: {\n status: status.status,\n error: status.error,\n blockId: aiMenuState.blockId,\n },\n });\n } else {\n this.store.setState({\n aiMenuState: {\n status: status,\n blockId: aiMenuState.blockId,\n },\n });\n }\n },\n\n /**\n * @deprecated Use {@link invokeAI} instead\n */\n async callLLM(opts: InvokeAIOptions) {\n return this.invokeAI(opts);\n },\n\n /**\n * Execute a call to an LLM and apply the result to the editor\n */\n async invokeAI(opts: InvokeAIOptions) {\n this.setAIResponseStatus(\"thinking\");\n editor.getExtension(ForkYDocExtension)?.fork();\n\n try {\n // Create a new AbortController for this request\n const abortController = new AbortController();\n\n if (!chatSession) {\n // note: in the current implementation opts.transport is only used when creating a new chat\n // (so changing transport for a subsequent call in the same chat-session is not supported)\n chatSession = {\n previousRequestOptions: opts,\n chat:\n opts.chatProvider?.() ||\n this.options.state.chatProvider?.() ||\n new Chat<UIMessage>({\n sendAutomaticallyWhen: () => false,\n transport: opts.transport || this.options.state.transport,\n }),\n abortController,\n };\n } else {\n chatSession.previousRequestOptions = opts;\n chatSession.abortController = abortController;\n }\n const chat = chatSession.chat;\n\n // merge the global options with the local options\n const globalOpts = options.state;\n opts = {\n ...globalOpts,\n ...opts,\n } as InvokeAIOptions;\n\n const aiRequest = await buildAIRequest({\n editor,\n useSelection: opts.useSelection,\n deleteEmptyCursorBlock: opts.deleteEmptyCursorBlock,\n streamToolsProvider:\n opts.streamToolsProvider ??\n this.options.state.streamToolsProvider,\n documentStateBuilder:\n opts.documentStateBuilder ??\n this.options.state.documentStateBuilder,\n onBlockUpdated: (blockId) => {\n const aiMenuState = store.state.aiMenuState;\n const aiMenuOpenState =\n aiMenuState === \"closed\" ? undefined : aiMenuState;\n if (!aiMenuOpenState || aiMenuOpenState.status !== \"ai-writing\") {\n return;\n }\n\n // TODO: Sometimes, the updated block doesn't actually exist in\n // the editor. I don't know why this happens, seems like a bug?\n const nodeInfo = getNodeById(\n blockId,\n editor.prosemirrorState.doc,\n );\n if (!nodeInfo) {\n return;\n }\n\n // NOTE: does this setState with an anon object trigger unnecessary re-renders?\n store.setState({\n aiMenuState: {\n blockId,\n status: \"ai-writing\",\n },\n });\n\n if (autoScroll) {\n const blockElement = editor.prosemirrorView.domAtPos(\n nodeInfo.posBeforeNode + 1,\n );\n (blockElement.node as HTMLElement).scrollIntoView({\n block: \"center\",\n });\n }\n },\n onStart: () => {\n autoScroll = true;\n this.setAIResponseStatus(\"ai-writing\");\n\n if (\n aiRequest.emptyCursorBlockToDelete &&\n aiRequest.editor.getBlock(aiRequest.emptyCursorBlockToDelete)\n ) {\n aiRequest.editor.removeBlocks([\n aiRequest.emptyCursorBlockToDelete,\n ]);\n }\n },\n });\n\n const result = await sendMessageWithAIRequest(\n chat,\n aiRequest,\n {\n role: \"user\",\n parts: [\n {\n type: \"text\",\n text: opts.userPrompt,\n },\n ],\n },\n opts.chatRequestOptions || this.options.state.chatRequestOptions,\n chatSession.abortController.signal,\n );\n\n if (\n (result.ok && chat.status !== \"error\") ||\n abortController.signal.aborted\n ) {\n this.setAIResponseStatus(\"user-reviewing\");\n } else {\n // eslint-disable-next-line no-console\n console.warn(\"Error calling LLM\", {\n result,\n chatStatus: chat.status,\n chatError: chat.error,\n });\n this.setAIResponseStatus({\n status: \"error\",\n error: result.ok ? chat.error : result.error,\n });\n }\n } catch (e) {\n this.setAIResponseStatus({\n status: \"error\",\n error: e,\n });\n // eslint-disable-next-line no-console\n console.error(\n \"Unexpected error calling LLM\",\n e,\n chatSession?.chat.messages,\n );\n }\n },\n };\n },\n);\n","import { useBlockNoteContext } from \"@blocknote/react\";\n\nimport { getAIDictionary } from \"../i18n/dictionary.js\";\n\nexport function useAIDictionary() {\n const ctx = useBlockNoteContext();\n return getAIDictionary(ctx!.editor!);\n}\n","import { mergeCSSClasses } from \"@blocknote/core\";\nimport { filterSuggestionItems } from \"@blocknote/core/extensions\";\nimport {\n DefaultReactSuggestionItem,\n useComponentsContext,\n useSuggestionMenuKeyboardHandler,\n} from \"@blocknote/react\";\nimport {\n ChangeEvent,\n KeyboardEvent,\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport type PromptSuggestionMenuProps = {\n items: DefaultReactSuggestionItem[];\n onManualPromptSubmit: (userPrompt: string) => void;\n promptText?: string;\n onPromptTextChange?: (userPrompt: string) => void;\n icon?: ReactNode;\n rightSection?: ReactNode;\n placeholder?: string;\n disabled?: boolean;\n};\n\nexport const PromptSuggestionMenu = (props: PromptSuggestionMenuProps) => {\n // const dict = useAIDictionary();\n const Components = useComponentsContext()!;\n\n const { onManualPromptSubmit, promptText, onPromptTextChange, disabled } =\n props;\n\n // Only used internal state when `props.prompText` is undefined (i.e., uncontrolled mode)\n const [internalPromptText, setInternalPromptText] = useState<string>(\"\");\n const promptTextToUse = promptText || internalPromptText;\n\n const handleEnter = useCallback(\n async (event: KeyboardEvent) => {\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing) {\n // console.log(\"ENTER\", currentEditingPrompt);\n onManualPromptSubmit(promptTextToUse);\n }\n },\n [promptTextToUse, onManualPromptSubmit],\n );\n\n const handleChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const newValue = event.currentTarget.value;\n if (onPromptTextChange) {\n onPromptTextChange(newValue);\n }\n\n // Only update internal state if it's uncontrolled\n if (promptText === undefined) {\n setInternalPromptText(newValue);\n }\n },\n [onPromptTextChange, setInternalPromptText, promptText],\n );\n\n const items: DefaultReactSuggestionItem[] = useMemo(() => {\n return filterSuggestionItems(props.items, promptTextToUse);\n }, [promptTextToUse, props.items]);\n\n const { selectedIndex, setSelectedIndex, handler } =\n useSuggestionMenuKeyboardHandler(items, (item) => item.onItemClick());\n\n const activeDescendantId =\n items.length > 0 && selectedIndex >= 0 && selectedIndex < items.length\n ? `bn-suggestion-menu-item-${selectedIndex}`\n : undefined;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n // TODO: handle backspace to close\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing) {\n if (items.length > 0) {\n handler(event);\n } else {\n // TODO: check focus?\n handleEnter(event);\n }\n } else {\n handler(event);\n }\n },\n [handleEnter, handler, items.length],\n );\n\n // Resets index when items change\n useEffect(() => {\n setSelectedIndex(0);\n }, [promptTextToUse, setSelectedIndex]);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const hasBeenDisabled = useRef(disabled);\n\n useEffect(() => {\n // This effect is used so that after the input has been disabled (for example, when AI results are loaded),\n // the input is focused again.\n if (inputRef.current && hasBeenDisabled.current && !disabled) {\n inputRef.current.focus();\n }\n\n if (disabled) {\n hasBeenDisabled.current = true;\n }\n }, [disabled]);\n\n return (\n <div className={\"bn-combobox\"}>\n <Components.Generic.Form.Root>\n <Components.Generic.Form.TextInput\n ref={inputRef}\n className={\"bn-combobox-input\"}\n name={\"ai-prompt\"}\n variant={\"large\"}\n icon={props.icon}\n value={promptTextToUse || \"\"}\n placeholder={props.placeholder}\n disabled={props.disabled}\n onKeyDown={handleKeyDown}\n onChange={handleChange}\n autoComplete={\"off\"}\n rightSection={props.rightSection}\n aria-activedescendant={activeDescendantId}\n />\n </Components.Generic.Form.Root>\n {items.length > 0 && (\n <Components.SuggestionMenu.Root\n className={\"bn-combobox-items\"}\n id={\"ai-suggestion-menu\"}\n >\n {items.map((item, i) => (\n <Components.SuggestionMenu.Item\n key={item.title}\n className={mergeCSSClasses(\n \"bn-suggestion-menu-item\",\n item.size === \"small\" ? \"bn-suggestion-menu-item-small\" : \"\",\n )}\n id={`bn-suggestion-menu-item-${i}`}\n isSelected={i === selectedIndex}\n onClick={item.onItemClick}\n item={item}\n />\n ))}\n </Components.SuggestionMenu.Root>\n )}\n </div>\n );\n};\n","import {\n BlockNoteEditor,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"@blocknote/core\";\nimport { DefaultReactSuggestionItem } from \"@blocknote/react\";\nimport {\n RiArrowGoBackFill,\n RiBallPenLine,\n RiCheckFill,\n RiCheckLine,\n RiEarthLine,\n RiListCheck3,\n RiLoopLeftFill,\n RiMagicLine,\n RiText,\n RiTextWrap,\n} from \"react-icons/ri\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { aiDocumentFormats } from \"../../api/index.js\";\nimport { getAIDictionary } from \"../../i18n/dictionary.js\";\n\nexport type AIMenuSuggestionItem = Omit<\n DefaultReactSuggestionItem,\n \"onItemClick\"\n> & {\n onItemClick: (setPrompt: (userPrompt: string) => void) => void;\n key: string;\n};\n\n/**\n * Default items we show in the AI Menu when there is no selection active.\n * For example, when the AI menu is triggered via the slash menu\n */\nfunction getDefaultAIMenuItemsWithoutSelection<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n return [\n {\n key: \"continue_writing\",\n title: dict.ai_default_commands.continue_writing.title,\n aliases: dict.ai_default_commands.continue_writing.aliases,\n icon: <RiBallPenLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt:\n \"Continue writing at the current cursor position related to the previous text. Add multiple blocks if needed. If the document looks like a template / draft, follow the template. Be extensive if needed.\",\n // By default, LLM will be able to add / update / delete blocks. For \"continue writing\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n\n {\n key: \"summarize\",\n title: dict.ai_default_commands.summarize.title,\n aliases: dict.ai_default_commands.summarize.aliases,\n icon: <RiTextWrap size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt: \"Summarize\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"action_items\",\n title: dict.ai_default_commands.add_action_items.title,\n aliases: dict.ai_default_commands.add_action_items.aliases,\n icon: <RiListCheck3 size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n userPrompt: \"Add action items\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: true,\n delete: false,\n update: false,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"write_anything\",\n title: dict.ai_default_commands.write_anything.title,\n aliases: dict.ai_default_commands.write_anything.aliases,\n icon: <RiBallPenLine size={18} />,\n onItemClick: (setPrompt) => {\n setPrompt(dict.ai_default_commands.write_anything.prompt_placeholder);\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when there is a selection active.\n * For example, when the AI menu is triggered via formatting toolbar (AIToolbarButton)\n */\nfunction getDefaultAIMenuItemsWithSelection<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"improve_writing\",\n title: dict.ai_default_commands.improve_writing.title,\n aliases: dict.ai_default_commands.improve_writing.aliases,\n icon: <RiText size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Improve writing\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"fix_spelling\",\n title: dict.ai_default_commands.fix_spelling.title,\n aliases: dict.ai_default_commands.fix_spelling.aliases,\n icon: <RiCheckLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Fix spelling\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n {\n key: \"translate\",\n title: dict.ai_default_commands.translate.title,\n aliases: dict.ai_default_commands.translate.aliases,\n icon: <RiEarthLine size={18} />,\n onItemClick: (setPrompt) => {\n setPrompt(dict.ai_default_commands.translate.prompt_placeholder);\n },\n size: \"small\",\n },\n {\n key: \"simplify\",\n title: dict.ai_default_commands.simplify.title,\n aliases: dict.ai_default_commands.simplify.aliases,\n icon: <RiMagicLine size={18} />,\n onItemClick: async () => {\n await ai.invokeAI({\n useSelection: true,\n userPrompt: \"Simplify\",\n // By default, LLM will be able to add / update / delete blocks. For \"summarize\", we only want to allow adding new blocks.\n streamToolsProvider: aiDocumentFormats.html.getStreamToolsProvider({\n defaultStreamTools: {\n add: false,\n delete: false,\n update: true,\n },\n }),\n });\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when the AI response is done.\n */\nfunction getDefaultAIMenuItemsForReview<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"accept\",\n title: dict.ai_menu.actions.accept.title,\n aliases: dict.ai_menu.actions.accept.aliases,\n icon: <RiCheckFill size={18} />,\n onItemClick: () => {\n ai.acceptChanges();\n },\n size: \"small\",\n },\n {\n key: \"revert\",\n title: dict.ai_menu.actions.revert.title,\n aliases: dict.ai_menu.actions.revert.aliases,\n icon: <RiArrowGoBackFill size={18} />,\n onItemClick: () => {\n ai.rejectChanges();\n },\n size: \"small\",\n },\n ];\n}\n\n/**\n * Default items we show in the AI Menu when the AI response is done.\n */\nfunction getDefaultAIMenuItemsForError<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): AIMenuSuggestionItem[] {\n const dict = getAIDictionary(editor);\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n\n return [\n {\n key: \"retry\",\n title: dict.ai_menu.actions.retry.title,\n aliases: dict.ai_menu.actions.retry.aliases,\n icon: <RiLoopLeftFill size={18} />,\n onItemClick: async () => {\n await ai.retry();\n },\n size: \"small\",\n },\n {\n key: \"cancel\",\n title: dict.ai_menu.actions.cancel.title,\n aliases: dict.ai_menu.actions.cancel.aliases,\n icon: <RiArrowGoBackFill size={18} />,\n onItemClick: () => {\n ai.rejectChanges();\n },\n size: \"small\",\n },\n ];\n}\n\nexport function getDefaultAIMenuItems(\n editor: BlockNoteEditor<any, any, any>,\n aiResponseStatus:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"error\"\n | \"user-reviewing\"\n | \"closed\",\n): AIMenuSuggestionItem[] {\n if (aiResponseStatus === \"user-input\") {\n return editor.getSelection()\n ? getDefaultAIMenuItemsWithSelection(editor)\n : getDefaultAIMenuItemsWithoutSelection(editor);\n } else if (aiResponseStatus === \"user-reviewing\") {\n return getDefaultAIMenuItemsForReview(editor);\n } else if (aiResponseStatus === \"error\") {\n return getDefaultAIMenuItemsForError(editor);\n } else {\n return [];\n }\n}\n","import { BlockNoteEditor } from \"@blocknote/core\";\nimport {\n useBlockNoteEditor,\n useComponentsContext,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { useAIDictionary } from \"../../hooks/useAIDictionary.js\";\nimport { PromptSuggestionMenu } from \"./PromptSuggestionMenu.js\";\nimport {\n AIMenuSuggestionItem,\n getDefaultAIMenuItems,\n} from \"./getDefaultAIMenuItems.js\";\n\nexport type AIMenuProps = {\n items?: (\n editor: BlockNoteEditor<any, any, any>,\n aiResponseStatus:\n | \"user-input\"\n | \"thinking\"\n | \"ai-writing\"\n | \"error\"\n | \"user-reviewing\"\n | \"closed\",\n ) => AIMenuSuggestionItem[];\n onManualPromptSubmit?: (userPrompt: string) => void;\n};\n\nexport const AIMenu = (props: AIMenuProps) => {\n const editor = useBlockNoteEditor();\n const [prompt, setPrompt] = useState(\"\");\n const dict = useAIDictionary();\n\n const Components = useComponentsContext()!;\n\n const ai = useExtension(AIExtension);\n\n const aiResponseStatus = useExtensionState(AIExtension, {\n selector: (state) =>\n state.aiMenuState !== \"closed\" ? state.aiMenuState.status : \"closed\",\n });\n\n const { items: externalItems } = props;\n // note, technically there might be a bug with this useMemo when quickly changing the selection and opening the menu\n // would not call getDefaultAIMenuItems with the correct selection, because the component is reused and the memo not retriggered\n // practically this should not happen (you can test it by using a high transition duration in useUIElementPositioning)\n const items = useMemo(() => {\n let items: AIMenuSuggestionItem[] = [];\n if (externalItems) {\n items = externalItems(editor, aiResponseStatus);\n } else {\n items = getDefaultAIMenuItems(editor, aiResponseStatus);\n }\n\n // map from AI items to React Items required by PromptSuggestionMenu\n return items.map((item) => {\n return {\n ...item,\n onItemClick: () => {\n item.onItemClick(setPrompt);\n },\n };\n });\n }, [externalItems, aiResponseStatus, editor]);\n\n const onManualPromptSubmitDefault = useCallback(\n async (userPrompt: string) => {\n await ai.invokeAI({\n userPrompt,\n useSelection: editor.getSelection() !== undefined,\n });\n },\n [ai, editor],\n );\n\n useEffect(() => {\n // this is a bit hacky to run a useeffect to reset the prompt when the AI response is done\n if (\n aiResponseStatus === \"ai-writing\" ||\n aiResponseStatus === \"user-reviewing\" ||\n aiResponseStatus === \"error\"\n ) {\n setPrompt(\"\");\n }\n }, [aiResponseStatus]);\n\n const placeholder = useMemo(() => {\n if (aiResponseStatus === \"thinking\") {\n return dict.ai_menu.status.thinking;\n } else if (aiResponseStatus === \"ai-writing\") {\n return dict.ai_menu.status.editing;\n } else if (aiResponseStatus === \"error\") {\n return dict.ai_menu.status.error;\n }\n\n return dict.ai_menu.input_placeholder;\n }, [aiResponseStatus, dict]);\n\n const rightSection = useMemo(() => {\n if (aiResponseStatus === \"thinking\" || aiResponseStatus === \"ai-writing\") {\n return (\n <Components.SuggestionMenu.Loader\n className={\"bn-suggestion-menu-loader bn-combobox-right-section\"}\n />\n );\n } else if (aiResponseStatus === \"error\") {\n return (\n <div className={\"bn-combobox-right-section bn-combobox-error\"}>\n {/* Taken from Google Material Icons */}\n {/* https://fonts.google.com/icons?selected=Material+Symbols+Rounded:error:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=error&icon.size=24&icon.color=%23e8eaed&icon.set=Material+Symbols&icon.style=Rounded&icon.platform=web */}\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n height=\"1em\"\n viewBox=\"0 -960 960 960\"\n width=\"1em\"\n fill=\"currentColor\"\n >\n <path d=\"M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm0-160q17 0 28.5-11.5T520-480v-160q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640v160q0 17 11.5 28.5T480-440Zm0 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z\" />\n </svg>\n </div>\n );\n }\n\n return undefined;\n }, [Components, aiResponseStatus]);\n\n return (\n <PromptSuggestionMenu\n onManualPromptSubmit={\n props.onManualPromptSubmit || onManualPromptSubmitDefault\n }\n items={items}\n promptText={prompt}\n onPromptTextChange={setPrompt}\n placeholder={placeholder}\n disabled={\n aiResponseStatus === \"thinking\" || aiResponseStatus === \"ai-writing\"\n }\n icon={\n <div className=\"bn-combobox-icon\">\n <RiSparkling2Fill />\n </div>\n }\n rightSection={rightSection}\n />\n );\n};\n","import {\n BlockPopover,\n FloatingUIOptions,\n useBlockNoteEditor,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { autoUpdate, flip, offset, size } from \"@floating-ui/react\";\nimport { FC, useMemo } from \"react\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { AIMenu, AIMenuProps } from \"./AIMenu.js\";\n\nexport const AIMenuController = (props: {\n aiMenu?: FC<AIMenuProps>;\n floatingUIOptions?: FloatingUIOptions;\n}) => {\n const editor = useBlockNoteEditor();\n const ai = useExtension(AIExtension);\n\n const aiMenuState = useExtensionState(AIExtension, {\n editor,\n selector: (state) => state.aiMenuState,\n });\n\n const blockId = aiMenuState === \"closed\" ? undefined : aiMenuState.blockId;\n\n const floatingUIOptions = useMemo<FloatingUIOptions>(\n () =>\n ({\n ...props.floatingUIOptions,\n useFloatingOptions: {\n open: aiMenuState !== \"closed\",\n placement: \"bottom\",\n middleware: [\n offset(10),\n flip(),\n size({\n apply({ rects, elements }) {\n Object.assign(elements.floating.style, {\n width: `${rects.reference.width}px`,\n });\n },\n }),\n ],\n onOpenChange: (open) => {\n if (open || aiMenuState === \"closed\") {\n return;\n }\n\n if (aiMenuState.status === \"user-input\") {\n ai.closeAIMenu();\n } else if (\n aiMenuState.status === \"user-reviewing\" ||\n aiMenuState.status === \"error\"\n ) {\n ai.rejectChanges();\n }\n },\n whileElementsMounted(reference, floating, update) {\n return autoUpdate(reference, floating, update, {\n animationFrame: true,\n });\n },\n ...props.floatingUIOptions?.useFloatingOptions,\n },\n useDismissProps: {\n enabled:\n aiMenuState === \"closed\" || aiMenuState.status === \"user-input\",\n // We should just be able to set `referencePress: true` instead of\n // using this listener, but this doesn't seem to trigger.\n // (probably because we don't assign the referenceProps to the reference element)\n outsidePress: (event: MouseEvent) => {\n if (event.target instanceof Element) {\n const blockElement = event.target.closest(\".bn-block\");\n if (\n blockElement &&\n blockElement.getAttribute(\"data-id\") === blockId\n ) {\n ai.closeAIMenu();\n }\n }\n\n return true;\n },\n ...props.floatingUIOptions?.useDismissProps,\n },\n elementProps: {\n style: {\n zIndex: 100,\n },\n ...props.floatingUIOptions?.elementProps,\n },\n // we use the focus manager instead of `autoFocus={true}` to prevent \"page-scrolls-to-top-when-opening-the-floating-element\"\n // see https://floating-ui.com/docs/floatingfocusmanager#page-scrolls-to-top-when-opening-the-floating-element\n focusManagerProps: {\n disabled: false,\n // needed for https://github.com/floating-ui/floating-ui/pull/3202\n // (related: https://github.com/mui/base-ui/issues/3950)\n getInsideElements: () => {\n if (!editor.domElement) {\n return [];\n }\n return [editor.domElement];\n },\n ...props.floatingUIOptions?.focusManagerProps,\n },\n }) satisfies FloatingUIOptions,\n [ai, aiMenuState, blockId, props.floatingUIOptions, editor.domElement],\n );\n\n const Component = props.aiMenu || AIMenu;\n\n return (\n <BlockPopover blockId={blockId} {...floatingUIOptions}>\n {aiMenuState !== \"closed\" && <Component />}\n </BlockPopover>\n );\n};\n","import { BlockSchema, InlineContentSchema, StyleSchema } from \"@blocknote/core\";\nimport { FormattingToolbarExtension } from \"@blocknote/core/extensions\";\nimport {\n useBlockNoteEditor,\n useComponentsContext,\n useExtension,\n} from \"@blocknote/react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { useAIDictionary } from \"../../hooks/useAIDictionary.js\";\n\nexport const AIToolbarButton = () => {\n const dict = useAIDictionary();\n const Components = useComponentsContext()!;\n\n const editor = useBlockNoteEditor<\n BlockSchema,\n InlineContentSchema,\n StyleSchema\n >();\n\n const ai = useExtension(AIExtension);\n const formattingToolbar = useExtension(FormattingToolbarExtension);\n\n const onClick = () => {\n const selection = editor.getSelection();\n if (!selection) {\n throw new Error(\"No selection\");\n }\n\n const position = selection.blocks[selection.blocks.length - 1].id;\n\n ai.openAIMenuAtBlock(position);\n formattingToolbar.store.setState(false);\n };\n\n if (!editor.isEditable) {\n return null;\n }\n\n return (\n <Components.Generic.Toolbar.Button\n className={\"bn-button\"}\n label={dict.formatting_toolbar.ai.tooltip}\n mainTooltip={dict.formatting_toolbar.ai.tooltip}\n icon={<RiSparkling2Fill />}\n onClick={onClick}\n />\n );\n};\n","import {\n BlockNoteEditor,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"@blocknote/core\";\nimport { DefaultReactSuggestionItem } from \"@blocknote/react\";\nimport { RiSparkling2Fill } from \"react-icons/ri\";\n\nimport { AIExtension } from \"../../AIExtension.js\";\nimport { getAIDictionary } from \"../../i18n/dictionary.js\";\nconst Icons = {\n AI: RiSparkling2Fill,\n};\n\n/**\n * Returns AI related items that can be added to the slash menu\n */\nexport function getAISlashMenuItems<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, I, S>): DefaultReactSuggestionItem[] {\n const ai = editor.getExtension(AIExtension);\n if (!ai) {\n return [];\n }\n const items = [\n {\n key: \"ai\",\n onItemClick: () => {\n const cursor = editor.getTextCursorPosition();\n if (\n cursor.block.content &&\n Array.isArray(cursor.block.content) && // isarray check not ideal\n cursor.block.content.length === 0 &&\n cursor.prevBlock\n ) {\n ai.openAIMenuAtBlock(cursor.prevBlock.id);\n } else {\n ai.openAIMenuAtBlock(cursor.block.id);\n }\n },\n ...getAIDictionary(editor).slash_menu.ai,\n icon: <Icons.AI />,\n },\n ];\n\n return items;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"builtAt":1779277597101,"assets":[{"name":"blocknote-xl-ai.cjs","size":16875},{"name":"locales.cjs","size":23775},{"name":"server.cjs","size":760},{"name":"server-D-Xyj9BS.cjs","size":38286},{"name":"blocknote-xl-ai.cjs.map","size":63114},{"name":"locales.cjs.map","size":56423},{"name":"server-D-Xyj9BS.cjs.map","size":186169}],"chunks":[{"id":"a1ee98a","entry":true,"initial":true,"files":["blocknote-xl-ai.cjs"],"names":["blocknote-xl-ai"]},{"id":"842df4c","entry":true,"initial":true,"files":["locales.cjs"],"names":["locales"]},{"id":"791e108","entry":true,"initial":true,"files":["server.cjs"],"names":["server"]},{"id":"91fd206","entry":false,"initial":true,"files":["server-D-Xyj9BS.cjs"],"names":["server"]}],"modules":[{"name":"./src/style.css","size":0,"chunks":["a1ee98a"]},{"name":"./src/plugins/AgentCursorPlugin.ts","size":2201,"chunks":["a1ee98a"]},{"name":"./src/AIExtension.ts","size":7732,"chunks":["a1ee98a"]},{"name":"./src/hooks/useAIDictionary.ts","size":161,"chunks":["a1ee98a"]},{"name":"./src/components/AIMenu/PromptSuggestionMenu.tsx","size":3233,"chunks":["a1ee98a"]},{"name":"./src/components/AIMenu/getDefaultAIMenuItems.tsx","size":6452,"chunks":["a1ee98a"]},{"name":"./src/components/AIMenu/AIMenu.tsx","size":3485,"chunks":["a1ee98a"]},{"name":"./src/components/AIMenu/AIMenuController.tsx","size":2391,"chunks":["a1ee98a"]},{"name":"./src/components/FormattingToolbar/AIToolbarButton.tsx","size":1068,"chunks":["a1ee98a"]},{"name":"./src/components/SuggestionMenu/getAISlashMenuItems.tsx","size":670,"chunks":["a1ee98a"]},{"name":"./src/index.ts","size":0,"chunks":["a1ee98a"]},{"name":"./src/i18n/locales/ar.ts","size":1427,"chunks":["842df4c"]},{"name":"./src/i18n/locales/de.ts","size":1481,"chunks":["842df4c"]},{"name":"./src/i18n/locales/en.ts","size":1379,"chunks":["842df4c"]},{"name":"./src/i18n/locales/es.ts","size":1449,"chunks":["842df4c"]},{"name":"./src/i18n/locales/fr.ts","size":1465,"chunks":["842df4c"]},{"name":"./src/i18n/locales/he.ts","size":1297,"chunks":["842df4c"]},{"name":"./src/i18n/locales/hr.ts","size":1390,"chunks":["842df4c"]},{"name":"./src/i18n/locales/is.ts","size":1458,"chunks":["842df4c"]},{"name":"./src/i18n/locales/it.ts","size":1451,"chunks":["842df4c"]},{"name":"./src/i18n/locales/ja.ts","size":1268,"chunks":["842df4c"]},{"name":"./src/i18n/locales/ko.ts","size":1290,"chunks":["842df4c"]},{"name":"./src/i18n/locales/nl.ts","size":1432,"chunks":["842df4c"]},{"name":"./src/i18n/locales/no.ts","size":1405,"chunks":["842df4c"]},{"name":"./src/i18n/locales/pl.ts","size":1406,"chunks":["842df4c"]},{"name":"./src/i18n/locales/pt.ts","size":1444,"chunks":["842df4c"]},{"name":"./src/i18n/locales/ru.ts","size":1445,"chunks":["842df4c"]},{"name":"./src/i18n/locales/sk.ts","size":1407,"chunks":["842df4c"]},{"name":"./src/i18n/locales/uk.ts","size":1452,"chunks":["842df4c"]},{"name":"./src/i18n/locales/vi.ts","size":1402,"chunks":["842df4c"]},{"name":"./src/i18n/locales/zh-tw.ts","size":1244,"chunks":["842df4c"]},{"name":"./src/i18n/locales/zh.ts","size":1236,"chunks":["842df4c"]},{"name":"./src/i18n/locales/index.ts","size":0,"chunks":["842df4c"]},{"name":"./rolldown/runtime.js","size":1008,"chunks":["91fd206"]},{"name":"./src/util/emptyBlock.ts","size":216,"chunks":["91fd206"]},{"name":"./src/util/trimArray.ts","size":320,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/trimEmptyBlocks.ts","size":270,"chunks":["91fd206"]},{"name":"./src/api/aiRequest/builder.ts","size":1312,"chunks":["91fd206"]},{"name":"./src/streamTool/jsonSchema.ts","size":1326,"chunks":["91fd206"]},{"name":"./src/streamTool/ChunkExecutionError.ts","size":295,"chunks":["91fd206"]},{"name":"./src/streamTool/StreamToolExecutor.ts","size":3431,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/injectDocumentStateMessages.ts","size":1755,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/toolDefinitions.ts","size":745,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/clientside/ClientSideTransport.ts","size":1172,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/appendableStream.ts","size":1069,"chunks":["91fd206"]},{"name":"./src/util/stream.ts","size":1003,"chunks":["91fd206"]},{"name":"./src/streamTool/filterNewOrUpdatedOperations.ts","size":925,"chunks":["91fd206"]},{"name":"./src/streamTool/filterValidOperations.ts","size":692,"chunks":["91fd206"]},{"name":"./src/streamTool/toValidatedOperations.ts","size":1106,"chunks":["91fd206"]},{"name":"./src/streamTool/preprocess.ts","size":431,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/UIMessageStreamToOperationsResult.ts","size":348,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/chatHandlers.ts","size":4033,"chunks":["91fd206"]},{"name":"./src/api/aiRequest/sendMessageWithAIRequest.ts","size":786,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/addCursorPosition.ts","size":410,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/convertBlocks.ts","size":234,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/flattenBlocks.ts","size":201,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/suffixIds.ts","size":229,"chunks":["91fd206"]},{"name":"./src/api/formats/DocumentStateBuilder.ts","size":1530,"chunks":["91fd206"]},{"name":"./src/prosemirror/fragmentUtil.ts","size":285,"chunks":["91fd206"]},{"name":"./src/prosemirror/agent.ts","size":5303,"chunks":["91fd206"]},{"name":"./src/prosemirror/changeset.ts","size":5344,"chunks":["91fd206"]},{"name":"./src/streamTool/streamTool.ts","size":100,"chunks":["91fd206"]},{"name":"./src/util/AbortError.ts","size":179,"chunks":["91fd206"]},{"name":"./src/api/formats/base-tools/util/validateBlockArray.ts","size":587,"chunks":["91fd206"]},{"name":"./src/api/formats/base-tools/createAddBlocksTool.ts","size":4117,"chunks":["91fd206"]},{"name":"./src/api/formats/base-tools/createUpdateBlockTool.ts","size":3314,"chunks":["91fd206"]},{"name":"./src/api/formats/base-tools/delete.ts","size":1440,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/tools/getPartialHTML.ts","size":657,"chunks":["91fd206"]},{"name":"./src/prosemirror/rebaseTool.ts","size":940,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/tools/rebaseTool.ts","size":837,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/tools/validate.ts","size":246,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/tools/index.ts","size":1618,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/htmlBlocks.ts","size":2552,"chunks":["91fd206"]},{"name":"./src/api/schema/mergeSchema.ts","size":767,"chunks":["91fd206"]},{"name":"./src/api/schema/schemaToJSONSchema.ts","size":3581,"chunks":["91fd206"]},{"name":"./src/api/formats/json/tools/validate.ts","size":1409,"chunks":["91fd206"]},{"name":"./src/api/formats/json/tools/index.ts","size":1242,"chunks":["91fd206"]},{"name":"./src/api/formats/json/json.ts","size":2244,"chunks":["91fd206"]},{"name":"./src/api/formats/markdown-blocks/tools/rebaseTool.ts","size":651,"chunks":["91fd206"]},{"name":"./src/api/formats/markdown-blocks/tools/validate.ts","size":248,"chunks":["91fd206"]},{"name":"./src/api/formats/markdown-blocks/tools/index.ts","size":1377,"chunks":["91fd206"]},{"name":"./src/api/formats/markdown-blocks/markdownBlocks.ts","size":2373,"chunks":["91fd206"]},{"name":"./src/api/formats/formats.ts","size":195,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/index.ts","size":160,"chunks":["91fd206"]},{"name":"./src/i18n/dictionary.ts","size":185,"chunks":["91fd206"]},{"name":"./src/blocknoteAIClient/client.ts","size":376,"chunks":["91fd206"]},{"name":"./src/server.ts","size":0,"chunks":["91fd206"]}]}
1
+ {"builtAt":1780397429997,"assets":[{"name":"blocknote-xl-ai.cjs","size":16875},{"name":"locales.cjs","size":23775},{"name":"server.cjs","size":760},{"name":"server-D-Xyj9BS.cjs","size":38286},{"name":"blocknote-xl-ai.cjs.map","size":63127},{"name":"locales.cjs.map","size":56423},{"name":"server-D-Xyj9BS.cjs.map","size":186169}],"chunks":[{"id":"a1ee98a","entry":true,"initial":true,"files":["blocknote-xl-ai.cjs"],"names":["blocknote-xl-ai"]},{"id":"842df4c","entry":true,"initial":true,"files":["locales.cjs"],"names":["locales"]},{"id":"791e108","entry":true,"initial":true,"files":["server.cjs"],"names":["server"]},{"id":"91fd206","entry":false,"initial":true,"files":["server-D-Xyj9BS.cjs"],"names":["server"]}],"modules":[{"name":"./src/style.css","size":0,"chunks":["a1ee98a"]},{"name":"./src/plugins/AgentCursorPlugin.ts","size":2201,"chunks":["a1ee98a"]},{"name":"./src/AIExtension.ts","size":7732,"chunks":["a1ee98a"]},{"name":"./src/hooks/useAIDictionary.ts","size":161,"chunks":["a1ee98a"]},{"name":"./src/components/AIMenu/PromptSuggestionMenu.tsx","size":3233,"chunks":["a1ee98a"]},{"name":"./src/components/AIMenu/getDefaultAIMenuItems.tsx","size":6452,"chunks":["a1ee98a"]},{"name":"./src/components/AIMenu/AIMenu.tsx","size":3485,"chunks":["a1ee98a"]},{"name":"./src/components/AIMenu/AIMenuController.tsx","size":2391,"chunks":["a1ee98a"]},{"name":"./src/components/FormattingToolbar/AIToolbarButton.tsx","size":1068,"chunks":["a1ee98a"]},{"name":"./src/components/SuggestionMenu/getAISlashMenuItems.tsx","size":670,"chunks":["a1ee98a"]},{"name":"./src/index.ts","size":0,"chunks":["a1ee98a"]},{"name":"./src/i18n/locales/ar.ts","size":1427,"chunks":["842df4c"]},{"name":"./src/i18n/locales/de.ts","size":1481,"chunks":["842df4c"]},{"name":"./src/i18n/locales/en.ts","size":1379,"chunks":["842df4c"]},{"name":"./src/i18n/locales/es.ts","size":1449,"chunks":["842df4c"]},{"name":"./src/i18n/locales/fr.ts","size":1465,"chunks":["842df4c"]},{"name":"./src/i18n/locales/he.ts","size":1297,"chunks":["842df4c"]},{"name":"./src/i18n/locales/hr.ts","size":1390,"chunks":["842df4c"]},{"name":"./src/i18n/locales/is.ts","size":1458,"chunks":["842df4c"]},{"name":"./src/i18n/locales/it.ts","size":1451,"chunks":["842df4c"]},{"name":"./src/i18n/locales/ja.ts","size":1268,"chunks":["842df4c"]},{"name":"./src/i18n/locales/ko.ts","size":1290,"chunks":["842df4c"]},{"name":"./src/i18n/locales/nl.ts","size":1432,"chunks":["842df4c"]},{"name":"./src/i18n/locales/no.ts","size":1405,"chunks":["842df4c"]},{"name":"./src/i18n/locales/pl.ts","size":1406,"chunks":["842df4c"]},{"name":"./src/i18n/locales/pt.ts","size":1444,"chunks":["842df4c"]},{"name":"./src/i18n/locales/ru.ts","size":1445,"chunks":["842df4c"]},{"name":"./src/i18n/locales/sk.ts","size":1407,"chunks":["842df4c"]},{"name":"./src/i18n/locales/uk.ts","size":1452,"chunks":["842df4c"]},{"name":"./src/i18n/locales/vi.ts","size":1402,"chunks":["842df4c"]},{"name":"./src/i18n/locales/zh-tw.ts","size":1244,"chunks":["842df4c"]},{"name":"./src/i18n/locales/zh.ts","size":1236,"chunks":["842df4c"]},{"name":"./src/i18n/locales/index.ts","size":0,"chunks":["842df4c"]},{"name":"./rolldown/runtime.js","size":1008,"chunks":["91fd206"]},{"name":"./src/util/emptyBlock.ts","size":216,"chunks":["91fd206"]},{"name":"./src/util/trimArray.ts","size":320,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/trimEmptyBlocks.ts","size":270,"chunks":["91fd206"]},{"name":"./src/api/aiRequest/builder.ts","size":1312,"chunks":["91fd206"]},{"name":"./src/streamTool/jsonSchema.ts","size":1326,"chunks":["91fd206"]},{"name":"./src/streamTool/ChunkExecutionError.ts","size":295,"chunks":["91fd206"]},{"name":"./src/streamTool/StreamToolExecutor.ts","size":3431,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/injectDocumentStateMessages.ts","size":1755,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/toolDefinitions.ts","size":745,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/clientside/ClientSideTransport.ts","size":1172,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/appendableStream.ts","size":1069,"chunks":["91fd206"]},{"name":"./src/util/stream.ts","size":1003,"chunks":["91fd206"]},{"name":"./src/streamTool/filterNewOrUpdatedOperations.ts","size":925,"chunks":["91fd206"]},{"name":"./src/streamTool/filterValidOperations.ts","size":692,"chunks":["91fd206"]},{"name":"./src/streamTool/toValidatedOperations.ts","size":1106,"chunks":["91fd206"]},{"name":"./src/streamTool/preprocess.ts","size":431,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/UIMessageStreamToOperationsResult.ts","size":348,"chunks":["91fd206"]},{"name":"./src/streamTool/vercelAiSdk/util/chatHandlers.ts","size":4033,"chunks":["91fd206"]},{"name":"./src/api/aiRequest/sendMessageWithAIRequest.ts","size":786,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/addCursorPosition.ts","size":410,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/convertBlocks.ts","size":234,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/flattenBlocks.ts","size":201,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/suffixIds.ts","size":229,"chunks":["91fd206"]},{"name":"./src/api/formats/DocumentStateBuilder.ts","size":1530,"chunks":["91fd206"]},{"name":"./src/prosemirror/fragmentUtil.ts","size":285,"chunks":["91fd206"]},{"name":"./src/prosemirror/agent.ts","size":5303,"chunks":["91fd206"]},{"name":"./src/prosemirror/changeset.ts","size":5344,"chunks":["91fd206"]},{"name":"./src/streamTool/streamTool.ts","size":100,"chunks":["91fd206"]},{"name":"./src/util/AbortError.ts","size":179,"chunks":["91fd206"]},{"name":"./src/api/formats/base-tools/util/validateBlockArray.ts","size":587,"chunks":["91fd206"]},{"name":"./src/api/formats/base-tools/createAddBlocksTool.ts","size":4117,"chunks":["91fd206"]},{"name":"./src/api/formats/base-tools/createUpdateBlockTool.ts","size":3314,"chunks":["91fd206"]},{"name":"./src/api/formats/base-tools/delete.ts","size":1440,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/tools/getPartialHTML.ts","size":657,"chunks":["91fd206"]},{"name":"./src/prosemirror/rebaseTool.ts","size":940,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/tools/rebaseTool.ts","size":837,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/tools/validate.ts","size":246,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/tools/index.ts","size":1618,"chunks":["91fd206"]},{"name":"./src/api/formats/html-blocks/htmlBlocks.ts","size":2552,"chunks":["91fd206"]},{"name":"./src/api/schema/mergeSchema.ts","size":767,"chunks":["91fd206"]},{"name":"./src/api/schema/schemaToJSONSchema.ts","size":3581,"chunks":["91fd206"]},{"name":"./src/api/formats/json/tools/validate.ts","size":1409,"chunks":["91fd206"]},{"name":"./src/api/formats/json/tools/index.ts","size":1242,"chunks":["91fd206"]},{"name":"./src/api/formats/json/json.ts","size":2244,"chunks":["91fd206"]},{"name":"./src/api/formats/markdown-blocks/tools/rebaseTool.ts","size":651,"chunks":["91fd206"]},{"name":"./src/api/formats/markdown-blocks/tools/validate.ts","size":248,"chunks":["91fd206"]},{"name":"./src/api/formats/markdown-blocks/tools/index.ts","size":1377,"chunks":["91fd206"]},{"name":"./src/api/formats/markdown-blocks/markdownBlocks.ts","size":2373,"chunks":["91fd206"]},{"name":"./src/api/formats/formats.ts","size":195,"chunks":["91fd206"]},{"name":"./src/api/promptHelpers/index.ts","size":160,"chunks":["91fd206"]},{"name":"./src/i18n/dictionary.ts","size":185,"chunks":["91fd206"]},{"name":"./src/blocknoteAIClient/client.ts","size":376,"chunks":["91fd206"]},{"name":"./src/server.ts","size":0,"chunks":["91fd206"]}]}
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "directory": "packages/xl-ai"
12
12
  },
13
13
  "license": "GPL-3.0 OR PROPRIETARY",
14
- "version": "0.51.2",
14
+ "version": "0.51.4",
15
15
  "files": [
16
16
  "dist",
17
17
  "types",
@@ -63,9 +63,9 @@
63
63
  "dependencies": {
64
64
  "@ai-sdk/provider-utils": "^4.0.2",
65
65
  "@ai-sdk/react": "^3.0.5",
66
- "@blocknote/core": "0.51.2",
67
- "@blocknote/mantine": "0.51.2",
68
- "@blocknote/react": "0.51.2",
66
+ "@blocknote/core": "0.51.4",
67
+ "@blocknote/mantine": "0.51.4",
68
+ "@blocknote/react": "0.51.4",
69
69
  "@floating-ui/react": "^0.27.18",
70
70
  "@handlewithcare/prosemirror-suggest-changes": "^0.1.8",
71
71
  "@tiptap/core": "^3.13.0",
@@ -90,7 +90,7 @@
90
90
  "@ai-sdk/mistral": "^3.0.2",
91
91
  "@ai-sdk/openai": "^3.0.2",
92
92
  "@ai-sdk/openai-compatible": "^2.0.2",
93
- "@blocknote/xl-multi-column": "0.51.2",
93
+ "@blocknote/xl-multi-column": "0.51.4",
94
94
  "@mswjs/interceptors": "^0.37.6",
95
95
  "@types/diff": "^6.0.0",
96
96
  "@types/json-diff": "^1.0.3",
@@ -70,7 +70,7 @@ export const AIMenuController = (props: {
70
70
  // We should just be able to set `referencePress: true` instead of
71
71
  // using this listener, but this doesn't seem to trigger.
72
72
  // (probably because we don't assign the referenceProps to the reference element)
73
- outsidePress: (event) => {
73
+ outsidePress: (event: MouseEvent) => {
74
74
  if (event.target instanceof Element) {
75
75
  const blockElement = event.target.closest(".bn-block");
76
76
  if (