diff --git a/clients/vscode/src/chat/WebviewHelper.ts b/clients/vscode/src/chat/WebviewHelper.ts index 3b2789748b4f..36ae3f0b4c5a 100644 --- a/clients/vscode/src/chat/WebviewHelper.ts +++ b/clients/vscode/src/chat/WebviewHelper.ts @@ -43,6 +43,7 @@ import { vscodeRangeToChatPanelPositionRange, chatPanelLocationToVSCodeRange, } from "./utils"; +import path from "path"; export class WebviewHelper { webview?: Webview; @@ -728,6 +729,117 @@ export class WebviewHelper { } return infoList; }, + provideSymbolAtInfo: async (opts?: AtInputOpts): Promise => { + const maxResults = opts?.limit || 50; + const query = opts?.query?.toLowerCase(); + + const editor = window.activeTextEditor; + if (!editor) return null; + const document = editor.document; + + // Try document symbols first + const documentSymbols = await commands.executeCommand( + "vscode.executeDocumentSymbolProvider", + document.uri, + ); + + let results: SymbolAtInfo[] = []; + + if (documentSymbols && documentSymbols.length > 0) { + const processSymbol = (symbol: DocumentSymbol | SymbolInformation) => { + if (results.length >= maxResults) return; + + const symbolName = symbol.name.toLowerCase(); + if (query && !symbolName.includes(query)) return; + + if (getAllowedSymbolKinds().includes(symbol.kind)) { + results.push(vscodeSymbolToSymbolAtInfo(symbol, document.uri, this.gitProvider)); + } + if (isDocumentSymbol(symbol)) { + symbol.children.forEach(processSymbol); + } + }; + documentSymbols.forEach(processSymbol); + } + + // Try workspace symbols if no document symbols found + if (results.length === 0 && query) { + const workspaceSymbols = await commands.executeCommand( + "vscode.executeWorkspaceSymbolProvider", + query, + ); + + if (workspaceSymbols) { + results = workspaceSymbols + .filter((symbol) => getAllowedSymbolKinds().includes(symbol.kind)) + .slice(0, maxResults) + .map((symbol) => vscodeSymbolToSymbolAtInfo(symbol, symbol.location.uri, this.gitProvider)); + } + } + + return results.length > 0 ? results : null; + }, + + provideFileAtInfo: async (opts?: AtInputOpts): Promise => { + const maxResults = opts?.limit || 50; + const query = opts?.query; + + const globPattern = "**/*"; + const excludePattern = "**/node_modules/**"; + try { + const files = await workspace.findFiles(globPattern, excludePattern); + + const filteredFiles = query + ? files.filter((uri) => { + const a = path.basename(uri.fsPath); + const b = query.toLowerCase(); + this.logger.info("uri:" + a + " " + "query:" + b + " result:" + a.toLowerCase().startsWith(b)); + this.logger.info(b); + return a.toLowerCase().startsWith(b); + }) + : files; + + const sortedFiles = filteredFiles.sort((a, b) => { + const nameA = a.fsPath.toLowerCase(); + const nameB = b.fsPath.toLowerCase(); + return nameA < nameB ? -1 : nameA > nameB ? 1 : 0; + }); + + const limitedFiles = sortedFiles.slice(0, maxResults); + + return limitedFiles.map((uri) => uriToFileAtInfo(uri, this.gitProvider)); + } catch (error) { + this.logger.error("Failed to find files:", error); + return null; + } + }, + getSymbolAtInfoContent: async (info: SymbolAtInfo): Promise => { + try { + const uri = chatPanelFilepathToLocalUri(info.location.filepath, this.gitProvider); + if (!uri) return null; + + const document = await workspace.openTextDocument(uri); + const range = chatPanelLocationToVSCodeRange(info.location.location); + if (!range) return null; + + return document.getText(range); + } catch (error) { + this.logger.error("Failed to get symbol content:", error); + return null; + } + }, + getFileAtInfoContent: async (info: FileAtInfo): Promise => { + try { + const uri = chatPanelFilepathToLocalUri(info.filepath, this.gitProvider); + if (!uri) return null; + + const document = await workspace.openTextDocument(uri); + return document.getText(); + } catch (error) { + this.logger.error("Failed to get file content:", error); + return null; + } + }, }); } } diff --git a/ee/tabby-ui/app/chat/page.tsx b/ee/tabby-ui/app/chat/page.tsx index 3fdc0d0f2c67..347cc19f3f61 100644 --- a/ee/tabby-ui/app/chat/page.tsx +++ b/ee/tabby-ui/app/chat/page.tsx @@ -79,6 +79,10 @@ export default function ChatPage() { supportsProvideWorkspaceGitRepoInfo, setSupportsProvideWorkspaceGitRepoInfo ] = useState(false) + const [supportProvideFileAtInfo, setSupportProvideFileAtInfo] = + useState(false) + const [supportGetFileAtInfoContent, setSupportGetFileAtInfoContent] = + useState(false) const sendMessage = (message: ChatMessage) => { if (chatRef.current) { @@ -245,6 +249,12 @@ export default function ChatPage() { server ?.hasCapability('readWorkspaceGitRepositories') .then(setSupportsProvideWorkspaceGitRepoInfo) + server + ?.hasCapability('provideFileAtInfo') + .then(setSupportProvideFileAtInfo) + server + ?.hasCapability('getFileAtInfoContent') + .then(setSupportGetFileAtInfoContent) } checkCapabilities() @@ -407,6 +417,16 @@ export default function ChatPage() { ? server?.readWorkspaceGitRepositories : undefined } + provideFileAtInfo={ + isInEditor && supportProvideFileAtInfo + ? server?.provideFileAtInfo + : undefined + } + getFileAtInfoContent={ + isInEditor && supportGetFileAtInfoContent + ? server?.getFileAtInfoContent + : undefined + } /> ) diff --git a/ee/tabby-ui/components/chat/FileList.tsx b/ee/tabby-ui/components/chat/FileList.tsx new file mode 100644 index 000000000000..cf08dc63bfe4 --- /dev/null +++ b/ee/tabby-ui/components/chat/FileList.tsx @@ -0,0 +1,141 @@ +/* eslint-disable no-console */ +import React, { useEffect, useRef } from 'react' + +import { SuggestionItem } from './types' + +interface FileListProps { + items: SuggestionItem[] + selectedIndex: number + onSelect: (item: { + id: string + label: string + category: 'file' | 'symbol' + }) => void + onUpdateSelectedIndex: (index: number) => void +} + +const MAX_VISIBLE_ITEMS = 4 +const ITEM_HEIGHT = 42 // px + +export const FileList: React.FC = ({ + items, + selectedIndex, + onSelect, + onUpdateSelectedIndex +}) => { + console.log('[FileList] Rendering with items:', items.length) + console.log('[FileList] Selected index:', selectedIndex) + + const selectedItemRef = useRef(null) + const containerRef = useRef(null) + + const containerHeight = + Math.min(items.length, MAX_VISIBLE_ITEMS) * ITEM_HEIGHT + + useEffect(() => { + const container = containerRef.current + const selectedItem = selectedItemRef.current + if (container && selectedItem) { + const containerTop = container.scrollTop + const containerBottom = containerTop + container.clientHeight + const itemTop = selectedItem.offsetTop + const itemBottom = itemTop + selectedItem.offsetHeight + + if (itemTop < containerTop) { + container.scrollTop = itemTop + } else if (itemBottom > containerBottom) { + container.scrollTop = itemBottom - container.clientHeight + } + } + }, [selectedIndex]) + + const renderContent = () => { + if (!items.length) { + console.log('[FileList] No items to display') + return ( +
+ No files found +
+ ) + } + + return ( +
+ {items.map((item, index) => { + console.log(`[FileList] Rendering item: ${item.label}`) + const filepath = + 'filepath' in item.filepath + ? item.filepath.filepath + : item.filepath.uri + const isSelected = index === selectedIndex + + return ( + + ) + })} +
+ ) + } + + return <>{renderContent()} +} diff --git a/ee/tabby-ui/components/chat/chat.tsx b/ee/tabby-ui/components/chat/chat.tsx index 360afcec4bc7..329506e75140 100644 --- a/ee/tabby-ui/components/chat/chat.tsx +++ b/ee/tabby-ui/components/chat/chat.tsx @@ -1,7 +1,9 @@ import React, { RefObject } from 'react' import { compact, findIndex, isEqual, some, uniqWith } from 'lodash-es' import type { + AtInputOpts, Context, + FileAtInfo, FileContext, FileLocation, GitRepository, @@ -85,6 +87,8 @@ type ChatContextValue = { setSelectedRepoId: React.Dispatch> repos: RepositorySourceListQuery['repositoryList'] | undefined fetchingRepos: boolean + provideFileAtInfo?: (opts?: AtInputOpts) => Promise + getFileAtInfoContent?: (info: FileAtInfo) => Promise } export const ChatContext = React.createContext( @@ -126,6 +130,8 @@ interface ChatProps extends React.ComponentProps<'div'> { chatInputRef: RefObject supportsOnApplyInEditorV2: boolean readWorkspaceGitRepositories?: () => Promise + provideFileAtInfo?: (opts?: AtInputOpts) => Promise + getFileAtInfoContent?: (info: FileAtInfo) => Promise } function ChatRenderer( @@ -149,7 +155,9 @@ function ChatRenderer( openInEditor, chatInputRef, supportsOnApplyInEditorV2, - readWorkspaceGitRepositories + readWorkspaceGitRepositories, + provideFileAtInfo, + getFileAtInfoContent }: ChatProps, ref: React.ForwardedRef ) { @@ -602,7 +610,9 @@ function ChatRenderer( setSelectedRepoId, repos, fetchingRepos, - initialized + initialized, + provideFileAtInfo, + getFileAtInfoContent }} >
diff --git a/ee/tabby-ui/components/chat/editor/CategoryMenu.tsx b/ee/tabby-ui/components/chat/editor/CategoryMenu.tsx new file mode 100644 index 000000000000..67aff84edffd --- /dev/null +++ b/ee/tabby-ui/components/chat/editor/CategoryMenu.tsx @@ -0,0 +1,70 @@ +/* eslint-disable no-console */ +import React from 'react' + +interface CategoryMenuProps { + items: { + label: string + category: 'file' | 'symbol' + }[] + selectedIndex: number + onSelect: (category: 'file' | 'symbol') => void + onUpdateSelectedIndex: (index: number) => void +} + +export const CategoryMenu: React.FC = ({ + items, + selectedIndex, + onSelect, + onUpdateSelectedIndex +}) => { + console.log('[CategoryMenu] Rendering component with items:', items) + + return ( +
+ {items.map((item, idx) => { + const isSelected = idx === selectedIndex + + const handleMouseEnter = () => onUpdateSelectedIndex(idx) + + const handleClick = () => { + console.log(`[CategoryMenu] ${item.category} category selected`) + onSelect(item.category) + } + + return ( + + ) + })} +
+ ) +} diff --git a/ee/tabby-ui/components/chat/editor/mention-component.tsx b/ee/tabby-ui/components/chat/editor/mention-component.tsx new file mode 100644 index 000000000000..21d068748e6a --- /dev/null +++ b/ee/tabby-ui/components/chat/editor/mention-component.tsx @@ -0,0 +1,37 @@ +/* eslint-disable no-console */ +// mention-component.tsx +import React from 'react' +import { NodeViewWrapper } from '@tiptap/react' + +import { cn } from '@/lib/utils' + +export const MentionComponent = ({ node }: { node: any }) => { + console.log(node) + return ( + + + {node.attrs.category === 'file' ? ( + + + + + ) : ( + + )} + {node.attrs.label} + + + ) +} diff --git a/ee/tabby-ui/components/chat/editor/mention-extension.ts b/ee/tabby-ui/components/chat/editor/mention-extension.ts new file mode 100644 index 000000000000..04cc59397c23 --- /dev/null +++ b/ee/tabby-ui/components/chat/editor/mention-extension.ts @@ -0,0 +1,39 @@ +// mention-extension.ts +import Mention from '@tiptap/extension-mention' +import { ReactNodeViewRenderer } from '@tiptap/react' + +import { MentionComponent } from './mention-component' + +export const MENTION_EXTENSION_NAME = 'mention' + +export const MentionExtension = Mention.extend({ + addNodeView() { + return ReactNodeViewRenderer(MentionComponent) + }, + + addAttributes() { + return { + id: { + default: null, + parseHTML: element => element.getAttribute('data-id'), + renderHTML: attributes => ({ + 'data-id': attributes.id + }) + }, + label: { + default: null, + parseHTML: element => element.getAttribute('data-label'), + renderHTML: attributes => ({ + 'data-label': attributes.label + }) + }, + category: { + default: 'file', + parseHTML: element => element.getAttribute('data-category'), + renderHTML: attributes => ({ + 'data-category': attributes.category + }) + } + } + } +}) diff --git a/ee/tabby-ui/components/chat/prompt-form.css b/ee/tabby-ui/components/chat/prompt-form.css new file mode 100644 index 000000000000..493f83c38970 --- /dev/null +++ b/ee/tabby-ui/components/chat/prompt-form.css @@ -0,0 +1,13 @@ +/* mention-styles.css */ +.source-mention { + @apply inline-flex items-baseline space-x-1; +} + +.tiptap .mention { + @apply bg-muted text-muted-foreground px-1.5 py-0.5 rounded; +} + +.tiptap p.is-editor-empty:first-child::before { + content: attr(data-placeholder); + @apply text-muted-foreground float-left; +} diff --git a/ee/tabby-ui/components/chat/prompt-form.tsx b/ee/tabby-ui/components/chat/prompt-form.tsx index 98fcd56d54ec..cf02f929a806 100644 --- a/ee/tabby-ui/components/chat/prompt-form.tsx +++ b/ee/tabby-ui/components/chat/prompt-form.tsx @@ -1,31 +1,31 @@ -import * as React from 'react' +/* eslint-disable no-console */ +import React, { useCallback, useEffect, useRef, useState } from 'react' +import { Extension } from '@tiptap/core' +import Document from '@tiptap/extension-document' +import Paragraph from '@tiptap/extension-paragraph' +import Placeholder from '@tiptap/extension-placeholder' +import Text from '@tiptap/extension-text' +import { EditorContent, useEditor } from '@tiptap/react' import { UseChatHelpers } from 'ai/react' -import { debounce, has } from 'lodash-es' -import useSWR from 'swr' import { useEnterSubmit } from '@/lib/hooks/use-enter-submit' -import fetcher from '@/lib/tabby/fetcher' -import type { ISearchHit, SearchReponse } from '@/lib/types' -import { cn } from '@/lib/utils' -import { Button, buttonVariants } from '@/components/ui/button' -import { - IconArrowElbow, - IconEdit, - IconSymbolFunction -} from '@/components/ui/icons' -import { Popover, PopoverAnchor, PopoverContent } from '@/components/ui/popover' +import { Button } from '@/components/ui/button' +import { IconArrowElbow } from '@/components/ui/icons' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' -import { - SearchableSelect, - SearchableSelectAnchor, - SearchableSelectContent, - SearchableSelectOption, - SearchableSelectTextarea -} from '@/components/searchable-select' + +import { Popover, PopoverContent } from '../ui/popover' +import { ChatContext } from './chat' +import { FileList } from './FileList' +import { SuggestionItem } from './types' + +import './prompt-form.css' + +import { CategoryMenu } from './editor/CategoryMenu' +import { MentionExtension } from './editor/mention-extension' export interface PromptProps extends Pick { @@ -39,280 +39,416 @@ export interface PromptFormRef { focus: () => void } +type MenuView = 'categories' | 'files' | 'symbols' + +interface MenuState { + view: MenuView + category?: 'file' | 'symbol' +} + +interface SuggestionState { + items: SuggestionItem[] + command: (item: { + id: string + label: string + category: 'file' | 'symbol' + }) => void + clientRect: () => DOMRect | null + selectedIndex: number +} + +const CustomKeyboardShortcuts = Extension.create({ + addKeyboardShortcuts() { + return { + 'Shift-Enter': () => { + console.log('[CustomKeyboardShortcuts] Shift-Enter pressed') + return this.editor.commands.first(({ commands }) => [ + () => commands.newlineInCode(), + () => commands.createParagraphNear(), + () => commands.liftEmptyBlock(), + () => commands.splitBlock() + ]) + } + } + } +}) + function PromptFormRenderer( { onSubmit, input, setInput, isLoading, - chatInputRef, - isInitializing + isInitializing, + chatInputRef }: PromptProps, ref: React.ForwardedRef ) { - const { formRef, onKeyDown } = useEnterSubmit() - const [queryCompletionUrl, setQueryCompletionUrl] = React.useState< - string | null - >(null) - const [suggestionOpen, setSuggestionOpen] = React.useState(false) - // store the input selection for replacing inputValue - const prevInputSelectionEnd = React.useRef() - // for updating the input selection after replacing - const nextInputSelectionRange = React.useRef<[number, number]>() - const [options, setOptions] = React.useState([]) - const [selectedCompletionsMap, setSelectedCompletionsMap] = React.useState< - Record - >({}) - - const { data: completionData } = useSWR( - queryCompletionUrl, - fetcher, - { - revalidateOnFocus: false, - dedupingInterval: 0, - errorRetryCount: 0 + const { formRef } = useEnterSubmit() + const { provideFileAtInfo } = React.useContext(ChatContext) + const popoverRef = useRef(null) + const selectedItemRef = useRef(null) + + const [suggestionState, setSuggestionState] = + useState(null) + const suggestionRef = useRef | null>(null) + + const [menuState, setMenuState] = useState({ view: 'categories' }) + + const categoryItems: { + label: string + category: 'file' | 'symbol' + }[] = [ + { label: 'Files', category: 'file' }, + { label: 'Symbols', category: 'symbol' } + ] + + const [categorySelectedIndex, setCategorySelectedIndex] = useState(0) + + const updateSelectedIndex = useCallback((index: number) => { + console.log('[PromptForm] Updating mention suggestion index:', index) + setSuggestionState(prev => + prev ? { ...prev, selectedIndex: index } : null + ) + }, []) + + const scrollToSelected = useCallback( + (containerEl: HTMLElement | null, selectedEl: HTMLElement | null) => { + console.log('[PromptForm] Scrolling to selected element') + if (!containerEl || !selectedEl) return + + const containerRect = containerEl.getBoundingClientRect() + const selectedRect = selectedEl.getBoundingClientRect() + + if (selectedRect.bottom > containerRect.bottom) { + containerEl.scrollTop += selectedRect.bottom - containerRect.bottom + } else if (selectedRect.top < containerRect.top) { + containerEl.scrollTop -= containerRect.top - selectedRect.top + } + }, + [] + ) + + useEffect(() => { + if (suggestionState?.selectedIndex !== undefined) { + console.log('[PromptForm] Selected index changed, updating scroll') + scrollToSelected(popoverRef.current, selectedItemRef.current) } + }, [suggestionState?.selectedIndex, scrollToSelected]) + + const handleKeyDown = useCallback( + (event: KeyboardEvent) => { + console.log('[PromptForm] Key pressed in mention suggestions:', event.key) + const currentSuggestion = suggestionRef.current + if (!currentSuggestion?.items?.length) return false + + switch (event.key) { + case 'ArrowUp': { + event.preventDefault() + console.log('[PromptForm] Handling ArrowUp') + setSuggestionState(prev => { + if (!prev) return null + const newIndex = + prev.selectedIndex > 0 + ? prev.selectedIndex - 1 + : currentSuggestion.items.length - 1 + return { ...prev, selectedIndex: newIndex } + }) + return true + } + + case 'ArrowDown': { + event.preventDefault() + console.log('[PromptForm] Handling ArrowDown') + setSuggestionState(prev => { + if (!prev) return null + const newIndex = + prev.selectedIndex < currentSuggestion.items.length - 1 + ? prev.selectedIndex + 1 + : 0 + return { ...prev, selectedIndex: newIndex } + }) + return true + } + + case 'Enter': { + event.preventDefault() + console.log('[PromptForm] Handling Enter') + const selectedItem = + currentSuggestion.items[suggestionState?.selectedIndex ?? 0] + if (selectedItem) { + currentSuggestion.command({ + id: selectedItem.id, + label: selectedItem.label, + category: selectedItem.category + }) + } + return true + } + + default: + return false + } + }, + [suggestionState?.selectedIndex] ) - React.useEffect(() => { - const suggestions = completionData?.hits ?? [] - setOptions(suggestions) - setSuggestionOpen(!!suggestions?.length) - }, [completionData?.hits]) + useEffect(() => { + function handleCategoryKeyDown(e: KeyboardEvent) { + if (suggestionState) return - React.useImperativeHandle(ref, () => ({ - focus: () => chatInputRef.current?.focus() - })) + if (menuState.view === 'categories') { + console.log('[PromptForm] Key pressed in categories:', e.key) - React.useEffect(() => { - if ( - input && - chatInputRef.current && - chatInputRef.current !== document.activeElement - ) { - chatInputRef.current.focus() + switch (e.key) { + case 'ArrowUp': { + e.preventDefault() + setCategorySelectedIndex( + prev => (prev - 1 + categoryItems.length) % categoryItems.length + ) + break + } + case 'ArrowDown': { + e.preventDefault() + setCategorySelectedIndex(prev => (prev + 1) % categoryItems.length) + break + } + case 'Enter': { + e.preventDefault() + const selectedCategory = categoryItems[categorySelectedIndex] + setMenuState({ + view: selectedCategory.category === 'file' ? 'files' : 'symbols', + category: selectedCategory.category + }) + break + } + default: + break + } + } } - }, [input, chatInputRef]) - React.useLayoutEffect(() => { - if (nextInputSelectionRange.current?.length) { - chatInputRef.current?.setSelectionRange?.( - nextInputSelectionRange.current[0], - nextInputSelectionRange.current[1] - ) - nextInputSelectionRange.current = undefined - } - }, [chatInputRef]) - - const handleSearchCompletion = React.useMemo(() => { - return debounce((e: React.ChangeEvent) => { - const value = e.target?.value ?? '' - const end = e.target?.selectionEnd ?? 0 - const queryNameMatches = getSearchCompletionQueryName(value, end) - const queryName = queryNameMatches?.[1] - if (queryName) { - const query = encodeURIComponent(`name:${queryName} AND kind:function`) - const url = `/v1beta/search?q=${query}` - setQueryCompletionUrl(url) - } else { - setOptions([]) - setSuggestionOpen(false) - } - }, 200) - }, []) + window.addEventListener('keydown', handleCategoryKeyDown) + return () => window.removeEventListener('keydown', handleCategoryKeyDown) + }, [suggestionState, menuState, categoryItems, categorySelectedIndex]) - const handleCompletionSelect = (item: ISearchHit) => { - const selectionEnd = prevInputSelectionEnd.current ?? 0 - const queryNameMatches = getSearchCompletionQueryName(input, selectionEnd) - if (queryNameMatches) { - setSelectedCompletionsMap({ - ...selectedCompletionsMap, - [`@${item.doc?.name}`]: item + const editor = useEditor({ + extensions: [ + Document, + Paragraph, + Text, + CustomKeyboardShortcuts, + Placeholder.configure({ + showOnlyWhenEditable: true, + placeholder: 'Ask anything...' + }), + MentionExtension.configure({ + HTMLAttributes: { + class: 'mention' + }, + suggestion: { + char: '@', + allowSpaces: true, + items: async ({ query }) => { + console.log('[MentionExtension] Fetching items for query:', query) + if (!provideFileAtInfo) return [] + try { + const files = await provideFileAtInfo({ query }) + console.log('[MentionExtension] Files fetched:', files?.length) + if (!files) return [] + return files.map(file => ({ + type: 'source', + category: 'file' as const, + id: file.name, + label: file.name, + filepath: file.filepath, + data: { + sourceId: file.name, + sourceName: file.name, + sourceKind: 'file' + } + })) + } catch (error) { + console.error('[MentionExtension] Error fetching files:', error) + return [] + } + }, + render: () => ({ + onStart: props => { + console.log('[MentionExtension] Suggestion started') + const newState = { + items: props.items, + command: props.command, + clientRect: props.clientRect!, + selectedIndex: 0 + } + suggestionRef.current = { + items: props.items, + command: props.command + } + setSuggestionState(newState) + }, + onUpdate: props => { + console.log('[MentionExtension] Suggestion updated') + const newState = { + items: props.items, + command: props.command, + clientRect: props.clientRect!, + selectedIndex: 0 + } + suggestionRef.current = { + items: props.items, + command: props.command + } + setSuggestionState(newState) + }, + onKeyDown: ({ event }) => { + console.log( + '[MentionExtension] Key down in suggestion:', + event.key + ) + if (['ArrowUp', 'ArrowDown', 'Enter'].includes(event.key)) { + return handleKeyDown(event) + } + return false + }, + onExit: () => { + console.log('[MentionExtension] Exiting suggestion') + setMenuState({ view: 'categories' }) + suggestionRef.current = null + setSuggestionState(null) + } + }) + } }) - const replaceString = `@${item?.doc?.name} ` - const prevInput = input - .substring(0, selectionEnd) - .replace(new RegExp(queryNameMatches[0]), '') - const nextSelectionEnd = prevInput.length + replaceString.length - nextInputSelectionRange.current = [nextSelectionEnd, nextSelectionEnd] - setInput(prevInput + replaceString + input.slice(selectionEnd)) + ], + content: input, + onUpdate: ({ editor }) => { + console.log('[PromptForm] Editor content updated') + setInput(editor.getText()) } - setOptions([]) - setSuggestionOpen(false) - } + }) + + React.useImperativeHandle(ref, () => ({ + focus: () => { + console.log('[PromptForm] Focus requested') + editor?.commands.focus() + } + })) - const handlePromptSubmit: React.FormEventHandler< - HTMLFormElement - > = async e => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() + console.log('[PromptForm] Form submitted') + if (!input?.trim() || isLoading || isInitializing) { + console.log( + '[PromptForm] Submit prevented - empty input or loading state' + ) return } - let finalInput = input - Object.keys(selectedCompletionsMap).forEach(key => { - const completion = selectedCompletionsMap[key] - if (!completion?.doc) return - finalInput = finalInput.replaceAll( - key, - `\n${'```'}${completion.doc?.language ?? ''}\n${ - completion.doc.body ?? '' - }\n${'```'}\n` - ) - }) - - setInput('') - await onSubmit(finalInput) - } - - const handleTextareaKeyDown = ( - e: React.KeyboardEvent, - isOpen: boolean - ) => { - if (e.key === 'Enter' && isOpen) { - e.preventDefault() - } else if ( - isOpen && - ['ArrowRight', 'ArrowLeft', 'Home', 'End'].includes(e.key) - ) { - setOptions([]) - setSuggestionOpen(false) - } else { - if (!isOpen) { - ;(e as any).preventDownshiftDefault = true - } - onKeyDown(e) - } + await onSubmit(input) + editor?.commands.setContent('') } return ( -
- { - if (isOpen && options?.length) { - setSuggestionOpen(isOpen) - } else { - setSuggestionOpen(false) - setOptions([]) - } - }} - > - {({ open, highlightedIndex }) => { - const highlightedOption = options?.[highlightedIndex] - - return ( - <> - -
- - - - { - if (has(e, 'target.value')) { - prevInputSelectionEnd.current = e.target.selectionEnd - setInput(e.target.value) - // TODO: Temporarily disabling the current search function. Will be replaced with a different search functionality in the future. - // handleSearchCompletion(e) - } else { - prevInputSelectionEnd.current = undefined - } - }} - onKeyDown={e => handleTextareaKeyDown(e, open)} - /> -
- - - - - Send message - -
-
-
- e.preventDefault()} - className="w-[60vw] md:w-[430px]" - > - - -
- {open && - !!options?.length && - options.map((item, index) => ( - -
-
- -
- {item?.doc?.name}(...) -
-
-
- {item?.doc?.body} -
-
-
- ))} -
-
- e.preventDefault()} - onKeyDownCapture={e => e.preventDefault()} - className="rounded-none" - collisionPadding={{ bottom: 120 }} - > -
-
- {highlightedOption?.doc?.kind - ? `(${highlightedOption?.doc?.kind}) ` - : ''} - {highlightedOption?.doc?.name} -
-
- {highlightedOption?.doc?.body} -
-
-
-
-
- - ) - }} -
-
+ <> +
+
+ + +
+ +
+ +
+ + + + + Send message + +
+
+
+ + {suggestionState && ( + + 0 + ? Math.min(suggestionState.items.length * 42, 4 * 42) + : 70), + height: 'auto', + maxHeight: '200px' + }} + align="start" + onOpenAutoFocus={e => e.preventDefault()} + onPointerDownOutside={e => e.preventDefault()} + onFocusOutside={e => e.preventDefault()} + > + {menuState.view === 'categories' ? ( + { + console.log('[PromptForm] Category selected:', cat) + setMenuState({ + view: cat === 'file' ? 'files' : 'symbols', + category: cat + }) + }} + onUpdateSelectedIndex={index => { + setCategorySelectedIndex(index) + }} + /> + ) : menuState.view === 'files' ? ( + { + console.log('[PromptForm] File selected:', item) + suggestionState.command(item) + }} + onUpdateSelectedIndex={updateSelectedIndex} + /> + ) : ( +
+ Symbol search coming soon... +
+ )} +
+
+ )} + ) } @@ -320,29 +456,4 @@ export const PromptForm = React.forwardRef( PromptFormRenderer ) -/** - * Retrieves the name of the completion query from a given string@. - * @param {string} val - The input string to search for the completion query name. - * @param {number | undefined} selectionEnd - The index at which the selection ends in the input string. - * @return {string | undefined} - The name of the completion query if found, otherwise undefined. - */ -export function getSearchCompletionQueryName( - val: string, - selectionEnd: number | undefined -): RegExpExecArray | null { - const queryString = val.substring(0, selectionEnd) - const matches = /@(\w+)$/.exec(queryString) - return matches -} - -function IconForCompletionKind({ - kind, - ...rest -}: { kind: string | undefined } & React.ComponentProps<'svg'>) { - switch (kind) { - case 'function': - return - default: - return - } -} +export default PromptForm diff --git a/ee/tabby-ui/components/chat/types.ts b/ee/tabby-ui/components/chat/types.ts new file mode 100644 index 000000000000..d76dad2039da --- /dev/null +++ b/ee/tabby-ui/components/chat/types.ts @@ -0,0 +1,51 @@ +// types.ts +import { FileAtInfo } from 'tabby-chat-panel/index' + +import { ContextSourceKind } from '@/lib/gql/generates/graphql' + +export type MentionCategory = 'file' | 'symbol' + +export interface SourceItem { + id: string + label: string + category: MentionCategory + type: 'source' + data: { + sourceKind: ContextSourceKind + sourceId: string + sourceName: string + } +} + +export type OptionItem = SourceItem +export type MenuView = 'categories' | 'files' | 'symbols' + +export interface MenuState { + view: MenuView + category?: 'file' | 'symbol' +} + +export interface MentionNodeAttrs { + id: string + label: string + category: 'file' | 'symbol' +} + +export interface SuggestionItem extends FileAtInfo { + id: string + label: string + category: 'file' | 'symbol' + type: 'source' + data: { + sourceId: string + sourceName: string + sourceKind: string + } +} + +export interface SuggestionState { + items: SuggestionItem[] + command: (item: MentionNodeAttrs) => void + clientRect: () => DOMRect | null + selectedIndex: number +} diff --git a/ee/tabby-ui/package.json b/ee/tabby-ui/package.json index f44474941979..4f4b1792b67d 100644 --- a/ee/tabby-ui/package.json +++ b/ee/tabby-ui/package.json @@ -48,13 +48,16 @@ "@radix-ui/react-toggle": "^1.1.0", "@radix-ui/react-tooltip": "^1.0.7", "@sindresorhus/slugify": "^2.2.1", - "@tiptap/extension-document": "^2.6.6", - "@tiptap/extension-mention": "^2.6.6", - "@tiptap/extension-paragraph": "^2.6.6", - "@tiptap/extension-placeholder": "^2.6.6", - "@tiptap/extension-text": "^2.6.6", + "@tiptap/core": "^2.6.6", + "@tiptap/extension-document": "^2.10.4", + "@tiptap/extension-mention": "^2.10.4", + "@tiptap/extension-paragraph": "^2.10.4", + "@tiptap/extension-placeholder": "^2.10.4", + "@tiptap/extension-text": "^2.10.4", "@tiptap/pm": "^2.6.6", - "@tiptap/react": "^2.6.6", + "@tiptap/react": "^2.10.4", + "@tiptap/starter-kit": "^2.6.6", + "@tiptap/suggestion": "^2.10.4", "@uidotdev/usehooks": "^2.4.1", "@uiw/codemirror-extensions-langs": "^4.21.21", "@urql/core": "^4.2.3", @@ -168,4 +171,4 @@ "typescript": "^5.1.3", "vitest": "^1.5.2" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54a06d0b907a..062f2d8df0ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -360,10 +360,10 @@ importers: version: 5.2.0 esbuild-plugin-copy: specifier: ^2.1.1 - version: 2.1.1(esbuild@0.20.2) + version: 2.1.1(esbuild@0.19.12) esbuild-plugin-polyfill-node: specifier: ^0.3.0 - version: 0.3.0(esbuild@0.20.2) + version: 0.3.0(esbuild@0.19.12) eslint: specifier: ^8.55.0 version: 8.57.0 @@ -511,27 +511,36 @@ importers: '@sindresorhus/slugify': specifier: ^2.2.1 version: 2.2.1 - '@tiptap/extension-document': + '@tiptap/core': specifier: ^2.6.6 - version: 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6)) + version: 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/extension-document': + specifier: ^2.10.4 + version: 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) '@tiptap/extension-mention': - specifier: ^2.6.6 - version: 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)(@tiptap/suggestion@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)) + specifier: ^2.10.4 + version: 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)(@tiptap/suggestion@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)) '@tiptap/extension-paragraph': - specifier: ^2.6.6 - version: 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6)) + specifier: ^2.10.4 + version: 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) '@tiptap/extension-placeholder': - specifier: ^2.6.6 - version: 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6) + specifier: ^2.10.4 + version: 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) '@tiptap/extension-text': - specifier: ^2.6.6 - version: 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6)) + specifier: ^2.10.4 + version: 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) '@tiptap/pm': specifier: ^2.6.6 - version: 2.6.6 + version: 2.10.4 '@tiptap/react': + specifier: ^2.10.4 + version: 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@tiptap/starter-kit': specifier: ^2.6.6 - version: 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 2.10.4 + '@tiptap/suggestion': + specifier: ^2.10.4 + version: 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) '@uidotdev/usehooks': specifier: ^2.4.1 version: 2.4.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -3351,8 +3360,8 @@ packages: resolution: {integrity: sha512-kcbt7w23pcVYGLnJkh2LZpXF1OX5RDM4DLOtwPug2HvRE8ow/YfY8ZEM1YCFlA41D8rBPBVP918cYeIx4BVUbw==} engines: {node: '>=14.19.0', npm: '>=7.0.0'} - '@remirror/core-constants@2.0.2': - resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==} + '@remirror/core-constants@3.0.0': + resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} '@repeaterjs/repeater@3.0.5': resolution: {integrity: sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA==} @@ -3790,67 +3799,155 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders' - '@tiptap/core@2.6.6': - resolution: {integrity: sha512-VO5qTsjt6rwworkuo0s5AqYMfDA0ZwiTiH6FHKFSu2G/6sS7HKcc/LjPq+5Legzps4QYdBDl3W28wGsGuS1GdQ==} + '@tiptap/core@2.10.4': + resolution: {integrity: sha512-fExFRTRgb6MSpg2VvR5qO2dPTQAZWuUoU4UsBCurIVcPWcyVv4FG1YzgMyoLDKy44rebFtwUGJbfU9NzX7Q/bA==} peerDependencies: - '@tiptap/pm': ^2.6.6 + '@tiptap/pm': ^2.7.0 - '@tiptap/extension-bubble-menu@2.6.6': - resolution: {integrity: sha512-IkfmlZq67aaegym5sBddBc/xXWCArxn5WJEl1oxKEayjQhybKSaqI7tk0lOx/x7fa5Ml1WlGpCFh+KKXbQTG0g==} + '@tiptap/extension-blockquote@2.10.4': + resolution: {integrity: sha512-4JSwAM3B92YWvGzu/Vd5rovPrCGwLSaSLD5rxcLyfxLSrTDQd3n7lp78pzVgGhunVECzaGF5A0ByWWpEyS0a3w==} peerDependencies: - '@tiptap/core': ^2.6.6 - '@tiptap/pm': ^2.6.6 + '@tiptap/core': ^2.7.0 - '@tiptap/extension-document@2.6.6': - resolution: {integrity: sha512-6qlH5VWzLHHRVeeciRC6C4ZHpMsAGPNG16EF53z0GeMSaaFD/zU3B239QlmqXmLsAl8bpf8Bn93N0t2ABUvScw==} + '@tiptap/extension-bold@2.10.4': + resolution: {integrity: sha512-SdO4oFQKaERCGfwOc1CLYQRtThENam2KWfWmvpsymknokt5qYzU57ft0SE1HQV9vVYEzZ9HrWIgv2xrgu0g9kg==} peerDependencies: - '@tiptap/core': ^2.6.6 + '@tiptap/core': ^2.7.0 - '@tiptap/extension-floating-menu@2.6.6': - resolution: {integrity: sha512-lPkESOfAUxgmXRiNqUU23WSyja5FUfSWjsW4hqe+BKNjsUt1OuFMEtYJtNc+MCGhhtPfFvM3Jg6g9jd6g5XsLQ==} + '@tiptap/extension-bubble-menu@2.10.4': + resolution: {integrity: sha512-GVtZwJaQyLBptMsmDtYl5GEobd1Uu7C9sc9Z+PdXwMuxmFfg+j07bCKCj5JJj/tjgXCSLVxWdTlDHxNrgzQHjw==} peerDependencies: - '@tiptap/core': ^2.6.6 - '@tiptap/pm': ^2.6.6 + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 - '@tiptap/extension-mention@2.6.6': - resolution: {integrity: sha512-fghNe4ZQRiZ7i3+sSrZx87zPZjaCwVtxn56/5UinoBUP/ZpCGwGtI+ErKhCBVyLW1fKyd0MmlihK/IGIeCBw1A==} + '@tiptap/extension-bullet-list@2.10.4': + resolution: {integrity: sha512-JVwDPgOBYRU2ivaadOh4IaQYXQEiSw6sB36KT/bwqJF2GnEvLiMwptdRMn9Uuh6xYR3imjIZtV6uZAoneZdd6g==} peerDependencies: - '@tiptap/core': ^2.6.6 - '@tiptap/pm': ^2.6.6 - '@tiptap/suggestion': ^2.6.6 + '@tiptap/core': ^2.7.0 - '@tiptap/extension-paragraph@2.6.6': - resolution: {integrity: sha512-fD/onCr16UQWx+/xEmuFC2MccZZ7J5u4YaENh8LMnAnBXf78iwU7CAcmuc9rfAEO3qiLoYGXgLKiHlh2ZfD4wA==} + '@tiptap/extension-code-block@2.10.4': + resolution: {integrity: sha512-qS4jnbJqghNMT2+B+GQ807ATgqkL9OQ//NlL+ZwVSe+DPDduNA9B6IB9SrWENDfOnzekpi7kcEcm+RenELARRQ==} peerDependencies: - '@tiptap/core': ^2.6.6 + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 - '@tiptap/extension-placeholder@2.6.6': - resolution: {integrity: sha512-J0ZMvF93NsRrt+R7IQ3GhxNq32vq+88g25oV/YFJiwvC48HMu1tQB6kG1I3LJpu5b8lN+LnfANNqDOEhiBfjaA==} + '@tiptap/extension-code@2.10.4': + resolution: {integrity: sha512-Vj/N0nbSQiV1o7X7pRySK9Fu72Dd266gm27TSlsts6IwJu5MklFvz7ezJUWoLjt2wmCV8/U/USmk/39ic9qjvg==} peerDependencies: - '@tiptap/core': ^2.6.6 - '@tiptap/pm': ^2.6.6 + '@tiptap/core': ^2.7.0 - '@tiptap/extension-text@2.6.6': - resolution: {integrity: sha512-e84uILnRzNzcwK1DVQNpXVmBG1Cq3BJipTOIDl1LHifOok7MBjhI/X+/NR0bd3N2t6gmDTWi63+4GuJ5EeDmsg==} + '@tiptap/extension-document@2.10.4': + resolution: {integrity: sha512-1Pqrl6Rr9bVEHJ3zO2dM7UUA0Qn/r70JQ9YLlestjW1sbMaMuY3Ifvu2uSyUE7SAGV3gvxwNVQCrv8f0VlVEaA==} peerDependencies: - '@tiptap/core': ^2.6.6 + '@tiptap/core': ^2.7.0 - '@tiptap/pm@2.6.6': - resolution: {integrity: sha512-56FGLPn3fwwUlIbLs+BO21bYfyqP9fKyZQbQyY0zWwA/AG2kOwoXaRn7FOVbjP6CylyWpFJnpRRmgn694QKHEg==} + '@tiptap/extension-dropcursor@2.10.4': + resolution: {integrity: sha512-0XEM/yNLaMc/sZlYOau7XpHyYiHT9LwXUe7kmze/L8eowIa/iLvmRbcnUd3rtlZ7x7wooE6UO9c7OtlREg4ZBw==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 - '@tiptap/react@2.6.6': - resolution: {integrity: sha512-AUmdb/J1O/vCO2b8LL68ctcZr9a3931BwX4fUUZ1kCrCA5lTj2xz0rjeAtpxEdzLnR+Z7q96vB7vf7bPYOUAew==} + '@tiptap/extension-floating-menu@2.10.4': + resolution: {integrity: sha512-K2MDiu6CwQ7+Jr6g1Lh3Tuxm1L6SefSHMpQO0UW3aRGwgEV5pjlrztnBFX4K9b7MNuQ4dJGCUK9u8Cv7Xss0qg==} peerDependencies: - '@tiptap/core': ^2.6.6 - '@tiptap/pm': ^2.6.6 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-gapcursor@2.10.4': + resolution: {integrity: sha512-KbJfoaqTZePpkWAN+klpK5j0UVtELxN7H5B0J556/UCB/rnq+OsdEFHPks2Ss9TidqWzRUqcxUE50UZ7b8h7Ug==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 - '@tiptap/suggestion@2.6.6': - resolution: {integrity: sha512-jogG0QgGit9UtTznVnhQfNImZfQM89NR0is20yRQzC0HmD8B8f3jmGrotG63Why2oKbeoe3CpM5/5eDE/paqCA==} + '@tiptap/extension-hard-break@2.10.4': + resolution: {integrity: sha512-nW9wubW1A/CO2Ssn9wNMP08tR9Oarg9VUGzJ5qNuz38DDNyntE1SyDS+XStkeMq5nKqJ3YKhukyAJH/PiRq4Mg==} peerDependencies: - '@tiptap/core': ^2.6.6 - '@tiptap/pm': ^2.6.6 + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-heading@2.10.4': + resolution: {integrity: sha512-7D0h0MIvE97Gx3Qwuo2xnPDK07WfCnyh4tpOPBOus4e1g6sgxVkwDwhbkYWiwvIrf4BUVJflnke/DEDCVp6/Eg==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-history@2.10.4': + resolution: {integrity: sha512-fg6BNxbpMMtgKaiNI/GLcCzkxIQMwSYBhO9LA0CxLvmsWGU+My4r9W3DK6HwNoRJ9+6OleDPSLo1P73fbSTtEA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-horizontal-rule@2.10.4': + resolution: {integrity: sha512-s9ycm/BOGoW3L0Epnj541vdngHbFbMM488HoODd1CmVSw1C+wBWFgsukgqKjlyE3VGfZXuSb1ur9zinW0RiLJQ==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-italic@2.10.4': + resolution: {integrity: sha512-8MIQ+wsbyxNCZDCFTVTOXrS2AvFyOhtlBNgVU2+6r6xnJV4AcfEA3qclysqrjOlL117ped/nzDeoB0AeX0CI+Q==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-list-item@2.10.4': + resolution: {integrity: sha512-8K3WUD5fPyw2poQKnJGGm7zlfeIbpld92+SRF4M9wkp95EzvgexTlodvxlrL3i8zKXcQQVyExWA8kCcGPFb9bA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-mention@2.10.4': + resolution: {integrity: sha512-pVouKWxSVQSy4zn6HrljPIP1AG826gkm/w18Asi8QnZvR0AMqGLh9q7qd9Kc0j8NKoCzlzK8hECGlYPEaBldow==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + '@tiptap/suggestion': ^2.7.0 + + '@tiptap/extension-ordered-list@2.10.4': + resolution: {integrity: sha512-NaeEu+qFG2O0emc8WlwOM7DKNKOaqHWuNkuKrrmQzslgL+UQSEGlGMo6NEJ5sLLckPBDpIa0MuRm30407JE+cg==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-paragraph@2.10.4': + resolution: {integrity: sha512-SRNVhT8OXqjpZtcyuOtofbtOpXXFrQrjqqCc/yXebda//2SfUTOvB16Lss77vQOWi6xr7TF1mZuowJgSTkcczw==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-placeholder@2.10.4': + resolution: {integrity: sha512-leWG4xP7cvddR6alGZS7yojOh9941bxehgAeQDLlEisaJcNa2Od5Vbap2zipjc5sXMxZakQVChL27oH1wWhHkQ==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-strike@2.10.4': + resolution: {integrity: sha512-OibipsomFpOJWTPVX/z4Z53HgwDA93lE/loHGa+ONJfML1dO6Zd6UTwzaVO1/g8WOwRgwkYu/6JnhxLKRlP8Lg==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-text-style@2.10.4': + resolution: {integrity: sha512-ibq7avkcwHyUSG53Hf+P31rrwsKVbbiqbWZM4kXC7M2X3iUwFrtvaa+SWzyWQfE1jl2cCrD1+rfSkj/alcOKGg==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-text@2.10.4': + resolution: {integrity: sha512-wPdVxCHrIS9S+8n08lgyyqRZPj9FBbyLlFt74/lV5yBC3LOorq1VKdjrTskmaj4jud7ImXoKDyBddAYTHdJ1xw==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/pm@2.10.4': + resolution: {integrity: sha512-pZ4NEkRtYoDLe0spARvXZ1N3hNv/5u6vfPdPtEbmNpoOSjSNqDC1kVM+qJY0iaCYpxbxcv7cxn3kBumcFLQpJQ==} + + '@tiptap/react@2.10.4': + resolution: {integrity: sha512-JTeqDB+xgjo46QC9ILRXe2TcSfxKVRwhZ3vDvYoemN7giRk5a/WsCF1VQIT1fax+tCl6kfv3U1f4Mkx0DkbPkA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tiptap/starter-kit@2.10.4': + resolution: {integrity: sha512-tu/WCs9Mkr5Nt8c3/uC4VvAbQlVX0OY7ygcqdzHGUeG9zP3twdW7o5xM3kyDKR2++sbVzqu5Ll5qNU+1JZvPGQ==} + + '@tiptap/suggestion@2.10.4': + resolution: {integrity: sha512-7Bzcn1REA7OmVRxiMF2kVK9EhosXotdLAGaEvSbn4zQtHCJG0tREuYvPy53LGzVuPkBDR6Pf6sp1QbGvSne/8g==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 '@tootallnate/once@1.1.2': resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} @@ -3992,15 +4089,24 @@ packages: '@types/katex@0.16.3': resolution: {integrity: sha512-CeVMX9EhVUW8MWnei05eIRks4D5Wscw/W9Byz1s3PA+yJvcdvq9SaDjiUKvRvEgjpdTyJMjQA43ae4KTwsvOPg==} + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + '@types/lodash-es@4.17.10': resolution: {integrity: sha512-YJP+w/2khSBwbUSFdGsSqmDvmnN3cCKoPOL7Zjle6s30ZtemkkqhjVfFqGwPN7ASil5VyjE2GtyU/yqYY6mC0A==} '@types/lodash@4.14.200': resolution: {integrity: sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==} + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + '@types/mdast@3.0.13': resolution: {integrity: sha512-HjiGiWedR0DVFkeNljpa6Lv4/IZU1+30VY5d747K7lBudFc3R0Ibr6yJ9lN3BE28VnZyDfLF/VB1Ql1ZIbKrmg==} + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/minimatch@5.1.2': resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} @@ -8842,8 +8948,8 @@ packages: prosemirror-collab@1.3.1: resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} - prosemirror-commands@1.6.0: - resolution: {integrity: sha512-xn1U/g36OqXn2tn5nGmvnnimAj/g1pUx2ypJJIe8WkVX83WyJVC5LTARaxZa2AtQRwntu9Jc5zXs9gL9svp/mg==} + prosemirror-commands@1.6.2: + resolution: {integrity: sha512-0nDHH++qcf/BuPLYvmqZTUUsPJUCPBUXt0J1ErTcDIS369CTp773itzLGIgIXG4LJXOlwYCr44+Mh4ii6MP1QA==} prosemirror-dropcursor@1.8.1: resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==} @@ -8860,14 +8966,14 @@ packages: prosemirror-keymap@1.2.2: resolution: {integrity: sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==} - prosemirror-markdown@1.13.0: - resolution: {integrity: sha512-UziddX3ZYSYibgx8042hfGKmukq5Aljp2qoBiJRejD/8MH70siQNz5RB1TrdTPheqLMy4aCe4GYNF10/3lQS5g==} + prosemirror-markdown@1.13.1: + resolution: {integrity: sha512-Sl+oMfMtAjWtlcZoj/5L/Q39MpEnVZ840Xo330WJWUvgyhNmLBLN7MsHn07s53nG/KImevWHSE6fEj4q/GihHw==} prosemirror-menu@1.2.4: resolution: {integrity: sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==} - prosemirror-model@1.22.3: - resolution: {integrity: sha512-V4XCysitErI+i0rKFILGt/xClnFJaohe/wrrlT2NSZ+zk8ggQfDH4x2wNK7Gm0Hp4CIoWizvXFP7L9KMaCuI0Q==} + prosemirror-model@1.24.1: + resolution: {integrity: sha512-YM053N+vTThzlWJ/AtPtF1j0ebO36nvbmDy4U7qA2XQB8JVaQp1FmB9Jhrps8s+z+uxhhVTny4m20ptUvhk0Mg==} prosemirror-schema-basic@1.2.3: resolution: {integrity: sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==} @@ -8878,21 +8984,21 @@ packages: prosemirror-state@1.4.3: resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==} - prosemirror-tables@1.5.0: - resolution: {integrity: sha512-VMx4zlYWm7aBlZ5xtfJHpqa3Xgu3b7srV54fXYnXgsAcIGRqKSrhiK3f89omzzgaAgAtDOV4ImXnLKhVfheVNQ==} + prosemirror-tables@1.6.1: + resolution: {integrity: sha512-p8WRJNA96jaNQjhJolmbxTzd6M4huRE5xQ8OxjvMhQUP0Nzpo4zz6TztEiwk6aoqGBhz9lxRWR1yRZLlpQN98w==} - prosemirror-trailing-node@2.0.9: - resolution: {integrity: sha512-YvyIn3/UaLFlFKrlJB6cObvUhmwFNZVhy1Q8OpW/avoTbD/Y7H5EcjK4AZFKhmuS6/N6WkGgt7gWtBWDnmFvHg==} + prosemirror-trailing-node@3.0.0: + resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} peerDependencies: prosemirror-model: ^1.22.1 prosemirror-state: ^1.4.2 prosemirror-view: ^1.33.8 - prosemirror-transform@1.10.0: - resolution: {integrity: sha512-9UOgFSgN6Gj2ekQH5CTDJ8Rp/fnKR2IkYfGdzzp5zQMFsS4zDllLVx/+jGcX86YlACpG7UR5fwAXiWzxqWtBTg==} + prosemirror-transform@1.10.2: + resolution: {integrity: sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==} - prosemirror-view@1.34.1: - resolution: {integrity: sha512-KS2xmqrAM09h3SLu1S2pNO/ZoIP38qkTJ6KFd7+BeSfmX/ek0n5yOfGuiTZjFNTC8GOsEIUa1tHxt+2FMu3yWQ==} + prosemirror-view@1.37.1: + resolution: {integrity: sha512-MEAnjOdXU1InxEmhjgmEzQAikaS6lF3hD64MveTPpjOGNTl87iRLA1HupC/DEV6YuK7m4Q9DHFNTjwIVtqz5NA==} proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -13857,7 +13963,7 @@ snapshots: - encoding - supports-color - '@remirror/core-constants@2.0.2': {} + '@remirror/core-constants@3.0.0': {} '@repeaterjs/repeater@3.0.5': {} @@ -14218,81 +14324,175 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 3.3.3(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.2.2)) - '@tiptap/core@2.6.6(@tiptap/pm@2.6.6)': + '@tiptap/core@2.10.4(@tiptap/pm@2.10.4)': dependencies: - '@tiptap/pm': 2.6.6 + '@tiptap/pm': 2.10.4 - '@tiptap/extension-bubble-menu@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)': + '@tiptap/extension-blockquote@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': dependencies: - '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) - '@tiptap/pm': 2.6.6 + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-bold@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-bubble-menu@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 tippy.js: 6.3.7 - '@tiptap/extension-document@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))': + '@tiptap/extension-bullet-list@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-code-block@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 + + '@tiptap/extension-code@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-document@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-dropcursor@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)': dependencies: - '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 - '@tiptap/extension-floating-menu@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)': + '@tiptap/extension-floating-menu@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)': dependencies: - '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) - '@tiptap/pm': 2.6.6 + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 tippy.js: 6.3.7 - '@tiptap/extension-mention@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)(@tiptap/suggestion@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6))': + '@tiptap/extension-gapcursor@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)': dependencies: - '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) - '@tiptap/pm': 2.6.6 - '@tiptap/suggestion': 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6) + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 - '@tiptap/extension-paragraph@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))': + '@tiptap/extension-hard-break@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': dependencies: - '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) - '@tiptap/extension-placeholder@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)': + '@tiptap/extension-heading@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': dependencies: - '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) - '@tiptap/pm': 2.6.6 + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) - '@tiptap/extension-text@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))': + '@tiptap/extension-history@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)': dependencies: - '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 - '@tiptap/pm@2.6.6': + '@tiptap/extension-horizontal-rule@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 + + '@tiptap/extension-italic@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-list-item@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-mention@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)(@tiptap/suggestion@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 + '@tiptap/suggestion': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) + + '@tiptap/extension-ordered-list@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-paragraph@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-placeholder@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 + + '@tiptap/extension-strike@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-text-style@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/extension-text@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + + '@tiptap/pm@2.10.4': dependencies: prosemirror-changeset: 2.2.1 prosemirror-collab: 1.3.1 - prosemirror-commands: 1.6.0 + prosemirror-commands: 1.6.2 prosemirror-dropcursor: 1.8.1 prosemirror-gapcursor: 1.3.2 prosemirror-history: 1.4.1 prosemirror-inputrules: 1.4.0 prosemirror-keymap: 1.2.2 - prosemirror-markdown: 1.13.0 + prosemirror-markdown: 1.13.1 prosemirror-menu: 1.2.4 - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 prosemirror-schema-basic: 1.2.3 prosemirror-schema-list: 1.4.1 prosemirror-state: 1.4.3 - prosemirror-tables: 1.5.0 - prosemirror-trailing-node: 2.0.9(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)(prosemirror-view@1.34.1) - prosemirror-transform: 1.10.0 - prosemirror-view: 1.34.1 + prosemirror-tables: 1.6.1 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.1) + prosemirror-transform: 1.10.2 + prosemirror-view: 1.37.1 - '@tiptap/react@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@tiptap/react@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) - '@tiptap/extension-bubble-menu': 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6) - '@tiptap/extension-floating-menu': 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6) - '@tiptap/pm': 2.6.6 + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/extension-bubble-menu': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) + '@tiptap/extension-floating-menu': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 '@types/use-sync-external-store': 0.0.6 + fast-deep-equal: 3.1.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) use-sync-external-store: 1.2.2(react@18.2.0) - '@tiptap/suggestion@2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)': - dependencies: - '@tiptap/core': 2.6.6(@tiptap/pm@2.6.6) - '@tiptap/pm': 2.6.6 + '@tiptap/starter-kit@2.10.4': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/extension-blockquote': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-bold': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-bullet-list': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-code': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-code-block': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) + '@tiptap/extension-document': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-dropcursor': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) + '@tiptap/extension-gapcursor': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) + '@tiptap/extension-hard-break': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-heading': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-history': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) + '@tiptap/extension-horizontal-rule': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4) + '@tiptap/extension-italic': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-list-item': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-ordered-list': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-paragraph': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-strike': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-text': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/extension-text-style': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4)) + '@tiptap/pm': 2.10.4 + + '@tiptap/suggestion@2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))(@tiptap/pm@2.10.4)': + dependencies: + '@tiptap/core': 2.10.4(@tiptap/pm@2.10.4) + '@tiptap/pm': 2.10.4 '@tootallnate/once@1.1.2': {} @@ -14420,16 +14620,25 @@ snapshots: '@types/katex@0.16.3': {} + '@types/linkify-it@5.0.0': {} + '@types/lodash-es@4.17.10': dependencies: '@types/lodash': 4.14.200 '@types/lodash@4.14.200': {} + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + '@types/mdast@3.0.13': dependencies: '@types/unist': 2.0.8 + '@types/mdurl@2.0.0': {} + '@types/minimatch@5.1.2': {} '@types/mocha@10.0.6': {} @@ -16760,11 +16969,11 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - esbuild-plugin-copy@2.1.1(esbuild@0.20.2): + esbuild-plugin-copy@2.1.1(esbuild@0.19.12): dependencies: chalk: 4.1.2 chokidar: 3.6.0 - esbuild: 0.20.2 + esbuild: 0.19.12 fs-extra: 10.1.0 globby: 11.1.0 @@ -16774,12 +16983,6 @@ snapshots: esbuild: 0.19.12 import-meta-resolve: 3.1.1 - esbuild-plugin-polyfill-node@0.3.0(esbuild@0.20.2): - dependencies: - '@jspm/core': 2.0.1 - esbuild: 0.20.2 - import-meta-resolve: 3.1.1 - esbuild@0.19.11: optionalDependencies: '@esbuild/aix-ppc64': 0.19.11 @@ -20497,105 +20700,106 @@ snapshots: prosemirror-changeset@2.2.1: dependencies: - prosemirror-transform: 1.10.0 + prosemirror-transform: 1.10.2 prosemirror-collab@1.3.1: dependencies: prosemirror-state: 1.4.3 - prosemirror-commands@1.6.0: + prosemirror-commands@1.6.2: dependencies: - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.0 + prosemirror-transform: 1.10.2 prosemirror-dropcursor@1.8.1: dependencies: prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.0 - prosemirror-view: 1.34.1 + prosemirror-transform: 1.10.2 + prosemirror-view: 1.37.1 prosemirror-gapcursor@1.3.2: dependencies: prosemirror-keymap: 1.2.2 - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 prosemirror-state: 1.4.3 - prosemirror-view: 1.34.1 + prosemirror-view: 1.37.1 prosemirror-history@1.4.1: dependencies: prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.0 - prosemirror-view: 1.34.1 + prosemirror-transform: 1.10.2 + prosemirror-view: 1.37.1 rope-sequence: 1.3.4 prosemirror-inputrules@1.4.0: dependencies: prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.0 + prosemirror-transform: 1.10.2 prosemirror-keymap@1.2.2: dependencies: prosemirror-state: 1.4.3 w3c-keyname: 2.2.8 - prosemirror-markdown@1.13.0: + prosemirror-markdown@1.13.1: dependencies: + '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 prosemirror-menu@1.2.4: dependencies: crelt: 1.0.6 - prosemirror-commands: 1.6.0 + prosemirror-commands: 1.6.2 prosemirror-history: 1.4.1 prosemirror-state: 1.4.3 - prosemirror-model@1.22.3: + prosemirror-model@1.24.1: dependencies: orderedmap: 2.1.1 prosemirror-schema-basic@1.2.3: dependencies: - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 prosemirror-schema-list@1.4.1: dependencies: - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.0 + prosemirror-transform: 1.10.2 prosemirror-state@1.4.3: dependencies: - prosemirror-model: 1.22.3 - prosemirror-transform: 1.10.0 - prosemirror-view: 1.34.1 + prosemirror-model: 1.24.1 + prosemirror-transform: 1.10.2 + prosemirror-view: 1.37.1 - prosemirror-tables@1.5.0: + prosemirror-tables@1.6.1: dependencies: prosemirror-keymap: 1.2.2 - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.0 - prosemirror-view: 1.34.1 + prosemirror-transform: 1.10.2 + prosemirror-view: 1.37.1 - prosemirror-trailing-node@2.0.9(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)(prosemirror-view@1.34.1): + prosemirror-trailing-node@3.0.0(prosemirror-model@1.24.1)(prosemirror-state@1.4.3)(prosemirror-view@1.37.1): dependencies: - '@remirror/core-constants': 2.0.2 + '@remirror/core-constants': 3.0.0 escape-string-regexp: 4.0.0 - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 prosemirror-state: 1.4.3 - prosemirror-view: 1.34.1 + prosemirror-view: 1.37.1 - prosemirror-transform@1.10.0: + prosemirror-transform@1.10.2: dependencies: - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 - prosemirror-view@1.34.1: + prosemirror-view@1.37.1: dependencies: - prosemirror-model: 1.22.3 + prosemirror-model: 1.24.1 prosemirror-state: 1.4.3 - prosemirror-transform: 1.10.0 + prosemirror-transform: 1.10.2 proto-list@1.2.4: {}