Refactor chat style and other stuff

This commit is contained in:
Nicola Benaglia 2025-04-18 20:12:48 +02:00
parent 22326411a0
commit 3a302cf5b2
10 changed files with 377 additions and 341 deletions

View File

@ -457,7 +457,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
<Typography
component="span"
variant="body2"
sx={{ color: 'text.primary', display: 'inline' }}
sx={{ color: theme.palette.text.primary, display: 'inline' }}
>
{wallet?.address0}
</Typography>

View File

@ -21,8 +21,8 @@ export const Download: React.FC<SVGProps> = ({
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M12.8047 0.393196V7.21185H16.3036L10.0003 13.5139L3.69697 7.21185H7.19584V0H12.8045L12.8047 0.393196ZM2.7047 16.8587V13.9861H0V18.6179C0 19.3774 0.622589 20 1.38213 20H18.6179C19.3774 20 20 19.3774 20 18.6179V13.9861H17.2962V17.2963L2.70461 17.2954L2.7047 16.8587Z"
fill={setColor}
fill-opacity={setOpacity}

View File

@ -17,8 +17,8 @@ export const Logout: React.FC<SVGProps> = ({ color, opacity, ...children }) => {
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M7.56485 0H16.3611C17.2662 0 18 0.727797 18 1.62558V18.3744C18 19.2722 17.2662 20 16.3611 20H7.56485C6.65969 20 5.92593 19.2722 5.92593 18.3744V12.6013H10.6168C11.4569 12.6013 12.1404 11.9039 12.1404 11.0467V8.87329C12.1404 8.01613 11.4569 7.31875 10.6168 7.31875H5.92593V1.62558C5.92593 0.727797 6.65969 0 7.56485 0ZM11.1667 11.0467C11.1667 11.3719 10.9205 11.6354 10.6168 11.6354H4.8144C4.74521 11.6354 4.68911 11.6955 4.68911 11.7696V12.8632C4.68911 13.3492 4.17007 13.6259 3.8078 13.3329L0.218431 10.4298C-0.0728102 10.1942 -0.0728102 9.72579 0.218431 9.49024L3.8078 6.58709C4.17005 6.29409 4.68911 6.57077 4.68911 7.05684V8.1504C4.68911 8.2245 4.74521 8.28454 4.8144 8.28454H10.6168C10.9205 8.28454 11.1667 8.54813 11.1667 8.87329V11.0467Z"
fill={setColor}
fill-opacity={setOpacity}

View File

@ -16,8 +16,8 @@ export const Return: React.FC<SVGProps> = ({ color, opacity, ...children }) => {
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M2.645 5.81803H15C15.9471 5.81803 16.8557 6.20131 17.5257 6.88278C18.195 7.56497 18.5714 8.49007 18.5714 9.45445V10.909C18.5714 11.8734 18.195 12.7985 17.5257 13.4807C16.8557 14.1622 15.9471 14.5454 15 14.5454C12.0164 14.5454 8.57143 14.5454 8.57143 14.5454C8.17714 14.5454 7.85714 14.8713 7.85714 15.2727C7.85714 15.6742 8.17714 16 8.57143 16H15C16.3264 16 17.5979 15.464 18.5357 14.5091C19.4736 13.5541 20 12.2596 20 10.909C20 10.4268 20 9.93664 20 9.45445C20 8.10461 19.4736 6.80932 18.5357 5.8544C17.5979 4.9002 16.3264 4.36347 15 4.36347H2.645L6.17929 1.27906C6.47857 1.01797 6.51286 0.55832 6.25643 0.253588C6 -0.0511433 5.54857 -0.0860541 5.24929 0.175041L0.249285 4.53874C0.0914279 4.67692 0 4.87838 0 5.09075C0 5.30312 0.0914279 5.50458 0.249285 5.64276L5.24929 10.0065C5.54857 10.2676 6 10.2326 6.25643 9.92791C6.51286 9.62318 6.47857 9.16353 6.17929 8.90244L2.645 5.81803Z"
fill={setColor}
fill-opacity={opacity}

View File

@ -5,7 +5,7 @@ import React, {
useMemo,
useRef,
useState,
} from "react";
} from 'react';
import {
AppCircle,
AppCircleContainer,
@ -23,68 +23,71 @@ import {
PublishQAppCTAParent,
PublishQAppCTARight,
PublishQAppDotsBG,
} from "./Apps-styles";
import { Avatar, Box, ButtonBase, InputBase, styled } from "@mui/material";
import { Add } from "@mui/icons-material";
import { MyContext, getBaseApiReact } from "../../App";
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
import IconSearch from "../../assets/svgs/Search.svg";
import IconClearInput from "../../assets/svgs/ClearInput.svg";
import qappDevelopText from "../../assets/svgs/qappDevelopText.svg";
import qappDots from "../../assets/svgs/qappDots.svg";
} from './Apps-styles';
import { Avatar, Box, ButtonBase, InputBase, styled } from '@mui/material';
import { Add } from '@mui/icons-material';
import { MyContext, getBaseApiReact } from '../../App';
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
import IconSearch from '../../assets/svgs/Search.svg';
import IconClearInput from '../../assets/svgs/ClearInput.svg';
import qappDevelopText from '../../assets/svgs/qappDevelopText.svg';
import qappDots from '../../assets/svgs/qappDots.svg';
import { Spacer } from "../../common/Spacer";
import { AppInfoSnippet } from "./AppInfoSnippet";
import { Virtuoso } from "react-virtuoso";
import { executeEvent } from "../../utils/events";
import { AppsDesktopLibraryBody, AppsDesktopLibraryHeader } from "./AppsDesktop-styles";
import { Spacer } from '../../common/Spacer';
import { AppInfoSnippet } from './AppInfoSnippet';
import { Virtuoso } from 'react-virtuoso';
import { executeEvent } from '../../utils/events';
import {
AppsDesktopLibraryBody,
AppsDesktopLibraryHeader,
} from './AppsDesktop-styles';
const officialAppList = [
"q-tube",
"q-blog",
"q-share",
"q-support",
"q-mail",
"q-fund",
"q-shop",
"q-trade",
"q-support",
"q-manager",
"q-wallets",
"q-search",
"q-nodecontrol"
'q-tube',
'q-blog',
'q-share',
'q-support',
'q-mail',
'q-fund',
'q-shop',
'q-trade',
'q-support',
'q-manager',
'q-wallets',
'q-search',
'q-nodecontrol',
];
const ScrollerStyled = styled("div")({
const ScrollerStyled = styled('div')({
// Hide scrollbar for WebKit browsers (Chrome, Safari)
"::-webkit-scrollbar": {
width: "0px",
height: "0px",
'::-webkit-scrollbar': {
width: '0px',
height: '0px',
},
// Hide scrollbar for Firefox
scrollbarWidth: "none",
scrollbarWidth: 'none',
// Hide scrollbar for IE and older Edge
"-msOverflowStyle": "none",
'-msOverflowStyle': 'none',
});
const StyledVirtuosoContainer = styled("div")({
position: "relative",
width: "100%",
display: "flex",
flexDirection: "column",
const StyledVirtuosoContainer = styled('div')({
position: 'relative',
width: '100%',
display: 'flex',
flexDirection: 'column',
// Hide scrollbar for WebKit browsers (Chrome, Safari)
"::-webkit-scrollbar": {
width: "0px",
height: "0px",
'::-webkit-scrollbar': {
width: '0px',
height: '0px',
},
// Hide scrollbar for Firefox
scrollbarWidth: "none",
scrollbarWidth: 'none',
// Hide scrollbar for IE and older Edge
"-msOverflowStyle": "none",
'-msOverflowStyle': 'none',
});
export const AppsCategoryDesktop = ({
@ -93,29 +96,28 @@ export const AppsCategoryDesktop = ({
category,
isShow,
}) => {
const [searchValue, setSearchValue] = useState("");
const [searchValue, setSearchValue] = useState('');
const virtuosoRef = useRef();
const { rootHeight } = useContext(MyContext);
const categoryList = useMemo(() => {
if(category?.id === 'all') return availableQapps
if (category?.id === 'all') return availableQapps;
return availableQapps.filter(
(app) => app?.metadata?.category === category?.id
);
}, [availableQapps, category]);
const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value
const [debouncedValue, setDebouncedValue] = useState(''); // Debounced value
// Debounce logic
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(searchValue);
}, 350);
setTimeout(() => {
virtuosoRef.current.scrollToIndex({
index: 0
});
if (virtuosoRef.current) {
virtuosoRef.current.scrollToIndex({ index: 0 });
}
}, 500);
// Cleanup timeout if searchValue changes before the timeout completes
return () => {
@ -127,8 +129,13 @@ export const AppsCategoryDesktop = ({
const searchedList = useMemo(() => {
if (!debouncedValue) return categoryList;
return categoryList.filter((app) =>
app.name.toLowerCase().includes(debouncedValue.toLowerCase()) || (app?.metadata?.title && app?.metadata?.title?.toLowerCase().includes(debouncedValue.toLowerCase()))
return categoryList.filter(
(app) =>
app.name.toLowerCase().includes(debouncedValue.toLowerCase()) ||
(app?.metadata?.title &&
app?.metadata?.title
?.toLowerCase()
.includes(debouncedValue.toLowerCase()))
);
}, [debouncedValue, categoryList]);
@ -141,7 +148,7 @@ export const AppsCategoryDesktop = ({
myName={myName}
isFromCategory={true}
parentStyles={{
padding: '0px 10px'
padding: '0px 10px',
}}
/>
);
@ -150,27 +157,29 @@ export const AppsCategoryDesktop = ({
return (
<AppsLibraryContainer
sx={{
display: !isShow && "none",
padding: "0px",
height: "100vh",
overflow: "hidden",
paddingTop: "30px",
display: !isShow && 'none',
padding: '0px',
height: '100vh',
overflow: 'hidden',
paddingTop: '30px',
}}
>
<AppsDesktopLibraryHeader
sx={{
maxWidth: "1500px",
width: "90%",
maxWidth: '1500px',
width: '90%',
}}
>
<AppsWidthLimiter
sx={{
alignItems: "flex-end",
alignItems: 'flex-end',
}}
>
<AppsSearchContainer sx={{
width: "412px",
}}>
<AppsSearchContainer
sx={{
width: '412px',
}}
>
<AppsSearchLeft>
<img src={IconSearch} />
<InputBase
@ -179,8 +188,8 @@ export const AppsCategoryDesktop = ({
sx={{ ml: 1, flex: 1 }}
placeholder="Search for apps"
inputProps={{
"aria-label": "Search for apps",
fontSize: "16px",
'aria-label': 'Search for apps',
fontSize: '16px',
fontWeight: 400,
}}
/>
@ -189,7 +198,7 @@ export const AppsCategoryDesktop = ({
{searchValue && (
<ButtonBase
onClick={() => {
setSearchValue("");
setSearchValue('');
}}
>
<img src={IconClearInput} />
@ -202,9 +211,9 @@ export const AppsCategoryDesktop = ({
<AppsDesktopLibraryBody
sx={{
height: `calc(100vh - 36px)`,
overflow: "auto",
padding: "0px",
alignItems: "center",
overflow: 'auto',
padding: '0px',
alignItems: 'center',
}}
>
<Spacer height="25px" />
@ -215,7 +224,7 @@ export const AppsCategoryDesktop = ({
</AppsWidthLimiter>
<AppsWidthLimiter>
<StyledVirtuosoContainer
sx={{
sx={{
height: `calc(100vh - 36px - 90px - 25px)`,
}}
>

View File

@ -135,9 +135,9 @@ export const AppsLibraryDesktop = ({
setDebouncedValue(searchValue);
}, 350);
setTimeout(() => {
virtuosoRef.current.scrollToIndex({
index: 0,
});
if (virtuosoRef.current) {
virtuosoRef.current.scrollToIndex({ index: 0 });
}
}, 500);
// Cleanup timeout if searchValue changes before the timeout completes
return () => {

View File

@ -167,7 +167,7 @@ export const MessageItem = React.memo(
>
<div
style={{
backgroundColor: theme.palette.background.default,
backgroundColor: theme.palette.background.paper,
borderRadius: '7px',
display: 'flex',
gap: '7px',

View File

@ -1,42 +1,36 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { EditorProvider, useCurrentEditor, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { Color } from "@tiptap/extension-color";
import ListItem from "@tiptap/extension-list-item";
import TextStyle from "@tiptap/extension-text-style";
import Placeholder from "@tiptap/extension-placeholder";
import Image from "@tiptap/extension-image";
import IconButton from "@mui/material/IconButton";
import FormatBoldIcon from "@mui/icons-material/FormatBold";
import FormatItalicIcon from "@mui/icons-material/FormatItalic";
import StrikethroughSIcon from "@mui/icons-material/StrikethroughS";
import FormatClearIcon from "@mui/icons-material/FormatClear";
import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted";
import FormatListNumberedIcon from "@mui/icons-material/FormatListNumbered";
import CodeIcon from "@mui/icons-material/Code";
import ImageIcon from "@mui/icons-material/Image"; // Import Image icon
import FormatQuoteIcon from "@mui/icons-material/FormatQuote";
import HorizontalRuleIcon from "@mui/icons-material/HorizontalRule";
import UndoIcon from "@mui/icons-material/Undo";
import RedoIcon from "@mui/icons-material/Redo";
import FormatHeadingIcon from "@mui/icons-material/FormatSize";
import DeveloperModeIcon from "@mui/icons-material/DeveloperMode";
import Compressor from "compressorjs";
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { EditorProvider, useCurrentEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import { Color } from '@tiptap/extension-color';
import ListItem from '@tiptap/extension-list-item';
import TextStyle from '@tiptap/extension-text-style';
import Placeholder from '@tiptap/extension-placeholder';
import IconButton from '@mui/material/IconButton';
import FormatBoldIcon from '@mui/icons-material/FormatBold';
import FormatItalicIcon from '@mui/icons-material/FormatItalic';
import StrikethroughSIcon from '@mui/icons-material/StrikethroughS';
import FormatClearIcon from '@mui/icons-material/FormatClear';
import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered';
import CodeIcon from '@mui/icons-material/Code';
import ImageIcon from '@mui/icons-material/Image'; // Import Image icon
import FormatQuoteIcon from '@mui/icons-material/FormatQuote';
import HorizontalRuleIcon from '@mui/icons-material/HorizontalRule';
import UndoIcon from '@mui/icons-material/Undo';
import RedoIcon from '@mui/icons-material/Redo';
import FormatHeadingIcon from '@mui/icons-material/FormatSize';
import DeveloperModeIcon from '@mui/icons-material/DeveloperMode';
import Compressor from 'compressorjs';
import Mention from '@tiptap/extension-mention';
import ImageResize from "tiptap-extension-resize-image"; // Import the ResizeImage extension
import { isMobile } from "../../App";
import tippy from "tippy.js";
import "tippy.js/dist/tippy.css";
import Popover from '@mui/material/Popover';
import List from '@mui/material/List';
import ListItemMui from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import { ReactRenderer } from '@tiptap/react'
import MentionList from './MentionList.jsx'
import { useRecoilState } from "recoil";
import { isDisabledEditorEnterAtom } from "../../atoms/global.js";
import { Box, Checkbox, Typography } from "@mui/material";
import ImageResize from 'tiptap-extension-resize-image'; // Import the ResizeImage extension
import { isMobile } from '../../App';
import tippy from 'tippy.js';
import 'tippy.js/dist/tippy.css';
import { ReactRenderer } from '@tiptap/react';
import MentionList from './MentionList.jsx';
import { useRecoilState } from 'recoil';
import { isDisabledEditorEnterAtom } from '../../atoms/global.js';
import { Box, Checkbox, Typography, useTheme } from '@mui/material';
function textMatcher(doc, from) {
const textBeforeCursor = doc.textBetween(0, from, ' ', ' ');
@ -47,9 +41,16 @@ function textMatcher(doc, from) {
const query = match[0];
return { start, query };
}
const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEditorEnter }) => {
const MenuBar = ({
setEditorRef,
isChat,
isDisabledEditorEnter,
setIsDisabledEditorEnter,
}) => {
const { editor } = useCurrentEditor();
const fileInputRef = useRef(null);
const theme = useTheme();
if (!editor) {
return null;
@ -67,15 +68,15 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
new Compressor(file, {
quality: 0.6,
maxWidth: 1200,
mimeType: "image/webp",
mimeType: 'image/webp',
success(result) {
compressedFile = new File([result], "image.webp", {
type: "image/webp",
compressedFile = new File([result], 'image.webp', {
type: 'image/webp',
});
resolve();
},
error(err) {
console.error("Image compression error:", err);
console.error('Image compression error:', err);
},
});
});
@ -87,9 +88,9 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
editor
.chain()
.focus()
.setImage({ src: url, style: "width: auto" })
.setImage({ src: url, style: 'width: auto' })
.run();
fileInputRef.current.value = "";
fileInputRef.current.value = '';
};
reader.readAsDataURL(compressedFile);
}
@ -102,7 +103,7 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
const handlePaste = (event) => {
const items = event.clipboardData.items;
for (const item of items) {
if (item.type.startsWith("image/")) {
if (item.type.startsWith('image/')) {
const file = item.getAsFile();
if (file) {
event.preventDefault(); // Prevent the default paste behavior
@ -114,24 +115,29 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
useEffect(() => {
if (editor) {
editor.view.dom.addEventListener("paste", handlePaste);
editor.view.dom.addEventListener('paste', handlePaste);
return () => {
editor.view.dom.removeEventListener("paste", handlePaste);
editor.view.dom.removeEventListener('paste', handlePaste);
};
}
}, [editor]);
return (
<div className="control-group">
<div className="button-group" style={{
display: 'flex'
}}>
<div
className="button-group"
style={{
display: 'flex',
}}
>
<IconButton
onClick={() => editor.chain().focus().toggleBold().run()}
disabled={!editor.can().chain().focus().toggleBold().run()}
sx={{
color: editor.isActive("bold") ? "white" : "gray",
padding: isMobile ? "5px" : "revert",
color: editor.isActive('bold')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<FormatBoldIcon />
@ -140,8 +146,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
onClick={() => editor.chain().focus().toggleItalic().run()}
disabled={!editor.can().chain().focus().toggleItalic().run()}
sx={{
color: editor.isActive("italic") ? "white" : "gray",
padding: isMobile ? "5px" : "revert",
color: editor.isActive('italic')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<FormatItalicIcon />
@ -150,8 +158,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
onClick={() => editor.chain().focus().toggleStrike().run()}
disabled={!editor.can().chain().focus().toggleStrike().run()}
sx={{
color: editor.isActive("strike") ? "white" : "gray",
padding: isMobile ? "5px" : "revert",
color: editor.isActive('strike')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<StrikethroughSIcon />
@ -160,8 +170,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
onClick={() => editor.chain().focus().toggleCode().run()}
disabled={!editor.can().chain().focus().toggleCode().run()}
sx={{
color: editor.isActive("code") ? "white" : "gray",
padding: isMobile ? "5px" : "revert",
color: editor.isActive('code')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<CodeIcon />
@ -170,13 +182,13 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
onClick={() => editor.chain().focus().unsetAllMarks().run()}
sx={{
color:
editor.isActive("bold") ||
editor.isActive("italic") ||
editor.isActive("strike") ||
editor.isActive("code")
? "white"
: "gray",
padding: isMobile ? "5px" : "revert",
editor.isActive('bold') ||
editor.isActive('italic') ||
editor.isActive('strike') ||
editor.isActive('code')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<FormatClearIcon />
@ -184,8 +196,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
<IconButton
onClick={() => editor.chain().focus().toggleBulletList().run()}
sx={{
color: editor.isActive("bulletList") ? "white" : "gray",
padding: isMobile ? "5px" : "revert",
color: editor.isActive('bulletList')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<FormatListBulletedIcon />
@ -193,8 +207,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
<IconButton
onClick={() => editor.chain().focus().toggleOrderedList().run()}
sx={{
color: editor.isActive("orderedList") ? "white" : "gray",
padding: isMobile ? "5px" : "revert",
color: editor.isActive('orderedList')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<FormatListNumberedIcon />
@ -202,8 +218,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
<IconButton
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
sx={{
color: editor.isActive("codeBlock") ? "white" : "gray",
padding: isMobile ? "5px" : "revert",
color: editor.isActive('codeBlock')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<DeveloperModeIcon />
@ -211,8 +229,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
<IconButton
onClick={() => editor.chain().focus().toggleBlockquote().run()}
sx={{
color: editor.isActive("blockquote") ? "white" : "gray",
padding: isMobile ? "5px" : "revert",
color: editor.isActive('blockquote')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<FormatQuoteIcon />
@ -220,7 +240,7 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
<IconButton
onClick={() => editor.chain().focus().setHorizontalRule().run()}
disabled={!editor.can().chain().focus().setHorizontalRule().run()}
sx={{ color: "gray", padding: isMobile ? "5px" : "revert" }}
sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }}
>
<HorizontalRuleIcon />
</IconButton>
@ -229,8 +249,10 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
editor.chain().focus().toggleHeading({ level: 1 }).run()
}
sx={{
color: editor.isActive("heading", { level: 1 }) ? "white" : "gray",
padding: isMobile ? "5px" : "revert",
color: editor.isActive('heading', { level: 1 })
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<FormatHeadingIcon fontSize="small" />
@ -238,66 +260,68 @@ const MenuBar = ({ setEditorRef, isChat, isDisabledEditorEnter, setIsDisabledEdi
<IconButton
onClick={() => editor.chain().focus().undo().run()}
disabled={!editor.can().chain().focus().undo().run()}
sx={{ color: "gray", padding: isMobile ? "5px" : "revert" }}
sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }}
>
<UndoIcon />
</IconButton>
<IconButton
onClick={() => editor.chain().focus().redo().run()}
disabled={!editor.can().chain().focus().redo().run()}
sx={{ color: "gray" }}
sx={{ color: 'gray' }}
>
<RedoIcon />
</IconButton>
{isChat && (
<Box
sx={{
display: "flex",
alignItems: "center",
marginLeft: '5px',
cursor: 'pointer'
}}
onClick={()=> {
setIsDisabledEditorEnter(!isDisabledEditorEnter)
}}
>
<Checkbox
edge="start"
tabIndex={-1}
disableRipple
checked={isDisabledEditorEnter}
sx={{
"&.Mui-checked": {
color: "gray", // Customize the color when checked
},
"& .MuiSvgIcon-root": {
color: "gray",
},
}}
/>
<Typography
sx={{
fontSize: "14px",
color: 'gray'
}}
>
disable enter
</Typography>
</Box>
sx={{
display: 'flex',
alignItems: 'center',
marginLeft: '5px',
cursor: 'pointer',
}}
onClick={() => {
setIsDisabledEditorEnter(!isDisabledEditorEnter);
}}
>
<Checkbox
edge="start"
tabIndex={-1}
disableRipple
checked={isDisabledEditorEnter}
sx={{
'&.Mui-checked': {
color: theme.palette.text.secondary,
},
'& .MuiSvgIcon-root': {
color: theme.palette.text.secondary,
},
}}
/>
<Typography
sx={{
fontSize: '14px',
color: theme.palette.text.primary,
}}
>
disable enter
</Typography>
</Box>
)}
{!isChat && (
<>
<IconButton
onClick={triggerImageUpload}
sx={{ color: "gray", padding: isMobile ? "5px" : "revert" }}
sx={{
color: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
}}
>
<ImageIcon />
</IconButton>
<input
type="file"
ref={fileInputRef}
style={{ display: "none" }}
style={{ display: 'none' }}
onChange={(event) => handleImageUpload(event.target.files[0])}
accept="image/*"
/>
@ -322,7 +346,7 @@ const extensions = [
},
}),
Placeholder.configure({
placeholder: "Start typing here...",
placeholder: 'Start typing here...',
}),
ImageResize,
];
@ -340,12 +364,13 @@ export default ({
overrideMobile,
customEditorHeight,
membersWithNames,
enableMentions
enableMentions,
}) => {
const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useRecoilState(isDisabledEditorEnterAtom)
const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useRecoilState(
isDisabledEditorEnterAtom
);
const extensionsFiltered = isChat
? extensions.filter((item) => item?.name !== "image")
? extensions.filter((item) => item?.name !== 'image')
: extensions;
const editorRef = useRef(null);
const setEditorRefFunc = (editorInstance) => {
@ -359,20 +384,14 @@ export default ({
// { id: 3, label: 'Charlie' },
// ];
const users = useMemo(()=> {
return (membersWithNames || [])?.map((item)=> {
const users = useMemo(() => {
return (membersWithNames || [])?.map((item) => {
return {
id: item,
label: item
}
})
}, [membersWithNames])
label: item,
};
});
}, [membersWithNames]);
const usersRef = useRef([]);
useEffect(() => {
@ -386,13 +405,13 @@ export default ({
const handleBlur = () => {
const htmlContent = editorRef.current.getHTML();
if (!htmlContent?.trim() || htmlContent?.trim() === "<p></p>") {
if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') {
// Set focus state based on content
}
};
const additionalExtensions = useMemo(()=> {
if(!enableMentions) return []
const additionalExtensions = useMemo(() => {
if (!enableMentions) return [];
return [
Mention.configure({
HTMLAttributes: {
@ -409,122 +428,129 @@ export default ({
let popup; // Reference to the Tippy.js instance
let component;
return {
onStart: props => {
component = new ReactRenderer(MentionList, {
props,
editor: props.editor,
})
if (!props.clientRect) {
return
}
popup = tippy('body', {
getReferenceClientRect: props.clientRect,
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()
},
}
return {
onStart: (props) => {
component = new ReactRenderer(MentionList, {
props,
editor: props.editor,
});
if (!props.clientRect) {
return;
}
popup = tippy('body', {
getReferenceClientRect: props.clientRect,
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();
},
};
},
},
})
]
}, [enableMentions])
}),
];
}, [enableMentions]);
const handleSetIsDisabledEditorEnter = useCallback((val)=> {
setIsDisabledEditorEnter(val)
const handleSetIsDisabledEditorEnter = useCallback((val) => {
setIsDisabledEditorEnter(val);
localStorage.setItem('settings-disable-editor-enter', JSON.stringify(val));
}, [])
}, []);
return (
<div style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%'
}}>
<EditorProvider
slotBefore={
(isFocusedParent || !isMobile || overrideMobile) && (
<MenuBar setEditorRef={setEditorRefFunc} isChat={isChat} isDisabledEditorEnter={isDisabledEditorEnter} setIsDisabledEditorEnter={handleSetIsDisabledEditorEnter} />
)
}
extensions={[...extensionsFiltered, ...additionalExtensions
]}
content={content}
onCreate={({ editor }) => {
editor.on("focus", handleFocus); // Listen for focus event
editor.on("blur", handleBlur); // Listen for blur event
<div
style={{
display: 'flex',
flexDirection: 'column',
height: '100%',
justifyContent: 'space-between',
}}
onUpdate={({ editor }) => {
editor.on('focus', handleFocus); // Ensure focus is updated
editor.on('blur', handleBlur); // Ensure blur is updated
}}
editorProps={{
attributes: {
class: "tiptap-prosemirror",
style:
isMobile ?
`overflow: auto; min-height: ${
customEditorHeight ? "200px" : "0px"
}; max-height:calc(100svh - ${customEditorHeight || "140px"})`: `overflow: auto; max-height: 250px`,
},
handleKeyDown(view, event) {
if (!disableEnter && !isDisabledEditorEnter && event.key === "Enter") {
if (event.shiftKey) {
view.dispatch(
view.state.tr.replaceSelectionWith(
view.state.schema.nodes.hardBreak.create()
)
);
return true;
} else {
if (typeof onEnter === "function") {
onEnter();
>
<EditorProvider
slotBefore={
(isFocusedParent || !isMobile || overrideMobile) && (
<MenuBar
setEditorRef={setEditorRefFunc}
isChat={isChat}
isDisabledEditorEnter={isDisabledEditorEnter}
setIsDisabledEditorEnter={handleSetIsDisabledEditorEnter}
/>
)
}
extensions={[...extensionsFiltered, ...additionalExtensions]}
content={content}
onCreate={({ editor }) => {
editor.on('focus', handleFocus); // Listen for focus event
editor.on('blur', handleBlur); // Listen for blur event
}}
onUpdate={({ editor }) => {
editor.on('focus', handleFocus); // Ensure focus is updated
editor.on('blur', handleBlur); // Ensure blur is updated
}}
editorProps={{
attributes: {
class: 'tiptap-prosemirror',
style: isMobile
? `overflow: auto; min-height: ${
customEditorHeight ? '200px' : '0px'
}; max-height:calc(100svh - ${customEditorHeight || '140px'})`
: `overflow: auto; max-height: 250px`,
},
handleKeyDown(view, event) {
if (
!disableEnter &&
!isDisabledEditorEnter &&
event.key === 'Enter'
) {
if (event.shiftKey) {
view.dispatch(
view.state.tr.replaceSelectionWith(
view.state.schema.nodes.hardBreak.create()
)
);
return true;
} else {
if (typeof onEnter === 'function') {
onEnter();
}
return true;
}
return true;
}
}
return false;
},
}}
/>
return false;
},
}}
/>
</div>
);
};

View File

@ -1,6 +1,6 @@
.tiptap {
margin-top: 0;
color: ''; /* Set default font color to white */
color: theme => theme.palette.text.primary;
width: 100%;
}
@ -26,7 +26,7 @@
line-height: 1.1;
margin-top: 2.5rem;
text-wrap: pretty;
color: white; /* Ensure heading font color is white */
color: theme => theme.palette.text.primary;
}
.tiptap h1,
@ -55,18 +55,18 @@
/* Code and preformatted text styles */
.tiptap code {
background-color: #27282c; /* Set code background color to #27282c */
background-color: theme => theme.palette.background.default;
border-radius: 0.4rem;
color: white; /* Ensure inline code text color is white */
color: theme => theme.palette.text.primary;
font-size: 0.85rem;
padding: 0.25em 0.3em;
text-wrap: pretty;
}
.tiptap pre {
background: #27282c; /* Set code block background color to #27282c */
background: theme => theme.palette.background.default;
border-radius: 0.5rem;
color: white; /* Ensure code block text color is white */
color: theme => theme.palette.text.primary;
font-family: 'JetBrainsMono', monospace;
margin: 1.5rem 0;
padding: 0.75rem 1rem;
@ -86,7 +86,7 @@
border-left: 3px solid var(--gray-3);
margin: 1.5rem 0;
padding-left: 1rem;
color: white; /* Ensure blockquote text color is white */
color: theme => theme.palette.text.primary;
text-wrap: pretty;
}
@ -102,11 +102,12 @@
.tiptap p {
font-size: 16px;
color: white; /* Ensure paragraph text color is white */
color: theme => theme.palette.text.primary;
margin: 0px;
}
.tiptap p.is-editor-empty:first-child::before {
color: #adb5bd;
color: theme => theme.palette.text.primary;
content: attr(data-placeholder);
float: left;
height: 0;
@ -133,17 +134,17 @@
.tiptap [data-type='mention'] {
box-decoration-break: clone;
color: lightblue;
color: theme => theme.palette.text.secondary;
padding: 0.1rem 0.3rem;
}
.unread-divider {
width: 90%;
color: white;
border-bottom: 1px solid white;
border-radius: 2px;
color: theme => theme.palette.text.primary;
display: flex;
justify-content: center;
border-radius: 2px;
width: 90%;
}
.mention-item {
@ -168,11 +169,11 @@
font-size: 16px;
width: 100%;
border: none;
color: white;
color: theme => theme.palette.text.primary;
cursor: pointer;
&:hover,
&:hover.is-selected {
background-color: gray;
background-color: theme => theme.palette.background.secondary;
}
}
}

View File

@ -15,7 +15,7 @@ const darkThemeOptions: ThemeOptions = {
},
background: {
default: 'rgb(49, 51, 56)',
paper: 'rgb(46, 46, 49)',
paper: 'rgb(96, 96, 97)',
},
text: {
primary: 'rgb(255, 255, 255)',