feat: modularize block system (#718)

This commit is contained in:
Jeremy 2025-01-27 14:19:47 +05:30 committed by GitHub
parent 5e8cddc886
commit 38527ff92e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 684 additions and 604 deletions

View file

@ -11,6 +11,7 @@ import {
import {
type Dispatch,
memo,
ReactNode,
type SetStateAction,
useEffect,
useRef,
@ -32,22 +33,16 @@ import {
LogsIcon,
MessageIcon,
PenIcon,
SparklesIcon,
StopIcon,
SummarizeIcon,
} from './icons';
import { BlockKind } from './block';
import { blockDefinitions, BlockKind } from './block';
import { BlockToolbarItem } from './create-block';
import { UseChatHelpers } from 'ai/react';
type ToolProps = {
type:
| 'final-polish'
| 'request-suggestions'
| 'adjust-reading-level'
| 'code-review'
| 'add-comments'
| 'add-logs';
description: string;
icon: JSX.Element;
icon: ReactNode;
selectedTool: string | null;
setSelectedTool: Dispatch<SetStateAction<string | null>>;
isToolbarVisible?: boolean;
@ -57,10 +52,14 @@ type ToolProps = {
message: Message | CreateMessage,
chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>;
onClick: ({
appendMessage,
}: {
appendMessage: UseChatHelpers['append'];
}) => void;
};
const Tool = ({
type,
description,
icon,
selectedTool,
@ -69,14 +68,15 @@ const Tool = ({
setIsToolbarVisible,
isAnimating,
append,
onClick,
}: ToolProps) => {
const [isHovered, setIsHovered] = useState(false);
useEffect(() => {
if (selectedTool !== type) {
if (selectedTool !== description) {
setIsHovered(false);
}
}, [selectedTool, type]);
}, [selectedTool, description]);
const handleSelect = () => {
if (!isToolbarVisible && setIsToolbarVisible) {
@ -86,44 +86,15 @@ const Tool = ({
if (!selectedTool) {
setIsHovered(true);
setSelectedTool(type);
setSelectedTool(description);
return;
}
if (selectedTool !== type) {
setSelectedTool(type);
if (selectedTool !== description) {
setSelectedTool(description);
} else {
if (type === 'final-polish') {
append({
role: 'user',
content:
'Please add final polish and check for grammar, add section titles for better structure, and ensure everything reads smoothly.',
});
setSelectedTool(null);
} else if (type === 'request-suggestions') {
append({
role: 'user',
content:
'Please add suggestions you have that could improve the writing.',
});
setSelectedTool(null);
} else if (type === 'add-comments') {
append({
role: 'user',
content: 'Please add comments to explain the code.',
});
setSelectedTool(null);
} else if (type === 'add-logs') {
append({
role: 'user',
content: 'Please add logs to help debug the code.',
});
setSelectedTool(null);
}
setSelectedTool(null);
onClick({ appendMessage: append });
}
};
@ -132,13 +103,13 @@ const Tool = ({
<TooltipTrigger asChild>
<motion.div
className={cx('p-3 rounded-full', {
'bg-primary !text-primary-foreground': selectedTool === type,
'bg-primary !text-primary-foreground': selectedTool === description,
})}
onHoverStart={() => {
setIsHovered(true);
}}
onHoverEnd={() => {
if (selectedTool !== type) setIsHovered(false);
if (selectedTool !== description) setIsHovered(false);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
@ -158,7 +129,7 @@ const Tool = ({
handleSelect();
}}
>
{selectedTool === type ? <ArrowUpIcon /> : icon}
{selectedTool === description ? <ArrowUpIcon /> : icon}
</motion.div>
</TooltipTrigger>
<TooltipContent
@ -283,52 +254,6 @@ const ReadingLevelSelector = ({
);
};
const toolsByBlockKind: Record<
BlockKind,
Array<{
type:
| 'final-polish'
| 'request-suggestions'
| 'adjust-reading-level'
| 'code-review'
| 'add-comments'
| 'add-logs';
description: string;
icon: JSX.Element;
}>
> = {
text: [
{
type: 'final-polish',
description: 'Add final polish',
icon: <PenIcon />,
},
{
type: 'adjust-reading-level',
description: 'Adjust reading level',
icon: <SummarizeIcon />,
},
{
type: 'request-suggestions',
description: 'Request suggestions',
icon: <MessageIcon />,
},
],
code: [
{
type: 'add-comments',
description: 'Add comments',
icon: <CodeIcon />,
},
{
type: 'add-logs',
description: 'Add logs',
icon: <LogsIcon />,
},
],
image: [],
};
export const Tools = ({
isToolbarVisible,
selectedTool,
@ -336,7 +261,7 @@ export const Tools = ({
append,
isAnimating,
setIsToolbarVisible,
blockKind,
tools,
}: {
isToolbarVisible: boolean;
selectedTool: string | null;
@ -347,9 +272,9 @@ export const Tools = ({
) => Promise<string | null | undefined>;
isAnimating: boolean;
setIsToolbarVisible: Dispatch<SetStateAction<boolean>>;
blockKind: BlockKind;
tools: Array<BlockToolbarItem>;
}) => {
const [primaryTool, ...secondaryTools] = toolsByBlockKind[blockKind];
const [primaryTool, ...secondaryTools] = tools;
return (
<motion.div
@ -362,20 +287,19 @@ export const Tools = ({
{isToolbarVisible &&
secondaryTools.map((secondaryTool) => (
<Tool
key={secondaryTool.type}
type={secondaryTool.type}
key={secondaryTool.description}
description={secondaryTool.description}
icon={secondaryTool.icon}
selectedTool={selectedTool}
setSelectedTool={setSelectedTool}
append={append}
isAnimating={isAnimating}
onClick={secondaryTool.onClick}
/>
))}
</AnimatePresence>
<Tool
type={primaryTool.type}
description={primaryTool.description}
icon={primaryTool.icon}
selectedTool={selectedTool}
@ -384,6 +308,7 @@ export const Tools = ({
setIsToolbarVisible={setIsToolbarVisible}
append={append}
isAnimating={isAnimating}
onClick={primaryTool.onClick}
/>
</motion.div>
);
@ -451,7 +376,17 @@ const PureToolbar = ({
}
}, [isLoading, setIsToolbarVisible]);
if (toolsByBlockKind[blockKind].length === 0) {
const blockDefinition = blockDefinitions.find(
(definition) => definition.kind === blockKind,
);
if (!blockDefinition) {
throw new Error('Block definition not found!');
}
const toolsByBlockKind = blockDefinition.toolbar;
if (toolsByBlockKind.length === 0) {
return null;
}
@ -473,7 +408,7 @@ const PureToolbar = ({
: {
opacity: 1,
y: 0,
height: toolsByBlockKind[blockKind].length * 50,
height: toolsByBlockKind.length * 50,
transition: { delay: 0 },
scale: 1,
}
@ -530,7 +465,7 @@ const PureToolbar = ({
selectedTool={selectedTool}
setIsToolbarVisible={setIsToolbarVisible}
setSelectedTool={setSelectedTool}
blockKind={blockKind}
tools={toolsByBlockKind}
/>
)}
</motion.div>