From 1c39bb572d0b9994ace05374e5429bd07f3948ab Mon Sep 17 00:00:00 2001 From: Nikhil Kothari Date: Sun, 22 Oct 2023 12:42:56 +0530 Subject: [PATCH] feat: added tiptap on webapp (#497) * wip: tiptap integration * fix: styling for tiptap editor and channel mention * feat: added tiptap on web app * feat: added styling for links in tiptap * feat: blockquote styling in editor * fix: highlight color of mentions * fix: added styles on renderer * fix: blockquote stylesx --- .../src/types/RavenMessaging/RavenMessage.ts | 31 + raven-app/package.json | 19 +- .../common/EmojiPicker/EmojiPicker.tsx | 25 + .../common/EmojiPicker/emojiPicker.styles.css | 23 + .../src/components/common/TooltipButton.tsx | 14 + .../chat/ChatInput/ChannelMentionList.tsx | 110 ++++ .../chat/ChatInput/FileInput/useFileUpload.ts | 82 +++ .../feature/chat/ChatInput/MentionList.tsx | 113 ++++ .../feature/chat/ChatInput/NoNewLine.tsx | 23 + .../chat/ChatInput/RightToolbarButtons.tsx | 158 +++++ .../chat/ChatInput/TextFormattingMenu.tsx | 174 ++++++ .../feature/chat/ChatInput/Tiptap.tsx | 430 +++++++++++++ .../feature/chat/ChatInput/ToolPanel.tsx | 23 + .../feature/chat/ChatInput/index.ts | 0 .../feature/chat/ChatInput/tiptap.styles.css | 101 +++ .../feature/chat/chat-history/ChatBoxBody.tsx | 87 ++- .../feature/chat/chat-history/ChatHistory.tsx | 3 +- .../feature/chat/chat-input/styles.css | 2 +- .../feature/chat/chat-input/useSendMessage.ts | 42 ++ .../chat/message-reply/PreviousMessageBox.tsx | 11 +- .../feature/file-upload/FileDrop.tsx | 56 +- .../feature/file-upload/FileListItem.tsx | 12 +- .../markdown-viewer/MarkdownRenderer.css | 88 +++ .../EditMessageModal.tsx | 114 +--- .../EmojiPickerButton.tsx | 10 +- .../src/types/RavenMessaging/RavenMessage.ts | 31 + raven-app/yarn.lock | 581 +++++++++++++++++- .../doctype/raven_message/raven_message.json | 6 + .../doctype/raven_message/raven_message.py | 8 +- 29 files changed, 2223 insertions(+), 154 deletions(-) create mode 100644 mobile/src/types/RavenMessaging/RavenMessage.ts create mode 100644 raven-app/src/components/common/EmojiPicker/EmojiPicker.tsx create mode 100644 raven-app/src/components/common/EmojiPicker/emojiPicker.styles.css create mode 100644 raven-app/src/components/common/TooltipButton.tsx create mode 100644 raven-app/src/components/feature/chat/ChatInput/ChannelMentionList.tsx create mode 100644 raven-app/src/components/feature/chat/ChatInput/FileInput/useFileUpload.ts create mode 100644 raven-app/src/components/feature/chat/ChatInput/MentionList.tsx create mode 100644 raven-app/src/components/feature/chat/ChatInput/NoNewLine.tsx create mode 100644 raven-app/src/components/feature/chat/ChatInput/RightToolbarButtons.tsx create mode 100644 raven-app/src/components/feature/chat/ChatInput/TextFormattingMenu.tsx create mode 100644 raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx create mode 100644 raven-app/src/components/feature/chat/ChatInput/ToolPanel.tsx create mode 100644 raven-app/src/components/feature/chat/ChatInput/index.ts create mode 100644 raven-app/src/components/feature/chat/ChatInput/tiptap.styles.css create mode 100644 raven-app/src/components/feature/chat/chat-input/useSendMessage.ts create mode 100644 raven-app/src/types/RavenMessaging/RavenMessage.ts diff --git a/mobile/src/types/RavenMessaging/RavenMessage.ts b/mobile/src/types/RavenMessaging/RavenMessage.ts new file mode 100644 index 00000000..e9f3a460 --- /dev/null +++ b/mobile/src/types/RavenMessaging/RavenMessage.ts @@ -0,0 +1,31 @@ + +export interface RavenMessage{ + creation: string + name: string + modified: string + owner: string + modified_by: string + docstatus: 0 | 1 | 2 + parent?: string + parentfield?: string + parenttype?: string + idx?: number + /** Channel ID : Link - Raven Channel */ + channel_id: string + /** Text : Long Text */ + text?: string + /** JSON : JSON */ + json?: any + /** File : Attach */ + file?: string + /** File Thumbnail : Attach */ + file_thumbnail?: string + /** Message Type : Select */ + message_type?: "Text" | "Image" | "File" + /** Message Reactions : JSON */ + message_reactions?: any + /** Is Reply : Check */ + is_reply?: 0 | 1 + /** Linked Message : Link - Raven Message */ + linked_message?: string +} \ No newline at end of file diff --git a/raven-app/package.json b/raven-app/package.json index d602950b..36f95332 100644 --- a/raven-app/package.json +++ b/raven-app/package.json @@ -14,14 +14,28 @@ "@chakra-ui/react": "^2.5.5", "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.5", + "@tiptap/extension-code-block-lowlight": "^2.1.12", + "@tiptap/extension-highlight": "^2.1.12", + "@tiptap/extension-link": "^2.1.12", + "@tiptap/extension-mention": "^2.1.12", + "@tiptap/extension-placeholder": "^2.1.12", + "@tiptap/extension-typography": "^2.1.12", + "@tiptap/extension-underline": "^2.1.12", + "@tiptap/pm": "^2.1.12", + "@tiptap/react": "^2.1.12", + "@tiptap/starter-kit": "^2.1.12", + "@tiptap/suggestion": "^2.1.12", "@types/js-cookie": "^3.0.3", "@types/unist": "^2.0.6", "chakra-react-select": "^4.6.0", "cmdk": "^0.2.0", + "emoji-picker-element": "^1.18.4", "emoji-picker-react": "^4.4.7", "framer-motion": "^9.0.2", "frappe-react-sdk": "^1.3.7", + "highlight.js": "^11.9.0", "js-cookie": "^3.0.5", + "lowlight": "^3.1.0", "quill-image-drop-and-paste": "^1.3.0", "quill-linkify": "^0.3.0", "quill-mention": "^4.0.0", @@ -38,7 +52,8 @@ "react-virtuoso": "^4.3.8", "rehype-raw": "^6.1.1", "remark-gfm": "^3.0.1", - "timeago-react": "^3.0.5" + "timeago-react": "^3.0.5", + "tippy.js": "^6.3.7" }, "devDependencies": { "@types/react": "^18.0.27", @@ -47,4 +62,4 @@ "typescript": "^4.9.3", "vite": "^4.1.0" } -} \ No newline at end of file +} diff --git a/raven-app/src/components/common/EmojiPicker/EmojiPicker.tsx b/raven-app/src/components/common/EmojiPicker/EmojiPicker.tsx new file mode 100644 index 00000000..5b9b1a57 --- /dev/null +++ b/raven-app/src/components/common/EmojiPicker/EmojiPicker.tsx @@ -0,0 +1,25 @@ +import { createElement, useEffect, useRef } from "react" +import 'emoji-picker-element' +import { useColorModeValue } from "@chakra-ui/react" +import './emojiPicker.styles.css' + +export const EmojiPicker = ({ onSelect }: { onSelect: (emoji: string) => void }) => { + + const className = useColorModeValue('light', 'dark') + const ref = useRef(null) + + useEffect(() => { + + const handler = (event: any) => { + onSelect(event.detail.unicode) + } + ref.current?.addEventListener('emoji-click', handler) + ref.current.skinToneEmoji = '👍' + + return () => { + ref.current?.removeEventListener('emoji-click', handler) + } + }, []) + + return createElement('emoji-picker', { ref, class: className }) +} \ No newline at end of file diff --git a/raven-app/src/components/common/EmojiPicker/emojiPicker.styles.css b/raven-app/src/components/common/EmojiPicker/emojiPicker.styles.css new file mode 100644 index 00000000..5fc3a6ca --- /dev/null +++ b/raven-app/src/components/common/EmojiPicker/emojiPicker.styles.css @@ -0,0 +1,23 @@ +emoji-picker { + --indicator-color: var(--chakra-colors-blue-500); + --input-padding: var(--chakra-space-2); + --input-font-size: var(--chakra-fontSizes-sm); +} + +.picker { + border-radius: 10px; +} + +emoji-picker.light { + --background: var(--chakra-colors-gray-50); + --border-color: var(--chakra-colors-gray-50); + --button-active-background: var(--chakra-colors-gray-200); + --button-hover-background: var(--chakra-colors-gray-200); +} + +emoji-picker.dark { + --background: var(--chakra-colors-gray-800); + --border-color: var(--chakra-colors-gray-900); + --button-active-background: var(--chakra-colors-gray-600); + --button-hover-background: var(--chakra-colors-gray-600); +} \ No newline at end of file diff --git a/raven-app/src/components/common/TooltipButton.tsx b/raven-app/src/components/common/TooltipButton.tsx new file mode 100644 index 00000000..10c2cb34 --- /dev/null +++ b/raven-app/src/components/common/TooltipButton.tsx @@ -0,0 +1,14 @@ +import { Button, ButtonProps, IconButton, IconButtonProps } from "@chakra-ui/react"; +import { forwardRef } from "react"; + +export const TooltipButton = forwardRef(({ children, ...rest }: ButtonProps, ref) => ( + +)) + +export const TooltipIconButton = forwardRef(({ children, ...rest }: IconButtonProps, ref) => ( + + {children} + +)) \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/ChannelMentionList.tsx b/raven-app/src/components/feature/chat/ChatInput/ChannelMentionList.tsx new file mode 100644 index 00000000..c251a4e0 --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/ChannelMentionList.tsx @@ -0,0 +1,110 @@ +// import './MentionList.scss' + +import { useIsUserActive } from '@/hooks/useIsUserActive' +import { ChannelListItem } from '@/utils/channel/ChannelListProvider' +import { getChannelIcon } from '@/utils/layout/channelIcon' +import { UserFields } from '@/utils/users/UserListProvider' +import { Avatar, AvatarBadge, Button, HStack, Icon, Stack, StackDivider, Text, useColorModeValue } from '@chakra-ui/react' +import { ReactRendererOptions } from '@tiptap/react' +import { + forwardRef, useEffect, useImperativeHandle, + useState, +} from 'react' + +export default forwardRef((props: ReactRendererOptions['props'], ref) => { + + const buttonGroupBgColor = useColorModeValue('white', 'gray.900') + const [selectedIndex, setSelectedIndex] = useState(0) + + const selectItem = (index: number) => { + const item = props?.items[index] + if (item) { + props.command({ id: item.name, label: item.channel_name }) + } + } + + const upHandler = () => { + setSelectedIndex((selectedIndex + props?.items.length - 1) % props?.items.length) + } + + const downHandler = () => { + setSelectedIndex((selectedIndex + 1) % props?.items.length) + } + + const enterHandler = () => { + selectItem(selectedIndex) + } + + useEffect(() => setSelectedIndex(0), [props?.items]) + + useImperativeHandle(ref, () => ({ + onKeyDown: ({ event }: { event: KeyboardEvent }) => { + if (event.key === 'ArrowUp') { + upHandler() + return true + } + + if (event.key === 'ArrowDown') { + downHandler() + return true + } + + if (event.key === 'Enter') { + enterHandler() + return true + } + + return false + }, + })) + + return ( + } spacing='0' rounded='md' shadow='md' bgColor={buttonGroupBgColor}> + {props?.items.length + ? props.items.map((item: ChannelListItem, index: number) => ( + + )) + :
No result
+ } +
+ ) +}) + +const MentionItem = ({ item, index, selectItem, selectedIndex, itemsLength }: { itemsLength: number, selectedIndex: number, index: number, item: ChannelListItem, selectItem: (index: number) => void }) => { + + const { selectedBgColor, selectedColor, textColor, backgroundColor } = useColorModeValue({ + selectedBgColor: 'gray.900', + selectedColor: 'white', + textColor: 'gray.900', + backgroundColor: 'white' + }, { + selectedBgColor: 'gray.100', + selectedColor: 'gray.900', + textColor: 'gray.100', + backgroundColor: 'gray.800' + }) + return selectItem(index)} + > + + {item.channel_name} + +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/FileInput/useFileUpload.ts b/raven-app/src/components/feature/chat/ChatInput/FileInput/useFileUpload.ts new file mode 100644 index 00000000..41360e5a --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/FileInput/useFileUpload.ts @@ -0,0 +1,82 @@ +import { CustomFile } from '@/components/feature/file-upload/FileDrop' +import { useRef, useState } from 'react' +import { Message } from '../../../../../../../types/Messaging/Message' +import { useFrappeCreateDoc, useFrappeFileUpload, useFrappeUpdateDoc } from 'frappe-react-sdk' +import { getFileExtension } from '@/utils/operations' + + +export const fileExt = ['jpg', 'JPG', 'jpeg', 'JPEG', 'png', 'PNG', 'gif', 'GIF'] + +export default function useFileUpload(channelID: string, selectedMessage?: Message | null) { + + const fileInputRef = useRef(null) + + const [files, setFiles] = useState([]) + + const { createDoc, loading: creatingDoc, error: errorCreatingDoc, reset: resetCreateDoc } = useFrappeCreateDoc() + const { upload, loading: uploadingFile, progress, error: errorUploadingDoc, reset: resetUploadDoc } = useFrappeFileUpload() + const { updateDoc, loading: updatingDoc, error: errorUpdatingDoc, reset: resetUpdateDoc } = useFrappeUpdateDoc() + + const addFile = (file: File) => { + + const newFile: CustomFile = file as CustomFile + if (newFile) { + newFile.fileID = file.name + Date.now() + newFile.uploadProgress = 0 + setFiles((f: any) => [...f, newFile]) + } + } + const removeFile = (id: string) => { + let newFiles = files.filter(file => file.fileID !== id) + setFiles(newFiles) + } + + const uploadFiles = async () => { + + if (files.length > 0) { + const promises = files.map(async (f: CustomFile) => { + let docname = '' + return createDoc('Raven Message', { + channel_id: channelID + }).then((d) => { + docname = d.name + f.uploading = true + f.uploadProgress = progress + return upload(f, { + isPrivate: true, + doctype: 'Raven Message', + docname: d.name, + fieldname: 'file', + }) + }).then((r) => { + f.uploading = false + return updateDoc("Raven Message", docname, { + file: r.file_url, + message_type: fileExt.includes(getFileExtension(f.name)) ? "Image" : "File", + }) + }) + }) + + return Promise.all(promises) + .then(() => { + setFiles([]) + resetCreateDoc() + resetUploadDoc() + resetUpdateDoc() + }).catch((e) => { + console.log(e) + }) + } else { + return Promise.resolve() + } + } + + return { + fileInputRef, + files, + setFiles, + removeFile, + addFile, + uploadFiles + } +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/MentionList.tsx b/raven-app/src/components/feature/chat/ChatInput/MentionList.tsx new file mode 100644 index 00000000..70d550d7 --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/MentionList.tsx @@ -0,0 +1,113 @@ +// import './MentionList.scss' + +import { useIsUserActive } from '@/hooks/useIsUserActive' +import { UserFields } from '@/utils/users/UserListProvider' +import { Avatar, AvatarBadge, Button, HStack, Stack, StackDivider, Text, useColorModeValue } from '@chakra-ui/react' +import { ReactRendererOptions } from '@tiptap/react' +import { + forwardRef, useEffect, useImperativeHandle, + useState, +} from 'react' + +export default forwardRef((props: ReactRendererOptions['props'], ref) => { + + const buttonGroupBgColor = useColorModeValue('white', 'gray.900') + const [selectedIndex, setSelectedIndex] = useState(0) + + const selectItem = (index: number) => { + const item = props?.items[index] + + if (item) { + props.command({ id: item.name, label: item.full_name }) + } + } + + const upHandler = () => { + setSelectedIndex((selectedIndex + props?.items.length - 1) % props?.items.length) + } + + const downHandler = () => { + setSelectedIndex((selectedIndex + 1) % props?.items.length) + } + + const enterHandler = () => { + selectItem(selectedIndex) + } + + useEffect(() => setSelectedIndex(0), [props?.items]) + + useImperativeHandle(ref, () => ({ + onKeyDown: ({ event }: { event: KeyboardEvent }) => { + if (event.key === 'ArrowUp') { + upHandler() + return true + } + + if (event.key === 'ArrowDown') { + downHandler() + return true + } + + if (event.key === 'Enter') { + enterHandler() + return true + } + + return false + }, + })) + + return ( + } spacing='0' rounded='md' shadow='md' bgColor={buttonGroupBgColor}> + {props?.items.length + ? props.items.map((item: UserFields, index: number) => ( + + )) + :
No result
+ } +
+ ) +}) + +const MentionItem = ({ item, index, selectItem, selectedIndex, itemsLength }: { itemsLength: number, selectedIndex: number, index: number, item: UserFields, selectItem: (index: number) => void }) => { + + const isActive = useIsUserActive(item.name) + + const { selectedBgColor, selectedColor, textColor, backgroundColor } = useColorModeValue({ + selectedBgColor: 'gray.900', + selectedColor: 'white', + textColor: 'gray.900', + backgroundColor: 'white' + }, { + selectedBgColor: 'gray.100', + selectedColor: 'gray.900', + textColor: 'gray.100', + backgroundColor: 'gray.800' + }) + return selectItem(index)} + > + + {isActive && } + + {item.full_name} + +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/NoNewLine.tsx b/raven-app/src/components/feature/chat/ChatInput/NoNewLine.tsx new file mode 100644 index 00000000..e769bed2 --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/NoNewLine.tsx @@ -0,0 +1,23 @@ +import { Extension } from '@tiptap/core' +import { Plugin, PluginKey } from 'prosemirror-state' + +export const NoNewLine = Extension.create({ + name: 'no_new_line', + + addProseMirrorPlugins() { + return [ + new Plugin({ + key: new PluginKey('eventHandler'), + props: { + handleKeyDown: (view, event) => { + if (event.key === 'Enter' && !event.shiftKey) { + return true + } + } + // … and many, many more. + // Here is the full list: https://prosemirror.net/docs/ref/#view.EditorProps + }, + }), + ] + }, +}) \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/RightToolbarButtons.tsx b/raven-app/src/components/feature/chat/ChatInput/RightToolbarButtons.tsx new file mode 100644 index 00000000..01c3e4b4 --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/RightToolbarButtons.tsx @@ -0,0 +1,158 @@ +import { ButtonGroup, HStack, IconButton, Popover, PopoverContent, PopoverTrigger, StackDivider } from '@chakra-ui/react' +import { useCurrentEditor } from '@tiptap/react' +import { BiAt, BiHash, BiSend, BiSmile } from 'react-icons/bi' +import { ICON_PROPS } from './ToolPanel' +import { EmojiPicker } from '../../../common/EmojiPicker/EmojiPicker' +import { ToolbarFileProps } from './Tiptap' +import { AiOutlinePaperClip } from 'react-icons/ai' + +type RightToolbarButtonsProps = { + fileProps?: ToolbarFileProps, + sendMessage: (html: string, json: any) => Promise, + messageSending: boolean +} +/** + * Component to render the right toolbar buttons: + * 1. User Mention + * 2. Channel Mention + * 3. Emoji picker + * 4. File upload + * 5. Send button + * @param props + * @returns + */ +export const RightToolbarButtons = ({ fileProps, ...sendProps }: RightToolbarButtonsProps) => { + return ( + }> + + + + {fileProps && } + + + + ) +} + +const MentionButtons = () => { + const { editor } = useCurrentEditor() + + if (!editor) { + return null + } + + return + editor.chain().focus().insertContent('#').run()} + aria-label='mention channel' + title='Mention a channel' + icon={} + isDisabled={ + !editor.can() + .chain() + .focus() + .insertContent('#') + .run() || !editor.isEditable + } /> + editor.chain().focus().insertContent('@').run()} + aria-label='mention user' + title='Mention a user' + icon={} + isDisabled={ + !editor.can() + .chain() + .focus() + .insertContent('@') + .run() || !editor.isEditable + } /> + +} + + +const EmojiPickerButton = () => { + const { editor } = useCurrentEditor() + + if (!editor) { + return null + } + + return + + } /> + + + editor.chain().focus().insertContent(e).run()} /> + + +} + +const FilePickerButton = ({ fileProps }: { fileProps: ToolbarFileProps }) => { + const { editor } = useCurrentEditor() + const fileButtonClicked = () => { + if (fileProps.fileInputRef?.current) { + fileProps.fileInputRef?.current.openFileInput() + } + } + + return } /> +} + + +const SendButton = ({ sendMessage, messageSending }: { + sendMessage: RightToolbarButtonsProps['sendMessage'], + messageSending: boolean +}) => { + const { editor } = useCurrentEditor() + const onClick = () => { + if (editor) { + + // console.log('editor.isActive', editor.state) + + // console.log(editor.getJSON()) + + + const hasContent = editor.getText().trim().length > 0 + + let html = '' + let json = {} + if (hasContent) { + html = editor.getHTML() + json = editor.getJSON() + } + + console.log("Will send a message") + editor.setEditable(false) + sendMessage(html, json) + .then(() => { + editor.chain().focus().clearContent().run() + editor.setEditable(true) + }) + .catch(() => { + editor.setEditable(true) + }) + } + } + + return } + /> +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/TextFormattingMenu.tsx b/raven-app/src/components/feature/chat/ChatInput/TextFormattingMenu.tsx new file mode 100644 index 00000000..5d6c800b --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/TextFormattingMenu.tsx @@ -0,0 +1,174 @@ +import { Box, ButtonGroup, HStack, IconButton, StackDivider, border, useColorModeValue } from '@chakra-ui/react' +import { useCurrentEditor } from '@tiptap/react' +import { BiBold, BiCode, BiCodeAlt, BiHighlight, BiItalic, BiLink, BiListOl, BiListUl, BiStrikethrough, BiUnderline } from 'react-icons/bi' +import { BsBlockquoteLeft } from 'react-icons/bs' +import { ICON_PROPS } from './ToolPanel' + +type Props = {} +export const TextFormattingMenu = (props: Props) => { + + const { editor } = useCurrentEditor() + + const highlightBgColor = useColorModeValue('blue.50', 'blue.900') + + if (!editor) { + return + } + return ( + // + }> + + editor.chain().focus().toggleBold().run()} + aria-label='bold' + title='Bold' + icon={} + colorScheme={editor.isActive('bold') ? 'blue' : 'gray'} + bgColor={editor.isActive('bold') ? highlightBgColor : 'transparent'} + isDisabled={ + !editor.can() + .chain() + .focus() + .toggleBold() + .run() + } /> + + editor.chain().focus().toggleItalic().run()} + aria-label='italic' + title='Italic' + icon={} + colorScheme={editor.isActive('italic') ? 'blue' : 'gray'} + bgColor={editor.isActive('italic') ? highlightBgColor : 'transparent'} + isDisabled={ + !editor.can() + .chain() + .focus() + .toggleItalic() + .run() + } /> + editor.chain().focus().toggleUnderline().run()} + aria-label='underline' + title='Underline' + icon={} + colorScheme={editor.isActive('underline') ? 'blue' : 'gray'} + bgColor={editor.isActive('underline') ? highlightBgColor : 'transparent'} + isDisabled={ + !editor.can() + .chain() + .focus() + .toggleUnderline() + .run() + } + /> + + + + + + editor.chain().focus().toggleCodeBlock().run()} + aria-label='code' + title='Code' + icon={} + colorScheme={editor.isActive('codeBlock') ? 'blue' : 'gray'} + bgColor={editor.isActive('codeBlock') ? highlightBgColor : 'transparent'} + isDisabled={ + !editor.can() + .chain() + .focus() + .toggleCodeBlock() + .run() + } + /> + editor.chain().focus().toggleStrike().run()} + aria-label='strike' + title='Strike' + icon={} + colorScheme={editor.isActive('strike') ? 'blue' : 'gray'} + bgColor={editor.isActive('strike') ? highlightBgColor : 'transparent'} + isDisabled={ + !editor.can() + .chain() + .focus() + .toggleStrike() + .run() + } + /> + + + + editor.chain().focus().toggleBlockquote().run()} + aria-label='blockquote' + bgColor={editor.isActive('blockquote') ? highlightBgColor : 'transparent'} + title='Blockquote' + isDisabled={ + !editor.can() + .chain() + .focus() + .toggleBlockquote() + .run() + } + icon={} + colorScheme={editor.isActive('blockquote') ? 'blue' : 'gray'} + /> + + + editor.chain().focus().toggleOrderedList().run()} + aria-label='ordered list' + title='Ordered List' + icon={} + colorScheme={editor.isActive('orderedList') ? 'blue' : 'gray'} + bgColor={editor.isActive('orderedList') ? highlightBgColor : 'transparent'} + isDisabled={ + !editor.can() + .chain() + .focus() + .toggleOrderedList() + .run() + } + /> + editor.chain().focus().liftEmptyBlock().toggleBulletList().run()} + aria-label='bullet list' + title='Bullet List' + bgColor={editor.isActive('bulletList') ? highlightBgColor : 'transparent'} + icon={} + colorScheme={editor.isActive('bulletList') ? 'blue' : 'gray'} + isDisabled={ + !editor.can() + .chain() + .focus() + .toggleBulletList() + .run() + } + /> + + + + + editor.chain().focus().toggleHighlight().run()} + title='Highlight' + bgColor={editor.isActive('highlight') ? highlightBgColor : 'transparent'} + icon={} + isDisabled={ + !editor.can() + .chain() + .focus() + .toggleHighlight() + .run() + } + /> + + + // + ) +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx b/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx new file mode 100644 index 00000000..3d43f280 --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx @@ -0,0 +1,430 @@ +import { Card, CardBody, useColorModeValue } from '@chakra-ui/react' +// import TextStyle from '@tiptap/extension-text-style' +import { EditorProvider, Extension, ReactRenderer } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import Underline from '@tiptap/extension-underline' +import React, { useContext } from 'react' +import { TextFormattingMenu } from './TextFormattingMenu' +import Highlight from '@tiptap/extension-highlight' +import Link from '@tiptap/extension-link' +import Placeholder from '@tiptap/extension-placeholder' +import './tiptap.styles.css' +import Mention from '@tiptap/extension-mention' +import { UserListContext } from '@/utils/users/UserListProvider' +import MentionList from './MentionList' +import tippy from 'tippy.js' +import { PluginKey } from '@tiptap/pm/state' +import { ChannelListContext, ChannelListContextType } from '@/utils/channel/ChannelListProvider' +import ChannelMentionList from './ChannelMentionList' +import { ToolPanel } from './ToolPanel' +import { RightToolbarButtons } from './RightToolbarButtons' +import { common, createLowlight } from 'lowlight' +import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight' +import css from 'highlight.js/lib/languages/css' +import js from 'highlight.js/lib/languages/javascript' +import ts from 'highlight.js/lib/languages/typescript' +import html from 'highlight.js/lib/languages/xml' +import json from 'highlight.js/lib/languages/json' +import python from 'highlight.js/lib/languages/python' +import { Plugin } from 'prosemirror-state' +const lowlight = createLowlight(common) + +lowlight.register('html', html) +lowlight.register('css', css) +lowlight.register('js', js) +lowlight.register('ts', ts) +lowlight.register('json', json) +lowlight.register('python', python) +export interface ToolbarFileProps { + fileInputRef: React.MutableRefObject, + addFile: (files: File) => void, +} +type TiptapEditorProps = { + slotBefore?: React.ReactNode, + slotAfter?: React.ReactNode, + fileProps?: ToolbarFileProps, + onMessageSend: (message: string, json: any) => Promise, + messageSending: boolean, + defaultText?: string +} + +const COOL_PLACEHOLDERS = [ + "Sure - you can send your message via pigeons, only if you want them covered in poop 😷", + "Delivering messages atop dragons 🐉 is available on a chargeable basis.", + "Note 🚨: Service beyond the wall is currently disrupted due to bad weather.", + "Pigeons just have better brand recognition tbh 🤷🏻", + "Ravens double up as spies. Eyes everywhere 👀", + "Ravens do not 'slack' off. See what we did there? 😉", + "Were you expecting a funny placeholder? 😂", + "Want to know who writes these placeholders? 🤔. No one.", + "Type a message..." +] + +const UserMention = Mention.extend({ + name: 'userMention', +}) + .configure({ + suggestion: { + char: '@', + pluginKey: new PluginKey('userMention'), + } + }) + +const ChannelMention = Mention.extend({ + name: 'channelMention', +}) + .configure({ + suggestion: { + char: '#', + pluginKey: new PluginKey('channelMention'), + } + }) +export const Tiptap = ({ slotAfter, slotBefore, fileProps, onMessageSend, messageSending, defaultText = '' }: TiptapEditorProps) => { + + const { users } = useContext(UserListContext) + + const { channels } = useContext(ChannelListContext) as ChannelListContextType + + + // this is a dummy extension only to create custom keydown behavior + const KeyboardHandler = Extension.create({ + name: 'keyboardHandler', + addKeyboardShortcuts() { + return { + Enter: () => { + + console.log("Enter clicked") + const isCodeBlockActive = this.editor.isActive('codeBlock'); + const isListItemActive = this.editor.isActive('listItem'); + + //@ts-expect-error + const isSuggestionOpen = this.editor.state.userMention$.active || this.editor.state.channelMention$.active + const hasContent = this.editor.getText().trim().length > 0 + + if (isCodeBlockActive || isListItemActive || isSuggestionOpen) { + return false; + } + let html = '' + let json = {} + if (hasContent) { + html = this.editor.getHTML() + json = this.editor.getJSON() + } + + this.editor.setEditable(false) + + console.log('Calling message') + onMessageSend(html, json) + .then(() => { + this.editor.commands.clearContent(); + this.editor.setEditable(true) + }) + .catch(() => { + this.editor.setEditable(true) + }) + return this.editor.commands.clearContent(); + }, + + 'Mod-Enter': () => { + const isCodeBlockActive = this.editor.isActive('codeBlock'); + const isListItemActive = this.editor.isActive('listItem'); + const hasContent = this.editor.getText().trim().length > 0 + /** + * when inside of a codeblock and setting for sending the message with CMD/CTRL-Enter + * force calling the `onSubmit` function and clear the editor content + */ + if (isCodeBlockActive) { + return this.editor.commands.newlineInCode(); + } + + if (isListItemActive) { + return false + } + + if (!isCodeBlockActive && !isListItemActive) { + let html = '' + let json = {} + if (hasContent) { + html = this.editor.getHTML() + json = this.editor.getJSON() + } + + this.editor.setEditable(false) + onMessageSend(html, json) + .then(() => { + this.editor.commands.clearContent(); + this.editor.setEditable(true) + }) + .catch(() => { + this.editor.setEditable(true) + }) + return this.editor.commands.clearContent(); + } + + return false; + }, + + // 'Shift-Enter': () => { + // /** + // * currently we do not have an option to show a soft line break in the posts, so we overwrite + // * the behavior from tiptap with the default behavior on pressing enter + // */ + // return this.editor.commands.first(({ commands }) => [ + // () => commands.newlineInCode(), + // () => commands.createParagraphNear(), + // () => commands.liftEmptyBlock(), + // () => commands.splitBlock(), + // ]); + // }, + }; + }, + addProseMirrorPlugins() { + return [ + new Plugin({ + props: { + handleDOMEvents: { + drop(view, event) { + const hasFiles = + event.dataTransfer && + event.dataTransfer.files && + event.dataTransfer.files.length + + if (!hasFiles || !fileProps) { + return + } + + const images = Array.from(event.dataTransfer.files).filter(file => + /image/i.test(file.type) + ) + + if (images.length === 0) { + return + } + + event.preventDefault() + + images.forEach(image => { + fileProps.addFile(image) + }) + }, + paste(view, event) { + const hasFiles = + event.clipboardData && + event.clipboardData.files && + event.clipboardData.files.length + + if (!hasFiles || !fileProps) { + return + } + + const images = Array.from( + event.clipboardData.files + ).filter(file => /image/i.test(file.type)) + + if (images.length === 0) { + return + } + + event.preventDefault() + + images.forEach(image => { + + fileProps.addFile(image) + }) + } + } + } + }) + ] + } + }); + const extensions = [ + StarterKit.configure({ + heading: false, + codeBlock: false, + }), + UserMention.configure({ + HTMLAttributes: { + class: 'mention', + }, + renderLabel({ options, node }) { + return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}` + }, + suggestion: { + items: (query) => { + return users.filter((user) => user.full_name.toLowerCase().startsWith(query.query.toLowerCase())) + .slice(0, 10); + }, + // char: '@', + render: () => { + let component: any; + let popup: any; + + return { + onStart: props => { + component = new ReactRenderer(MentionList, { + props, + editor: props.editor, + }) + + if (!props.clientRect) { + return + } + + popup = tippy('body' as any, { + getReferenceClientRect: props.clientRect as any, + appendTo: () => document.body, + content: component.element, + showOnCreate: true, + interactive: true, + trigger: 'manual', + placement: 'bottom-start', + }) + }, + + onUpdate(props) { + component.updateProps(props) + + if (!props.clientRect) { + return + } + + popup[0].setProps({ + getReferenceClientRect: props.clientRect, + }) + }, + + onKeyDown(props) { + if (props.event.key === 'Escape') { + popup[0].hide() + + return true + } + + return component.ref?.onKeyDown(props) + }, + + onExit() { + popup[0].destroy() + component.destroy() + }, + } + }, + + } + }), + ChannelMention.configure({ + HTMLAttributes: { + class: 'mention', + }, + renderLabel({ options, node }) { + return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}` + }, + suggestion: { + items: (query) => { + return channels.filter((channel) => channel.channel_name.toLowerCase().startsWith(query.query.toLowerCase())) + .slice(0, 10); + }, + // char: '#', + render: () => { + let component: any; + let popup: any; + + return { + onStart: props => { + component = new ReactRenderer(ChannelMentionList, { + props, + editor: props.editor, + }) + + if (!props.clientRect) { + return + } + + popup = tippy('body' as any, { + getReferenceClientRect: props.clientRect as any, + appendTo: () => document.body, + content: component.element, + showOnCreate: true, + interactive: true, + trigger: 'manual', + placement: 'bottom-start', + }) + }, + + onUpdate(props) { + component.updateProps(props) + + if (!props.clientRect) { + return + } + + popup[0].setProps({ + getReferenceClientRect: props.clientRect, + }) + }, + + onKeyDown(props) { + if (props.event.key === 'Escape') { + popup[0].hide() + + return true + } + + return component.ref?.onKeyDown(props) + }, + + onExit() { + popup[0].destroy() + component.destroy() + }, + } + }, + + } + }), + Underline, + Highlight.configure({ + multicolor: true, + }), + Link.configure({ + protocols: ['mailto', 'https', 'http'] + }), + Placeholder.configure({ + // Pick a random placeholder from the list. + placeholder: COOL_PLACEHOLDERS[Math.floor(Math.random() * (COOL_PLACEHOLDERS.length))], + }), + CodeBlockLowlight.configure({ + lowlight + }), + KeyboardHandler + ] + + const { cardBorderColor, bgColor } = useColorModeValue({ + cardBorderColor: 'gray.200', + bgColor: 'gray.50', + }, { + cardBorderColor: 'gray.700', + bgColor: 'gray.800' + }) + + return ( + + + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/ToolPanel.tsx b/raven-app/src/components/feature/chat/ChatInput/ToolPanel.tsx new file mode 100644 index 00000000..8d6a5c46 --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/ToolPanel.tsx @@ -0,0 +1,23 @@ +import { HStack, StackProps, useColorModeValue } from '@chakra-ui/react' + +export const ICON_PROPS = { + size: '20px' +} +export const ToolPanel = (props: StackProps) => { + + const buttonGroupBgColor = useColorModeValue('white', 'gray.900') + const borderTopColor = useColorModeValue('gray.100', 'gray.700') + return ( + + + ) +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/index.ts b/raven-app/src/components/feature/chat/ChatInput/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/raven-app/src/components/feature/chat/ChatInput/tiptap.styles.css b/raven-app/src/components/feature/chat/ChatInput/tiptap.styles.css new file mode 100644 index 00000000..0af19a4a --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/tiptap.styles.css @@ -0,0 +1,101 @@ +.tiptap p.is-editor-empty:first-child::before { + color: var(--chakra-colors-gray-500); + content: attr(data-placeholder); + float: left; + font-size: 14px; + height: 0; + pointer-events: none; +} + +.tiptap.ProseMirror { + min-height: 60px; + padding: var(--chakra-space-2); +} + +.tiptap blockquote { + border-left: 3px solid var(--chakra-colors-gray-300); + padding-left: 0.8rem; + margin: 1rem; +} + +.tiptap ul, +.tiptap ol { + padding-left: var(--chakra-space-8); +} + +.tiptap a, +.tiptap .mention { + color: var(--chakra-colors-blue-500); +} + +.tiptap a { + text-decoration: underline; + cursor: pointer; +} + +.tiptap pre { + background: #0d0d0d; + border-radius: 0.5rem; + color: #fff; + font-family: "JetBrainsMono", monospace; + padding: 0.75rem 1rem; +} + +.tiptap pre code { + background: none; + color: inherit; + font-size: 0.8rem; + padding: 0; +} + +.tiptap pre .hljs-comment, +.tiptap pre .hljs-quote { + color: #616161; +} + +.tiptap pre .hljs-variable, +.tiptap pre .hljs-template-variable, +.tiptap pre .hljs-attribute, +.tiptap pre .hljs-tag, +.tiptap pre .hljs-name, +.tiptap pre .hljs-regexp, +.tiptap pre .hljs-link, +.tiptap pre .hljs-name, +.tiptap pre .hljs-selector-id, +.tiptap pre .hljs-selector-class { + color: #f98181; +} + +.tiptap pre .hljs-number, +.tiptap pre .hljs-meta, +.tiptap pre .hljs-built_in, +.tiptap pre .hljs-builtin-name, +.tiptap pre .hljs-literal, +.tiptap pre .hljs-type, +.tiptap pre .hljs-params { + color: #fbbc88; +} + +.tiptap pre .tiptap pre .hljs-string, +.tiptap pre .hljs-symbol, +.tiptap pre .hljs-bullet { + color: #b9f18d; +} + +.tiptap pre .hljs-title, +.tiptap pre .hljs-section { + color: #faf594; +} + +.tiptap pre .hljs-keyword, +.tiptap pre .hljs-selector-tag { + color: #70cff8; +} + +.tiptap pre .hljs-emphasis { + font-style: italic; +} + +.tiptap pre .hljs-strong { + font-weight: 700; +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/chat-history/ChatBoxBody.tsx b/raven-app/src/components/feature/chat/chat-history/ChatBoxBody.tsx index 58b10114..e9fed8f5 100644 --- a/raven-app/src/components/feature/chat/chat-history/ChatBoxBody.tsx +++ b/raven-app/src/components/feature/chat/chat-history/ChatBoxBody.tsx @@ -1,4 +1,4 @@ -import { Box, Stack } from "@chakra-ui/react" +import { Box, Stack, Wrap, WrapItem } from "@chakra-ui/react" import { ChatHistory } from "./ChatHistory" import { useFrappeEventListener, useFrappeGetCall } from "frappe-react-sdk" import { Message, MessagesWithDate } from "../../../../../../types/Messaging/Message" @@ -12,6 +12,12 @@ import { ChatInput } from "../chat-input" import { useUserData } from "@/hooks/useUserData" import { ChannelMembersContext, ChannelMembersContextType } from "@/utils/channel/ChannelMembersProvider" import { UserContext } from "@/utils/auth/UserProvider" +import { Tiptap } from "../ChatInput/Tiptap" +import useFileUpload from "../ChatInput/FileInput/useFileUpload" +import { CustomFile, FileDrop } from "../../file-upload/FileDrop" +import { FileListItem } from "../../file-upload/FileListItem" +import { PreviousMessageBox } from "../message-reply/PreviousMessageBox" +import { useSendMessage } from "../chat-input/useSendMessage" interface ChatBoxBodyProps { channelData: ChannelListItem | DMChannelListItem @@ -56,6 +62,40 @@ export const ChatBoxBody = ({ channelData }: ChatBoxBodyProps) => { return false }, [user, channelMembers]) + const { fileInputRef, files, setFiles, removeFile, uploadFiles, addFile } = useFileUpload(channelData.name) + + const { sendMessage, loading } = useSendMessage(channelData.name, files.length, uploadFiles, handleCancelReply, selectedMessage) + + const PreviousMessagePreview = () => { + + if (selectedMessage) { + return + } + return null + } + const FilePreviewList = () => { + if (files.length === 0) { + return null + } + return + {files.map((f: CustomFile) => removeFile(f.fileID)} />)} + + } + + const EditorSlot = () => { + if (selectedMessage || files.length > 0) { + return + {PreviousMessagePreview()} + {FilePreviewList()} + + } else { + return null + } + } + if (isLoading) { return } @@ -66,23 +106,44 @@ export const ChatBoxBody = ({ channelData }: ChatBoxBodyProps) => { if (data) { return ( - - - {channelData?.is_archived == 0 && ((isUserInChannel || channelData?.type === 'Open') && + + + + + {channelData?.is_archived == 0 && (isUserInChannel || channelData?.type === 'Open') + && + } + /> + } + {channelData?.is_archived == 0 && (!isUserInChannel && channelData?.type !== 'Open' && + )} + + {/* {channelData?.is_archived == 0 && ((isUserInChannel || channelData?.type === 'Open') && )} - {channelData?.is_archived == 0 && (!isUserInChannel && channelData?.type !== 'Open' && - )} + channelMembers={channelMembers} />)} */} + {channelData && channelData.is_archived == 1 && } ) diff --git a/raven-app/src/components/feature/chat/chat-history/ChatHistory.tsx b/raven-app/src/components/feature/chat/chat-history/ChatHistory.tsx index fb5f20b0..b3ac1a06 100644 --- a/raven-app/src/components/feature/chat/chat-history/ChatHistory.tsx +++ b/raven-app/src/components/feature/chat/chat-history/ChatHistory.tsx @@ -1,4 +1,4 @@ -import { Box, useColorMode } from "@chakra-ui/react"; +import { Box, Stack, useColorMode } from "@chakra-ui/react"; import { DividerWithText } from "../../../layout/Divider/DividerWithText"; import { DateObjectToFormattedDateString } from "../../../../utils/operations"; import { DateBlock, FileMessage, Message, MessageBlock, MessagesWithDate } from "../../../../../../types/Messaging/Message"; @@ -58,6 +58,7 @@ export const ChatHistory = ({ parsedMessages, replyToMessage, channelData }: Cha return ( diff --git a/raven-app/src/components/feature/chat/chat-input/styles.css b/raven-app/src/components/feature/chat/chat-input/styles.css index 30b50235..39c3eaef 100644 --- a/raven-app/src/components/feature/chat/chat-input/styles.css +++ b/raven-app/src/components/feature/chat/chat-input/styles.css @@ -86,7 +86,7 @@ .mention { background-color: var(--gray) !important; border-radius: 4px; - color: #1a97c0; + color: var(--chakra-colors-blue-500); } .ql-mention-list { diff --git a/raven-app/src/components/feature/chat/chat-input/useSendMessage.ts b/raven-app/src/components/feature/chat/chat-input/useSendMessage.ts new file mode 100644 index 00000000..674bec4c --- /dev/null +++ b/raven-app/src/components/feature/chat/chat-input/useSendMessage.ts @@ -0,0 +1,42 @@ +import { useFrappePostCall, useSWRConfig } from 'frappe-react-sdk' +import { Message } from '../../../../../../types/Messaging/Message' +import { CustomFile } from '../../file-upload/FileDrop' + +export const useSendMessage = (channelID: string, noOfFiles: number, uploadFiles: () => Promise, handleCancelReply: VoidFunction, selectedMessage?: Message | null) => { + + const { mutate } = useSWRConfig() + const { call, loading } = useFrappePostCall('raven.raven_messaging.doctype.raven_message.raven_message.send_message') + + const sendMessage = async (content: string, json?: any): Promise => { + + if (content) { + return call({ + channel_id: channelID, + text: content, + json: json, + is_reply: selectedMessage ? 1 : 0, + linked_message: selectedMessage ? selectedMessage.name : null + }) + .then(() => handleCancelReply()) + // .then(() => uploadFiles()) + .then(() => { + mutate(`get_messages_for_channel_${channelID}`) + handleCancelReply() + }) + } else if (noOfFiles > 0) { + // return uploadFiles() + // .then(() => { + // mutate(`get_messages_for_channel_${channelID}`) + // handleCancelReply() + // }) + } else { + return Promise.resolve() + } + } + + + return { + sendMessage, + loading + } +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/message-reply/PreviousMessageBox.tsx b/raven-app/src/components/feature/chat/message-reply/PreviousMessageBox.tsx index f1e963a1..b07aab73 100644 --- a/raven-app/src/components/feature/chat/message-reply/PreviousMessageBox.tsx +++ b/raven-app/src/components/feature/chat/message-reply/PreviousMessageBox.tsx @@ -12,6 +12,9 @@ import { useNavigate } from "react-router-dom" import { ChannelListItem, DMChannelListItem } from '@/utils/channel/ChannelListProvider' import { UserFields } from '@/utils/users/UserListProvider' import { useGetUserRecords } from '@/hooks/useGetUserRecords' +import { BiCross } from 'react-icons/bi' +import { AiOutlineCloseCircle } from 'react-icons/ai' +import { CloseIcon } from '@chakra-ui/icons' interface PreviousMessageBoxProps { previous_message_id?: string, @@ -29,7 +32,7 @@ export const PreviousMessageBox = ({ previous_message_id, previous_message_conte if (previous_message_content) { return ( - + @@ -64,10 +67,10 @@ export const PreviousMessageBox = ({ previous_message_id, previous_message_conte } + icon={} aria-label="Remove message" /> @@ -123,7 +126,7 @@ const PreviousMessageBoxInChat = ({ previous_message_id, channelData, users }: P return } if (data) { - return handleScrollToMessage(previous_message_id)} p='2' border={'1px'} borderColor={colorMode === 'light' ? 'gray.200' : 'gray.600'} rounded={'md'} _hover={{ cursor: 'pointer', boxShadow: 'sm', bgColor: colorMode === 'light' ? 'white' : 'black' }}> + return handleScrollToMessage(previous_message_id)} p='2' border={'1px'} borderColor={colorMode === 'light' ? 'gray.400' : 'gray.600'} rounded={'md'} _hover={{ cursor: 'pointer', boxShadow: 'sm', bgColor: colorMode === 'light' ? 'white' : 'black' }}> diff --git a/raven-app/src/components/feature/file-upload/FileDrop.tsx b/raven-app/src/components/feature/file-upload/FileDrop.tsx index 34c792c0..93f7efae 100644 --- a/raven-app/src/components/feature/file-upload/FileDrop.tsx +++ b/raven-app/src/components/feature/file-upload/FileDrop.tsx @@ -1,4 +1,4 @@ -import { Box, BoxProps, Stack, Text, useToast } from "@chakra-ui/react" +import { Box, BoxProps, Center, Stack, Text, useColorModeValue, useToast } from "@chakra-ui/react" import { forwardRef, useImperativeHandle, useState } from "react" import { Accept, useDropzone } from "react-dropzone" @@ -18,7 +18,8 @@ export interface FileDropProps extends BoxProps { /** Takes input MIME type as 'key' & array of extensions as 'value'; empty array - all extensions supported */ accept?: Accept, /** Maximum file size in mb that can be selected */ - maxFileSize?: number + maxFileSize?: number, + children?: React.ReactNode } /** @@ -27,7 +28,7 @@ export interface FileDropProps extends BoxProps { */ export const FileDrop = forwardRef((props: FileDropProps, ref) => { - const { files, onFileChange, maxFiles, accept, maxFileSize, ...compProps } = props + const { files, onFileChange, maxFiles, accept, maxFileSize, children, ...compProps } = props const toast = useToast() const [onDragEnter, setOnDragEnter] = useState(false) @@ -82,26 +83,43 @@ export const FileDrop = forwardRef((props: FileDropProps, ref) => { } })); + const { borderColor, textColor, bgColor } = useColorModeValue({ + borderColor: "gray.300", + textColor: "gray.600", + // Using hex values to add opacity + bgColor: "#F7FAFCAA" + }, { + borderColor: "gray.700", + textColor: "white", + bgColor: "#171923AA" + }) + return ( - + + {children} + {(maxFiles === undefined || files.length < maxFiles) && - - - Drag 'n' drop your files here, or click to select files - + borderRadius="md" + borderColor={borderColor} + bgColor={bgColor} + > + Drop your files here. A Raven will pick it up. + + } + ) }) \ No newline at end of file diff --git a/raven-app/src/components/feature/file-upload/FileListItem.tsx b/raven-app/src/components/feature/file-upload/FileListItem.tsx index 76fcf6e8..290de219 100644 --- a/raven-app/src/components/feature/file-upload/FileListItem.tsx +++ b/raven-app/src/components/feature/file-upload/FileListItem.tsx @@ -1,4 +1,4 @@ -import { Text, Stack, IconButton, HStack, Icon, Image, Center, CircularProgress, CircularProgressLabel, useColorMode } from '@chakra-ui/react' +import { Text, Stack, IconButton, HStack, Icon, Image, Center, CircularProgress, CircularProgressLabel, useColorModeValue } from '@chakra-ui/react' import { TbTrash } from 'react-icons/tb' import { useGetFilePreviewUrl } from '../../../hooks/useGetFilePreviewUrl' import { getFileExtensionIcon } from '../../../utils/layout/fileExtensionIcon' @@ -14,12 +14,18 @@ interface FileListItemProps { export const FileListItem = ({ file, removeFile, isUploading, uploadProgress }: FileListItemProps) => { - const { colorMode } = useColorMode() + const { borderColor, bgColor } = useColorModeValue({ + borderColor: 'gray.200', + bgColor: 'white' + }, { + borderColor: 'gray.800', + bgColor: 'gray.900' + }) const previewURL = useGetFilePreviewUrl(file) const fileSizeString = getFileSize(file) return ( - +
{previewURL ? File preview : }
diff --git a/raven-app/src/components/feature/markdown-viewer/MarkdownRenderer.css b/raven-app/src/components/feature/markdown-viewer/MarkdownRenderer.css index 18dcfcd9..1638e677 100644 --- a/raven-app/src/components/feature/markdown-viewer/MarkdownRenderer.css +++ b/raven-app/src/components/feature/markdown-viewer/MarkdownRenderer.css @@ -17,4 +17,92 @@ .markdown { font-size: var(--chakra-fontSizes-sm) +} + +.markdown .mention { + color: var(--chakra-colors-blue-500); +} + +.markdown blockquote { + border-left: 3px solid var(--chakra-colors-gray-300); + padding-left: 0.8rem; + margin: 1rem; +} + +.markdown ul, +.markdown ol { + padding-left: var(--chakra-space-8); + line-height: 1.4; +} + +.markdown ul li, +.markdown ol li { + line-height: 2; +} + +.markdown pre { + background: #0d0d0d; + border-radius: 0.5rem; + color: #fff; + font-family: "JetBrainsMono", monospace; + padding: 0.75rem 1rem; +} + +.markdown pre code { + background: none; + color: inherit; + font-size: 0.8rem; + padding: 0; +} + +.markdown pre .hljs-comment, +.markdown pre .hljs-quote { + color: #616161; +} + +.markdown pre .hljs-variable, +.markdown pre .hljs-template-variable, +.markdown pre .hljs-attribute, +.markdown pre .hljs-tag, +.markdown pre .hljs-name, +.markdown pre .hljs-regexp, +.markdown pre .hljs-link, +.markdown pre .hljs-name, +.markdown pre .hljs-selector-id, +.markdown pre .hljs-selector-class { + color: #f98181; +} + +.markdown pre .hljs-number, +.markdown pre .hljs-meta, +.markdown pre .hljs-built_in, +.markdown pre .hljs-builtin-name, +.markdown pre .hljs-literal, +.markdown pre .hljs-type, +.markdown pre .hljs-params { + color: #fbbc88; +} + +.markdown pre .markdown pre .hljs-string, +.markdown pre .hljs-symbol, +.markdown pre .hljs-bullet { + color: #b9f18d; +} + +.markdown pre .hljs-title, +.markdown pre .hljs-section { + color: #faf594; +} + +.markdown pre .hljs-keyword, +.markdown pre .hljs-selector-tag { + color: #70cff8; +} + +.markdown pre .hljs-emphasis { + font-style: italic; +} + +.markdown pre .hljs-strong { + font-weight: 700; } \ No newline at end of file diff --git a/raven-app/src/components/feature/message-action-palette/EditMessageModal.tsx b/raven-app/src/components/feature/message-action-palette/EditMessageModal.tsx index 4aae8c99..9e8cc33e 100644 --- a/raven-app/src/components/feature/message-action-palette/EditMessageModal.tsx +++ b/raven-app/src/components/feature/message-action-palette/EditMessageModal.tsx @@ -1,14 +1,8 @@ -import { Box, Button, ButtonGroup, HStack, IconButton, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Popover, PopoverContent, PopoverTrigger, Stack, useColorMode, useToast } from "@chakra-ui/react" -import EmojiPicker, { EmojiClickData } from "emoji-picker-react" +import { HStack, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, Stack, Text, useToast } from "@chakra-ui/react" import { useFrappeUpdateDoc, useSWRConfig } from "frappe-react-sdk" -import { useCallback, useContext, useEffect, useState } from "react" -import { useHotkeys } from "react-hotkeys-hook" -import { FaRegSmile } from "react-icons/fa" +import { useEffect } from "react" import { ErrorBanner } from "../../layout/AlertBanner" -import { ModalTypes, useModalManager } from "../../../hooks/useModalManager" -import { UserListContext } from "@/utils/users/UserListProvider" -import { ChannelListContext, ChannelListItem } from "@/utils/channel/ChannelListProvider" -import { QuillEditor } from "../chat/chat-input/QuillEditor" +import { Tiptap } from "../chat/ChatInput/Tiptap" interface EditMessageModalProps { isOpen: boolean, @@ -17,32 +11,9 @@ interface EditMessageModalProps { originalText: string } -type value = { - id: string, - value: string -} - export const EditMessageModal = ({ isOpen, onClose, channelMessageID, originalText }: EditMessageModalProps) => { - const channels = useContext(ChannelListContext) - const users = useContext(UserListContext) - const { mutate } = useSWRConfig() - - const allUsers: value[] = users ? users.users.map((user) => { - return { - id: user.name, - value: user.full_name || user.name - } - }) : [] - - const allChannels: value[] = channels ? Object.values(channels.channels).map((channel: ChannelListItem) => { - return { - id: channel.name, - value: channel.channel_name || channel.name - } - }) : [] - const toast = useToast() const { updateDoc, error, loading, reset } = useFrappeUpdateDoc() @@ -50,17 +21,10 @@ export const EditMessageModal = ({ isOpen, onClose, channelMessageID, originalTe reset() }, [isOpen, reset]) - const [text, setText] = useState(originalText) - - useHotkeys('enter', () => onSubmit(), { - enabled: isOpen, - preventDefault: true, - enableOnFormTags: true, - }) - - const onSubmit = () => { - updateDoc('Raven Message', channelMessageID, - { text: text }).then((d) => { + const onSubmit = async (html: string, json: any) => { + console.log("Submit") + return updateDoc('Raven Message', channelMessageID, + { text: html, json }).then((d) => { onClose(true) toast({ title: "Message updated", @@ -82,73 +46,21 @@ export const EditMessageModal = ({ isOpen, onClose, channelMessageID, originalTe }) } - const handleClose = (refresh: boolean = false) => { - reset() - onClose(refresh) - } - - const { colorMode } = useColorMode() - - const modalManager = useModalManager() - - const onEmojiPickerOpen = () => { - modalManager.openModal(ModalTypes.EmojiPicker) - } - - const onEmojiClick = (emojiObject: EmojiClickData) => { - // remove html tags from text but do not remove span with class mention - const textWithoutHTML = text.replace(/<(?!\/?span)[^>]+>/gi, "") - // add emoji to text - const newText = `${textWithoutHTML} ${emojiObject.emoji}` - // set text - setText(newText) - modalManager.closeModal() - } - return ( Edit Message - - + - - - - - - - } onClick={onEmojiPickerOpen} /> - - - {/* @ts-ignore */} - - - - - - - - + + + Press Enter to save + +
- - - - - - - - ) diff --git a/raven-app/src/components/feature/message-action-palette/EmojiPickerButton.tsx b/raven-app/src/components/feature/message-action-palette/EmojiPickerButton.tsx index 15741df6..ab12ec2e 100644 --- a/raven-app/src/components/feature/message-action-palette/EmojiPickerButton.tsx +++ b/raven-app/src/components/feature/message-action-palette/EmojiPickerButton.tsx @@ -1,8 +1,8 @@ import { ModalTypes, useModalManager } from '@/hooks/useModalManager' import { Box, IconButton, Popover, PopoverContent, PopoverTrigger, Portal, Tooltip, useColorMode } from '@chakra-ui/react' -import EmojiPicker, { EmojiClickData } from 'emoji-picker-react' import { useEffect } from 'react' import { BsEmojiSmile } from 'react-icons/bs' +import { EmojiPicker } from '../../common/EmojiPicker/EmojiPicker' interface EmojiPickerButtonProps { handleScroll: (newState: boolean) => void, @@ -22,8 +22,8 @@ export const EmojiPickerButton = ({ handleScroll, saveReaction }: EmojiPickerBut modalManager.openModal(ModalTypes.EmojiPicker) } - const onEmojiClick = (emojiObject: EmojiClickData) => { - saveReaction(emojiObject.emoji) + const onEmojiClick = (emoji: string) => { + saveReaction(emoji) modalManager.closeModal() } @@ -34,7 +34,6 @@ export const EmojiPickerButton = ({ handleScroll, saveReaction }: EmojiPickerBut onClose={modalManager.closeModal} placement='auto-end' isLazy - lazyBehavior="unmount" gutter={48}> @@ -44,8 +43,7 @@ export const EmojiPickerButton = ({ handleScroll, saveReaction }: EmojiPickerBut - {/* @ts-ignore */} - + diff --git a/raven-app/src/types/RavenMessaging/RavenMessage.ts b/raven-app/src/types/RavenMessaging/RavenMessage.ts new file mode 100644 index 00000000..e9f3a460 --- /dev/null +++ b/raven-app/src/types/RavenMessaging/RavenMessage.ts @@ -0,0 +1,31 @@ + +export interface RavenMessage{ + creation: string + name: string + modified: string + owner: string + modified_by: string + docstatus: 0 | 1 | 2 + parent?: string + parentfield?: string + parenttype?: string + idx?: number + /** Channel ID : Link - Raven Channel */ + channel_id: string + /** Text : Long Text */ + text?: string + /** JSON : JSON */ + json?: any + /** File : Attach */ + file?: string + /** File Thumbnail : Attach */ + file_thumbnail?: string + /** Message Type : Select */ + message_type?: "Text" | "Image" | "File" + /** Message Reactions : JSON */ + message_reactions?: any + /** Is Reply : Check */ + is_reply?: 0 | 1 + /** Linked Message : Link - Raven Message */ + linked_message?: string +} \ No newline at end of file diff --git a/raven-app/yarn.lock b/raven-app/yarn.lock index 484af4c2..ae7ad336 100644 --- a/raven-app/yarn.lock +++ b/raven-app/yarn.lock @@ -1301,7 +1301,7 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@popperjs/core@^2.9.3": +"@popperjs/core@^2.9.0", "@popperjs/core@^2.9.3": version "2.11.8" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== @@ -1448,6 +1448,37 @@ dependencies: "@babel/runtime" "^7.13.10" +"@remirror/core-constants@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@remirror/core-constants/-/core-constants-2.0.2.tgz#f05eccdc69e3a65e7d524b52548f567904a11a1a" + integrity sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ== + +"@remirror/core-helpers@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@remirror/core-helpers/-/core-helpers-3.0.0.tgz#3a35c2346bc23ebc3cee585b7840b5567755c5f1" + integrity sha512-tusEgQJIqg4qKj6HSBUFcyRnWnziw3neh4T9wOmsPGHFC3w9kl5KSrDb9UAgE8uX6y32FnS7vJ955mWOl3n50A== + dependencies: + "@remirror/core-constants" "^2.0.2" + "@remirror/types" "^1.0.1" + "@types/object.omit" "^3.0.0" + "@types/object.pick" "^1.3.2" + "@types/throttle-debounce" "^2.1.0" + case-anything "^2.1.13" + dash-get "^1.0.2" + deepmerge "^4.3.1" + fast-deep-equal "^3.1.3" + make-error "^1.3.6" + object.omit "^3.0.0" + object.pick "^1.3.0" + throttle-debounce "^3.0.1" + +"@remirror/types@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@remirror/types/-/types-1.0.1.tgz#768502497a0fbbc23338a1586b893f729310cf70" + integrity sha512-VlZQxwGnt1jtQ18D6JqdIF+uFZo525WEqrfp9BOc3COPpK4+AWCgdnAWL+ho6imWcoINlGjR/+3b6y5C1vBVEA== + dependencies: + type-fest "^2.19.0" + "@remix-run/router@1.6.2": version "1.6.2" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.6.2.tgz#bbe75f8c59e0b7077584920ce2cc76f8f354934d" @@ -1458,6 +1489,214 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== +"@tiptap/core@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.1.12.tgz#904fdf147e91b5e60561c76e7563c1b5a32f54ab" + integrity sha512-ZGc3xrBJA9KY8kln5AYTj8y+GDrKxi7u95xIl2eccrqTY5CQeRu6HRNM1yT4mAjuSaG9jmazyjGRlQuhyxCKxQ== + +"@tiptap/extension-blockquote@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.1.12.tgz#97b43419606acf9bfd93b9f482a1827dcac8c3e9" + integrity sha512-Qb3YRlCfugx9pw7VgLTb+jY37OY4aBJeZnqHzx4QThSm13edNYjasokbX0nTwL1Up4NPTcY19JUeHt6fVaVVGg== + +"@tiptap/extension-bold@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.1.12.tgz#5dbf41105fc0fbde8adbff629312187fbebc39b0" + integrity sha512-AZGxIxcGU1/y6V2YEbKsq6BAibL8yQrbRm6EdcBnby41vj1WziewEKswhLGmZx5IKM2r2ldxld03KlfSIlKQZg== + +"@tiptap/extension-bubble-menu@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.1.12.tgz#4103a21a6433e58690c8f742ece39fad78dc26eb" + integrity sha512-gAGi21EQ4wvLmT7klgariAc2Hf+cIjaNU2NWze3ut6Ku9gUo5ZLqj1t9SKHmNf4d5JG63O8GxpErqpA7lHlRtw== + dependencies: + tippy.js "^6.3.7" + +"@tiptap/extension-bullet-list@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.1.12.tgz#7c905a577ce30ef2cb335870a23f9d24fd26f6aa" + integrity sha512-vtD8vWtNlmAZX8LYqt2yU9w3mU9rPCiHmbp4hDXJs2kBnI0Ju/qAyXFx6iJ3C3XyuMnMbJdDI9ee0spAvFz7cQ== + +"@tiptap/extension-code-block-lowlight@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.1.12.tgz#ccbca5d0d92bee373dc8e2e2ae6c27f62f66437c" + integrity sha512-dtIbpI9QrWa9TzNO4v5q/zf7+d83wpy5i9PEccdJAVtRZ0yOI8JIZAWzG5ex3zAoCA0CnQFdsPSVykYSDdxtDA== + +"@tiptap/extension-code-block@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.1.12.tgz#20416baef1b5fc839490a8416e97fdcbb5fdf918" + integrity sha512-RXtSYCVsnk8D+K80uNZShClfZjvv1EgO42JlXLVGWQdIgaNyuOv/6I/Jdf+ZzhnpsBnHufW+6TJjwP5vJPSPHA== + +"@tiptap/extension-code@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.1.12.tgz#86d2eb5f63725af472c5fd858e5a9c7ccae06ef3" + integrity sha512-CRiRq5OTC1lFgSx6IMrECqmtb93a0ZZKujEnaRhzWliPBjLIi66va05f/P1vnV6/tHaC3yfXys6dxB5A4J8jxw== + +"@tiptap/extension-document@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.1.12.tgz#e19e4716dfad60cbeb6abaf2f362fed759963529" + integrity sha512-0QNfAkCcFlB9O8cUNSwTSIQMV9TmoEhfEaLz/GvbjwEq4skXK3bU+OQX7Ih07waCDVXIGAZ7YAZogbvrn/WbOw== + +"@tiptap/extension-dropcursor@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.1.12.tgz#9da0c275291c9d47497d3db41b4d70d96366b4ff" + integrity sha512-0tT/q8nL4NBCYPxr9T0Brck+RQbWuczm9nV0bnxgt0IiQXoRHutfPWdS7GA65PTuVRBS/3LOco30fbjFhkfz/A== + +"@tiptap/extension-floating-menu@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.1.12.tgz#68a658b2b9bdd3a0fc1afc5165231838061a8fde" + integrity sha512-uo0ydCJNg6AWwLT6cMUJYVChfvw2PY9ZfvKRhh9YJlGfM02jS4RUG/bJBts6R37f+a5FsOvAVwg8EvqPlNND1A== + dependencies: + tippy.js "^6.3.7" + +"@tiptap/extension-gapcursor@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.1.12.tgz#63844c3abd1a38af915839cf0c097b6d2e5a86fe" + integrity sha512-zFYdZCqPgpwoB7whyuwpc8EYLYjUE5QYKb8vICvc+FraBUDM51ujYhFSgJC3rhs8EjI+8GcK8ShLbSMIn49YOQ== + +"@tiptap/extension-hard-break@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.1.12.tgz#54d0c9996e1173594852394975a9356eec98bc9a" + integrity sha512-nqKcAYGEOafg9D+2cy1E4gHNGuL12LerVa0eS2SQOb+PT8vSel9OTKU1RyZldsWSQJ5rq/w4uIjmLnrSR2w6Yw== + +"@tiptap/extension-heading@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.1.12.tgz#05ae4684d6f29ae611495ab114038e14a5d1dff6" + integrity sha512-MoANP3POAP68Ko9YXarfDKLM/kXtscgp6m+xRagPAghRNujVY88nK1qBMZ3JdvTVN6b/ATJhp8UdrZX96TLV2w== + +"@tiptap/extension-highlight@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.1.12.tgz#184efb75238c9cbc6c18d523b735de4329f78ecc" + integrity sha512-buen31cYPyiiHA2i0o2i/UcjRTg/42mNDCizGr1OJwvv3AELG3qOFc4Y58WJWIvWNv+1Dr4ZxHA3GNVn0ANWyg== + +"@tiptap/extension-history@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.1.12.tgz#03bcb9422e8ea2b82dc45207d1a1b0bc0241b055" + integrity sha512-6b7UFVkvPjq3LVoCTrYZAczt5sQrQUaoDWAieVClVZoFLfjga2Fwjcfgcie8IjdPt8YO2hG/sar/c07i9vM0Sg== + +"@tiptap/extension-horizontal-rule@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.1.12.tgz#2191d4ff68ed39381d65971ad8e2aa1be43e6d6b" + integrity sha512-RRuoK4KxrXRrZNAjJW5rpaxjiP0FJIaqpi7nFbAua2oHXgsCsG8qbW2Y0WkbIoS8AJsvLZ3fNGsQ8gpdliuq3A== + +"@tiptap/extension-italic@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.1.12.tgz#e99480eb77f8b4e5444fc236add8a831d5aa2343" + integrity sha512-/XYrW4ZEWyqDvnXVKbgTXItpJOp2ycswk+fJ3vuexyolO6NSs0UuYC6X4f+FbHYL5VuWqVBv7EavGa+tB6sl3A== + +"@tiptap/extension-link@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.1.12.tgz#a18f83a0b54342e6274ff9e5a5907ef7f15aa723" + integrity sha512-Sti5hhlkCqi5vzdQjU/gbmr8kb578p+u0J4kWS+SSz3BknNThEm/7Id67qdjBTOQbwuN07lHjDaabJL0hSkzGQ== + dependencies: + linkifyjs "^4.1.0" + +"@tiptap/extension-list-item@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.1.12.tgz#3eb28dc998490a98f14765783770b3cf6587d39e" + integrity sha512-Gk7hBFofAPmNQ8+uw8w5QSsZOMEGf7KQXJnx5B022YAUJTYYxO3jYVuzp34Drk9p+zNNIcXD4kc7ff5+nFOTrg== + +"@tiptap/extension-mention@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.1.12.tgz#a395e7757b45630ec3047f14b0ba2dde8e1c9c93" + integrity sha512-Nc8wFlyPp+/48IpOFPk2O3hYsF465wizcM3aihMvZM96Ahic7dvv9yVptyOfoOwgpExl2FIn1QPjRDXF60VAUg== + +"@tiptap/extension-ordered-list@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.1.12.tgz#f41a45bc66b4d19e379d4833f303f2e0cd6b9d60" + integrity sha512-tF6VGl+D2avCgn9U/2YLJ8qVmV6sPE/iEzVAFZuOSe6L0Pj7SQw4K6AO640QBob/d8VrqqJFHCb6l10amJOnXA== + +"@tiptap/extension-paragraph@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.1.12.tgz#922447b2aa1c7184787d351ceec593a74d24ed03" + integrity sha512-hoH/uWPX+KKnNAZagudlsrr4Xu57nusGekkJWBcrb5MCDE91BS+DN2xifuhwXiTHxnwOMVFjluc0bPzQbkArsw== + +"@tiptap/extension-placeholder@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.1.12.tgz#f6267a563d17a5ae8a04da32231eac8d8868519e" + integrity sha512-K52o7B1zkP4vaVy3z4ZwHn+tQy6KlXtedj1skLg+796ImwH2GYS5z6MFOTfKzBO2hLncUzLco/s0C5PLCD6SDw== + +"@tiptap/extension-strike@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.1.12.tgz#2b049aedf2985e9c9e3c3f1cc0b203a574c85bd8" + integrity sha512-HlhrzIjYUT8oCH9nYzEL2QTTn8d1ECnVhKvzAe6x41xk31PjLMHTUy8aYjeQEkWZOWZ34tiTmslV1ce6R3Dt8g== + +"@tiptap/extension-text@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.1.12.tgz#466e3244bdd9b2db2304c0c9a1d51ce59f5327d0" + integrity sha512-rCNUd505p/PXwU9Jgxo4ZJv4A3cIBAyAqlx/dtcY6cjztCQuXJhuQILPhjGhBTOLEEL4kW2wQtqzCmb7O8i2jg== + +"@tiptap/extension-typography@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-typography/-/extension-typography-2.1.12.tgz#0bf3ba500b49b4f239205e76dda512bda7d3b4f7" + integrity sha512-OFkQHmUbKQwVO0b/NP2MLuuqQIWxw/gHaWQF/atgZf3mG0YDV2x3P/u+RBpKnsIujPZFvoEBRJGnstvEAB7zfQ== + +"@tiptap/extension-underline@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.1.12.tgz#abd59c4b6c8434dbadb4ff9bff23eefcc6bc095e" + integrity sha512-NwwdhFT8gDD0VUNLQx85yFBhP9a8qg8GPuxlGzAP/lPTV8Ubh3vSeQ5N9k2ZF/vHlEvnugzeVCbmYn7wf8vn1g== + +"@tiptap/pm@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.1.12.tgz#88a4b19be0eabb13d42ddd540c19ba1bbe74b322" + integrity sha512-Q3MXXQABG4CZBesSp82yV84uhJh/W0Gag6KPm2HRWPimSFELM09Z9/5WK9RItAYE0aLhe4Krnyiczn9AAa1tQQ== + dependencies: + prosemirror-changeset "^2.2.0" + prosemirror-collab "^1.3.0" + prosemirror-commands "^1.3.1" + prosemirror-dropcursor "^1.5.0" + prosemirror-gapcursor "^1.3.1" + prosemirror-history "^1.3.0" + prosemirror-inputrules "^1.2.0" + prosemirror-keymap "^1.2.0" + prosemirror-markdown "^1.10.1" + prosemirror-menu "^1.2.1" + prosemirror-model "^1.18.1" + prosemirror-schema-basic "^1.2.0" + prosemirror-schema-list "^1.2.2" + prosemirror-state "^1.4.1" + prosemirror-tables "^1.3.0" + prosemirror-trailing-node "^2.0.2" + prosemirror-transform "^1.7.0" + prosemirror-view "^1.28.2" + +"@tiptap/react@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.1.12.tgz#23566c7992b9642137171b282335e646922ae559" + integrity sha512-RMO4QmmpL7sPR7w8o1Wq0hrUe/ttHzsn5I/eWwqg1d3fGx5y9mOdfCoQ9XBtm49Xzdejy3QVzt4zYp9fX0X/xg== + dependencies: + "@tiptap/extension-bubble-menu" "^2.1.12" + "@tiptap/extension-floating-menu" "^2.1.12" + +"@tiptap/starter-kit@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.1.12.tgz#2bf28091ed08dc8f7b903ba92925e4ffe06257ea" + integrity sha512-+RoP1rWV7rSCit2+3wl2bjvSRiePRJE/7YNKbvH8Faz/+AMO23AFegHoUFynR7U0ouGgYDljGkkj35e0asbSDA== + dependencies: + "@tiptap/core" "^2.1.12" + "@tiptap/extension-blockquote" "^2.1.12" + "@tiptap/extension-bold" "^2.1.12" + "@tiptap/extension-bullet-list" "^2.1.12" + "@tiptap/extension-code" "^2.1.12" + "@tiptap/extension-code-block" "^2.1.12" + "@tiptap/extension-document" "^2.1.12" + "@tiptap/extension-dropcursor" "^2.1.12" + "@tiptap/extension-gapcursor" "^2.1.12" + "@tiptap/extension-hard-break" "^2.1.12" + "@tiptap/extension-heading" "^2.1.12" + "@tiptap/extension-history" "^2.1.12" + "@tiptap/extension-horizontal-rule" "^2.1.12" + "@tiptap/extension-italic" "^2.1.12" + "@tiptap/extension-list-item" "^2.1.12" + "@tiptap/extension-ordered-list" "^2.1.12" + "@tiptap/extension-paragraph" "^2.1.12" + "@tiptap/extension-strike" "^2.1.12" + "@tiptap/extension-text" "^2.1.12" + +"@tiptap/suggestion@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.1.12.tgz#a13782d1e625ec03b3f61b6839ecc95b6b685d3f" + integrity sha512-rhlLWwVkOodBGRMK0mAmE34l2a+BqM2Y7q1ViuQRBhs/6sZ8d83O4hARHKVwqT5stY4i1l7d7PoemV3uAGI6+g== + "@types/debug@^4.0.0": version "4.1.8" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317" @@ -1472,6 +1711,13 @@ dependencies: "@types/unist" "*" +"@types/hast@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.2.tgz#e6c1126a33955cb9493a5074ddf1873fb48248c7" + integrity sha512-B5hZHgHsXvfCoO3xgNJvBnX7N8p86TqQeGKXcokW4XXi+qY4vxxPSFYofytvVmpFxzPv7oxDQzjg5Un5m2/xiw== + dependencies: + "@types/unist" "*" + "@types/js-cookie@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.3.tgz#d6bfbbdd0c187354ca555213d1962f6d0691ff4e" @@ -1501,6 +1747,16 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== +"@types/object.omit@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/object.omit/-/object.omit-3.0.2.tgz#13d23915cc16fa54b0d4cfbcb79840f4fe1474d9" + integrity sha512-BxWU36cMP+FKD3OLFluQaj2cBev2sx2LJaHELuphHwnleq+xnEhTmuYYYx4pOT/1U/ZoR6B+RdvxWh2FD6lGGA== + +"@types/object.pick@^1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@types/object.pick/-/object.pick-1.3.3.tgz#f4d4a76e9ef1161e965b963d2bb33c3f6c300125" + integrity sha512-qZqHmdGEALeSATMB1djT1S5szv6Wtpb7DKpHrt2XG4iyKlV7C2Xk8GmDXr1KXakOqUfX6ohw7ceruYt4NVmB1Q== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -1551,6 +1807,11 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== +"@types/throttle-debounce@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz#1c3df624bfc4b62f992d3012b84c56d41eab3776" + integrity sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ== + "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.6": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" @@ -1584,6 +1845,11 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + aria-hidden@^1.1.1, aria-hidden@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954" @@ -1638,6 +1904,11 @@ caniuse-lite@^1.0.30001489: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz#ca82ee2d4e4dbf2bd2589c9360d3fcc2c7ba3bd8" integrity sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ== +case-anything@^2.1.13: + version "2.1.13" + resolved "https://registry.yarnpkg.com/case-anything/-/case-anything-2.1.13.tgz#0cdc16278cb29a7fcdeb072400da3f342ba329e9" + integrity sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng== + ccount@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" @@ -1742,6 +2013,11 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +crelt@^1.0.0: + version "1.0.6" + resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72" + integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g== + css-box-model@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1" @@ -1754,6 +2030,11 @@ csstype@^3.0.11, csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== +dash-get@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/dash-get/-/dash-get-1.0.2.tgz#4c9e9ad5ef04c4bf9d3c9a451f6f7997298dcc7c" + integrity sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ== + debug@^4.0.0, debug@^4.1.0, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -1780,6 +2061,11 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deepmerge@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + define-properties@^1.1.3, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" @@ -1798,6 +2084,13 @@ detect-node-es@^1.1.0: resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== +devlop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + diff@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" @@ -1816,6 +2109,11 @@ electron-to-chromium@^1.4.411: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.411.tgz#8cb7787f0442fcb4209590e9951bdb482caa93b2" integrity sha512-5VXLW4Qw89vM2WTICHua/y8v7fKGDRVa2VPOtBB9IpLvW316B+xd8yD1wTmLPY2ot/00P/qt87xdolj4aG/Lzg== +emoji-picker-element@^1.18.4: + version "1.18.4" + resolved "https://registry.yarnpkg.com/emoji-picker-element/-/emoji-picker-element-1.18.4.tgz#0d1136af09c1989661559e99dbaa2a9e163c0974" + integrity sha512-Jo+HyHIPSqrHTyLnw+SJn0KWCd1WhA38Iqcz6wKxfqkevK9V8ECJELgFyxXUCRmgQkVmPrTVGLpB1mqIb8/iSQ== + emoji-picker-react@^4.4.7: version "4.4.9" resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.4.9.tgz#564fc387ae2ce8c8086a12a9231d6fd9567d0ae5" @@ -1839,6 +2137,11 @@ engine.io-parser@~5.2.1: resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.1.tgz#9f213c77512ff1a6cc0c7a86108a7ffceb16fcfb" integrity sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ== +entities@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1904,6 +2207,11 @@ extend@^3.0.0, extend@^3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + fast-diff@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" @@ -2099,6 +2407,11 @@ hastscript@^7.0.0: property-information "^6.0.0" space-separated-tokens "^2.0.0" +highlight.js@^11.9.0, highlight.js@~11.9.0: + version "11.9.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.9.0.tgz#04ab9ee43b52a41a047432c8103e2158a1b8b5b0" + integrity sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw== + hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -2163,11 +2476,25 @@ is-date-object@^1.0.1: dependencies: has-tostringtag "^1.0.0" +is-extendable@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + is-plain-obj@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + is-regex@^1.0.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -2176,6 +2503,11 @@ is-regex@^1.0.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + js-cookie@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" @@ -2211,6 +2543,18 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +linkify-it@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" + integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw== + dependencies: + uc.micro "^1.0.1" + +linkifyjs@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.1.tgz#73d427e3bbaaf4ca8e71c589ad4ffda11a9a5fde" + integrity sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA== + lodash.mergewith@4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" @@ -2233,6 +2577,15 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lowlight@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-3.1.0.tgz#aa394c5f3a7689fce35fa49a7c850ba3ead4f590" + integrity sha512-CEbNVoSikAxwDMDPjXlqlFYiZLkDJHwyGu/MfOsJnF3d7f3tds5J3z8s/l9TMXhzfsJCCJEAsD78842mwmg0PQ== + dependencies: + "@types/hast" "^3.0.0" + devlop "^1.0.0" + highlight.js "~11.9.0" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -2247,6 +2600,22 @@ magic-string@^0.27.0: dependencies: "@jridgewell/sourcemap-codec" "^1.4.13" +make-error@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +markdown-it@^13.0.1: + version "13.0.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536" + integrity sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w== + dependencies: + argparse "^2.0.1" + entities "~3.0.1" + linkify-it "^4.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + markdown-table@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" @@ -2390,6 +2759,11 @@ mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0: dependencies: "@types/mdast" "^3.0.0" +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + memoize-one@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" @@ -2707,6 +3081,25 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +object.omit@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-3.0.0.tgz#0e3edc2fce2ba54df5577ff529f6d97bd8a522af" + integrity sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ== + dependencies: + is-extendable "^1.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== + dependencies: + isobject "^3.0.1" + +orderedmap@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.1.1.tgz#61481269c44031c449915497bf5a4ad273c512d2" + integrity sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g== + parchment@^1.1.2, parchment@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5" @@ -2772,6 +3165,160 @@ property-information@^6.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.2.0.tgz#b74f522c31c097b5149e3c3cb8d7f3defd986a1d" integrity sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg== +prosemirror-changeset@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz#dae94b63aec618fac7bb9061648e6e2a79988383" + integrity sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ== + dependencies: + prosemirror-transform "^1.0.0" + +prosemirror-collab@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz#0e8c91e76e009b53457eb3b3051fb68dad029a33" + integrity sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ== + dependencies: + prosemirror-state "^1.0.0" + +prosemirror-commands@^1.0.0, prosemirror-commands@^1.3.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.5.2.tgz#e94aeea52286f658cd984270de9b4c3fff580852" + integrity sha512-hgLcPaakxH8tu6YvVAaILV2tXYsW3rAdDR8WNkeKGcgeMVQg3/TMhPdVoh7iAmfgVjZGtcOSjKiQaoeKjzd2mQ== + dependencies: + prosemirror-model "^1.0.0" + prosemirror-state "^1.0.0" + prosemirror-transform "^1.0.0" + +prosemirror-dropcursor@^1.5.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz#49b9fb2f583e0d0f4021ff87db825faa2be2832d" + integrity sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw== + dependencies: + prosemirror-state "^1.0.0" + prosemirror-transform "^1.1.0" + prosemirror-view "^1.1.0" + +prosemirror-gapcursor@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz#5fa336b83789c6199a7341c9493587e249215cb4" + integrity sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ== + dependencies: + prosemirror-keymap "^1.0.0" + prosemirror-model "^1.0.0" + prosemirror-state "^1.0.0" + prosemirror-view "^1.0.0" + +prosemirror-history@^1.0.0, prosemirror-history@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.3.2.tgz#ce6ad7ab9db83e761aee716f3040d74738311b15" + integrity sha512-/zm0XoU/N/+u7i5zepjmZAEnpvjDtzoPWW6VmKptcAnPadN/SStsBjMImdCEbb3seiNTpveziPTIrXQbHLtU1g== + dependencies: + prosemirror-state "^1.2.2" + prosemirror-transform "^1.0.0" + prosemirror-view "^1.31.0" + rope-sequence "^1.3.0" + +prosemirror-inputrules@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.2.1.tgz#8faf3d78c16150aedac71d326a3e3947417ce557" + integrity sha512-3LrWJX1+ULRh5SZvbIQlwZafOXqp1XuV21MGBu/i5xsztd+9VD15x6OtN6mdqSFI7/8Y77gYUbQ6vwwJ4mr6QQ== + dependencies: + prosemirror-state "^1.0.0" + prosemirror-transform "^1.0.0" + +prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz#14a54763a29c7b2704f561088ccf3384d14eb77e" + integrity sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ== + dependencies: + prosemirror-state "^1.0.0" + w3c-keyname "^2.2.0" + +prosemirror-markdown@^1.10.1: + version "1.11.2" + resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.11.2.tgz#f6e529e669d11fa3eec859e93c0d2c91788d6c80" + integrity sha512-Eu5g4WPiCdqDTGhdSsG9N6ZjACQRYrsAkrF9KYfdMaCmjIApH75aVncsWYOJvEk2i1B3i8jZppv3J/tnuHGiUQ== + dependencies: + markdown-it "^13.0.1" + prosemirror-model "^1.0.0" + +prosemirror-menu@^1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz#3cfdc7c06d10f9fbd1bce29082c498bd11a0a79a" + integrity sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA== + dependencies: + crelt "^1.0.0" + prosemirror-commands "^1.0.0" + prosemirror-history "^1.0.0" + prosemirror-state "^1.0.0" + +prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.19.0, prosemirror-model@^1.8.1: + version "1.19.3" + resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.3.tgz#f0d55285487fefd962d0ac695f716f4ec6705006" + integrity sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ== + dependencies: + orderedmap "^2.0.0" + +prosemirror-schema-basic@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.2.tgz#6695f5175e4628aab179bf62e5568628b9cfe6c7" + integrity sha512-/dT4JFEGyO7QnNTe9UaKUhjDXbTNkiWTq/N4VpKaF79bBjSExVV2NXmJpcM7z/gD7mbqNjxbmWW5nf1iNSSGnw== + dependencies: + prosemirror-model "^1.19.0" + +prosemirror-schema-list@^1.2.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.3.0.tgz#05374702cf35a3ba5e7ec31079e355a488d52519" + integrity sha512-Hz/7gM4skaaYfRPNgr421CU4GSwotmEwBVvJh5ltGiffUJwm7C8GfN/Bc6DR1EKEp5pDKhODmdXXyi9uIsZl5A== + dependencies: + prosemirror-model "^1.0.0" + prosemirror-state "^1.0.0" + prosemirror-transform "^1.7.3" + +prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.4.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.4.3.tgz#94aecf3ffd54ec37e87aa7179d13508da181a080" + integrity sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q== + dependencies: + prosemirror-model "^1.0.0" + prosemirror-transform "^1.0.0" + prosemirror-view "^1.27.0" + +prosemirror-tables@^1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.3.4.tgz#0b7cc16d49f90c5b834c9f29291c545478ce9ab0" + integrity sha512-z6uLSQ1BLC3rgbGwZmpfb+xkdvD7W/UOsURDfognZFYaTtc0gsk7u/t71Yijp2eLflVpffMk6X0u0+u+MMDvIw== + dependencies: + prosemirror-keymap "^1.1.2" + prosemirror-model "^1.8.1" + prosemirror-state "^1.3.1" + prosemirror-transform "^1.2.1" + prosemirror-view "^1.13.3" + +prosemirror-trailing-node@^2.0.2: + version "2.0.7" + resolved "https://registry.yarnpkg.com/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.7.tgz#ba782a7929f18bcae650b1c7082a2d10443eab19" + integrity sha512-8zcZORYj/8WEwsGo6yVCRXFMOfBo0Ub3hCUvmoWIZYfMP26WqENU0mpEP27w7mt8buZWuGrydBewr0tOArPb1Q== + dependencies: + "@remirror/core-constants" "^2.0.2" + "@remirror/core-helpers" "^3.0.0" + escape-string-regexp "^4.0.0" + +prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.7.0, prosemirror-transform@^1.7.3: + version "1.8.0" + resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.8.0.tgz#a47c64a3c373c1bd0ff46e95be3210c8dda0cd11" + integrity sha512-BaSBsIMv52F1BVVMvOmp1yzD3u65uC3HTzCBQV1WDPqJRQ2LuHKcyfn0jwqodo8sR9vVzMzZyI+Dal5W9E6a9A== + dependencies: + prosemirror-model "^1.0.0" + +prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.27.0, prosemirror-view@^1.28.2, prosemirror-view@^1.31.0: + version "1.32.1" + resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.32.1.tgz#bcd0877f1673ffe5f94c1e966b6fbdadcd2d5bbf" + integrity sha512-9SnB4HBgRczzTyIMZLPE1iszegL04hNfUyS8uPtP1RPxNM2NTCiIs8KwNsJU4nbZO9rxJTwVTv7Jm3zU4CR78A== + dependencies: + prosemirror-model "^1.16.0" + prosemirror-state "^1.0.0" + prosemirror-transform "^1.1.0" + quill-delta@^3.6.2: version "3.6.3" resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032" @@ -3080,6 +3627,11 @@ rollup@^3.21.0: optionalDependencies: fsevents "~2.3.2" +rope-sequence@^1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.4.tgz#df85711aaecd32f1e756f76e43a415171235d425" + integrity sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ== + sade@^1.7.3: version "1.8.1" resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" @@ -3164,6 +3716,11 @@ swr@^2.2.2: client-only "^0.0.1" use-sync-external-store "^1.2.0" +throttle-debounce@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" + integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== + timeago-react@^3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/timeago-react/-/timeago-react-3.0.6.tgz#d1b55af67ed21b1c5429b9ba8043d0d0f7dab295" @@ -3181,6 +3738,13 @@ tiny-invariant@^1.0.6: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== +tippy.js@^6.3.7: + version "6.3.7" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c" + integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ== + dependencies: + "@popperjs/core" "^2.9.0" + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -3211,11 +3775,21 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338" integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA== +type-fest@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + typescript@^4.9.3: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + unified@^10.0.0: version "10.1.2" resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df" @@ -3352,6 +3926,11 @@ vite@^4.1.0: optionalDependencies: fsevents "~2.3.2" +w3c-keyname@^2.2.0: + version "2.2.8" + resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5" + integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ== + web-namespaces@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" diff --git a/raven/raven_messaging/doctype/raven_message/raven_message.json b/raven/raven_messaging/doctype/raven_message/raven_message.json index 2fc91884..456f61a3 100644 --- a/raven/raven_messaging/doctype/raven_message/raven_message.json +++ b/raven/raven_messaging/doctype/raven_message/raven_message.json @@ -10,6 +10,7 @@ "field_order": [ "channel_id", "text", + "json", "file", "file_thumbnail", "message_type", @@ -32,6 +33,11 @@ "fieldtype": "Long Text", "label": "Text" }, + { + "fieldname": "json", + "fieldtype": "JSON", + "label": "JSON" + }, { "fieldname": "file", "fieldtype": "Attach", diff --git a/raven/raven_messaging/doctype/raven_message/raven_message.py b/raven/raven_messaging/doctype/raven_message/raven_message.py index 8920bd9e..8f8b3668 100644 --- a/raven/raven_messaging/doctype/raven_message/raven_message.py +++ b/raven/raven_messaging/doctype/raven_message/raven_message.py @@ -86,7 +86,7 @@ def track_visit(channel_id, commit=False): @frappe.whitelist(methods=['POST']) -def send_message(channel_id, text, is_reply, linked_message=None): +def send_message(channel_id, text, is_reply, linked_message=None, json=None): # remove empty list items clean_text = text.replace('

  • ', '').strip() @@ -99,14 +99,16 @@ def send_message(channel_id, text, is_reply, linked_message=None): 'text': clean_text, 'message_type': 'Text', 'is_reply': is_reply, - 'linked_message': linked_message + 'linked_message': linked_message, + 'json': json }) else: doc = frappe.get_doc({ 'doctype': 'Raven Message', 'channel_id': channel_id, 'text': clean_text, - 'message_type': 'Text' + 'message_type': 'Text', + 'json': json }) doc.insert() return "message sent"