2023-12-12 18:03:17 +08:00
|
|
|
|
import {
|
|
|
|
|
useMantineTheme,
|
|
|
|
|
Flex,
|
|
|
|
|
Stack,
|
|
|
|
|
ActionIcon,
|
|
|
|
|
ScrollArea,
|
|
|
|
|
Popover,
|
|
|
|
|
Textarea,
|
|
|
|
|
Text,
|
|
|
|
|
Indicator,
|
|
|
|
|
Drawer,
|
|
|
|
|
Group,
|
|
|
|
|
Button,
|
|
|
|
|
Menu,
|
|
|
|
|
createStyles,
|
|
|
|
|
} from "@mantine/core";
|
2023-12-12 15:13:17 +08:00
|
|
|
|
import { useDisclosure, useResizeObserver } from "@mantine/hooks";
|
2023-12-12 18:03:17 +08:00
|
|
|
|
import {
|
|
|
|
|
IconGitBranch,
|
|
|
|
|
IconSend,
|
|
|
|
|
IconPaperclip,
|
|
|
|
|
IconChevronDown,
|
|
|
|
|
IconTextPlus,
|
|
|
|
|
IconRobot,
|
|
|
|
|
} from "@tabler/icons-react";
|
2023-12-12 15:13:17 +08:00
|
|
|
|
import React, { useState, useEffect } from "react";
|
2023-12-12 18:03:17 +08:00
|
|
|
|
import {
|
|
|
|
|
IconGitBranchChecked,
|
|
|
|
|
IconShellCommand,
|
|
|
|
|
} from "@/views/components/ChatIcons";
|
|
|
|
|
import messageUtil from "@/util/MessageUtil";
|
|
|
|
|
import InputContexts from "./InputContexts";
|
|
|
|
|
import Topic from "./Topic";
|
2023-12-12 15:13:17 +08:00
|
|
|
|
import { observer } from "mobx-react-lite";
|
|
|
|
|
import { useMst } from "@/views/stores/RootStore";
|
|
|
|
|
import { ChatContext } from "@/views/stores/InputStore";
|
|
|
|
|
import { Message } from "@/views/stores/ChatStore";
|
|
|
|
|
|
|
|
|
|
const useStyles = createStyles((theme) => ({
|
2023-12-12 18:03:17 +08:00
|
|
|
|
actionIcon: {
|
|
|
|
|
color: "var(--vscode-dropdown-foreground)",
|
|
|
|
|
borderColor: "var(--vscode-dropdown-border)",
|
|
|
|
|
backgroundColor: "var(--vscode-dropdown-background)",
|
|
|
|
|
"&:hover": {
|
|
|
|
|
color: "var(--vscode-dropdown-foreground)",
|
|
|
|
|
borderColor: "var(--vscode-dropdown-border)",
|
|
|
|
|
backgroundColor: "var(--vscode-dropdown-background)",
|
|
|
|
|
},
|
|
|
|
|
"&[data-disabled]": {
|
|
|
|
|
borderColor: "transparent",
|
|
|
|
|
backgroundColor: "#e9ecef",
|
|
|
|
|
color: "#adb5bd",
|
|
|
|
|
cursor: "not-allowed",
|
|
|
|
|
backgroundImage: "none",
|
|
|
|
|
pointervents: "none",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}));
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
|
|
|
|
const InputMessage = observer((props: any) => {
|
2023-12-12 18:03:17 +08:00
|
|
|
|
const { classes } = useStyles();
|
|
|
|
|
const { input, chat } = useMst();
|
|
|
|
|
const {
|
|
|
|
|
contexts,
|
|
|
|
|
menuOpend,
|
|
|
|
|
menuType,
|
|
|
|
|
currentMenuIndex,
|
|
|
|
|
contextMenus,
|
|
|
|
|
commandMenus,
|
|
|
|
|
modelMenus,
|
|
|
|
|
} = input;
|
|
|
|
|
const { generating } = chat;
|
|
|
|
|
const showTopic = process.env.platform === "idea";
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
const [drawerOpened, { open: openDrawer, close: closeDrawer }] =
|
|
|
|
|
useDisclosure(false);
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
const theme = useMantineTheme();
|
|
|
|
|
const [commandMenusNode, setCommandMenusNode] = useState<any>(null);
|
|
|
|
|
const [inputRef, inputRect] = useResizeObserver();
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
const handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
|
|
|
const value = event.target.value;
|
|
|
|
|
// if value start with '/' command show menu
|
|
|
|
|
if (value.startsWith("/")) {
|
|
|
|
|
input.openMenu("commands");
|
|
|
|
|
input.setCurrentMenuIndex(0);
|
|
|
|
|
} else {
|
|
|
|
|
input.closeMenu();
|
|
|
|
|
}
|
|
|
|
|
input.setValue(value);
|
|
|
|
|
};
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
const handleSendClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
|
|
|
const inputValue = input.value;
|
|
|
|
|
if (inputValue) {
|
|
|
|
|
if (inputValue.trim() === "/help") {
|
|
|
|
|
chat.helpMessage();
|
|
|
|
|
} else {
|
|
|
|
|
const text = inputValue;
|
|
|
|
|
const chatContexts = contexts
|
|
|
|
|
? [...contexts].map((item) => ({ ...item }))
|
|
|
|
|
: undefined;
|
|
|
|
|
if (inputValue.trim().startsWith("/ask-code")) {
|
|
|
|
|
chat.devchatAsk(text, chatContexts);
|
2023-12-12 15:13:17 +08:00
|
|
|
|
} else {
|
2023-12-12 18:03:17 +08:00
|
|
|
|
chat.commonMessage(text, chatContexts);
|
2023-12-12 15:13:17 +08:00
|
|
|
|
}
|
2023-12-12 18:03:17 +08:00
|
|
|
|
}
|
|
|
|
|
// Clear the input field
|
|
|
|
|
input.setValue("");
|
|
|
|
|
input.clearContexts();
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
const handleContextClick = (contextName: string) => {
|
|
|
|
|
// Process and send the message to the extension
|
|
|
|
|
messageUtil.sendMessage({
|
|
|
|
|
command: "addContext",
|
|
|
|
|
selected: contextName,
|
|
|
|
|
});
|
|
|
|
|
};
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
|
|
|
if (menuOpend) {
|
|
|
|
|
if (event.key === "Escape") {
|
|
|
|
|
input.closeMenu();
|
|
|
|
|
}
|
|
|
|
|
if (menuType === "commands") {
|
|
|
|
|
if (event.key === "ArrowDown") {
|
|
|
|
|
const newIndex = currentMenuIndex + 1;
|
|
|
|
|
input.setCurrentMenuIndex(
|
|
|
|
|
newIndex < commandMenusNode.length ? newIndex : 0
|
|
|
|
|
);
|
|
|
|
|
event.preventDefault();
|
2023-12-12 15:13:17 +08:00
|
|
|
|
}
|
2023-12-12 18:03:17 +08:00
|
|
|
|
if (event.key === "ArrowUp") {
|
|
|
|
|
const newIndex = currentMenuIndex - 1;
|
|
|
|
|
input.setCurrentMenuIndex(
|
|
|
|
|
newIndex < 0 ? commandMenusNode.length - 1 : newIndex
|
|
|
|
|
);
|
|
|
|
|
event.preventDefault();
|
2023-12-12 15:13:17 +08:00
|
|
|
|
}
|
2023-12-12 18:03:17 +08:00
|
|
|
|
if ((event.key === "Enter" || event.key === "Tab") && !event.shiftKey) {
|
|
|
|
|
const commandNode = commandMenusNode[currentMenuIndex];
|
|
|
|
|
const commandPattern = commandNode.props["data-pattern"];
|
|
|
|
|
if (commandPattern === "help") {
|
|
|
|
|
chat.helpMessage();
|
|
|
|
|
input.setValue("");
|
|
|
|
|
} else {
|
|
|
|
|
input.setValue(`/${commandPattern} `);
|
|
|
|
|
}
|
|
|
|
|
input.closeMenu();
|
|
|
|
|
event.preventDefault();
|
2023-12-12 15:13:17 +08:00
|
|
|
|
}
|
2023-12-12 18:03:17 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (
|
|
|
|
|
event.key === "Enter" &&
|
|
|
|
|
!event.shiftKey &&
|
|
|
|
|
!event.nativeEvent.isComposing
|
|
|
|
|
) {
|
|
|
|
|
handleSendClick(event as any);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
const contextMenuIcon = (name: string) => {
|
|
|
|
|
if (name === "git diff --cached") {
|
|
|
|
|
return (
|
|
|
|
|
<IconGitBranchChecked size={14} color="var(--vscode-menu-foreground)" />
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (name === "git diff HEAD") {
|
|
|
|
|
return <IconGitBranch size={14} color="var(--vscode-menu-foreground)" />;
|
|
|
|
|
}
|
|
|
|
|
return <IconShellCommand size={14} color="var(--vscode-menu-foreground)" />;
|
|
|
|
|
};
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
input.fetchContextMenus().then();
|
|
|
|
|
input.fetchCommandMenus().then();
|
|
|
|
|
input.fetchModelMenus().then();
|
|
|
|
|
messageUtil.registerHandler(
|
|
|
|
|
"regCommandList",
|
|
|
|
|
(message: { result: object[] }) => {
|
|
|
|
|
input.updateCommands(message.result);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
messageUtil.registerHandler(
|
|
|
|
|
"appendContext",
|
|
|
|
|
(message: { command: string; context: string }) => {
|
|
|
|
|
// context is a temp file path
|
|
|
|
|
const match = /\|([^]+?)\]/.exec(message.context);
|
|
|
|
|
// Process and send the message to the extension
|
2023-12-12 15:13:17 +08:00
|
|
|
|
messageUtil.sendMessage({
|
2023-12-12 18:03:17 +08:00
|
|
|
|
command: "contextDetail",
|
|
|
|
|
file: match && match[1],
|
2023-12-12 15:13:17 +08:00
|
|
|
|
});
|
2023-12-12 18:03:17 +08:00
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
messageUtil.registerHandler(
|
|
|
|
|
"contextDetailResponse",
|
|
|
|
|
(message: { command: string; file: string; result: string }) => {
|
|
|
|
|
//result is a content json
|
|
|
|
|
// 1. diff json structure
|
|
|
|
|
// {
|
|
|
|
|
// languageId: languageId,
|
|
|
|
|
// path: fileSelected,
|
|
|
|
|
// content: codeSelected
|
|
|
|
|
// };
|
|
|
|
|
// 2. command json structure
|
|
|
|
|
// {
|
|
|
|
|
// command: commandString,
|
|
|
|
|
// content: stdout
|
|
|
|
|
// };
|
|
|
|
|
const context = JSON.parse(message.result);
|
|
|
|
|
if (typeof context !== "undefined" && context) {
|
|
|
|
|
const chatContext = ChatContext.create({
|
|
|
|
|
file: message.file,
|
|
|
|
|
path: context.path,
|
|
|
|
|
command: context.command,
|
|
|
|
|
content: context.content,
|
|
|
|
|
});
|
|
|
|
|
input.newContext(chatContext);
|
2023-12-12 15:13:17 +08:00
|
|
|
|
}
|
2023-12-12 18:03:17 +08:00
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
inputRef.current.focus();
|
|
|
|
|
}, []);
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
const getModelShowName = (modelName: string) => {
|
|
|
|
|
const nameMap = {
|
|
|
|
|
"gpt-3.5-turbo": "GPT-3.5",
|
|
|
|
|
"gpt-3.5-turbo-1106": "GPT-3.5-1106",
|
|
|
|
|
"gpt-3.5-turbo-16k": "GPT-3.5-16K",
|
|
|
|
|
"gpt-4": "GPT-4",
|
|
|
|
|
"gpt-4-1106-preview": "GPT-4-turbo",
|
|
|
|
|
"claude-2": "CLAUDE-2",
|
2023-12-12 15:13:17 +08:00
|
|
|
|
};
|
2023-12-12 18:03:17 +08:00
|
|
|
|
if (modelName in nameMap) {
|
|
|
|
|
return nameMap[modelName];
|
|
|
|
|
} else if (modelName.lastIndexOf("/") > -1) {
|
|
|
|
|
return modelName
|
|
|
|
|
.substring(modelName.lastIndexOf("/") + 1)
|
|
|
|
|
.toLocaleUpperCase();
|
|
|
|
|
} else {
|
|
|
|
|
return modelName.toUpperCase();
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-12-12 15:13:17 +08:00
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
let filtered;
|
|
|
|
|
if (input.value) {
|
|
|
|
|
filtered = commandMenus.filter((item) =>
|
|
|
|
|
`/${item.pattern}`.startsWith(input.value)
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
filtered = commandMenus;
|
|
|
|
|
}
|
|
|
|
|
const node = filtered.map(({ pattern, description, name }, index) => {
|
|
|
|
|
return (
|
|
|
|
|
<Flex
|
|
|
|
|
key={`command-menus-${index}`}
|
|
|
|
|
mih={40}
|
|
|
|
|
gap="md"
|
|
|
|
|
justify="flex-start"
|
|
|
|
|
align="flex-start"
|
|
|
|
|
direction="row"
|
|
|
|
|
wrap="wrap"
|
|
|
|
|
sx={{
|
|
|
|
|
padding: "5px 0",
|
|
|
|
|
"&:hover,&[aria-checked=true]": {
|
|
|
|
|
cursor: "pointer",
|
|
|
|
|
color: "var(--vscode-commandCenter-activeForeground)",
|
|
|
|
|
backgroundColor: "var(--vscode-commandCenter-activeBackground)",
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
input.setValue(`/${pattern} `);
|
|
|
|
|
input.closeMenu();
|
|
|
|
|
}}
|
|
|
|
|
aria-checked={index === currentMenuIndex}
|
|
|
|
|
data-pattern={pattern}
|
|
|
|
|
>
|
|
|
|
|
<Stack
|
|
|
|
|
spacing={0}
|
2023-12-12 15:13:17 +08:00
|
|
|
|
sx={{
|
2023-12-12 18:03:17 +08:00
|
|
|
|
paddingLeft: 10,
|
2023-12-12 15:13:17 +08:00
|
|
|
|
}}
|
2023-12-12 18:03:17 +08:00
|
|
|
|
>
|
|
|
|
|
<Text
|
|
|
|
|
sx={{
|
|
|
|
|
fontSize: "sm",
|
|
|
|
|
fontWeight: "bolder",
|
|
|
|
|
color: "var(--vscode-menu-foreground)",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
/{pattern}
|
|
|
|
|
</Text>
|
|
|
|
|
<Text
|
|
|
|
|
sx={{
|
|
|
|
|
fontSize: "sm",
|
|
|
|
|
color: theme.colors.gray[6],
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{description}
|
|
|
|
|
</Text>
|
|
|
|
|
</Stack>
|
|
|
|
|
</Flex>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
setCommandMenusNode(node);
|
|
|
|
|
if (node.length === 0) {
|
|
|
|
|
input.closeMenu();
|
|
|
|
|
}
|
|
|
|
|
}, [input.value, commandMenus, currentMenuIndex]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (drawerOpened && (!contexts || contexts.length === 0)) {
|
|
|
|
|
closeDrawer();
|
|
|
|
|
}
|
|
|
|
|
}, [contexts.length]);
|
|
|
|
|
|
|
|
|
|
const changeModel = (value) => {
|
|
|
|
|
chat.changeChatModel(value);
|
|
|
|
|
messageUtil.sendMessage({
|
|
|
|
|
command: "updateSetting",
|
|
|
|
|
key1: "devchat",
|
|
|
|
|
key2: "defaultModel",
|
|
|
|
|
value: value,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const menuStyles = {
|
|
|
|
|
arrow: {
|
|
|
|
|
borderColor: "var(--vscode-menu-border)",
|
|
|
|
|
},
|
|
|
|
|
dropdown: {
|
|
|
|
|
borderColor: "var(--vscode-menu-border)",
|
|
|
|
|
backgroundColor: "var(--vscode-menu-background)",
|
|
|
|
|
},
|
|
|
|
|
itemLabel: {
|
|
|
|
|
color: "var(--vscode-menu-foreground)",
|
|
|
|
|
},
|
|
|
|
|
item: {
|
|
|
|
|
padding: 5,
|
|
|
|
|
backgroundColor: "var(--vscode-menu-background)",
|
|
|
|
|
"&:hover,&[data-hovered=true]": {
|
|
|
|
|
color: "var(--vscode-commandCenter-activeForeground)",
|
|
|
|
|
borderColor: "var(--vscode-commandCenter-border)",
|
|
|
|
|
backgroundColor: "var(--vscode-commandCenter-activeBackground)",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const buttonStyles = {
|
|
|
|
|
root: {
|
|
|
|
|
color: "var(--vscode-dropdown-foreground)",
|
|
|
|
|
borderColor: "var(--vscode-dropdown-border)",
|
|
|
|
|
backgroundColor: "var(--vscode-dropdown-background)",
|
|
|
|
|
"&:hover": {
|
|
|
|
|
color: "var(--vscode-dropdown-foreground)",
|
|
|
|
|
borderColor: "var(--vscode-dropdown-border)",
|
|
|
|
|
backgroundColor: "var(--vscode-dropdown-background)",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Stack
|
|
|
|
|
spacing={0}
|
|
|
|
|
sx={{
|
|
|
|
|
padding: "0 5px",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Group
|
|
|
|
|
spacing={5}
|
|
|
|
|
sx={{
|
|
|
|
|
marginTop: 5,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Menu
|
|
|
|
|
width={chat.chatPanelWidth - 10}
|
|
|
|
|
position="bottom-start"
|
|
|
|
|
shadow="sm"
|
|
|
|
|
withArrow
|
|
|
|
|
styles={menuStyles}
|
|
|
|
|
disabled={contextMenus.length === 0}
|
2023-12-12 15:13:17 +08:00
|
|
|
|
>
|
2023-12-12 18:03:17 +08:00
|
|
|
|
<Menu.Target>
|
|
|
|
|
<ActionIcon
|
|
|
|
|
radius="xl"
|
|
|
|
|
variant="default"
|
|
|
|
|
disabled={generating || chat.disabled}
|
|
|
|
|
className={classes.actionIcon}
|
2023-12-12 15:13:17 +08:00
|
|
|
|
>
|
2023-12-12 18:03:17 +08:00
|
|
|
|
<IconTextPlus size="1rem" />
|
|
|
|
|
</ActionIcon>
|
|
|
|
|
</Menu.Target>
|
|
|
|
|
<Menu.Dropdown>
|
|
|
|
|
{[...contextMenus]
|
|
|
|
|
.sort((a, b) => {
|
|
|
|
|
if (a.name === "<custom command>") {
|
|
|
|
|
return 1; // Placing '<custom command>' at the end
|
|
|
|
|
} else if (b.name === "<custom command>") {
|
|
|
|
|
return -1; // Placing '<custom command>' at the front
|
|
|
|
|
} else {
|
|
|
|
|
return (a.name || "").localeCompare(b.name || ""); // Sorting alphabetically for other cases
|
2023-12-12 15:13:17 +08:00
|
|
|
|
}
|
2023-12-12 18:03:17 +08:00
|
|
|
|
})
|
|
|
|
|
.map(({ pattern, description, name }, index) => {
|
|
|
|
|
return (
|
|
|
|
|
<Menu.Item
|
|
|
|
|
key={`contexts-menus-${index}`}
|
|
|
|
|
icon={contextMenuIcon(name)}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
handleContextClick(name);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{name}
|
|
|
|
|
<Text sx={{ fontSize: "9pt", color: theme.colors.gray[6] }}>
|
|
|
|
|
{description}
|
|
|
|
|
</Text>
|
|
|
|
|
</Menu.Item>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</Menu.Dropdown>
|
|
|
|
|
</Menu>
|
|
|
|
|
<Menu
|
|
|
|
|
position="bottom-start"
|
|
|
|
|
withArrow
|
|
|
|
|
shadow="md"
|
|
|
|
|
styles={menuStyles}
|
|
|
|
|
disabled={modelMenus.length === 0}
|
|
|
|
|
>
|
|
|
|
|
<Menu.Target>
|
|
|
|
|
<Button
|
|
|
|
|
disabled={generating || chat.disabled}
|
|
|
|
|
variant="default"
|
|
|
|
|
size="xs"
|
|
|
|
|
radius="xl"
|
|
|
|
|
leftIcon={<IconRobot size="1rem" />}
|
|
|
|
|
styles={buttonStyles}
|
|
|
|
|
>
|
|
|
|
|
{getModelShowName(chat.chatModel)}
|
|
|
|
|
</Button>
|
|
|
|
|
</Menu.Target>
|
|
|
|
|
<Menu.Dropdown>
|
|
|
|
|
{modelMenus.map((modelName) => {
|
|
|
|
|
return (
|
|
|
|
|
<Menu.Item onClick={() => changeModel(modelName)}>
|
|
|
|
|
{getModelShowName(modelName)}
|
|
|
|
|
</Menu.Item>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</Menu.Dropdown>
|
|
|
|
|
</Menu>
|
|
|
|
|
{showTopic && <Topic styleName={classes.actionIcon} />}
|
|
|
|
|
</Group>
|
|
|
|
|
{contexts && contexts.length > 0 && (
|
|
|
|
|
<Drawer
|
|
|
|
|
opened={drawerOpened}
|
|
|
|
|
onClose={closeDrawer}
|
|
|
|
|
position="bottom"
|
|
|
|
|
title="DevChat Contexts"
|
|
|
|
|
overlayProps={{ opacity: 0.5, blur: 4 }}
|
|
|
|
|
closeButtonProps={{ children: <IconChevronDown size="1rem" /> }}
|
|
|
|
|
styles={{
|
|
|
|
|
content: {
|
|
|
|
|
background: "var(--vscode-sideBar-background)",
|
|
|
|
|
color: "var(--vscode-editor-foreground)",
|
|
|
|
|
},
|
|
|
|
|
header: {
|
|
|
|
|
background: "var(--vscode-sideBar-background)",
|
|
|
|
|
color: "var(--vscode-editor-foreground)",
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<InputContexts />
|
|
|
|
|
</Drawer>
|
|
|
|
|
)}
|
|
|
|
|
<Popover
|
|
|
|
|
position="top-start"
|
|
|
|
|
shadow="sm"
|
|
|
|
|
width={chat.chatPanelWidth - 10}
|
|
|
|
|
opened={menuOpend}
|
|
|
|
|
onChange={() => {
|
|
|
|
|
input.closeMenu();
|
|
|
|
|
inputRef.current.focus();
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Popover.Target>
|
|
|
|
|
<Textarea
|
|
|
|
|
id="chat-textarea"
|
|
|
|
|
disabled={generating || chat.disabled}
|
|
|
|
|
value={input.value}
|
|
|
|
|
ref={inputRef}
|
|
|
|
|
onKeyDown={handleKeyDown}
|
|
|
|
|
onChange={handleInputChange}
|
|
|
|
|
autosize
|
|
|
|
|
minRows={1}
|
|
|
|
|
maxRows={10}
|
|
|
|
|
radius="md"
|
|
|
|
|
size="xs"
|
|
|
|
|
sx={{
|
|
|
|
|
pointerEvents: "all",
|
|
|
|
|
marginTop: 5,
|
|
|
|
|
marginBottom: 5,
|
|
|
|
|
}}
|
|
|
|
|
placeholder="Ask DevChat a question or type ‘/’ for workflow"
|
|
|
|
|
styles={{
|
|
|
|
|
rightSection: {
|
|
|
|
|
alignItems: "flex-end",
|
|
|
|
|
marginBottom: "6px",
|
|
|
|
|
marginRight: contexts.length > 0 ? "24px" : "10px",
|
|
|
|
|
},
|
|
|
|
|
input: {
|
|
|
|
|
fontSize: "var(--vscode-editor-font-size)",
|
|
|
|
|
backgroundColor: "var(--vscode-input-background)",
|
|
|
|
|
borderColor: "var(--vscode-input-border)",
|
|
|
|
|
color: "var(--vscode-input-foreground)",
|
|
|
|
|
"&[data-disabled]": {
|
|
|
|
|
color: "var(--vscode-disabledForeground)",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
rightSection={
|
|
|
|
|
<>
|
|
|
|
|
{contexts.length > 0 && (
|
|
|
|
|
<Indicator label={contexts.length} size={12}>
|
|
|
|
|
<ActionIcon
|
|
|
|
|
size="md"
|
|
|
|
|
radius="md"
|
|
|
|
|
variant="default"
|
|
|
|
|
disabled={generating || chat.disabled}
|
|
|
|
|
onClick={openDrawer}
|
|
|
|
|
className={classes.actionIcon}
|
|
|
|
|
sx={{
|
|
|
|
|
pointerEvents: "all",
|
|
|
|
|
"&[data-disabled]": {
|
|
|
|
|
borderColor: "var(--vscode-input-border)",
|
|
|
|
|
backgroundColor:
|
|
|
|
|
"var(--vscode-toolbar-activeBackground)",
|
2023-12-12 15:13:17 +08:00
|
|
|
|
},
|
2023-12-12 18:03:17 +08:00
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<IconPaperclip size="1rem" />
|
|
|
|
|
</ActionIcon>
|
|
|
|
|
</Indicator>
|
|
|
|
|
)}
|
|
|
|
|
<ActionIcon
|
|
|
|
|
size="md"
|
|
|
|
|
radius="md"
|
|
|
|
|
variant="default"
|
|
|
|
|
disabled={generating || chat.disabled}
|
|
|
|
|
onClick={handleSendClick}
|
|
|
|
|
className={classes.actionIcon}
|
|
|
|
|
sx={{
|
|
|
|
|
marginLeft: "10px",
|
|
|
|
|
pointerEvents: "all",
|
|
|
|
|
backgroundColor: "#ED6A45",
|
|
|
|
|
border: "0",
|
|
|
|
|
color: "#FFFFFF",
|
|
|
|
|
"&:hover": {
|
|
|
|
|
backgroundColor: "#ED6A45",
|
|
|
|
|
color: "#FFFFFF",
|
|
|
|
|
opacity: 0.7,
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<IconSend size="1rem" />
|
|
|
|
|
</ActionIcon>
|
|
|
|
|
</>
|
2023-12-12 15:13:17 +08:00
|
|
|
|
}
|
2023-12-12 18:03:17 +08:00
|
|
|
|
/>
|
|
|
|
|
</Popover.Target>
|
|
|
|
|
<Popover.Dropdown
|
|
|
|
|
sx={{
|
|
|
|
|
padding: 0,
|
|
|
|
|
color: "var(--vscode-menu-foreground)",
|
|
|
|
|
borderColor: "var(--vscode-menu-border)",
|
|
|
|
|
backgroundColor: "var(--vscode-menu-background)",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Text sx={{ padding: "5px 5px 5px 10px" }}>DevChat Workflows</Text>
|
|
|
|
|
<ScrollArea.Autosize mah={240} type="always" placeholder="">
|
|
|
|
|
{commandMenusNode}
|
|
|
|
|
</ScrollArea.Autosize>
|
|
|
|
|
</Popover.Dropdown>
|
|
|
|
|
</Popover>
|
|
|
|
|
</Stack>
|
|
|
|
|
);
|
2023-12-12 15:13:17 +08:00
|
|
|
|
});
|
|
|
|
|
|
2023-12-12 18:03:17 +08:00
|
|
|
|
export default InputMessage;
|