Compare commits
No commits in common. "main" and "fix-webapp-address-syncing" have entirely different histories.
main
...
fix-webapp
3
.env
3
.env
@ -1,4 +1 @@
|
||||
REACT_APP_IMAGE_BASE_URL=https://img2.baidu.com
|
||||
REACT_APP_ASSISTANT_DISPLAY_NAME_EN=DevChat
|
||||
REACT_APP_ASSISTANT_DISPLAY_NAME_ZH=DevChat
|
||||
REACT_APP_LOGO_FILE=
|
||||
|
@ -1,4 +0,0 @@
|
||||
REACT_APP_IMAGE_BASE_URL=https://img2.baidu.com
|
||||
REACT_APP_ASSISTANT_DISPLAY_NAME_EN=
|
||||
REACT_APP_ASSISTANT_DISPLAY_NAME_ZH=
|
||||
REACT_APP_LOGO_FILE=
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,38 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,4 +22,3 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
avatar_devchat.svg
|
||||
|
34
README.md
34
README.md
@ -1,34 +0,0 @@
|
||||
# devchat-gui
|
||||
|
||||
The unified webview UI of DevChat plugins.
|
||||
|
||||
## Parameterize packaging
|
||||
|
||||
### Step 1: Config the assistant names and logo file [Optional]
|
||||
|
||||
Custom assistant name and logo are supported, they can be configured in the `.env` file
|
||||
|
||||
~~~env
|
||||
# Default to DevChat
|
||||
REACT_APP_ASSISTANT_DISPLAY_NAME_EN=English name of the AI assistant
|
||||
# Default to DevChat
|
||||
REACT_APP_ASSISTANT_DISPLAY_NAME_ZH=Chinese name of the AI assistant
|
||||
# Default to DevChat logo
|
||||
REACT_APP_LOGO_FILE=/path/to/the/logo.svg
|
||||
~~~
|
||||
|
||||
Notes:
|
||||
|
||||
1. The logo should be a `.svg` file;
|
||||
2. Recommend size of the logo is `64x64`;
|
||||
|
||||
|
||||
### Step 2: Build the UI for `VSCode` and `IntelliJ`
|
||||
|
||||
The `devchat-gui` repo is assumed to be a submodule of `devchat-vscode` and `devchat-intellij`. You need to build `devchat-gui` first before you build your plugins.
|
||||
|
||||
~~~bash
|
||||
cd gui
|
||||
yarn idea # for intellij
|
||||
yarn vscode # for vscode
|
||||
~~~
|
@ -53,11 +53,11 @@
|
||||
"lint": "eslint src --ext ts",
|
||||
"test": "mocha",
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"vscode": "node prebuild.js && webpack --config webpack.config.js && shx mv dist/* ../dist && git checkout -- src/views/components/MessageAvatar/avatar_devchat.svg",
|
||||
"vscode": "webpack --config webpack.config.js && mv dist/* ../dist",
|
||||
"vscode:watch": "webpack --config webpack.config.js --watch",
|
||||
"dev": "webpack serve --config webpack.config.js --open",
|
||||
"build:idea": "webpack --config webpack.idea.config.js",
|
||||
"idea": "node prebuild.js && webpack --config webpack.idea.config.js && shx mv dist/main.js dist/main.html ../src/main/resources/static && git checkout -- src/views/components/MessageAvatar/avatar_devchat.svg && echo '🎆done'"
|
||||
"idea": "webpack --config webpack.idea.config.js && mv dist/main.js dist/main.html ../src/main/resources/static && echo '🎆done'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.21.8",
|
||||
@ -96,7 +96,6 @@
|
||||
"proxyquire": "^2.1.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"shx": "^0.3.4",
|
||||
"sinon": "^15.1.0",
|
||||
"style-loader": "^3.3.2",
|
||||
"ts-jest": "^29.1.0",
|
||||
|
23
prebuild.js
23
prebuild.js
@ -1,23 +0,0 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const logoPath = process.env.REACT_APP_LOGO_FILE;
|
||||
if (!logoPath) {
|
||||
console.warn('REACT_APP_LOGO_FILE is not defined in your environment variables');
|
||||
process.exit(0);
|
||||
}
|
||||
if (!fs.existsSync(logoPath)) {
|
||||
console.warn('Logo file does not exist.');
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const destPath = path.join(__dirname, 'src/views/components/MessageAvatar/avatar_devchat.svg');
|
||||
|
||||
try {
|
||||
fs.copyFileSync(logoPath, destPath)
|
||||
fs.chmodSync(destPath, 0o644)
|
||||
} catch(e) {
|
||||
console.warn(`Failed to copy logo ${e}`)
|
||||
}
|
@ -1,15 +1,5 @@
|
||||
import axios from "axios";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import IDEServiceUtil from "./IDEServiceUtil";
|
||||
|
||||
interface EventData {
|
||||
ide: string | undefined;
|
||||
[key: string]: any; // For other potential properties
|
||||
}
|
||||
interface MessageData {
|
||||
ide: string | undefined;
|
||||
[key: string]: any; // For other potential properties
|
||||
}
|
||||
|
||||
class APIUtil {
|
||||
private static instance: APIUtil;
|
||||
@ -17,7 +7,6 @@ class APIUtil {
|
||||
private accessKey: string | undefined;
|
||||
private webappUrl: string | undefined;
|
||||
private currentMessageId: string | undefined;
|
||||
private extensionVersion: string | undefined;
|
||||
|
||||
|
||||
constructor() {
|
||||
@ -30,22 +19,6 @@ class APIUtil {
|
||||
}
|
||||
return APIUtil.instance;
|
||||
}
|
||||
|
||||
async getExtensionVersion(): Promise<string | undefined> {
|
||||
if (this.extensionVersion) {
|
||||
return this.extensionVersion;
|
||||
}
|
||||
|
||||
try {
|
||||
const version = await IDEServiceUtil.callService("get_extension_version", {});
|
||||
this.extensionVersion = version || "unknown";
|
||||
return this.extensionVersion;
|
||||
} catch (err) {
|
||||
console.error("Failed to get extension version:", err);
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
async fetchWebappUrl() {
|
||||
try {
|
||||
const res = await axios.get(
|
||||
@ -85,74 +58,38 @@ class APIUtil {
|
||||
})
|
||||
}
|
||||
|
||||
updateCurrentMessageId() {
|
||||
async createMessage(message: object) {
|
||||
this.currentMessageId = `msg-${uuidv4()}`;
|
||||
return this.currentMessageId;
|
||||
}
|
||||
|
||||
getCurrentMessageId() {
|
||||
return this.currentMessageId;
|
||||
}
|
||||
|
||||
async createMessage(message: MessageData, messageId?: string) {
|
||||
// 如果 messageId 为空,则使用 uuid 生成新的 ID
|
||||
var newMessageId = messageId || `msg-${uuidv4()}`;
|
||||
newMessageId = newMessageId || this.currentMessageId || '';
|
||||
|
||||
try {
|
||||
if (!this.webappUrl) this.webappUrl = await this.fetchWebappUrl();
|
||||
|
||||
// 获取版本号并更新ide字段
|
||||
const version = await this.getExtensionVersion() || "unknown";
|
||||
message.ide = `${message.ide}[${version}]`;
|
||||
|
||||
const res = await axios.post(
|
||||
`${this.webappUrl}/api/v1/messages`,
|
||||
{...message, message_id: newMessageId},
|
||||
{...message, message_id: this.currentMessageId},
|
||||
{ headers: {
|
||||
Authorization: `Bearer ${this.accessKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}}
|
||||
);
|
||||
)
|
||||
console.log("Message created: ", res?.data);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
async createEvent(event: EventData, messageId?: string) {
|
||||
// 如果 messageId 为空,则使用当前的 messageId
|
||||
const idToUse = messageId || this.currentMessageId;
|
||||
|
||||
// 获取版本号
|
||||
const version = await this.getExtensionVersion() || "unknow";
|
||||
// 更新event.ide, 添加version信息。
|
||||
// 原来值为vscode,修改后值为vscode[0.1.96]
|
||||
event.ide = `${event.ide}[${version}]`;
|
||||
const attemptCreate = async () => {
|
||||
async createEvent(event: object) {
|
||||
try {
|
||||
if (!this.webappUrl) this.webappUrl = await this.fetchWebappUrl();
|
||||
const res = await axios.post(
|
||||
`${this.webappUrl}/api/v1/messages/${idToUse}/events`,
|
||||
`${this.webappUrl}/api/v1/messages/${this.currentMessageId}/events`,
|
||||
event,
|
||||
{headers: {
|
||||
Authorization: `Bearer ${this.accessKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}}
|
||||
);
|
||||
)
|
||||
console.log("Event created: ", res?.data);
|
||||
return true;
|
||||
} catch(err) {
|
||||
if (axios.isAxiosError(err) && err.response?.status === 404) {
|
||||
return false;
|
||||
}
|
||||
console.error(err);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if (!(await attemptCreate())) {
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await attemptCreate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,73 +0,0 @@
|
||||
import axios from "axios";
|
||||
|
||||
interface ServiceResponse {
|
||||
result?: any;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
class IDEServiceUtil {
|
||||
private static instance: IDEServiceUtil;
|
||||
private host = "http://localhost";
|
||||
private port: number | undefined;
|
||||
|
||||
|
||||
constructor() {
|
||||
console.log("IDEServiceUtil ready");
|
||||
}
|
||||
|
||||
public static getInstance(): IDEServiceUtil {
|
||||
if (!IDEServiceUtil.instance) {
|
||||
IDEServiceUtil.instance = new IDEServiceUtil();
|
||||
}
|
||||
return IDEServiceUtil.instance;
|
||||
}
|
||||
config(port: number) {
|
||||
console.log("Recieved IDEService port: ", port);
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
|
||||
async getCurrentFileInfo() {
|
||||
try {
|
||||
if (!this.port) return undefined;
|
||||
const res = await axios.get(`${this.host}:${this.port}/current_file_info`)
|
||||
const info = res?.data?.result;
|
||||
console.log("currentFileInfo: ", info);
|
||||
return info;
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async callService(serviceName: string, data: Record<string, any>): Promise<any> {
|
||||
console.log("callService: ", serviceName, data);
|
||||
try {
|
||||
const url = `${this.host}:${this.port}/${serviceName}`;
|
||||
console.log("callService url: ", url);
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const headers = { 'Content-Type': 'application/json' };
|
||||
|
||||
const response = await axios.post<ServiceResponse>(url, data, { headers });
|
||||
console.log("callService response: ", response);
|
||||
|
||||
if (response.status !== 200) {
|
||||
console.log(`Server error: ${response.status}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const responseData = response.data;
|
||||
if (responseData.error) {
|
||||
console.log(`Server returned error: ${responseData.error}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return responseData.result;
|
||||
} catch (error) {
|
||||
console.error('Error calling service:', error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default IDEServiceUtil.getInstance();
|
@ -1,36 +1,17 @@
|
||||
import IdeaBridge from "./ideaBridge";
|
||||
|
||||
class MessageUtil {
|
||||
private static instance: MessageUtil;
|
||||
|
||||
handlers: { [x: string]: any };
|
||||
ideApi: any;
|
||||
vscodeApi: any;
|
||||
messageListener: any;
|
||||
hasInit: boolean;
|
||||
|
||||
constructor() {
|
||||
this.hasInit = false;
|
||||
this.handlers = {};
|
||||
this.messageListener = null;
|
||||
}
|
||||
|
||||
public static getInstance(): MessageUtil {
|
||||
if (!MessageUtil.instance) {
|
||||
MessageUtil.instance = new MessageUtil();
|
||||
}
|
||||
return MessageUtil.instance;
|
||||
}
|
||||
|
||||
public init() {
|
||||
if (this.hasInit) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hasInit = true;
|
||||
this.handlers = {};
|
||||
this.messageListener = null;
|
||||
if (process.env.platform === "vscode") {
|
||||
this.ideApi = window.acquireVsCodeApi();
|
||||
} else if (process.env.platform === "idea") {
|
||||
this.ideApi = window.acquireIdeaCodeApi();
|
||||
this.vscodeApi = window.acquireVsCodeApi();
|
||||
}
|
||||
|
||||
if (!this.messageListener) {
|
||||
@ -44,18 +25,27 @@ class MessageUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static getInstance(): MessageUtil {
|
||||
if (!MessageUtil.instance) {
|
||||
MessageUtil.instance = new MessageUtil();
|
||||
}
|
||||
return MessageUtil.instance;
|
||||
}
|
||||
|
||||
// Register a message handler for a specific message type
|
||||
registerHandler(messageType: string, handler: any) {
|
||||
this.init();
|
||||
if (process.env.platform === "idea") {
|
||||
IdeaBridge.registerHandler(messageType, handler);
|
||||
} else {
|
||||
if (!this.handlers[messageType]) {
|
||||
this.handlers[messageType] = [];
|
||||
}
|
||||
this.handlers[messageType].push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
// Unregister a message handler for a specific message type
|
||||
unregisterHandler(messageType: string | number, handler: any) {
|
||||
this.init();
|
||||
if (this.handlers[messageType]) {
|
||||
this.handlers[messageType] = this.handlers[messageType].filter(
|
||||
(h: any) => h !== handler
|
||||
@ -64,15 +54,10 @@ class MessageUtil {
|
||||
}
|
||||
|
||||
// Handle a received message
|
||||
handleMessage(message: { command: string | number } & Record<string, any>) {
|
||||
this.init();
|
||||
if (!('command' in message)) {
|
||||
throw new Error('Missing required field: command');
|
||||
}
|
||||
|
||||
handleMessage(message: { command: string | number }) {
|
||||
const handlers = this.handlers[message.command];
|
||||
if (handlers) {
|
||||
handlers.forEach((handler: (arg0: { command: string | number } & Record<string, any>) => any) =>
|
||||
handlers.forEach((handler: (arg0: { command: string | number }) => any) =>
|
||||
handler(message)
|
||||
);
|
||||
}
|
||||
@ -80,8 +65,11 @@ class MessageUtil {
|
||||
|
||||
// Send a message to the VSCode API
|
||||
sendMessage(message: any) {
|
||||
this.init();
|
||||
this.ideApi.postMessage(message);
|
||||
if (process.env.platform === "idea") {
|
||||
IdeaBridge.sendMessage(message);
|
||||
} else {
|
||||
this.vscodeApi.postMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
## sendMessage
|
||||
|
||||
- getUserAccessKey // 获取 access key
|
||||
- doCommit // 提交代码
|
||||
- updateSetting // 更新设置(目前只有更换模型)
|
||||
- getSetting // 获取默认模型
|
||||
- deleteChatMessage // 删除最近一条消息
|
||||
@ -11,24 +13,31 @@
|
||||
// 1. 打开设置
|
||||
// 2. 启动 ask code 安装
|
||||
// 3. 设置 access key
|
||||
- featureToggles // 判断有没有 ask code
|
||||
- isDevChatInstalled // 判断 ask code 是否安装
|
||||
|
||||
- historyMessages // 页面的历史消息
|
||||
- contextDetail // 获取 appendContext 响应之后,发送次请求获取文件的内容
|
||||
- addContext // 点击 context 菜单(比如 git diff)之后发送到消息
|
||||
- code_file_apply // 代码应用到 editor,替换 current file
|
||||
- code_apply // 代码应用到 editor 光标位置
|
||||
- sendMessage // 发送消息
|
||||
- regeneration // 错误时重新生成
|
||||
- regContextList // git diff 之类的列表
|
||||
- regModelList // model 列表
|
||||
- regCommandList // 输入 / 之后出现的列表
|
||||
|
||||
## registerHandler
|
||||
|
||||
- getUserAccessKey // 获取 access key
|
||||
- regCommandList // 获取 / 之后出现的列表
|
||||
- appendContext // 右键添加到 context 或者 context 菜单点击的响应
|
||||
- contextDetailResponse // 获取到的文件内容
|
||||
- loadHistoryMessages // 与 historyMessages 对应
|
||||
- isDevChatInstalled // 与 isDevChatInstalled 对应
|
||||
- deletedChatMessage // 与 deleteChatMessage 对应
|
||||
- regContextList // 与 regContextList 对应
|
||||
- regModelList // 与 regModelList
|
||||
- receiveMessagePartial // 部分对话
|
||||
- receiveMessage // 对话
|
||||
- systemMessage // 没用了
|
||||
|
@ -1,2 +0,0 @@
|
||||
export const ASSISTANT_DISPLAY_NAME: string = process.env.REACT_APP_ASSISTANT_DISPLAY_NAME_EN || "DevChat"
|
||||
export const ASSISTANT_DISPLAY_NAME_ZH: string = process.env.REACT_APP_ASSISTANT_DISPLAY_NAME_ZH || "DevChat"
|
614
src/util/ideaBridge.ts
Normal file
614
src/util/ideaBridge.ts
Normal file
@ -0,0 +1,614 @@
|
||||
const JStoIdea = {
|
||||
sendMessage: (
|
||||
message: string,
|
||||
context: any = [],
|
||||
parent: string = "",
|
||||
model: string = ""
|
||||
) => {
|
||||
const paramsContext: any = [];
|
||||
if (Array.isArray(context) && context.length > 0) {
|
||||
context.forEach((item) => {
|
||||
paramsContext.push({
|
||||
type: "code",
|
||||
...item.context,
|
||||
});
|
||||
});
|
||||
}
|
||||
const params = {
|
||||
action: "sendMessage/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
parent: parent,
|
||||
},
|
||||
payload: {
|
||||
contexts: paramsContext,
|
||||
message: message,
|
||||
model,
|
||||
},
|
||||
};
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
getModel: () => {
|
||||
const params = {
|
||||
action: "listModels/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
getContextList: () => {
|
||||
const params = {
|
||||
action: "listContexts/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
getCommandList: () => {
|
||||
const params = {
|
||||
action: "listCommands/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
insertCode: (code) => {
|
||||
const params = {
|
||||
action: "insertCode/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {
|
||||
content: code,
|
||||
},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
replaceFileContent: (code) => {
|
||||
const params = {
|
||||
action: "replaceFileContent/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {
|
||||
content: code,
|
||||
},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
newSrcFile: (language, content) => {
|
||||
const params = {
|
||||
action: "newSrcFile/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {
|
||||
language: language,
|
||||
content: content,
|
||||
},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
viewDiff: (code) => {
|
||||
const params = {
|
||||
action: "viewDiff/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {
|
||||
content: code,
|
||||
},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
|
||||
etcCommand: (command: any) => {
|
||||
/**
|
||||
* 有四种命令
|
||||
* 1. workbench.action.openSettings
|
||||
* 2. AskCodeIndexStart
|
||||
* 3. AccessKey.OpenAI
|
||||
* 4. DevChat.AccessKey.DevChat
|
||||
*/
|
||||
const content = Array.isArray(command.content) ? command.content[0] : "";
|
||||
switch (content) {
|
||||
case "workbench.action.openSettings":
|
||||
// 打开设置
|
||||
const params = {
|
||||
action: "showSettingDialog/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
break;
|
||||
case "DevChat.AccessKey.DevChat":
|
||||
// 设置key
|
||||
const setkeyparams = {
|
||||
action: "showSettingDialog/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(setkeyparams));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
getTopicList: () => {
|
||||
// 获取 topic 列表
|
||||
const params = {
|
||||
action: "listTopics/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
commit: (code: string) => {
|
||||
const params = {
|
||||
action: "commitCode/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {
|
||||
message: code,
|
||||
},
|
||||
};
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
getTopicDetail: (topicHash: string) => {
|
||||
const params = {
|
||||
action: "loadConversations/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
topicHash: topicHash,
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
setNewTopic: () => {
|
||||
const params = {
|
||||
action: "loadConversations/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
topicHash: "",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
deleteTopic: (topicHash: string) => {
|
||||
const params = {
|
||||
action: "deleteTopic/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
topicHash: topicHash,
|
||||
},
|
||||
payload: {
|
||||
topicHash: topicHash,
|
||||
},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
historyMessages: (message) => {
|
||||
const params = {
|
||||
action: "loadHistoryMessages/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {
|
||||
pageIndex: message?.page || 0,
|
||||
},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
deleteChatMessage: (message) => {
|
||||
const params = {
|
||||
action: "deleteLastConversation/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {
|
||||
promptHash: message?.hash || "",
|
||||
},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
openLink: (message) => {
|
||||
if (!message?.url) {
|
||||
return false;
|
||||
}
|
||||
const params = {
|
||||
action: "openLink/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {
|
||||
url: message?.url || "",
|
||||
},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
userInput: (message) => {
|
||||
const params = {
|
||||
action: "input/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {
|
||||
data: message?.text || "",
|
||||
},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
stopDevChat: () => {
|
||||
const params = {
|
||||
action: "stopGeneration/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
regeneration: () => {
|
||||
const params = {
|
||||
action: "regeneration/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
readConfig: () => {
|
||||
// 获取完整的用户设置
|
||||
const params = {
|
||||
action: "getSetting/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: {},
|
||||
};
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
saveConfig: (data) => {
|
||||
const params = {
|
||||
action: "updateSetting/request",
|
||||
metadata: {
|
||||
callback: "IdeaToJSMessage",
|
||||
},
|
||||
payload: data,
|
||||
};
|
||||
|
||||
console.log("ready to call java: ", JSON.stringify(params));
|
||||
window.JSJavaBridge.callJava(JSON.stringify(params));
|
||||
},
|
||||
};
|
||||
|
||||
class IdeaBridge {
|
||||
private static instance: IdeaBridge;
|
||||
handle: any = {};
|
||||
|
||||
constructor() {
|
||||
this.handle = {};
|
||||
// 注册全局的回调函数,用于接收来自 IDEA 的消息
|
||||
window.IdeaToJSMessage = (res: any) => {
|
||||
console.info("IdeaToJSMessage get res: ", res);
|
||||
switch (res.action) {
|
||||
case "updateSetting/response":
|
||||
this.resviceUpdateSetting(res);
|
||||
break;
|
||||
case "codeDiffApply/response":
|
||||
this.resviceCodeDiffApply(res);
|
||||
break;
|
||||
case "sendUserMessage/response":
|
||||
this.resviceSendUserMessage(res);
|
||||
break;
|
||||
case "deleteLastConversation/response":
|
||||
this.resviceDeleteMessage(res);
|
||||
break;
|
||||
case "loadHistoryMessages/response":
|
||||
this.resviceHistoryMessages(res);
|
||||
break;
|
||||
case "sendMessage/response":
|
||||
this.resviceMessage(res);
|
||||
break;
|
||||
case "listModels/response":
|
||||
this.resviceModelList(res);
|
||||
break;
|
||||
case "listContexts/response":
|
||||
this.resviceContextList(res);
|
||||
break;
|
||||
case "listCommands/response":
|
||||
this.resviceCommandList(res);
|
||||
break;
|
||||
case "addContext/notify":
|
||||
this.resviesContext(res);
|
||||
break;
|
||||
case "getSetting/response":
|
||||
this.resviceSettings(res);
|
||||
break;
|
||||
case "listTopics/response":
|
||||
this.resviceTopicList(res);
|
||||
break;
|
||||
case "loadConversations/response":
|
||||
this.resviceTopicDetail(res);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
window.onInitializationFinish = () => {
|
||||
// 初始化完成
|
||||
JStoIdea.getCommandList();
|
||||
};
|
||||
}
|
||||
|
||||
resviceUpdateSetting(res) {
|
||||
// 更新用户设置之后的回调
|
||||
this.executeHandlers("updateSetting", res.payload);
|
||||
}
|
||||
|
||||
resviceCodeDiffApply(res) {
|
||||
this.executeHandlers("codeDiffApply", res.payload);
|
||||
}
|
||||
|
||||
resviceSendUserMessage(res) {
|
||||
this.executeHandlers("chatWithDevChat", {
|
||||
command: "chatWithDevChat",
|
||||
message: res.payload.message || "",
|
||||
});
|
||||
}
|
||||
|
||||
resviceDeleteMessage(res) {
|
||||
const hash = res?.payload?.promptHash || "";
|
||||
// this.handle.deletedChatMessage({
|
||||
// hash,
|
||||
// });
|
||||
this.executeHandlers("deletedChatMessage", {
|
||||
hash,
|
||||
});
|
||||
}
|
||||
|
||||
resviceHistoryMessages(res) {
|
||||
const list: any = [];
|
||||
if (res?.payload?.messages?.length > 0) {
|
||||
res?.payload?.messages.forEach((item) => {
|
||||
list.push({
|
||||
...item,
|
||||
response: item.responses?.join("\n"),
|
||||
});
|
||||
});
|
||||
}
|
||||
// this.handle.reloadMessage({
|
||||
// entries: list.reverse(),
|
||||
// pageIndex: 0,
|
||||
// });
|
||||
this.executeHandlers("reloadMessage", {
|
||||
entries: list.reverse(),
|
||||
pageIndex: 0,
|
||||
reset: list.length === 0,
|
||||
});
|
||||
}
|
||||
|
||||
resviceTopicDetail(res) {
|
||||
// 用于重置后端全局的 topic id
|
||||
if (res?.payload?.reset) {
|
||||
// 重置后请求历史消息
|
||||
JStoIdea.historyMessages({ page: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
resviceTopicList(res) {
|
||||
const list = res.payload.topics;
|
||||
// this.handle.listTopics(list);
|
||||
this.executeHandlers("listTopics", {
|
||||
list,
|
||||
});
|
||||
}
|
||||
|
||||
resviesContext(res) {
|
||||
const params = {
|
||||
file: res.payload.path,
|
||||
result: "",
|
||||
};
|
||||
const contextObj = {
|
||||
path: res.payload.path,
|
||||
content: res.payload.content,
|
||||
command: "",
|
||||
};
|
||||
params.result = JSON.stringify(contextObj);
|
||||
// this.handle.contextDetailResponse(params);
|
||||
this.executeHandlers("contextDetailResponse", params);
|
||||
}
|
||||
|
||||
resviceSettings(res) {
|
||||
// 获取用户设置的回调
|
||||
const setting = res?.payload || {};
|
||||
|
||||
this.executeHandlers("readConfig", {
|
||||
value: setting,
|
||||
});
|
||||
}
|
||||
|
||||
resviceCommandList(res) {
|
||||
this.executeHandlers("regCommandList", {
|
||||
result: res.payload.commands,
|
||||
});
|
||||
}
|
||||
|
||||
resviceContextList(res) {
|
||||
// 接受到的上下文列表
|
||||
const result = res.payload.contexts.map((item) => ({
|
||||
name: item.command,
|
||||
pattern: item.command,
|
||||
description: item.description,
|
||||
}));
|
||||
|
||||
// this.handle.regContextList({ result });
|
||||
this.executeHandlers("regContextList", {
|
||||
result,
|
||||
});
|
||||
}
|
||||
|
||||
resviceModelList(response: any) {
|
||||
// 接受到模型列表
|
||||
this.executeHandlers("regModelList", {
|
||||
result: response.payload.models,
|
||||
});
|
||||
}
|
||||
|
||||
resviceMessage(response: any) {
|
||||
// 接受到消息
|
||||
if (response.metadata?.isFinalChunk) {
|
||||
// 结束对话
|
||||
this.executeHandlers("receiveMessage", {
|
||||
text: response.payload?.message || response.metadata?.error || "",
|
||||
isError: response.metadata?.error.length > 0,
|
||||
hash: response.payload?.promptHash || "",
|
||||
});
|
||||
} else {
|
||||
this.executeHandlers("receiveMessagePartial", {
|
||||
text: response?.payload?.message || "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static getInstance(): IdeaBridge {
|
||||
if (!IdeaBridge.instance) {
|
||||
IdeaBridge.instance = new IdeaBridge();
|
||||
}
|
||||
return IdeaBridge.instance;
|
||||
}
|
||||
|
||||
registerHandler(messageType: string, handler: any) {
|
||||
if (!this.handle[messageType]) {
|
||||
this.handle[messageType] = [];
|
||||
}
|
||||
this.handle[messageType].push(handler);
|
||||
}
|
||||
|
||||
executeHandlers(messageType: string, data: any) {
|
||||
if (this.handle[messageType]) {
|
||||
this.handle[messageType].forEach((handler) => {
|
||||
handler(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(message: any) {
|
||||
// 根据 command 分发到不同的方法·
|
||||
switch (message.command) {
|
||||
// 发送消息
|
||||
case "sendMessage":
|
||||
JStoIdea.sendMessage(
|
||||
message.text,
|
||||
message.contextInfo,
|
||||
message.parent_hash,
|
||||
message.model
|
||||
);
|
||||
break;
|
||||
// 重新生成消息,用于发送失败时再次发送
|
||||
case "regeneration":
|
||||
JStoIdea.regeneration();
|
||||
break;
|
||||
// 请求 model 列表
|
||||
case "regModelList":
|
||||
JStoIdea.getModel();
|
||||
break;
|
||||
case "regContextList":
|
||||
JStoIdea.getContextList();
|
||||
break;
|
||||
case "regCommandList":
|
||||
JStoIdea.getCommandList();
|
||||
break;
|
||||
case "code_apply":
|
||||
JStoIdea.insertCode(message.content);
|
||||
break;
|
||||
case "code_file_apply":
|
||||
JStoIdea.replaceFileContent(message.content);
|
||||
break;
|
||||
case "code_new_file":
|
||||
JStoIdea.newSrcFile(message.language, message.content);
|
||||
break;
|
||||
case "doCommand":
|
||||
JStoIdea.etcCommand(message);
|
||||
break;
|
||||
case "show_diff":
|
||||
JStoIdea.viewDiff(message.content);
|
||||
break;
|
||||
case "doCommit":
|
||||
JStoIdea.commit(message.content);
|
||||
break;
|
||||
case "getTopics":
|
||||
JStoIdea.getTopicList();
|
||||
break;
|
||||
case "getTopicDetail":
|
||||
JStoIdea.getTopicDetail(message.topicHash);
|
||||
break;
|
||||
case "historyMessages":
|
||||
JStoIdea.historyMessages(message);
|
||||
break;
|
||||
case "deleteChatMessage":
|
||||
JStoIdea.deleteChatMessage(message);
|
||||
break;
|
||||
case "openLink":
|
||||
JStoIdea.openLink(message);
|
||||
break;
|
||||
case "userInput":
|
||||
JStoIdea.userInput(message);
|
||||
break;
|
||||
case "setNewTopic":
|
||||
JStoIdea.setNewTopic();
|
||||
break;
|
||||
case "deleteTopic":
|
||||
JStoIdea.deleteTopic(message.topicHash);
|
||||
break;
|
||||
case "stopDevChat":
|
||||
JStoIdea.stopDevChat();
|
||||
break;
|
||||
case "readConfig":
|
||||
JStoIdea.readConfig();
|
||||
break;
|
||||
case "writeConfig":
|
||||
// 保存用户设置
|
||||
JStoIdea.saveConfig(message.value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default IdeaBridge.getInstance();
|
@ -26,78 +26,14 @@ export default function App() {
|
||||
};
|
||||
|
||||
const getConfig = () => {
|
||||
// 比较函数
|
||||
const compare_func = (configs: any) => {
|
||||
const [serverConfig, userConfig] = config.updateConfig(configs.server_config, configs.server_config_base, configs.user_config);
|
||||
if (serverConfig) {
|
||||
// save server config as base
|
||||
MessageUtil.sendMessage({ command: "writeServerConfigBase", value: serverConfig });
|
||||
}
|
||||
|
||||
const modelList = Object.keys(userConfig.models || {});
|
||||
|
||||
// 判断是否需要重设默认模型值
|
||||
if (!userConfig.default_model || !modelList.includes(userConfig.default_model)) {
|
||||
// 需要重新设置默认模型
|
||||
if (serverConfig && serverConfig.default_model && modelList.includes(serverConfig.default_model)) {
|
||||
// 优先使用服务器配置中的默认设置
|
||||
userConfig.default_model = serverConfig.default_model;
|
||||
} else {
|
||||
// 使用 chat 类型的第一个模型
|
||||
const chatModel = modelList.find(model => userConfig.models[model].category === "chat");
|
||||
userConfig.default_model = chatModel || modelList[0] || "";
|
||||
}
|
||||
}
|
||||
|
||||
// set user config
|
||||
config.setConfig(userConfig);
|
||||
};
|
||||
|
||||
// 结果记录
|
||||
let configs = {};
|
||||
let configCount = 0;
|
||||
|
||||
// 处理函数
|
||||
const handleConfig = (key: string, data: { value: any }) => {
|
||||
console.log("-----> receive:", key);
|
||||
configs[key] = {...data.value};
|
||||
configCount++;
|
||||
checkConfigs();
|
||||
};
|
||||
|
||||
// 检查是否所有配置都已准备好
|
||||
const checkConfigs = () => {
|
||||
if (configCount === 3) {
|
||||
compare_func(configs);
|
||||
// 假设setReady是一个函数,用于设置应用状态为准备就绪
|
||||
setReady(true);
|
||||
// update workflow list
|
||||
config.updateWorkflowList();
|
||||
}
|
||||
};
|
||||
|
||||
// 注册处理函数
|
||||
MessageUtil.registerHandler("readConfig", (data: { value: any }) => {
|
||||
console.log("readConfig registerHandler: ", data);
|
||||
config.setConfig(data.value);
|
||||
APIUtil.config(config.getAPIBase(), config.getUserKey());
|
||||
APIUtil.config(config.getAPIBase(), config.getUserKey())
|
||||
config.refreshModelList();
|
||||
setReady(true);
|
||||
config.fetchServerConfig();
|
||||
handleConfig("user_config", data);
|
||||
});
|
||||
MessageUtil.registerHandler("readServerConfigBase", (data: { value: any }) => handleConfig("server_config_base", data));
|
||||
MessageUtil.registerHandler("readServerConfig", (data: { value: any }) => handleConfig("server_config", data));
|
||||
|
||||
MessageUtil.registerHandler(
|
||||
"reloadConfig",
|
||||
(data: { value: any }) => {
|
||||
configs = {};
|
||||
configCount = 0;
|
||||
|
||||
// 发送消息
|
||||
MessageUtil.sendMessage({ command: "readConfig", key: "" });
|
||||
MessageUtil.sendMessage({ command: "readServerConfigBase", key: "" });
|
||||
});
|
||||
MessageUtil.handleMessage({ command: "reloadConfig" });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -7,13 +7,10 @@ import {
|
||||
Radio,
|
||||
Textarea,
|
||||
createStyles,
|
||||
MultiSelect
|
||||
} from "@mantine/core";
|
||||
import { useListState, useSetState } from "@mantine/hooks";
|
||||
import { useMst } from "@/views/stores/RootStore";
|
||||
import yaml from "js-yaml";
|
||||
import IDEServiceUtil from "@/util/IDEServiceUtil";
|
||||
import APIUtil from "@/util/APIUtil";
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
container: {
|
||||
@ -59,7 +56,7 @@ interface IWdiget {
|
||||
id: string;
|
||||
value: string;
|
||||
title?: string;
|
||||
type: "editor" | "checkbox" | "radio" | "button" | "text"| "multiSelect";
|
||||
type: "editor" | "checkbox" | "radio" | "button" | "text";
|
||||
submit?: string;
|
||||
cancel?: string;
|
||||
}
|
||||
@ -78,7 +75,6 @@ const ChatMark = ({
|
||||
const values = value ? yaml.load(value) : {};
|
||||
const [disabled, setDisabled] = useState(messageDone || !!value);
|
||||
const [checkboxArray, setcheckboxArray] = useState<any>([]);
|
||||
const platform = process.env.platform === "idea" ? "intellij" : process.env.platform;
|
||||
|
||||
const handleSubmit = () => {
|
||||
let formData = {};
|
||||
@ -95,24 +91,12 @@ const ChatMark = ({
|
||||
formData[widget.id] = widget.value;
|
||||
});
|
||||
chat.userInput(formData);
|
||||
|
||||
IDEServiceUtil.getCurrentFileInfo().then(info => APIUtil.createEvent({
|
||||
name: "submit",
|
||||
value: JSON.stringify(formData),
|
||||
ide: platform,
|
||||
language: info?.extension || info?.path?.split('.').pop()
|
||||
}));
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
chat.userInput({
|
||||
form: "canceled",
|
||||
});
|
||||
IDEServiceUtil.getCurrentFileInfo().then(info => APIUtil.createEvent({
|
||||
name: "cancel",
|
||||
ide: platform,
|
||||
language: info?.extension || info?.path?.split('.').pop()
|
||||
}));
|
||||
};
|
||||
|
||||
const handleButtonClick = ({ event, index }) => {
|
||||
@ -168,23 +152,6 @@ const ChatMark = ({
|
||||
widget["value"] = event.currentTarget.value;
|
||||
widgetsHandlers.setItem(index, widget);
|
||||
};
|
||||
const handleSelectChange = (values:string[],allValues:string[]) => {
|
||||
widgetsHandlers.apply((item) => {
|
||||
if (allValues.includes(item.id)) {
|
||||
if(!values.length){
|
||||
item.value='unchecked';
|
||||
return item;
|
||||
}
|
||||
item.value = "unchecked";
|
||||
values.forEach(el=>{
|
||||
if(item.id==el){
|
||||
item.value = "checked";
|
||||
}
|
||||
})
|
||||
}
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const lines = children.split("\n");
|
||||
@ -194,7 +161,6 @@ const ChatMark = ({
|
||||
const textRegex = /^([^>].*)/; // Text widget
|
||||
const buttonRegex = /^>\s*\((.*?)\)\s*(.*)/; // Button widget
|
||||
const checkboxRegex = /^>\s*\[([x ]*)\]\((.*?)\)\s*(.*)/; // Checkbox widget
|
||||
const selectRegex = /^>\s*\{([x ]*)\}\((.*?)\)\s*(.*)/; //MultiSelect widget
|
||||
const radioRegex = /^>\s*-\s*\((.*?)\)\s*(.*)/; // Radio button widget
|
||||
const editorRegex = /^>\s*\|\s*\((.*?)\)/; // Editor widget
|
||||
const editorContentRegex = /^>\s*(.*)/; // Editor widget
|
||||
@ -236,40 +202,6 @@ const ChatMark = ({
|
||||
? checkArrayTemp[checkArrayTemp.length - 1]
|
||||
: {};
|
||||
|
||||
if (prevIsCheckbox) {
|
||||
currentCheckboxData.group.push({
|
||||
id: id,
|
||||
// 只记录初始化时的状态,后续状态变化不会更新
|
||||
check: check,
|
||||
});
|
||||
} else {
|
||||
currentCheckboxData = {
|
||||
id: `select-all-${id}`,
|
||||
allChecked: false,
|
||||
indeterminate: false,
|
||||
check: false,
|
||||
group: [{ id: id, check: check }],
|
||||
};
|
||||
checkArrayTemp.push(currentCheckboxData);
|
||||
}
|
||||
}else if ((match = line.match(selectRegex))) {
|
||||
const [status, id, title] = match.slice(1);
|
||||
const check = value
|
||||
? "unchecked"
|
||||
: status === "x"
|
||||
? "checked"
|
||||
: "unchecked";
|
||||
widgetsHandlers.append({
|
||||
id,
|
||||
title,
|
||||
type: "multiSelect",
|
||||
value: check,
|
||||
});
|
||||
setAutoForm(true);
|
||||
let currentCheckboxData: any = prevIsCheckbox
|
||||
? checkArrayTemp[checkArrayTemp.length - 1]
|
||||
: {};
|
||||
|
||||
if (prevIsCheckbox) {
|
||||
currentCheckboxData.group.push({
|
||||
id: id,
|
||||
@ -357,9 +289,6 @@ const ChatMark = ({
|
||||
let radioValuesTemp: any = [];
|
||||
let wdigetsTemp: any = [];
|
||||
let prevIsCheckbox = false;
|
||||
let groupTemp: any = [];
|
||||
let valuesTemp: any = [];
|
||||
let selectValues: any = [];
|
||||
widgets.map((widget, index) => {
|
||||
if (widget.type === "text") {
|
||||
wdigetsTemp.push(<Text key={index}>{widget.value}</Text>);
|
||||
@ -477,34 +406,6 @@ const ChatMark = ({
|
||||
onChange={(event) => handleEditorChange({ event, index })}
|
||||
/>
|
||||
);
|
||||
}else if(widget.type === "multiSelect"){
|
||||
if(widget.value==="checked")selectValues.push(widget.id);
|
||||
groupTemp.push({label:widget.title,value:widget.id});
|
||||
valuesTemp.push(widget.id);
|
||||
// if next widget is not radio, then end current group
|
||||
const nextWidget =
|
||||
index + 1 < widgets.length ? widgets[index + 1] : null;
|
||||
if (!nextWidget || nextWidget.type !== "multiSelect") {
|
||||
const multiSelect = ((data, allValues) => {
|
||||
return (
|
||||
<MultiSelect
|
||||
disabled={disabled}
|
||||
styles={{searchInput:{outline:'none !important'}}}
|
||||
data={data}
|
||||
disableSelectedItemFiltering
|
||||
label=""
|
||||
nothingFound="暂无数据"
|
||||
placeholder="请选择您的task编号"
|
||||
searchable
|
||||
value={selectValues}
|
||||
onChange={(values)=>handleSelectChange(values,allValues)}
|
||||
/>);
|
||||
})(groupTemp, valuesTemp);
|
||||
groupTemp = [];
|
||||
valuesTemp = [];
|
||||
selectValues = [];
|
||||
wdigetsTemp.push(multiSelect);
|
||||
}
|
||||
}
|
||||
|
||||
prevIsCheckbox = widget.type === "checkbox";
|
||||
|
@ -15,7 +15,6 @@ import SvgAvatarDevChat from "../MessageAvatar/avatar_devchat.svg";
|
||||
import messageUtil from "@/util/MessageUtil";
|
||||
import { useRouter } from "@/views/router";
|
||||
import { useMst } from "@/views/stores/RootStore";
|
||||
import { ASSISTANT_DISPLAY_NAME } from "@/util/constants";
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
logoName: {
|
||||
@ -26,7 +25,7 @@ const useStyles = createStyles((theme) => ({
|
||||
export default function Head() {
|
||||
const router = useRouter();
|
||||
const { classes } = useStyles();
|
||||
const { t, i18n } = useTranslation();
|
||||
const { i18n } = useTranslation();
|
||||
const { config } = useMst();
|
||||
|
||||
useEffect(() => {
|
||||
@ -75,7 +74,7 @@ export default function Head() {
|
||||
>
|
||||
<Avatar color="indigo" size={25} radius="xl" src={SvgAvatarDevChat} />
|
||||
<Text weight="bold" className={classes.logoName}>
|
||||
{t(ASSISTANT_DISPLAY_NAME)}
|
||||
DevChat
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex align="center" gap="xs" sx={{ paddingRight: 10 }}>
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import messageUtil from "@/util/MessageUtil";
|
||||
import dayjs from "dayjs";
|
||||
import { ASSISTANT_DISPLAY_NAME } from "@/util/constants";
|
||||
|
||||
export default function Topic({ styleName, disabled }) {
|
||||
const [drawerOpened, { open: openDrawer, close: closeDrawer }] =
|
||||
@ -89,7 +88,7 @@ export default function Topic({ styleName, disabled }) {
|
||||
position="bottom"
|
||||
title={
|
||||
<Flex justify="space-between">
|
||||
<Text>{ASSISTANT_DISPLAY_NAME} Topics</Text>
|
||||
<Text>DevChat Topics</Text>
|
||||
<Flex>
|
||||
<ActionIcon onClick={refreshTopicList}>
|
||||
<IconRefresh size="1rem" />
|
||||
|
@ -32,7 +32,6 @@ import { useMst } from "@/views/stores/RootStore";
|
||||
import { ChatContext } from "@/views/stores/InputStore";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import getModelShowName from "@/util/getModelShowName";
|
||||
import { ASSISTANT_DISPLAY_NAME } from "@/util/constants";
|
||||
|
||||
const useStyles = createStyles(() => ({
|
||||
actionIcon: {
|
||||
@ -64,6 +63,7 @@ const InputMessage = observer((props: any) => {
|
||||
menuOpend,
|
||||
menuType,
|
||||
currentMenuIndex,
|
||||
contextMenus,
|
||||
commandMenus
|
||||
} = input;
|
||||
const { generating } = chat;
|
||||
@ -88,19 +88,7 @@ const InputMessage = observer((props: any) => {
|
||||
input.setValue(value);
|
||||
};
|
||||
|
||||
const resetCursor = () => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.selectionStart = 0;
|
||||
inputRef.current.selectionEnd = 0;
|
||||
inputRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSendClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (generating || chat.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inputValue = input.value;
|
||||
if (inputValue) {
|
||||
if (inputValue.trim() === "/help") {
|
||||
@ -118,22 +106,20 @@ const InputMessage = observer((props: any) => {
|
||||
}
|
||||
// Clear the input field
|
||||
input.setValue("");
|
||||
setTimeout(resetCursor, 0);
|
||||
input.clearContexts();
|
||||
// event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (generating) {
|
||||
if (event.key === 'Enter' &&
|
||||
!event.shiftKey &&
|
||||
!event.nativeEvent.isComposing) {
|
||||
event.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
const handleContextClick = (contextName: string) => {
|
||||
// Process and send the message to the extension
|
||||
messageUtil.sendMessage({
|
||||
command: "addContext",
|
||||
selected: contextName,
|
||||
});
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (menuOpend) {
|
||||
if (event.key === "Escape") {
|
||||
input.closeMenu();
|
||||
@ -179,7 +165,6 @@ const InputMessage = observer((props: any) => {
|
||||
!event.nativeEvent.isComposing
|
||||
) {
|
||||
handleSendClick(event as any);
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -188,8 +173,7 @@ const InputMessage = observer((props: any) => {
|
||||
messageUtil.registerHandler(
|
||||
"chatWithDevChat",
|
||||
(message: { command: string; message: string }) => {
|
||||
input.setValue(message.message);
|
||||
handleSendClick(event as any);
|
||||
chat.commonMessage(message.message, []);
|
||||
}
|
||||
);
|
||||
messageUtil.registerHandler(
|
||||
@ -418,7 +402,7 @@ const InputMessage = observer((props: any) => {
|
||||
opened={drawerOpened}
|
||||
onClose={closeDrawer}
|
||||
position="bottom"
|
||||
title={`${ASSISTANT_DISPLAY_NAME} Contexts`}
|
||||
title="DevChat Contexts"
|
||||
overlayProps={{ opacity: 0.5, blur: 4 }}
|
||||
closeButtonProps={{ children: <IconChevronDown size="1rem" /> }}
|
||||
styles={{
|
||||
@ -448,7 +432,7 @@ const InputMessage = observer((props: any) => {
|
||||
<Popover.Target>
|
||||
<Textarea
|
||||
id="chat-textarea"
|
||||
// disabled={generating || chat.disabled}
|
||||
disabled={generating || chat.disabled}
|
||||
value={input.value}
|
||||
ref={inputRef}
|
||||
onKeyDown={handleKeyDown}
|
||||
@ -463,7 +447,7 @@ const InputMessage = observer((props: any) => {
|
||||
marginTop: 5,
|
||||
marginBottom: 5,
|
||||
}}
|
||||
placeholder={t("devchat.input_placeholder", {assistantName: t(ASSISTANT_DISPLAY_NAME)})}
|
||||
placeholder={t("Ask DevChat a question or type ‘/’ for workflow")}
|
||||
styles={{
|
||||
rightSection: {
|
||||
alignItems: "flex-end",
|
||||
|
@ -23,7 +23,6 @@ import { useTranslation } from "react-i18next";
|
||||
import { useMst } from "@/views/stores/RootStore";
|
||||
import { IMessage } from "@/views/stores/ChatStore";
|
||||
import { IChatContext } from "@/views/stores/InputStore";
|
||||
import { ASSISTANT_DISPLAY_NAME } from "@/util/constants";
|
||||
|
||||
interface IProps {
|
||||
item?: IMessage;
|
||||
@ -61,7 +60,7 @@ const MessageAvatar = observer((props: IProps) => {
|
||||
) : (
|
||||
<Avatar color="cyan" size={25} radius="xl" src={SvgAvatarUser} />
|
||||
)}
|
||||
<Text weight="bold">{avatarType === "bot" ? t(ASSISTANT_DISPLAY_NAME) : t("User")}</Text>
|
||||
<Text weight="bold">{avatarType === "bot" ? "DevChat" : t("User")}</Text>
|
||||
{avatarType === "user" ? (
|
||||
<Flex
|
||||
gap="xs"
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { Tooltip, ActionIcon, CopyButton, Flex } from "@mantine/core";
|
||||
import { IconCheck, IconGitCommit, IconFileDiff, IconEdit, IconColumnInsertRight, IconReplace, IconCopy,IconFile } from "@tabler/icons-react";
|
||||
import { IconCheck, IconGitCommit, IconFileDiff, IconColumnInsertRight, IconReplace, IconCopy,IconFile } from "@tabler/icons-react";
|
||||
import React, { useState } from "react";
|
||||
import { useMst } from "@/views/stores/RootStore";
|
||||
|
||||
import messageUtil from '@/util/MessageUtil';
|
||||
import APIUtil from "@/util/APIUtil";
|
||||
import IDEServiceUtil from "@/util/IDEServiceUtil";
|
||||
|
||||
const IconButton = ({ label, color = 'gray', onClick, children }) => (
|
||||
<Tooltip sx={{ padding: '3px', fontSize: 'var(--vscode-editor-font-size)' }} label={label} withArrow position="left" color="gray">
|
||||
@ -15,16 +14,31 @@ const IconButton = ({ label, color = 'gray', onClick, children }) => (
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
const CodeCopyButton = ({ code, language, platform }) => {
|
||||
const CommitButton = ({ code }) => {
|
||||
const [commited, setCommited] = useState(false);
|
||||
const handleClick = () => {
|
||||
messageUtil.sendMessage({
|
||||
command: 'doCommit',
|
||||
content: code
|
||||
});
|
||||
setCommited(true);
|
||||
setTimeout(() => { setCommited(false); }, 2000);
|
||||
};
|
||||
return (
|
||||
<IconButton label={commited ? 'Committing' : 'Commit'} color={commited ? 'teal' : 'gray'} onClick={handleClick}>
|
||||
{commited ? <IconCheck size="1rem" /> : <IconGitCommit size="1rem" />}
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
const CodeCopyButton = ({ code }) => {
|
||||
const {config} = useMst();
|
||||
return (
|
||||
<CopyButton value={code} timeout={2000}>
|
||||
{({ copied, copy }) => (
|
||||
<IconButton label={copied ? 'Copied' : 'Copy'} color={copied ? 'teal' : 'gray'} onClick={() => {
|
||||
copy();
|
||||
APIUtil.createEvent(
|
||||
{name: 'copy', value: code, language: language, ide: platform},
|
||||
APIUtil.getCurrentMessageId()
|
||||
);
|
||||
APIUtil.createEvent({name: 'copy', value: 'copy'})
|
||||
}}>
|
||||
{copied ? <IconCheck size="1rem" /> : <IconCopy size="1rem" />}
|
||||
</IconButton>
|
||||
@ -33,30 +47,16 @@ const CodeCopyButton = ({ code, language, platform }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const DiffButton = ({ code, language, platform }) => {
|
||||
const DiffButton = ({ code }) => {
|
||||
const {config} = useMst();
|
||||
const handleClick = () => {
|
||||
const e = 'show_diff';
|
||||
let selectedCode = code;
|
||||
const selection = window.getSelection();
|
||||
if (selection) {
|
||||
selectedCode = selection.toString().trim();
|
||||
}
|
||||
|
||||
// If no code is selected, use the entire code block
|
||||
if (!selectedCode) {
|
||||
selectedCode = code;
|
||||
}
|
||||
|
||||
messageUtil.sendMessage({
|
||||
command: e,
|
||||
content: selectedCode
|
||||
content: code
|
||||
});
|
||||
APIUtil.createEvent(
|
||||
{name: e, value: selectedCode, language: language, ide: platform},
|
||||
APIUtil.getCurrentMessageId()
|
||||
);
|
||||
APIUtil.createEvent({name: e, value: e})
|
||||
};
|
||||
|
||||
return (
|
||||
<IconButton label='View Diff' onClick={handleClick}>
|
||||
<IconFileDiff size="1.125rem" />
|
||||
@ -64,36 +64,15 @@ const DiffButton = ({ code, language, platform }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const EditApplyButton = ({ code, language, platform }) => {
|
||||
const handleClick = () => {
|
||||
IDEServiceUtil.callService("diff_apply", {
|
||||
filepath: "",
|
||||
content: code,
|
||||
autoedit: true
|
||||
});
|
||||
APIUtil.createEvent(
|
||||
{name: "edit_apply", value: code, language: language, ide: platform},
|
||||
APIUtil.getCurrentMessageId()
|
||||
);
|
||||
};
|
||||
return (
|
||||
<IconButton label='Apply Code' onClick={handleClick}>
|
||||
<IconEdit size="1.125rem" />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
const CodeApplyButton = ({ code, language, platform }) => {
|
||||
const CodeApplyButton = ({ code }) => {
|
||||
const {config} = useMst();
|
||||
const handleClick = () => {
|
||||
const e = 'code_apply';
|
||||
messageUtil.sendMessage({
|
||||
command: e,
|
||||
content: code
|
||||
});
|
||||
APIUtil.createEvent(
|
||||
{name: e, value: code, language: language, ide: platform},
|
||||
APIUtil.getCurrentMessageId()
|
||||
);
|
||||
APIUtil.createEvent({name: e, value: e})
|
||||
};
|
||||
return (
|
||||
<IconButton label='Insert Code' onClick={handleClick}>
|
||||
@ -102,17 +81,15 @@ const CodeApplyButton = ({ code, language, platform }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const FileApplyButton = ({ code, language, platform }) => {
|
||||
const FileApplyButton = ({ code }) => {
|
||||
const {config} = useMst();
|
||||
const handleClick = () => {
|
||||
const e = 'code_file_apply';
|
||||
messageUtil.sendMessage({
|
||||
command: e,
|
||||
content: code
|
||||
});
|
||||
APIUtil.createEvent(
|
||||
{name: e, value: code, language: language, ide: platform},
|
||||
APIUtil.getCurrentMessageId()
|
||||
);
|
||||
APIUtil.createEvent({name: e, value: e})
|
||||
};
|
||||
return (
|
||||
<IconButton label='Replace File' onClick={handleClick}>
|
||||
@ -122,7 +99,8 @@ const FileApplyButton = ({ code, language, platform }) => {
|
||||
};
|
||||
|
||||
// Add a new button to create new file
|
||||
const NewFileButton = ({ code, language, platform }) => {
|
||||
const NewFileButton = ({ language,code }) => {
|
||||
const {config} = useMst();
|
||||
const handleClick = () => {
|
||||
const e = 'code_new_file';
|
||||
messageUtil.sendMessage({
|
||||
@ -130,10 +108,7 @@ const NewFileButton = ({ code, language, platform }) => {
|
||||
language: language,
|
||||
content: code
|
||||
});
|
||||
APIUtil.createEvent(
|
||||
{name: e, value: code, language: language, ide: platform},
|
||||
APIUtil.getCurrentMessageId()
|
||||
);
|
||||
APIUtil.createEvent({name: e, value: e})
|
||||
};
|
||||
return (
|
||||
<IconButton label='Create New File' onClick={handleClick}>
|
||||
@ -143,7 +118,7 @@ const NewFileButton = ({ code, language, platform }) => {
|
||||
};
|
||||
|
||||
// Similar changes can be made to DiffButton, CodeApplyButton, FileApplyButton, and CodeCopyButton
|
||||
const CodeButtons = ({ platform, language, code }) => (
|
||||
const CodeButtons = ({ language, code }) => (
|
||||
<Flex
|
||||
gap="5px"
|
||||
justify="flex-start"
|
||||
@ -152,16 +127,17 @@ const CodeButtons = ({ platform, language, code }) => (
|
||||
wrap="wrap"
|
||||
style={{ position: 'absolute', top: 8, right: 10 }}
|
||||
>
|
||||
<CodeCopyButton code={code} language={language} platform={platform} />
|
||||
<CodeCopyButton code={code} />
|
||||
{language && language === 'commitmsg'
|
||||
? <CommitButton code={code} />
|
||||
: (
|
||||
<>
|
||||
<DiffButton code={code} language={language} platform={platform} />
|
||||
{language === 'edits' && (
|
||||
<EditApplyButton code={code} language={language} platform={platform} />
|
||||
)}
|
||||
<CodeApplyButton code={code} language={language} platform={platform} />
|
||||
<FileApplyButton code={code} language={language} platform={platform} />
|
||||
<NewFileButton code={code} language={language} platform={platform} />
|
||||
<DiffButton code={code} />
|
||||
<CodeApplyButton code={code} />
|
||||
<FileApplyButton code={code} />
|
||||
<NewFileButton code={code} language={language} />
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
|
||||
|
@ -18,7 +18,6 @@ import { useTranslation } from "react-i18next";
|
||||
import { useRouter } from "@/views/router";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import APIUtil from "@/util/APIUtil";
|
||||
import { ASSISTANT_DISPLAY_NAME } from "@/util/constants";
|
||||
|
||||
(typeof global !== "undefined" ? global : window).Prism = Prism;
|
||||
require("prismjs/components/prism-java");
|
||||
@ -109,15 +108,12 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleCodeCopy = (platform, language) => {
|
||||
const handleCodeCopy = (event) => {
|
||||
const selection = window.getSelection()?.toString();
|
||||
console.log("Copied: ", selection);
|
||||
const e = 'manual_copy';
|
||||
APIUtil.createEvent(
|
||||
{name: e, value: selection, language: language, ide: platform},
|
||||
APIUtil.getCurrentMessageId()
|
||||
);
|
||||
};
|
||||
APIUtil.createEvent({name: e, value: selection})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let previousNode: any = null;
|
||||
@ -172,7 +168,7 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||
"Do you want to write some code or have a question about the project? "
|
||||
)
|
||||
) {
|
||||
return t("devchat.help", {assistantName: t(ASSISTANT_DISPLAY_NAME)}) + chat.helpWorkflowCommands();
|
||||
return t("devchat.help") + chat.helpWorkflowCommands();
|
||||
}
|
||||
if (
|
||||
children.includes(
|
||||
@ -180,23 +176,23 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||
)
|
||||
) {
|
||||
if (process.env.platform === "vscode") {
|
||||
return t("devchat.setkey_vscode", {assistantName: t(ASSISTANT_DISPLAY_NAME)});
|
||||
return t("devchat.setkey_vscode");
|
||||
}
|
||||
return t("devchat.setkey", {assistantName: t(ASSISTANT_DISPLAY_NAME)});
|
||||
return t("devchat.setkey");
|
||||
}
|
||||
if (
|
||||
children.includes(
|
||||
"DevChat intelligently navigates your codebase using GPT-4."
|
||||
)
|
||||
) {
|
||||
return t("ask-code-explain", {assistantName: t(ASSISTANT_DISPLAY_NAME)});
|
||||
return t("ask-code-explain");
|
||||
}
|
||||
if (
|
||||
children.includes(
|
||||
"Use this DevChat workflow to request code writing. Please input your specific requirement"
|
||||
)
|
||||
) {
|
||||
return t("code-explain", {assistantName: t(ASSISTANT_DISPLAY_NAME)});
|
||||
return t("code-explain");
|
||||
}
|
||||
if (
|
||||
children.includes(
|
||||
@ -263,18 +259,18 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||
code({ node, inline, className, children, index, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || "");
|
||||
const value = String(children).replace(/\n$/, "");
|
||||
let language = match && match[1];
|
||||
let lanugage = match && match[1];
|
||||
|
||||
if (!language) {
|
||||
language = "plaintext";
|
||||
if (!lanugage) {
|
||||
lanugage = "plaintext";
|
||||
}
|
||||
|
||||
let wrapLongLines = false;
|
||||
if (language === "markdown" || language === "text") {
|
||||
if (lanugage === "markdown" || lanugage === "text") {
|
||||
wrapLongLines = true;
|
||||
}
|
||||
|
||||
if (language === "step" || language === "Step") {
|
||||
if (lanugage === "step" || lanugage === "Step") {
|
||||
const status =
|
||||
activeStep &&
|
||||
Number(index) === codes.length &&
|
||||
@ -282,13 +278,13 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||
? "running"
|
||||
: "done";
|
||||
return (
|
||||
<Step language={language} status={status} index={index}>
|
||||
<Step language={lanugage} status={status} index={index}>
|
||||
{value}
|
||||
</Step>
|
||||
);
|
||||
}
|
||||
|
||||
if (language === "chatmark" || language === "ChatMark") {
|
||||
if (lanugage === "chatmark" || lanugage === "ChatMark") {
|
||||
const chatmarkValue = chatmarkProps[`chatmark-${index}`];
|
||||
return (
|
||||
<ChatMark messageDone={messageDone} {...chatmarkValue}>
|
||||
@ -297,17 +293,17 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
if ((language === "yaml" || language === "YAML") && props.hidden) {
|
||||
if ((lanugage === "yaml" || lanugage === "YAML") && props.hidden) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return !inline && language ? (
|
||||
return !inline && lanugage ? (
|
||||
<div
|
||||
style={{ position: "relative" }}
|
||||
className={classes.codeOverride}
|
||||
>
|
||||
<LanguageCorner language={language} />
|
||||
<CodeButtons platform={platform === "idea" ? "intellij" : platform} language={language} code={value} />
|
||||
<LanguageCorner language={lanugage} />
|
||||
<CodeButtons language={lanugage} code={value} />
|
||||
{/* <SyntaxHighlighter
|
||||
{...props}
|
||||
language={lanugage}
|
||||
@ -321,7 +317,7 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||
<Highlight
|
||||
code={value}
|
||||
theme={themes.okaidia}
|
||||
language={language}
|
||||
language={lanugage}
|
||||
>
|
||||
{({
|
||||
className,
|
||||
@ -340,7 +336,7 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||
whiteSpace: "pre",
|
||||
...props.style,
|
||||
}}
|
||||
onCopy={() => handleCodeCopy(platform, language)}
|
||||
onCopy={handleCodeCopy}
|
||||
{...props}
|
||||
>
|
||||
{tokens.map((line, i) => (
|
||||
@ -410,20 +406,9 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||
{children}
|
||||
</Anchor>
|
||||
) : (
|
||||
<Anchor
|
||||
className={classes.link}
|
||||
href="javascript:void()"
|
||||
onClick={() => {
|
||||
if (href) {
|
||||
messageUtil.sendMessage({
|
||||
command: "openLink",
|
||||
url: href,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<a {...props} href={href} className={className}>
|
||||
{children}
|
||||
</Anchor>
|
||||
</a>
|
||||
);
|
||||
},
|
||||
img({ node, ...props }) {
|
||||
|
@ -1,7 +1 @@
|
||||
{
|
||||
"config.api_base": "API Base of {{assistantName}}",
|
||||
"config.custom_api_base": "Custom API Base of {{assistantName}}",
|
||||
"config.access_key": "Access Key of {{assistantName}}",
|
||||
"devchat.help_question": "How do I use {{assistantName}}?",
|
||||
"devchat.input_placeholder": "Ask {{assistantName}} a question or type ‘/’ for workflow"
|
||||
}
|
||||
{}
|
||||
|
@ -3,7 +3,6 @@ import { initReactI18next } from "react-i18next";
|
||||
|
||||
import enTranslations from "./en.json";
|
||||
import zhTranslations from "./zh.json";
|
||||
import { ASSISTANT_DISPLAY_NAME, ASSISTANT_DISPLAY_NAME_ZH } from "@/util/constants";
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources: {
|
||||
@ -24,6 +23,5 @@ i18n.use(initReactI18next).init({
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
i18n.addResource('zh', 'translation', ASSISTANT_DISPLAY_NAME, ASSISTANT_DISPLAY_NAME_ZH);
|
||||
|
||||
export default i18n;
|
||||
|
@ -5,12 +5,12 @@
|
||||
"Refilled": "已重新填入",
|
||||
"Refill prompt": "重新填进输入框",
|
||||
"User": "用户",
|
||||
"devchat.input_placeholder": "向 {{assistantName}} 直接提问或输入 '/' 以查看可用的工作流",
|
||||
"Ask DevChat a question or type ‘/’ for workflow": "向 DevChat 直接提问或输入 '/' 以查看可用的工作流",
|
||||
"How do I use DevChat?": "如何使用 DevChat?",
|
||||
"balance": "你的账户余额为 {{formatedCurrency}},登录 <4>web.devchat.ai</4> 获得更多 tokens",
|
||||
"devchat.help_question": "如何使用 {{assistantName}}?",
|
||||
"devchat.help": "你想生成一些代码还是对这个项目有疑问?请首先右键单击相关的文件或代码片段,将它们添加到 {{assistantName}} 中作为上下文,然后在输入框中写下你的请求或问题,我将基于添加的上下文生成代码或回答你的问题。<br> <br> 此外,在输入框中键入“/”,{{assistantName}} 会列出可供使用的各类工作流,按 Tab 键或输入完整命令触发你需要的工作流。聊天愉快! <br> <br>下面是一些工作流的示例:<br> <br> ",
|
||||
"devchat.setkey": "你的环境或设置中缺少 {{assistantName}} 访问密钥。请输入你的 {{assistantName}} 访问密钥,这样我就可以开始正常工作了。<br> <br> <button value=\"get_devchat_key\">注册 {{assistantName}} 访问密钥</button> <button value=\"setting_devchat_key\">设置 {{assistantName}} 访问密钥</button>",
|
||||
"devchat.setkey_vscode": "你的环境或设置中缺少 {{assistantName}} 访问密钥。请输入你的 {{assistantName}} 访问密钥,这样我就可以开始正常工作了。<br> <br> <button value=\"get_devchat_key\" component=\"a\" href=\"https://web.devchat.ai\">注册 {{assistantName}} 访问密钥</button> <button value=\"setting_devchat_key\">设置 {{assistantName}} 访问密钥</button>",
|
||||
"devchat.help": "你想生成一些代码还是对这个项目有疑问?请首先右键单击相关的文件或代码片段,将它们添加到 DevChat 中作为上下文,然后在输入框中写下你的请求或问题,我将基于添加的上下文生成代码或回答你的问题。<br> <br> 此外,在输入框中键入“/”,DevChat 会列出可供使用的各类工作流,按 Tab 键或输入完整命令触发你需要的工作流。聊天愉快! <br> <br>下面是一些工作流的示例:<br> <br> ",
|
||||
"devchat.setkey": "你的环境或设置中缺少 DevChat 访问密钥。请输入你的 DevChat 访问密钥,这样我就可以开始正常工作了。<br> <br> <button value=\"get_devchat_key\">注册 DevChat 访问密钥</button> <button value=\"setting_devchat_key\">设置 DevChat 访问密钥</button>",
|
||||
"devchat.setkey_vscode": "你的环境或设置中缺少 DevChat 访问密钥。请输入你的 DevChat 访问密钥,这样我就可以开始正常工作了。<br> <br> <button value=\"get_devchat_key\" component=\"a\" href=\"https://web.devchat.ai\">注册 DevChat 访问密钥</button> <button value=\"setting_devchat_key\">设置 DevChat 访问密钥</button>",
|
||||
"Is DevChat Access Key ready?": "DevChat 访问密钥是否已经设置好?",
|
||||
"Ask questions about the current project's codebase, which requires proactive acquisition of additional context information to answer.": "询问关于当前项目代码库的问题,我将主动获取相关的上下文信息来回答。",
|
||||
"Generate code with a general template embedded into the prompt.": "使用隐式嵌入到提示词中的通用模板生成代码。",
|
||||
@ -19,23 +19,23 @@
|
||||
"Generate a commit message for the given git diff.": "为给定的 git diff 生成提交消息。",
|
||||
"Generate a release note for the given commit log.": "为给定的提交日志生成发布说明。",
|
||||
"Explain /ask-code": "解释 /ask-code",
|
||||
"ask-code-explain": "向我们的 AI 代理提出有关您代码库的任何问题,并获得答案。<br> <br>{{assistantName}} 利用 GPT-4 智能地导航您的代码库。它会自动选择和分析最多十个与您的问题最相关的源文件来提供答案。敬请期待 — 我们即将整合更高效的 LLama 2 - 70B 模型。<br> <br> 示例问题:<br> -为什么更改的引导时间有时会显示为 null?<br> -store.findAllAccounts 是如何实现的?<br> -当前的递归检索器会丢弃所有 TextNodes,只查询 IndexNodes。这是一个 bug。我们该如何修复它?",
|
||||
"ask-code-explain": "向我们的 AI 代理提出有关您代码库的任何问题,并获得答案。<br> <br>DevChat 利用 GPT-4 智能地导航您的代码库。它会自动选择和分析最多十个与您的问题最相关的源文件来提供答案。敬请期待 — 我们即将整合更高效的 LLama 2 - 70B 模型。<br> <br> 示例问题:<br> -为什么更改的引导时间有时会显示为 null?<br> -store.findAllAccounts 是如何实现的?<br> -当前的递归检索器会丢弃所有 TextNodes,只查询 IndexNodes。这是一个 bug。我们该如何修复它?",
|
||||
"Explain /release_note": "解释 /release_note",
|
||||
"note-explain": "使用这个工作流程,您可以生成专业的、格式化的发布说明(markdown格式)。我只需要关于本次发布的提交的一些基本信息。通过点击“+”按钮并选择 git_log_releasenote 将这些信息添加到上下文中。如果提交的范围与默认命令不同,您还可以选择 <custom command> 并输入如 git log 579398b^..HEAD --pretty=format:\"%h - %B\" 的命令行来包含从提交 579398b(包括此提交)到最新提交的所有变更。",
|
||||
"Explain /code": "解释 /code",
|
||||
"code-explain": "使用 {{assistantName}} 工作流程来请求编写代码。请输入您的具体需求,并提供适当的实施上下文。您可以选择相关的代码或文件,然后右键点击“添加到 {{assistantName}}”。如果您发现上下文仍然不足以解释清楚,您可以通过提供所选代码的类/函数定义来增强我对您代码的理解。要做到这一点,请点击所选代码旁边的“+”按钮,然后选择“符号定义”。请注意,这些信息显示在 {{assistantName}} 中可能需要几秒钟时间。",
|
||||
"code-explain": "使用 DevChat 工作流程来请求编写代码。请输入您的具体需求,并提供适当的实施上下文。您可以选择相关的代码或文件,然后右键点击“添加到 DevChat”。如果您发现上下文仍然不足以解释清楚,您可以通过提供所选代码的类/函数定义来增强我对您代码的理解。要做到这一点,请点击所选代码旁边的“+”按钮,然后选择“符号定义”。请注意,这些信息显示在 DevChat 中可能需要几秒钟时间。",
|
||||
"Config": "配置",
|
||||
"Singapore Node": "新加坡区节点",
|
||||
"China Node": "中国区节点",
|
||||
"Custom": "自定义",
|
||||
"config.custom_api_base": "自定义 {{assistantName}} API 地址",
|
||||
"config.api_base": "{{assistantName}} API 地址",
|
||||
"Custom API Base of Devchat": "自定义 DevChat API 地址",
|
||||
"API Base of Devchat": "DevChat API 地址",
|
||||
"the base URL for the API": "API 的基础 URL",
|
||||
"Access Key of OpenAI": "OpenAI 访问密钥",
|
||||
"Your Access Key": "你的访问密钥",
|
||||
"please keep this secret": "请不要泄露给他人",
|
||||
"API Base of OpenAI": "OpenAI API 地址",
|
||||
"config.access_key": "{{assistantName}} 访问密钥",
|
||||
"Access Key of Devchat": "DevChat 访问密钥",
|
||||
"Model Config": "模型配置",
|
||||
"/1M tokens": "/每百万token",
|
||||
"output price:": "输出价格:",
|
||||
@ -59,15 +59,20 @@
|
||||
"Please enter the proxy url and port": "请输入您的代理URL和端口",
|
||||
"Cancel": "取消",
|
||||
"Save": "保存",
|
||||
"Reload built-in & custom workflows": "重新加载内置和自定义工作流",
|
||||
"Sync settings from cloud": "从云端同步设置",
|
||||
"Max input tokens": "最大输入数",
|
||||
"/unit_tests": "/生成单测",
|
||||
"Generate unit tests.": "为函数生成Happy Path和Edge Case测试用例。",
|
||||
"/commit": "/提交信息",
|
||||
"Writes a well-formatted commit message for selected code changes and commits them via Git. Include an issue number if desired (e.g., input \"/commit to close #12\").": "为变更代码生成提交消息。",
|
||||
"/docstring": "/函数注释",
|
||||
"Automatically add docstrings. Select a function or method and execute this command to generate docstring.": "对所选函数生成函数注释。",
|
||||
"/comments": "/行间注释",
|
||||
"Automatically add doc comments. Select some code and execute this command to generate comments.": "对所选代码生成行间注释。",
|
||||
"/fix": "/代码纠错",
|
||||
"Try to find out potential bugs in the selected code and try fixing these bugs automaticly.": "对所选代码进行自动修复。",
|
||||
"/explain": "/代码解释",
|
||||
"Explain selected code.": "对所选代码生成解释。",
|
||||
"/refactor": "/代码重构",
|
||||
"rewrite selected code.": "对所选代码进行重构。",
|
||||
"workflowTip": "为了确保高准确度,我们的 {{name}} 采纳了包括规划、工具使用、反思在内的LLM设计模式。这些模式能显著提高回应的精确性,但请注意,由于深度处理的需要,回应时间可能超过<3>1分钟</3>。"
|
||||
}
|
@ -23,7 +23,6 @@ import {
|
||||
IconExternalLink,
|
||||
} from "@tabler/icons-react";
|
||||
import { useRouter } from "../router";
|
||||
import IDEServiceUtil from "@/util/IDEServiceUtil";
|
||||
|
||||
interface WorkflowConf {
|
||||
description: string;
|
||||
@ -54,6 +53,12 @@ const chatPanel = observer(() => {
|
||||
behavior: "smooth",
|
||||
});
|
||||
|
||||
const getFeatureToggles = () => {
|
||||
messageUtil.sendMessage({
|
||||
command: "featureToggles",
|
||||
});
|
||||
};
|
||||
|
||||
const timer = useTimeout(() => {
|
||||
if (chat.isBottom) {
|
||||
scrollToBottom();
|
||||
@ -109,6 +114,8 @@ const chatPanel = observer(() => {
|
||||
});
|
||||
});
|
||||
chat.fetchHistoryMessages();
|
||||
input.fetchContextMenus().then();
|
||||
getFeatureToggles();
|
||||
}
|
||||
);
|
||||
messageUtil.registerHandler(
|
||||
@ -142,49 +149,17 @@ const chatPanel = observer(() => {
|
||||
input.clearContexts();
|
||||
}
|
||||
);
|
||||
messageUtil.registerHandler(
|
||||
"featureToggles",
|
||||
(message: { features: object }) => {
|
||||
chat.updateFeatures(message.features);
|
||||
}
|
||||
);
|
||||
messageUtil.registerHandler("codeDiffApply", (_: any) => {
|
||||
const e = 'code_diff_apply';
|
||||
const platform = process.env.platform === "idea" ? "intellij" : process.env.platform;
|
||||
IDEServiceUtil.getCurrentFileInfo().then(info => APIUtil.createEvent({
|
||||
name: e,
|
||||
value: e,
|
||||
ide: platform,
|
||||
language: info?.extension || info?.path?.split('.').pop()
|
||||
}));
|
||||
});
|
||||
const e = 'code_diff_apply'
|
||||
APIUtil.createEvent({name: e, value: e})
|
||||
})
|
||||
|
||||
messageUtil.registerHandler("logEvent", (message: {id: string, language: string, name: string, value: Record<string, any>}) => {
|
||||
const platform = process.env.platform === "idea" ? "intellij" : process.env.platform;
|
||||
APIUtil.createEvent({
|
||||
name: message.name,
|
||||
value: typeof message.value === 'string' ? message.value : JSON.stringify(message.value),
|
||||
ide: platform,
|
||||
language: message.language
|
||||
}, message.id);
|
||||
console.log("logEvent:", message);
|
||||
});
|
||||
|
||||
messageUtil.registerHandler("logMessage", (message: {id: string, language: string, commandName: string, content: string, model: string}) => {
|
||||
const platform = process.env.platform === "idea" ? "intellij" : process.env.platform;
|
||||
const timestamp = new Date().toISOString(); // 自动生成当前时间的ISO格式时间戳
|
||||
|
||||
APIUtil.createMessage({
|
||||
content: message.content,
|
||||
command: message.commandName,
|
||||
timestamp: timestamp,
|
||||
ide: platform,
|
||||
language: message.language,
|
||||
model: message.model
|
||||
}, message.id);
|
||||
|
||||
console.log("logMessage:", { message, timestamp });
|
||||
});
|
||||
|
||||
messageUtil.registerHandler("getIDEServicePort", (data: any) => {
|
||||
IDEServiceUtil.config(data.result);
|
||||
});
|
||||
|
||||
messageUtil.sendMessage({ command: "getIDEServicePort" });
|
||||
messageUtil.sendMessage({ command: "regCommandList" });
|
||||
|
||||
timer.start();
|
||||
|
@ -25,7 +25,6 @@ import cloneDeep from "lodash.clonedeep";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ASSISTANT_DISPLAY_NAME } from "@/util/constants";
|
||||
|
||||
const commonInputStyle = {
|
||||
label: {
|
||||
@ -112,6 +111,10 @@ const Config = observer(() => {
|
||||
const [current, setCurrent] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
MessageUtil.registerHandler("updateSetting", (data) => {
|
||||
// 保存后的回调
|
||||
MessageUtil.sendMessage({ command: "readConfig" });
|
||||
});
|
||||
if (router.currentRoute !== "config") {return;}
|
||||
const modelArray = config.modelsTemplate.map((item) => ({
|
||||
value: item.name,
|
||||
@ -157,28 +160,11 @@ const Config = observer(() => {
|
||||
key: "",
|
||||
});
|
||||
setTimeout(() => {
|
||||
MessageUtil.handleMessage({ command: "reloadConfig" });
|
||||
config.setTemplate([], "");
|
||||
MessageUtil.sendMessage({ command: "readConfig", key: "" });
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleSync = () => {
|
||||
config.updateSettle(false);
|
||||
startLoading();
|
||||
// 调用 Local Service 更新工作流,更新、重载命令列表
|
||||
MessageUtil.handleMessage({ command: "reloadConfig" });
|
||||
};
|
||||
|
||||
const handleReload = () => {
|
||||
config.updateSettle(false);
|
||||
startLoading();
|
||||
// update workflow list
|
||||
config.updateWorkflowList().then(() => {
|
||||
config.updateSettle(true);
|
||||
router.updateRoute("chat");
|
||||
closeLoading();
|
||||
});
|
||||
};
|
||||
|
||||
const changeModelDetail = (key: string, value: number | string) => {
|
||||
const newModel = { ...form.values.models[current], [key]: value };
|
||||
form.setFieldValue("models", {
|
||||
@ -237,7 +223,7 @@ const Config = observer(() => {
|
||||
>
|
||||
<Stack>
|
||||
<Tabs
|
||||
defaultValue={ASSISTANT_DISPLAY_NAME}
|
||||
defaultValue="Devchat"
|
||||
variant="outline"
|
||||
sx={{
|
||||
".mantine-UnstyledButton-root::before": {
|
||||
@ -247,12 +233,12 @@ const Config = observer(() => {
|
||||
>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab
|
||||
value={ASSISTANT_DISPLAY_NAME}
|
||||
value="Devchat"
|
||||
sx={{
|
||||
color: "var(--vscode-editor-foreground)",
|
||||
}}
|
||||
>
|
||||
{ASSISTANT_DISPLAY_NAME}
|
||||
Devchat
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
value="OpenAI"
|
||||
@ -264,7 +250,7 @@ const Config = observer(() => {
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel
|
||||
value={ASSISTANT_DISPLAY_NAME}
|
||||
value="Devchat"
|
||||
pt="xs"
|
||||
p={10}
|
||||
sx={{
|
||||
@ -281,7 +267,7 @@ const Config = observer(() => {
|
||||
...selectStyle,
|
||||
}}
|
||||
placeholder="https://xxxx.xx"
|
||||
label={t("config.api_base", {assistantName: t(ASSISTANT_DISPLAY_NAME)})}
|
||||
label={t("API Base of Devchat")}
|
||||
withAsterisk
|
||||
description={t("the base URL for the API")}
|
||||
{...form.getInputProps("providers.devchat.api_base")}
|
||||
@ -289,7 +275,7 @@ const Config = observer(() => {
|
||||
{form.values.providers?.devchat?.api_base === "custom" && (
|
||||
<TextInput
|
||||
styles={commonInputStyle}
|
||||
label={t("config.custom_api_base", {assistantName: t(ASSISTANT_DISPLAY_NAME)})}
|
||||
label={t("Custom API Base of Devchat")}
|
||||
withAsterisk
|
||||
description={t("the base URL for the API")}
|
||||
{...form.getInputProps(
|
||||
@ -307,7 +293,7 @@ const Config = observer(() => {
|
||||
},
|
||||
}}
|
||||
withAsterisk
|
||||
label={t("config.access_key", {assistantName: t(ASSISTANT_DISPLAY_NAME)})}
|
||||
label={t("Access Key of Devchat")}
|
||||
placeholder={t("Your Access Key")}
|
||||
description={t("please keep this secret")}
|
||||
{...form.getInputProps("providers.devchat.api_key")}
|
||||
@ -395,7 +381,7 @@ const Config = observer(() => {
|
||||
...selectStyle,
|
||||
}}
|
||||
data={[
|
||||
{ value: "devchat", label: ASSISTANT_DISPLAY_NAME },
|
||||
{ value: "devchat", label: "Devchat" },
|
||||
{ value: "openai", label: "OpenAI" },
|
||||
]}
|
||||
value={form.values?.models[current]?.provider}
|
||||
@ -431,6 +417,13 @@ const Config = observer(() => {
|
||||
description={t("Please enter the path of your python")}
|
||||
{...form.getInputProps("python_for_chat")}
|
||||
/>
|
||||
<TextInput
|
||||
styles={commonInputStyle}
|
||||
label={t("Python for commands")}
|
||||
placeholder="/xxx/xxx"
|
||||
description={t("Please enter the path of your python")}
|
||||
{...form.getInputProps("python_for_commands")}
|
||||
/>
|
||||
<TextInput
|
||||
styles={commonInputStyle}
|
||||
label={t("Proxy setting")}
|
||||
@ -470,18 +463,6 @@ const Config = observer(() => {
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
onClick={handleSync}
|
||||
variant="outline"
|
||||
color="gray">
|
||||
{t("Sync settings from cloud")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleReload}
|
||||
variant="outline"
|
||||
color="gray">
|
||||
{t("Reload built-in & custom workflows")}
|
||||
</Button>
|
||||
</Stack>
|
||||
<Group
|
||||
grow
|
||||
@ -502,7 +483,6 @@ const Config = observer(() => {
|
||||
variant="outline"
|
||||
color="gray"
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
router.updateRoute("chat");
|
||||
}}
|
||||
>
|
||||
|
@ -5,9 +5,6 @@ import yaml from "js-yaml";
|
||||
import { RootInstance } from "./RootStore";
|
||||
import i18next from "i18next";
|
||||
import APIUtil from "@/util/APIUtil";
|
||||
import IDEServiceUtil from "@/util/IDEServiceUtil";
|
||||
import { ASSISTANT_DISPLAY_NAME } from "@/util/constants";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
interface Context {
|
||||
content: string;
|
||||
@ -101,6 +98,7 @@ export const ChatStore = types
|
||||
chatPanelWidth: 300,
|
||||
disabled: false,
|
||||
rechargeSite: "https://web.devchat.ai/pricing/",
|
||||
features: types.optional(types.frozen(), {}),
|
||||
key: types.optional(types.string, ""),
|
||||
})
|
||||
.actions((self) => {
|
||||
@ -173,10 +171,8 @@ export const ChatStore = types
|
||||
self.hasDone = false;
|
||||
self.errorMessage = "";
|
||||
self.currentMessage = "";
|
||||
const rootStore = getParent<RootInstance>(self);
|
||||
const config = rootStore.config;
|
||||
const config = getParent<RootInstance>(self).config
|
||||
const chatModel = config.getDefaultModel();
|
||||
const platform = process.env.platform;
|
||||
messageUtil.sendMessage({
|
||||
command: "sendMessage",
|
||||
text: text,
|
||||
@ -184,22 +180,12 @@ export const ChatStore = types
|
||||
parent_hash: lastNonEmptyHash(),
|
||||
model: chatModel,
|
||||
});
|
||||
const supportedCommands = new Set(rootStore.input.commandMenus.map(x => x.name));
|
||||
let command = text.startsWith("/") ? text.split(" ", 1)[0] : null;
|
||||
command = command && supportedCommands.has(command.slice(1)) ? command : null;
|
||||
|
||||
IDEServiceUtil.getCurrentFileInfo().then(info => APIUtil.createMessage({
|
||||
content: text,
|
||||
command: command,
|
||||
model: chatModel,
|
||||
language: info?.extension || info?.path?.split(".").pop(),
|
||||
ide: platform === "idea" ? "intellij" : platform
|
||||
}, APIUtil.updateCurrentMessageId()));
|
||||
APIUtil.createMessage({content: text, model: chatModel});
|
||||
};
|
||||
|
||||
const helpMessage = (originalMessage = false) => {
|
||||
let helps = `
|
||||
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 ${ASSISTANT_DISPLAY_NAME}. Feel free to ask me anything or let me help you with coding.
|
||||
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.
|
||||
|
||||
To see a list of workflows you can run in the context, just type "/". Happy prompting!
|
||||
|
||||
@ -208,17 +194,17 @@ To get started, here are some of the things that I can do for you:
|
||||
${helpWorkflowCommands()}`;
|
||||
|
||||
const setKeyMessage = `
|
||||
Your ${ASSISTANT_DISPLAY_NAME} Access Key is not detected in the current settings. Please set your Access Key below, and we'll have everything set up for you in no time.
|
||||
Your DevChat Access Key is not detected in the current settings. Please set your Access Key below, and we'll have everything set up for you in no time.
|
||||
|
||||
<button value="get_devchat_key" ${
|
||||
process.env.platform === "vscode"
|
||||
? 'href="https://web.devchat.ai" component="a"'
|
||||
: ""
|
||||
}>Get ${ASSISTANT_DISPLAY_NAME} key</button>
|
||||
<button value="setting_devchat_key">Set ${ASSISTANT_DISPLAY_NAME} key</button>
|
||||
}>Get DevChat key</button>
|
||||
<button value="setting_devchat_key">Set DevChat key</button>
|
||||
`;
|
||||
|
||||
const setKeyUser = `Is ${ASSISTANT_DISPLAY_NAME} Access Key ready?`;
|
||||
const setKeyUser = `Is DevChat Access Key ready?`;
|
||||
|
||||
const accessKey = getParent<RootInstance>(self).config.getUserKey();
|
||||
|
||||
@ -239,7 +225,7 @@ Your ${ASSISTANT_DISPLAY_NAME} Access Key is not detected in the current setting
|
||||
self.messages.push(
|
||||
Message.create({
|
||||
type: "user",
|
||||
message: originalMessage ? i18next.t("devchat.help_question", {assistantName: i18next.t(ASSISTANT_DISPLAY_NAME)}) : "/help",
|
||||
message: originalMessage ? "How do I use DevChat?" : "/help",
|
||||
})
|
||||
);
|
||||
self.messages.push(
|
||||
@ -357,6 +343,9 @@ Thinking...
|
||||
const rootStore = getParent<RootInstance>(self);
|
||||
rootStore.config.setConfigValue("default_model", chatModel);
|
||||
},
|
||||
updateFeatures: (features: any) => {
|
||||
self.features = features;
|
||||
},
|
||||
startSystemMessage: () => {
|
||||
self.generating = true;
|
||||
self.responsed = false;
|
||||
@ -394,13 +383,6 @@ Thinking...
|
||||
} else {
|
||||
self.messages[messagesLength - 1].message = message;
|
||||
}
|
||||
|
||||
// send event to server
|
||||
const platform = process.env.platform === "idea" ? "intellij" : process.env.platform;
|
||||
APIUtil.createEvent(
|
||||
{name: 'stopGenerating', value: message, language: "unknow", ide: platform},
|
||||
APIUtil.getCurrentMessageId()
|
||||
);
|
||||
},
|
||||
|
||||
newMessage: (message: IMessage) => {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import MessageUtil from "@/util/MessageUtil";
|
||||
import IDEServiceUtil from "@/util/IDEServiceUtil";
|
||||
import { types, Instance, flow } from "mobx-state-tree";
|
||||
import modelsTemplate from "@/models";
|
||||
import cloneDeep from "lodash.clonedeep";
|
||||
@ -10,65 +9,20 @@ const defaultAPIBase = [
|
||||
"https://api.devchat-ai.cn/v1",
|
||||
];
|
||||
|
||||
|
||||
function deepCopy(obj) {
|
||||
let copy = Array.isArray(obj) ? [] : {};
|
||||
|
||||
for (let key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
||||
copy[key] = deepCopy(obj[key]); // 递归调用
|
||||
} else {
|
||||
copy[key] = obj[key];
|
||||
export const fetchLLMs = async ({modelsUrl,devchatApiKey}) => {
|
||||
return new Promise<{data:any}>((resolve, reject) => {
|
||||
// 添加 header: "Authorization: Bearer ${devchatApiKey}"
|
||||
axios.get(`${modelsUrl}/models`, { headers: { 'Authorization': `Bearer ${devchatApiKey}` }}).then((res) => {
|
||||
// 获取 models 模版列表
|
||||
if (res?.data?.data && Array.isArray(res?.data?.data)) {
|
||||
resolve(res.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
export const doUpdateWorkflowList = async () => {
|
||||
try {
|
||||
// Get local service port
|
||||
const port = await IDEServiceUtil.callService("get_local_service_port", {});
|
||||
// check whether port is valid
|
||||
if (!port) {
|
||||
console.error("do update workflow and command list error: port is invalid");
|
||||
return undefined;
|
||||
}
|
||||
// Call local service to update Workflows
|
||||
await axios.post(`http://localhost:${port}/workflows/update`, {});
|
||||
await axios.post(`http://localhost:${port}/workflows/custom_update`, {});
|
||||
// Update command list
|
||||
MessageUtil.sendMessage({ command: "regCommandList" });
|
||||
} catch (e) {
|
||||
console.error("do update workflow and command list error:", e);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchServerConfigUtil = async ({ modelsUrl, devchatApiKey }) => {
|
||||
try {
|
||||
const response = await axios.get(`${modelsUrl}/models`, {
|
||||
headers: { 'Authorization': `Bearer ${devchatApiKey}` }
|
||||
}).catch((e) => {
|
||||
console.error("fetchLLMs error:", e);
|
||||
reject(e);
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
if (response.data.data && Array.isArray(response.data.data)) {
|
||||
// change data to models key
|
||||
response.data.models = response.data.data;
|
||||
delete response.data.data;
|
||||
}
|
||||
return response.data;
|
||||
} else {
|
||||
console.error("fetchServerConfig error: Non-200 status code", response.status);
|
||||
return undefined;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("fetchServerConfig error:", e);
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const Model = types.model({
|
||||
@ -99,27 +53,11 @@ export const ConfigStore = types
|
||||
provider: "devchat",
|
||||
})
|
||||
.actions((self) => {
|
||||
const setTemplate = (value: any,) => {
|
||||
const provider = self.provider;
|
||||
const list: any[] = [];
|
||||
|
||||
for (const name in value) {
|
||||
if (value.hasOwnProperty(name)) {
|
||||
const item: any = { name };
|
||||
for (const key in value[name]) {
|
||||
if (value[name].hasOwnProperty(key)) {
|
||||
item[key] = value[name][key];
|
||||
}
|
||||
}
|
||||
list.push(item);
|
||||
}
|
||||
}
|
||||
value = list;
|
||||
|
||||
const setTemplate = (value: any, provider: string) => {
|
||||
const models = value
|
||||
.map((item) => {
|
||||
return {
|
||||
name: item.name,
|
||||
name: item.model ?? item.id,
|
||||
max_input_tokens: item.max_input_tokens ?? 6000,
|
||||
provider: provider,
|
||||
stream: true,
|
||||
@ -137,13 +75,12 @@ export const ConfigStore = types
|
||||
.filter((item) => item.category === "chat");
|
||||
self.modelsTemplate = models;
|
||||
};
|
||||
const updateSettle = (value: boolean) => {
|
||||
self.settle = value;
|
||||
};
|
||||
|
||||
return {
|
||||
setTemplate,
|
||||
updateSettle,
|
||||
updateSettle: (value: boolean) => {
|
||||
self.settle = value;
|
||||
},
|
||||
getDefaultModel: () => {
|
||||
return self.defaultModel;
|
||||
},
|
||||
@ -175,105 +112,48 @@ export const ConfigStore = types
|
||||
}
|
||||
return "";
|
||||
},
|
||||
updateConfig(server_config: any, server_config_base: any, user_config: any) {
|
||||
console.log("----->:::updateConfig");
|
||||
// 如果server_config没有获取到,那么直接返回
|
||||
if (!server_config || !server_config.models) {
|
||||
return [undefined, user_config];
|
||||
}
|
||||
if (server_config_base === undefined) {
|
||||
server_config_base = {};
|
||||
}
|
||||
if (server_config_base.models === undefined) {
|
||||
server_config_base.models = {};
|
||||
}
|
||||
const provider = self.provider;
|
||||
|
||||
// 将 server_config 转换为本地配置存储的格式
|
||||
const localConfig: any = {"models": {}};
|
||||
server_config.models.forEach((model: any) => {
|
||||
const modelConfig: any = {};
|
||||
for (const key in model) {
|
||||
if (key !== 'model') {
|
||||
modelConfig[key] = model[key];
|
||||
}
|
||||
}
|
||||
modelConfig["provider"] = provider;
|
||||
modelConfig["stream"] = true;
|
||||
localConfig["models"][model.model || model.id] = modelConfig;
|
||||
});
|
||||
|
||||
// 合并 config 部分,不假设具体的配置项名称
|
||||
for (const key in server_config.config) {
|
||||
localConfig[key] = server_config.config[key];
|
||||
}
|
||||
|
||||
// 使用子函数处理对比和更新
|
||||
const userConfigNew = this.compareConfigs(localConfig, server_config_base, user_config);
|
||||
|
||||
return [localConfig, userConfigNew];
|
||||
},
|
||||
compareConfigs(localConfig: any, baseConfig: any, userConfigIn: any) {
|
||||
let userConfig = { ...userConfigIn };
|
||||
|
||||
for (const key in localConfig) {
|
||||
if (baseConfig.hasOwnProperty(key) && userConfig.hasOwnProperty(key)) {
|
||||
// 递归比较对象的每个叶子结点
|
||||
if (typeof localConfig[key] === 'object' && !Array.isArray(localConfig[key]) && localConfig[key] !== null) {
|
||||
userConfig[key] = this.compareConfigs(localConfig[key], baseConfig[key], userConfig[key]);
|
||||
} else {
|
||||
if (localConfig[key] !== baseConfig[key]) {
|
||||
// 检查用户配置中是否存在该条目
|
||||
if (!userConfig[key] || JSON.stringify(userConfig[key]) === JSON.stringify(baseConfig[key])) {
|
||||
userConfig[key] = localConfig[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 新增的配置项
|
||||
if (!userConfig[key]) {
|
||||
if (!Object.isExtensible(userConfig)) {
|
||||
// 如果 userConfig 不可扩展,创建一个新的可扩展对象
|
||||
userConfig = { ...userConfig };
|
||||
}
|
||||
userConfig[key] = localConfig[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理删除的配置项,仅针对 models 下的 model
|
||||
if (localConfig.models ) {
|
||||
const localModels = localConfig.models;
|
||||
const userModels = userConfig.models;
|
||||
|
||||
for (const modelKey in userModels) {
|
||||
if (!localModels.hasOwnProperty(modelKey)) {
|
||||
// 删除的 model,从 userConfig 中移除
|
||||
delete userModels[modelKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
return userConfig;
|
||||
},
|
||||
setConfig: function (data) {
|
||||
this.setTemplate(data.models);
|
||||
this.updateSettle(false);
|
||||
const newConfig: any = deepCopy(data);
|
||||
let needUpdate = false;
|
||||
const newConfig = { ...data };
|
||||
newConfig.models = newConfig.models || {};
|
||||
newConfig.providers = newConfig.providers || {};
|
||||
if (!newConfig.providers.openai) {
|
||||
newConfig.providers.openai = {
|
||||
newConfig.providers.openai = newConfig.providers.openai || {
|
||||
api_key: "",
|
||||
api_base: "",
|
||||
};
|
||||
}
|
||||
if (!newConfig.providers.devchat) {
|
||||
newConfig.providers.devchat = {
|
||||
newConfig.providers.devchat = newConfig.providers.devchat || {
|
||||
api_key: "",
|
||||
api_base: "",
|
||||
};
|
||||
|
||||
self.modelsTemplate.forEach((item) => {
|
||||
const currentModel: any = {
|
||||
...item,
|
||||
};
|
||||
delete currentModel.name;
|
||||
|
||||
if (!newConfig.models[item.name]) {
|
||||
newConfig.models[item.name] = {
|
||||
...currentModel,
|
||||
};
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (newConfig.models[item.name].provider !== currentModel.provider) {
|
||||
needUpdate = true;
|
||||
newConfig.models[item.name].provider = currentModel.provider;
|
||||
}
|
||||
// 只有之前配置过 openai 的,provider 才可以是 openai
|
||||
if (
|
||||
newConfig.models[item.name].provider === "openai" &&
|
||||
!newConfig.providers.openai.api_key
|
||||
) {
|
||||
needUpdate = true;
|
||||
newConfig.models[item.name].provider = "devchat";
|
||||
}
|
||||
});
|
||||
|
||||
// 尝试获取 devchat 的 api_base
|
||||
self.provider = "devchat";
|
||||
self.modelsUrl = data?.providers?.devchat?.cumstom_api_base || data?.providers?.devchat?.api_base;
|
||||
@ -293,34 +173,41 @@ export const ConfigStore = types
|
||||
self.provider = "devchat";
|
||||
}
|
||||
|
||||
const modelsChat = self.modelsTemplate.filter(model => model.category === "chat");
|
||||
|
||||
if (modelsChat.length > 0 && modelsChat.find((item) => item.name === newConfig.default_model) === undefined) {
|
||||
const defaultModelName = 'qwen-72b-chat'
|
||||
newConfig.default_model = modelsChat.some(x => x.name === defaultModelName) ? defaultModelName : modelsChat[0].name;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (!defaultAPIBase.includes(newConfig.providers.devchat.api_base)) {
|
||||
newConfig.providers.devchat.cumstom_api_base =
|
||||
newConfig.providers.devchat.api_base;
|
||||
newConfig.providers.devchat.api_base = "custom";
|
||||
}
|
||||
if (this.checkAndSetCompletionDefaults(newConfig)) {
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
self.config = newConfig;
|
||||
self.defaultModel = newConfig.default_model;
|
||||
if (needUpdate) {
|
||||
this.writeConfig();
|
||||
}
|
||||
this.updateSettle(true);
|
||||
},
|
||||
fetchServerConfig: flow(function* (){
|
||||
refreshModelList: flow(function* (){
|
||||
try {
|
||||
const data = yield fetchServerConfigUtil({ modelsUrl: self.modelsUrl, devchatApiKey: self.devchatApiKey });
|
||||
if (data !== undefined) {
|
||||
MessageUtil.handleMessage({ command: "readServerConfig", value: data });
|
||||
} else {
|
||||
console.log("fetchLLMs error: Failed to fetch server config");
|
||||
MessageUtil.handleMessage({ command: "readServerConfig", value: undefined });
|
||||
if (self.modelsTemplate.length === 0) {
|
||||
const { data } = yield fetchLLMs({modelsUrl:self.modelsUrl,devchatApiKey:self.devchatApiKey});
|
||||
setTemplate(data,self.provider);
|
||||
MessageUtil.sendMessage({ command: "readConfig", key: "" });
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("fetchLLMs error:", e);
|
||||
MessageUtil.handleMessage({ command: "readServerConfig", value: undefined });
|
||||
}
|
||||
}),
|
||||
updateWorkflowList: flow(function* (){
|
||||
yield doUpdateWorkflowList();
|
||||
}),
|
||||
checkAndSetCompletionDefaults: (newConfig) => {
|
||||
const codeModels = self.modelsTemplate.filter(model => model.category === "code");
|
||||
const isCustomAPIBase = self.modelsUrl.indexOf("api.devchat.ai") === -1 && self.modelsUrl.indexOf("api.devchat-ai.cn") === -1;
|
||||
@ -357,6 +244,10 @@ export const ConfigStore = types
|
||||
value: writeConfig,
|
||||
key: "",
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
MessageUtil.sendMessage({ command: "readConfig", key: "" });
|
||||
}, 1000);
|
||||
},
|
||||
setConfigValue: function (key: string, value: any) {
|
||||
if (key === "default_model") {
|
||||
|
@ -9,6 +9,22 @@ export interface Item {
|
||||
recommend: number;
|
||||
}
|
||||
|
||||
const regContextMenus = async () => {
|
||||
return new Promise<Item[]>((resolve, reject) => {
|
||||
try {
|
||||
messageUtil.sendMessage({ command: "regContextList" });
|
||||
messageUtil.registerHandler(
|
||||
"regContextList",
|
||||
(message: { result: Item[] }) => {
|
||||
resolve(message.result);
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const ChatContext = types.model({
|
||||
file: types.maybe(types.string),
|
||||
path: types.maybe(types.string),
|
||||
@ -24,6 +40,13 @@ export const MenuItem = types.model({
|
||||
recommend: types.number,
|
||||
});
|
||||
|
||||
export const ContextMenuItem = types.model({
|
||||
icon: types.maybe(types.string),
|
||||
name: types.string,
|
||||
pattern: types.maybe(types.string),
|
||||
description: types.string,
|
||||
});
|
||||
|
||||
export const InputStore = types
|
||||
.model("Input", {
|
||||
value: "",
|
||||
@ -31,7 +54,8 @@ export const InputStore = types
|
||||
menuType: "contexts",
|
||||
menuOpend: false,
|
||||
currentMenuIndex: 0,
|
||||
commandMenus: types.array(MenuItem)
|
||||
commandMenus: types.array(MenuItem),
|
||||
contextMenus: types.array(ContextMenuItem)
|
||||
})
|
||||
.actions((self) => ({
|
||||
setValue(value: string) {
|
||||
@ -63,6 +87,12 @@ export const InputStore = types
|
||||
setCurrentMenuIndex(index: number) {
|
||||
self.currentMenuIndex = index;
|
||||
},
|
||||
fetchContextMenus: flow(function* () {
|
||||
try {
|
||||
const items = yield regContextMenus();
|
||||
self.contextMenus.push(...items);
|
||||
} catch (error) {}
|
||||
}),
|
||||
fetchCommandMenus: (items: Item[]) => {
|
||||
self.commandMenus.clear();
|
||||
self.commandMenus.push(...items);
|
||||
|
35
yarn.lock
35
yarn.lock
@ -4377,7 +4377,7 @@ glob@7.2.0:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.0.0, glob@^7.0.3, glob@^7.1.3, glob@^7.1.4:
|
||||
glob@^7.0.3, glob@^7.1.3, glob@^7.1.4:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
@ -4812,11 +4812,6 @@ inline-style-parser@0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
|
||||
integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==
|
||||
|
||||
interpret@^1.0.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
|
||||
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
|
||||
|
||||
interpret@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4"
|
||||
@ -6507,7 +6502,7 @@ minimatch@^5.0.1:
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimist@^1.2.3, minimist@^1.2.6:
|
||||
minimist@^1.2.6:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
@ -7518,13 +7513,6 @@ readdirp@~3.6.0:
|
||||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
rechoir@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
|
||||
integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==
|
||||
dependencies:
|
||||
resolve "^1.1.6"
|
||||
|
||||
rechoir@^0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22"
|
||||
@ -7675,7 +7663,7 @@ resolve.exports@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
|
||||
integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
|
||||
|
||||
resolve@^1.1.6, resolve@^1.11.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0:
|
||||
resolve@^1.11.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0:
|
||||
version "1.22.8"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
|
||||
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
|
||||
@ -7903,23 +7891,6 @@ shell-quote@^1.8.1:
|
||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680"
|
||||
integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==
|
||||
|
||||
shelljs@^0.8.5:
|
||||
version "0.8.5"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c"
|
||||
integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==
|
||||
dependencies:
|
||||
glob "^7.0.0"
|
||||
interpret "^1.0.0"
|
||||
rechoir "^0.6.2"
|
||||
|
||||
shx@^0.3.4:
|
||||
version "0.3.4"
|
||||
resolved "https://registry.yarnpkg.com/shx/-/shx-0.3.4.tgz#74289230b4b663979167f94e1935901406e40f02"
|
||||
integrity sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==
|
||||
dependencies:
|
||||
minimist "^1.2.3"
|
||||
shelljs "^0.8.5"
|
||||
|
||||
side-channel@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
|
||||
|
Loading…
x
Reference in New Issue
Block a user