Merge pull request #255 from devchat-ai/72-implement-ask-codebase-feature
72 implement ask codebase feature
This commit is contained in:
commit
02fba2e811
941
package-lock.json
generated
941
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -157,10 +157,10 @@
|
||||
"when": "DevChat.llmModel == 'OpenAI'"
|
||||
},
|
||||
"DevChat.PythonVirtualEnv": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Path to the Python virtual environment for AskCode."
|
||||
},
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Path to the Python virtual environment for AskCode."
|
||||
},
|
||||
"DevChat.askcode.supportedFileTypes": {
|
||||
"type": "string",
|
||||
"default": ".+\\.js$, .+\\.ts$, .+\\.jsx$, .+\\.tsx$, .+\\.java$, .+\\.py$, .+\\.go$, .+\\.rb$, .+\\.php$, .+\\.cpp$, .+\\.c$, .+\\.cs$, .+\\.swift$, .+\\.rs$, .+\\.sh$, .+\\.bash$, .+\\.zsh$, .+\\.m$, .+\\.mm$, .+\\.h$, .+\\.hpp$, .+\\.hh$, .+\\.html$, .+\\.htm$, .+\\.xhtml$, .+\\.xml$, .+\\.css$, .+\\.scss$, .+\\.sass$, .+\\.less$, .+\\.json$, .+\\.yaml$, .+\\.yml$, .+\\.toml$, .+\\.ini$, .+\\.md$, .+\\.markdown$, .+\\.txt$, .+\\.csv$, .+\\.sql$, .+\\.sqlite$, .+\\.db$, .+\\.hql$, .+\\.psql$, .+\\.pgsql$, .+\\.plpgsql$",
|
||||
@ -507,6 +507,7 @@
|
||||
"quote": "^0.4.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"shell-escape": "^0.2.0",
|
||||
"string-argv": "^0.3.2",
|
||||
"uuid": "^9.0.0"
|
||||
|
@ -40,6 +40,9 @@ function apiKeyMissedMessage(): LogEntry {
|
||||
request: 'Is OPENAI_API_KEY ready?',
|
||||
response: `
|
||||
OPENAI_API_KEY is missing from your environment or settings. Kindly input your OpenAI or DevChat key, and I'll ensure DevChat is all set for you.
|
||||
|
||||
<button value="setting_openai_key">Set OpenAI key</button>
|
||||
<button value="setting_devchat_key">Set DevChat key</button>
|
||||
`,
|
||||
context: []
|
||||
} as LogEntry;
|
||||
@ -74,7 +77,7 @@ export async function isWaitForApiKey() {
|
||||
return !isApiSet;
|
||||
}
|
||||
|
||||
export async function loadTopicHistoryLogs(topicId: string | undefined) : Promise<Array<LogEntry> | undefined> {
|
||||
export async function loadTopicHistoryLogs(topicId: string | undefined): Promise<Array<LogEntry> | undefined> {
|
||||
if (!topicId) {
|
||||
return [];
|
||||
}
|
||||
@ -135,13 +138,13 @@ export function loadTopicHistoryFromCurrentMessageHistory(skip: number, count: n
|
||||
} as LoadHistoryMessages;
|
||||
}
|
||||
|
||||
export async function apiKeyInvalidMessage(): Promise<LoadHistoryMessages|undefined> {
|
||||
export async function apiKeyInvalidMessage(): Promise<LoadHistoryMessages | undefined> {
|
||||
const apiKey = await ApiKeyManager.getApiKey();
|
||||
isApiSet = true;
|
||||
if (!apiKey) {
|
||||
const startMessage = [ apiKeyMissedMessage() ];
|
||||
const startMessage = [apiKeyMissedMessage()];
|
||||
isApiSet = false;
|
||||
|
||||
|
||||
return {
|
||||
command: 'loadHistoryMessages',
|
||||
entries: startMessage,
|
||||
@ -162,19 +165,19 @@ export async function historyMessagesBase(): Promise<LoadHistoryMessages | undef
|
||||
return undefined;
|
||||
}
|
||||
updateCurrentMessageHistory(topicId!, logEntriesFlat);
|
||||
|
||||
|
||||
const apiKeyMessage = await apiKeyInvalidMessage();
|
||||
if (apiKeyMessage !== undefined) {
|
||||
return apiKeyMessage;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
command: 'loadHistoryMessages',
|
||||
entries: logEntriesFlat.length>0? logEntriesFlat : [welcomeMessage()],
|
||||
entries: logEntriesFlat.length > 0 ? logEntriesFlat : [],
|
||||
} as LoadHistoryMessages;
|
||||
}
|
||||
|
||||
export async function onApiKeyBase(apiKey: string): Promise<{command: string, text: string, hash: string, user: string, date: string, isError: boolean}> {
|
||||
export async function onApiKeyBase(apiKey: string): Promise<{ command: string, text: string, hash: string, user: string, date: string, isError: boolean }> {
|
||||
if (!isValidApiKey(apiKey)) {
|
||||
return { command: 'receiveMessage', text: 'Your API key is invalid. We support OpenAI and DevChat keys. Please reset the key.', hash: '', user: 'system', date: '', isError: false };
|
||||
}
|
||||
@ -182,6 +185,8 @@ export async function onApiKeyBase(apiKey: string): Promise<{command: string, te
|
||||
isApiSet = true;
|
||||
ApiKeyManager.writeApiKeySecret(apiKey);
|
||||
|
||||
const welcomeMessageText = welcomeMessage().response;
|
||||
return { command: 'receiveMessage', text: `Your OPENAI_API_KEY is set. Enjoy DevChat!\n${welcomeMessageText}`, hash: '', user: 'system', date: '', isError: false };
|
||||
const welcomeMessageText = welcomeMessage().response;
|
||||
return {
|
||||
command: 'receiveMessage', text: `Your OPENAI_API_KEY is set. Enjoy DevChat!\n${welcomeMessageText}`, hash: '', user: 'system', date: '', isError: false
|
||||
};
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
import { Container } from "@mantine/core";
|
||||
import React from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { okaidia } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import CodeButtons from "./CodeButtons";
|
||||
|
||||
const CodeBlock = (props: any) => {
|
||||
const { messageText, messageType } = props;
|
||||
|
||||
const LanguageCorner = (props: any) => {
|
||||
const { language } = props;
|
||||
|
||||
return (<div style={{ position: 'absolute', top: 0, left: 0 }}>
|
||||
{language && (
|
||||
<div style={{
|
||||
backgroundColor: '#333',
|
||||
color: '#fff',
|
||||
padding: '0.2rem 0.5rem',
|
||||
borderRadius: '0.2rem',
|
||||
fontSize: '0.8rem',
|
||||
}}>
|
||||
{language}
|
||||
</div>
|
||||
)}
|
||||
</div>);
|
||||
};
|
||||
|
||||
return (
|
||||
messageType === 'bot'
|
||||
? <ReactMarkdown
|
||||
components={{
|
||||
code({ node, inline, className, children, ...props }) {
|
||||
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const value = String(children).replace(/\n$/, '');
|
||||
|
||||
return !inline && match ? (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<LanguageCorner language={match[1]} />
|
||||
<CodeButtons language={match[1]} code={value} />
|
||||
<SyntaxHighlighter {...props} language={match[1]} customStyle={{ padding: '3em 1em 1em 2em', }} style={okaidia} PreTag="div">
|
||||
{value}
|
||||
</SyntaxHighlighter>
|
||||
</div >
|
||||
) : (
|
||||
<code {...props} className={className}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{messageText}
|
||||
</ReactMarkdown >
|
||||
: <Container
|
||||
sx={{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
pre: {
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
},
|
||||
}}>
|
||||
<pre>{messageText}</pre>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeBlock;
|
@ -2,7 +2,7 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { keyframes } from "@emotion/react";
|
||||
import { Container, Text } from "@mantine/core";
|
||||
import CodeBlock from "@/views/components/CodeBlock";
|
||||
import MessageBody from "@/views/components/MessageBody";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useMst } from "@/views/stores/RootStore";
|
||||
import { Message } from "@/views/stores/ChatStore";
|
||||
@ -48,29 +48,36 @@ const getBlocks = (message) => {
|
||||
const CurrentMessage = observer((props: any) => {
|
||||
const { width } = props;
|
||||
const { chat } = useMst();
|
||||
const { messages, currentMessage, generating, responsed, hasDone } = chat;
|
||||
|
||||
// split blocks
|
||||
const messageBlocks = getBlocks(chat.currentMessage);
|
||||
const lastMessageBlocks = getBlocks(chat.messages[chat.messages.length - 1]?.message);
|
||||
const messageBlocks = getBlocks(currentMessage);
|
||||
const lastMessageBlocks = getBlocks(messages[messages.length - 1]?.message);
|
||||
const fixedCount = lastMessageBlocks.length;
|
||||
const receivedCount = messageBlocks.length;
|
||||
const renderBlocks = messageBlocks.splice(-1);
|
||||
|
||||
useEffect(() => {
|
||||
if (chat.generating) {
|
||||
if (generating) {
|
||||
// new a bot message
|
||||
const messageItem = Message.create({ type: 'bot', message: chat.currentMessage });
|
||||
const messageItem = Message.create({ type: 'bot', message: currentMessage });
|
||||
chat.newMessage(messageItem);
|
||||
}
|
||||
}, [chat.generating]);
|
||||
}, [generating]);
|
||||
|
||||
useEffect(() => {
|
||||
if (receivedCount - fixedCount >= 1 || !chat.responsed) {
|
||||
chat.updateLastMessage(chat.currentMessage);
|
||||
if (generating && (receivedCount - fixedCount >= 1 || !responsed)) {
|
||||
chat.updateLastMessage(currentMessage);
|
||||
}
|
||||
}, [chat.currentMessage, chat.responsed]);
|
||||
}, [currentMessage, responsed, generating]);
|
||||
|
||||
return chat.generating
|
||||
useEffect(() => {
|
||||
if (hasDone) {
|
||||
chat.updateLastMessage(currentMessage);
|
||||
}
|
||||
}, [hasDone]);
|
||||
|
||||
return generating
|
||||
? <Container
|
||||
sx={{
|
||||
margin: 0,
|
||||
@ -80,7 +87,7 @@ const CurrentMessage = observer((props: any) => {
|
||||
whiteSpace: 'break-spaces'
|
||||
},
|
||||
}}>
|
||||
<CodeBlock messageText={renderBlocks.join('\n\n')} messageType="bot" />
|
||||
<MessageBody messageText={renderBlocks.join('\n\n')} messageType="bot" />
|
||||
<MessageBlink />
|
||||
</Container>
|
||||
: <></>;
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
@ -10,16 +10,27 @@ import { observer } from "mobx-react-lite";
|
||||
import { useMst } from "@/views/stores/RootStore";
|
||||
|
||||
import { IMessage } from "@/views/stores/ChatStore";
|
||||
import { IChatContext } from "@/views/stores/InputStore";
|
||||
|
||||
interface IProps {
|
||||
item: IMessage,
|
||||
item?: IMessage,
|
||||
avatarType?: "user" | "bot" | "system",
|
||||
copyMessage?: string,
|
||||
messageContexts?: IChatContext[],
|
||||
deleteHash?: string,
|
||||
showEdit?: boolean,
|
||||
showDelete: boolean
|
||||
showDelete?: boolean
|
||||
}
|
||||
|
||||
const MessageHeader = observer((props: IProps) => {
|
||||
const { item, showEdit = false, showDelete = true } = props;
|
||||
const { contexts, message, type, hash } = item;
|
||||
const MessageAvatar = observer((props: IProps) => {
|
||||
const {
|
||||
messageContexts = [],
|
||||
copyMessage = "",
|
||||
deleteHash = undefined,
|
||||
avatarType = "user",
|
||||
showEdit = false,
|
||||
showDelete = false
|
||||
} = props;
|
||||
const { input, chat } = useMst();
|
||||
const [done, setDone] = React.useState(false);
|
||||
return (<Flex
|
||||
@ -30,7 +41,7 @@ const MessageHeader = observer((props: IProps) => {
|
||||
direction="row"
|
||||
wrap="wrap">
|
||||
{
|
||||
type === 'bot'
|
||||
avatarType === 'bot'
|
||||
? <Avatar
|
||||
color="indigo"
|
||||
size={25}
|
||||
@ -42,8 +53,8 @@ const MessageHeader = observer((props: IProps) => {
|
||||
radius="xl"
|
||||
src={SvgAvatarUser} />
|
||||
}
|
||||
<Text weight='bold'>{type === 'bot' ? 'DevChat' : 'User'}</Text>
|
||||
{type === 'user'
|
||||
<Text weight='bold'>{avatarType === 'bot' ? 'DevChat' : 'User'}</Text>
|
||||
{avatarType === 'user'
|
||||
? <Flex
|
||||
gap="xs"
|
||||
justify="flex-end"
|
||||
@ -54,8 +65,8 @@ const MessageHeader = observer((props: IProps) => {
|
||||
<Tooltip sx={{ padding: '3px', fontSize: 'var(--vscode-editor-font-size)' }} label={done ? 'Refilled' : 'Refill prompt'} withArrow position="left" color="gray">
|
||||
<ActionIcon size='sm'
|
||||
onClick={() => {
|
||||
input.setValue(message);
|
||||
input.setContexts(contexts);
|
||||
input.setValue(copyMessage);
|
||||
input.setContexts(messageContexts);
|
||||
setDone(true);
|
||||
setTimeout(() => { setDone(false); }, 2000);
|
||||
}}>
|
||||
@ -70,11 +81,11 @@ const MessageHeader = observer((props: IProps) => {
|
||||
<IconEdit size="1.125rem" />
|
||||
</ActionIcon>
|
||||
</Tooltip >}
|
||||
{showDelete && hash !== 'message' && <Tooltip sx={{ padding: '3px', fontSize: 'var(--vscode-editor-font-size)' }} label="Delete message" withArrow position="left" color="gray">
|
||||
{showDelete && deleteHash !== 'message' && <Tooltip sx={{ padding: '3px', fontSize: 'var(--vscode-editor-font-size)' }} label="Delete message" withArrow position="left" color="gray">
|
||||
<ActionIcon size='sm'
|
||||
onClick={() => {
|
||||
if (item.hash) {
|
||||
chat.deleteMessage(item).then();
|
||||
if (deleteHash) {
|
||||
chat.deleteMessage(deleteHash).then();
|
||||
} else {
|
||||
chat.popMessage();
|
||||
chat.popMessage();
|
||||
@ -84,7 +95,7 @@ const MessageHeader = observer((props: IProps) => {
|
||||
</ActionIcon>
|
||||
</Tooltip >}
|
||||
</Flex >
|
||||
: <CopyButton value={message} timeout={2000}>
|
||||
: <CopyButton value={copyMessage} timeout={2000}>
|
||||
{({ copied, copy }) => (
|
||||
<Tooltip sx={{ padding: '3px', fontSize: 'var(--vscode-editor-font-size)' }} label={copied ? 'Copied' : 'Copy message'} withArrow position="left" color="gray">
|
||||
<ActionIcon size='xs' color={copied ? 'teal' : 'gray'} onClick={copy} style={{ marginLeft: 'auto', marginRight: '10px' }}>
|
||||
@ -97,4 +108,4 @@ const MessageHeader = observer((props: IProps) => {
|
||||
</Flex >);
|
||||
});
|
||||
|
||||
export default MessageHeader;
|
||||
export default MessageAvatar;
|
30
src/views/components/MessageBody/index.tsx
Normal file
30
src/views/components/MessageBody/index.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { Container } from "@mantine/core";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import MessageMarkdown from "@/views/components/MessageMarkdown";
|
||||
|
||||
interface IProps {
|
||||
messageText: string,
|
||||
messageType: string
|
||||
}
|
||||
|
||||
const MessageBody = observer((props: IProps) => {
|
||||
const { messageText, messageType } = props;
|
||||
return (
|
||||
messageType === 'bot'
|
||||
? <MessageMarkdown messageText={messageText} />
|
||||
: <Container
|
||||
sx={{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
pre: {
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
},
|
||||
}}>
|
||||
<pre>{messageText}</pre>
|
||||
</Container>
|
||||
);
|
||||
});
|
||||
|
||||
export default MessageBody;
|
@ -1,12 +1,8 @@
|
||||
|
||||
import { Center, Text, Accordion, Box, Stack, Container, Divider } from "@mantine/core";
|
||||
import React from "react";
|
||||
import CodeBlock from "@/views/components/CodeBlock";
|
||||
import MessageHeader from "@/views/components/MessageHeader";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { types } from "mobx-state-tree";
|
||||
import { useMst } from "@/views/stores/RootStore";
|
||||
|
||||
import { IInputStore } from "@/views/stores/InputStore";
|
||||
import { Accordion, Box, Center, Text } from "@mantine/core";
|
||||
import React from "react";
|
||||
|
||||
interface IProps {
|
||||
contexts?: IInputStore['contexts'];
|
||||
@ -89,44 +85,4 @@ const MessageContext = ({ contexts }: IProps) => {
|
||||
</>);
|
||||
};
|
||||
|
||||
|
||||
const MessageContainer = observer((props: any) => {
|
||||
const { width } = props;
|
||||
const { chat } = useMst();
|
||||
|
||||
return (<>
|
||||
{chat.messages.map((item, index: number) => {
|
||||
const { message: messageText, type: messageType, contexts } = item;
|
||||
// setMessage(messageText);
|
||||
return <Stack
|
||||
spacing={0}
|
||||
key={`message-${index}`}
|
||||
sx={{
|
||||
width: width,
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
}}>
|
||||
<MessageHeader
|
||||
key={`message-header-${index}`}
|
||||
showDelete={index === chat.messages.length - 2}
|
||||
item={item} />
|
||||
<Container
|
||||
key={`message-container-${index}`}
|
||||
sx={{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
width: width,
|
||||
pre: {
|
||||
whiteSpace: 'break-spaces'
|
||||
},
|
||||
}}>
|
||||
<MessageContext key={`message-context-${index}`} contexts={contexts} />
|
||||
<CodeBlock key={`message-codeblock-${index}`} messageType={messageType} messageText={messageText} />
|
||||
</Container >
|
||||
{index !== chat.messages.length - 1 && <Divider my={3} key={`message-divider-${index}`} />}
|
||||
</Stack >;
|
||||
})}
|
||||
</>);
|
||||
});
|
||||
|
||||
export default MessageContainer;
|
||||
export default MessageContext;
|
54
src/views/components/MessageList/index.tsx
Normal file
54
src/views/components/MessageList/index.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
import { Stack, Container, Divider } from "@mantine/core";
|
||||
import React, { useEffect } from "react";
|
||||
import MessageBody from "@/views/components/MessageBody";
|
||||
import MessageAvatar from "@/views/components/MessageAvatar";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useMst } from "@/views/stores/RootStore";
|
||||
import { Message } from "@/views/stores/ChatStore";
|
||||
import MessageContext from "@/views/components/MessageContext";
|
||||
|
||||
|
||||
const MessageList = observer((props: any) => {
|
||||
const { width } = props;
|
||||
const { chat } = useMst();
|
||||
|
||||
return (<>
|
||||
{chat.messages.map((item, index: number) => {
|
||||
const { message: messageText, type: messageType, hash: messageHash, contexts } = item;
|
||||
// setMessage(messageText);
|
||||
return <Stack
|
||||
spacing={0}
|
||||
key={`message-${index}`}
|
||||
sx={{
|
||||
width: width,
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
}}>
|
||||
<MessageAvatar
|
||||
key={`message-header-${index}`}
|
||||
showDelete={index === chat.messages.length - 2}
|
||||
deleteHash={messageHash}
|
||||
avatarType={messageType}
|
||||
copyMessage={messageText}
|
||||
messageContexts={contexts} />
|
||||
<Container
|
||||
key={`message-container-${index}`}
|
||||
sx={{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
width: width,
|
||||
pre: {
|
||||
whiteSpace: 'break-spaces'
|
||||
},
|
||||
}}>
|
||||
<MessageContext key={`message-context-${index}`} contexts={contexts} />
|
||||
<MessageBody key={`message-codeblock-${index}`} messageType={messageType} messageText={messageText} />
|
||||
</Container >
|
||||
{index !== chat.messages.length - 1 && <Divider my={3} key={`message-divider-${index}`} />}
|
||||
</Stack >;
|
||||
})}
|
||||
</>);
|
||||
});
|
||||
|
||||
export default MessageList;
|
154
src/views/components/MessageMarkdown/index.tsx
Normal file
154
src/views/components/MessageMarkdown/index.tsx
Normal file
@ -0,0 +1,154 @@
|
||||
import { Button, Anchor } from "@mantine/core";
|
||||
import React from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import rehypeRaw from "rehype-raw";
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { okaidia } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import CodeButtons from "./CodeButtons";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useMst } from "@/views/stores/RootStore";
|
||||
import { Message } from "@/views/stores/ChatStore";
|
||||
import messageUtil from '@/util/MessageUtil';
|
||||
|
||||
interface IProps {
|
||||
messageText: string
|
||||
}
|
||||
|
||||
const MessageMarkdown = observer((props: IProps) => {
|
||||
const { messageText } = props;
|
||||
const { chat } = useMst();
|
||||
|
||||
const LanguageCorner = (props: any) => {
|
||||
const { language } = props;
|
||||
|
||||
return (<div style={{ position: 'absolute', top: 0, left: 0 }}>
|
||||
{language && (
|
||||
<div style={{
|
||||
backgroundColor: '#333',
|
||||
color: '#fff',
|
||||
padding: '0.2rem 0.5rem',
|
||||
borderRadius: '0.2rem',
|
||||
fontSize: '0.8rem',
|
||||
}}>
|
||||
{language}
|
||||
</div>
|
||||
)}
|
||||
</div>);
|
||||
};
|
||||
|
||||
const handleExplain = (value: string | undefined) => {
|
||||
console.log(value);
|
||||
switch (value) {
|
||||
case "#ask_code":
|
||||
chat.addMessages([
|
||||
Message.create({
|
||||
type: 'user',
|
||||
message: 'Explain /ask_code'
|
||||
}),
|
||||
Message.create({
|
||||
type: 'bot',
|
||||
message: `***/ask_code***
|
||||
|
||||
If you would like to ask questions related to your own codebase, you can enable and use the /ask_code feature of DevChat.
|
||||
|
||||
While /ask_code is being enabled, DevChat will need to index your codebase before you can use this feature. Indexing usually takes a while, depending on the size of your codebase, your computing power and the network. Once it’s done, you can ask questions about your codebase by typing the “/ask_code” command, followed by your question.
|
||||
|
||||
Example questions:
|
||||
(Here we only show example questions from a few popular open-source projects’ codebases.)
|
||||
|
||||
How do I access POST form fields in Express?
|
||||
How do I pass command line arguments to a Node.js program?
|
||||
How do I print the value of a tensor object in TensorFlow?
|
||||
How do I force Kubernetes to re-pull an image in Kubernetes?
|
||||
How do I set focus on an input field after rendering in React?
|
||||
|
||||
How to index your codebase?
|
||||
|
||||
\`Please check DevChat.ask_code settings\` before enabling the feature, because once indexing has been started, changing the settings will not affect the process anymore, unless if you terminate it and re-index.
|
||||
|
||||
To enable, you can enter \`DevChat:Start AskCode Index\` in the Command Palette or click on the button to start indexing now.
|
||||
|
||||
<button value="settings">Settings</button>
|
||||
<button value="start_indexing">Start Indexing</button>
|
||||
`
|
||||
}),
|
||||
]);
|
||||
}
|
||||
chat.goScrollBottom();
|
||||
};
|
||||
const handleButton = (value: string | number | readonly string[] | undefined) => {
|
||||
switch (value) {
|
||||
case "settings": messageUtil.sendMessage({ command: 'doCommand', content: ['workbench.action.openSettings', 'DevChat'] }); break;
|
||||
case "start_indexing": messageUtil.sendMessage({ command: 'doCommand', content: ['DevChat.AskCodeIndexStart'] }); break;
|
||||
case "setting_openai_key": messageUtil.sendMessage({ command: 'doCommand', content: ['workbench.action.openSettings', 'DevChat: Api_key_OpenAI'] }); break;
|
||||
case "setting_devchat_key": messageUtil.sendMessage({ command: 'doCommand', content: ['workbench.action.openSettings', 'DevChat: Access_key_DevChat'] }); break;
|
||||
}
|
||||
};
|
||||
|
||||
return <ReactMarkdown
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
components={{
|
||||
code({ node, inline, className, children, ...props }) {
|
||||
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const value = String(children).replace(/\n$/, '');
|
||||
|
||||
return !inline && match ? (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<LanguageCorner language={match[1]} />
|
||||
<CodeButtons language={match[1]} code={value} />
|
||||
<SyntaxHighlighter {...props} language={match[1]} customStyle={{ padding: '3em 1em 1em 2em', }} style={okaidia} PreTag="div">
|
||||
{value}
|
||||
</SyntaxHighlighter>
|
||||
</div >
|
||||
) : (
|
||||
<code {...props} className={className}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
button({ node, className, children, value, ...props }) {
|
||||
return (
|
||||
<Button
|
||||
size='xs'
|
||||
sx={{
|
||||
backgroundColor: 'var(--vscode-button-background)',
|
||||
}}
|
||||
styles={{
|
||||
icon: {
|
||||
color: 'var(--vscode-button-foreground)'
|
||||
},
|
||||
label: {
|
||||
color: 'var(--vscode-button-foreground)',
|
||||
fontSize: 'var(--vscode-editor-font-size)',
|
||||
}
|
||||
}}
|
||||
onClick={() => {
|
||||
handleButton(value);
|
||||
}}>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
a({ node, className, children, href, ...props }) {
|
||||
const customAnchors = ["#code",
|
||||
"#commit_message",
|
||||
"#release_note",
|
||||
"#ask_code",
|
||||
"#extension"].filter((item) => item === href);
|
||||
return customAnchors.length > 0
|
||||
? <Anchor href={href} onClick={() => {
|
||||
handleExplain(href);
|
||||
}}>
|
||||
{children}
|
||||
</Anchor>
|
||||
: <a {...props} href={href} className={className}>
|
||||
{children}
|
||||
</a>;
|
||||
}
|
||||
}}>
|
||||
{messageText}
|
||||
</ReactMarkdown >;
|
||||
});
|
||||
|
||||
export default MessageMarkdown;
|
@ -13,7 +13,7 @@ import { Message } from "@/views/stores/ChatStore";
|
||||
|
||||
|
||||
import InputMessage from '@/views/components/InputMessage';
|
||||
import MessageContainer from '../components/MessageContainer';
|
||||
import MessageList from '@/views/components/MessageList';
|
||||
import { IconCircleArrowDown, IconCircleArrowDownFilled } from '@tabler/icons-react';
|
||||
|
||||
|
||||
@ -83,6 +83,10 @@ const chatPanel = observer(() => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [chat.scrollBottom]);
|
||||
|
||||
return (
|
||||
<Container
|
||||
ref={chatContainerRef}
|
||||
@ -109,7 +113,7 @@ const chatPanel = observer(() => {
|
||||
}}
|
||||
onScrollPositionChange={onScrollPositionChange}
|
||||
viewportRef={scrollViewport}>
|
||||
<MessageContainer
|
||||
<MessageList
|
||||
width={chatContainerRect.width} />
|
||||
<CurrentMessage width={chatContainerRect.width} />
|
||||
{chat.errorMessage &&
|
||||
|
@ -39,11 +39,10 @@ export const fetchHistoryMessages = async (params) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteMessage = async (item: IMessage) => {
|
||||
const { hash } = item;
|
||||
export const deleteMessage = async (messageHash: string) => {
|
||||
return new Promise<{ hash: string }>((resolve, reject) => {
|
||||
try {
|
||||
messageUtil.sendMessage({ command: 'deleteChatMessage', hash: hash });
|
||||
messageUtil.sendMessage({ command: 'deleteChatMessage', hash: messageHash });
|
||||
messageUtil.registerHandler('deletedChatMessage', (message) => {
|
||||
resolve({
|
||||
hash: message.hash
|
||||
@ -74,6 +73,7 @@ export const ChatStore = types.model('Chat', {
|
||||
isLastPage: false,
|
||||
isBottom: true,
|
||||
isTop: false,
|
||||
scrollBottom: 0
|
||||
})
|
||||
.actions(self => ({
|
||||
startGenerating: (text: string, chatContexts) => {
|
||||
@ -149,6 +149,9 @@ export const ChatStore = types.model('Chat', {
|
||||
newMessage: (message: IMessage) => {
|
||||
self.messages.push(message);
|
||||
},
|
||||
addMessages: (messages: IMessage[]) => {
|
||||
self.messages.push(...messages);
|
||||
},
|
||||
updateLastMessage: (message: string) => {
|
||||
if (self.messages.length > 0) {
|
||||
self.messages[self.messages.length - 1].message = message;
|
||||
@ -178,6 +181,9 @@ export const ChatStore = types.model('Chat', {
|
||||
self.isTop = false;
|
||||
self.isBottom = false;
|
||||
},
|
||||
goScrollBottom: () => {
|
||||
self.scrollBottom++;
|
||||
},
|
||||
fetchHistoryMessages: flow(function* (params: { pageIndex: number }) {
|
||||
const { pageIndex, entries } = yield fetchHistoryMessages(params);
|
||||
if (entries.length > 0) {
|
||||
@ -201,10 +207,31 @@ export const ChatStore = types.model('Chat', {
|
||||
}
|
||||
} else {
|
||||
self.isLastPage = true;
|
||||
if (self.messages.length === 0) {
|
||||
self.messages.push(
|
||||
Message.create({
|
||||
type: 'user',
|
||||
message: "How do I use DevChat?"
|
||||
}));
|
||||
self.messages.push(
|
||||
Message.create({
|
||||
type: 'bot',
|
||||
message: `
|
||||
Do you want to write some code or have a question about the project? Simply right-click on your chosen files or code snippets and add them to DevChat. Feel free to ask me anything or let me help you with coding.
|
||||
|
||||
Don't forget to check out the "+" button on the left of the input to add more context. To see a list of workflows you can run in the context, just type "/". Happy prompting!
|
||||
|
||||
To get started, here are the things that DevChat can do:
|
||||
|
||||
[/ask_code: ask questions about your own codebase](#ask_code)
|
||||
|
||||
<button value="settings">Settings</button>
|
||||
`}));
|
||||
}
|
||||
}
|
||||
}),
|
||||
deleteMessage: flow(function* (item: IMessage) {
|
||||
const { hash } = yield deleteMessage(item);
|
||||
deleteMessage: flow(function* (messageHash: string) {
|
||||
const { hash } = yield deleteMessage(messageHash);
|
||||
const index = self.messages.findIndex((item: any) => item.hash === hash);
|
||||
if (index > -1) {
|
||||
self.messages.splice(index);
|
||||
|
@ -69,7 +69,7 @@ export const InputStore = types
|
||||
clearContexts() {
|
||||
self.contexts.clear();
|
||||
},
|
||||
setContexts(contexts: IMessage['contexts']) {
|
||||
setContexts(contexts: IChatContext[]) {
|
||||
self.contexts.clear();
|
||||
contexts?.forEach(context => {
|
||||
self.contexts.push({ ...context });
|
||||
|
Loading…
x
Reference in New Issue
Block a user