commit
a01e04ad81
111
src/util/APIUtil.ts
Normal file
111
src/util/APIUtil.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
class APIUtil {
|
||||||
|
private static instance: APIUtil;
|
||||||
|
private baseUrl: string | undefined;
|
||||||
|
private accessKey: string | undefined;
|
||||||
|
private webappUrl: string | undefined;
|
||||||
|
private currentMessageId: string | undefined;
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
console.log("APIUtil ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): APIUtil {
|
||||||
|
if (!APIUtil.instance) {
|
||||||
|
APIUtil.instance = new APIUtil();
|
||||||
|
}
|
||||||
|
return APIUtil.instance;
|
||||||
|
}
|
||||||
|
async fetchWebappUrl() {
|
||||||
|
try {
|
||||||
|
const res = await axios.get(
|
||||||
|
`${this.baseUrl}/addresses/webapp`,
|
||||||
|
{ headers: { 'Authorization': `Bearer ${this.accessKey}` }}
|
||||||
|
)
|
||||||
|
const urlOrPath = res?.data;
|
||||||
|
if (!urlOrPath) {
|
||||||
|
throw new Error("No webapp url found");
|
||||||
|
}
|
||||||
|
let href = "";
|
||||||
|
if (urlOrPath.startsWith("http://") || urlOrPath.startsWith("https://")) {
|
||||||
|
href = urlOrPath;
|
||||||
|
} else {
|
||||||
|
href = new URL(urlOrPath, this.baseUrl).href
|
||||||
|
}
|
||||||
|
if (href.endsWith('/')) {
|
||||||
|
href = href.slice(0, -1);
|
||||||
|
}
|
||||||
|
if (href.endsWith('/api')) {
|
||||||
|
href = href.slice(0, -4);
|
||||||
|
}
|
||||||
|
console.log('Webapp url: ', href)
|
||||||
|
return href;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error fetch webapp url:", err);
|
||||||
|
return "https://app.devchat.ai";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config(baseUrl: string, accessKey: string) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
this.accessKey = accessKey;
|
||||||
|
if (!this.webappUrl) {
|
||||||
|
this.fetchWebappUrl().then(url => {
|
||||||
|
this.webappUrl = url;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createMessage(message: object) {
|
||||||
|
this.currentMessageId = `msg-${uuidv4()}`;
|
||||||
|
try {
|
||||||
|
const res = await axios.post(
|
||||||
|
`${this.webappUrl}/api/v1/messages`,
|
||||||
|
{...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: object) {
|
||||||
|
try {
|
||||||
|
const res = await axios.post(
|
||||||
|
`${this.webappUrl}/api/v1/messages/${this.currentMessageId}/events`,
|
||||||
|
event,
|
||||||
|
{headers: {
|
||||||
|
Authorization: `Bearer ${this.accessKey}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
console.log("Event created: ", res?.data);
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBalance() {
|
||||||
|
try {
|
||||||
|
if (!this.webappUrl) this.webappUrl = await this.fetchWebappUrl();
|
||||||
|
const res = await axios.get(
|
||||||
|
`${this.webappUrl}/api/v1/users/profile`,
|
||||||
|
{headers: { Authorization: `Bearer ${this.accessKey}` }}
|
||||||
|
)
|
||||||
|
return res?.data?.organization
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default APIUtil.getInstance();
|
@ -329,6 +329,9 @@ class IdeaBridge {
|
|||||||
case "updateSetting/response":
|
case "updateSetting/response":
|
||||||
this.resviceUpdateSetting(res);
|
this.resviceUpdateSetting(res);
|
||||||
break;
|
break;
|
||||||
|
case "codeDiffApply/response":
|
||||||
|
this.resviceCodeDiffApply(res);
|
||||||
|
break;
|
||||||
case "sendUserMessage/response":
|
case "sendUserMessage/response":
|
||||||
this.resviceSendUserMessage(res);
|
this.resviceSendUserMessage(res);
|
||||||
break;
|
break;
|
||||||
@ -377,6 +380,10 @@ class IdeaBridge {
|
|||||||
this.executeHandlers("updateSetting", res.payload);
|
this.executeHandlers("updateSetting", res.payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resviceCodeDiffApply(res) {
|
||||||
|
this.executeHandlers("codeDiffApply", res.payload);
|
||||||
|
}
|
||||||
|
|
||||||
resviceSendUserMessage(res) {
|
resviceSendUserMessage(res) {
|
||||||
this.executeHandlers("chatWithDevChat", {
|
this.executeHandlers("chatWithDevChat", {
|
||||||
command: "chatWithDevChat",
|
command: "chatWithDevChat",
|
||||||
|
@ -8,6 +8,7 @@ import { useMst } from "./stores/RootStore";
|
|||||||
import MessageUtil from "@/util/MessageUtil";
|
import MessageUtil from "@/util/MessageUtil";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
import "./i18n";
|
import "./i18n";
|
||||||
|
import APIUtil from "@/util/APIUtil";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [ready, setReady] = useState(false);
|
const [ready, setReady] = useState(false);
|
||||||
@ -28,6 +29,7 @@ export default function App() {
|
|||||||
MessageUtil.registerHandler("readConfig", (data: { value: any }) => {
|
MessageUtil.registerHandler("readConfig", (data: { value: any }) => {
|
||||||
console.log("readConfig registerHandler: ", data);
|
console.log("readConfig registerHandler: ", data);
|
||||||
config.setConfig(data.value);
|
config.setConfig(data.value);
|
||||||
|
APIUtil.config(config.getAPIBase(), config.getUserKey())
|
||||||
config.refreshModelList();
|
config.refreshModelList();
|
||||||
setReady(true);
|
setReady(true);
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import axios from "axios";
|
|
||||||
import messageUtil from "@/util/MessageUtil";
|
import messageUtil from "@/util/MessageUtil";
|
||||||
import { IconWallet } from "@tabler/icons-react";
|
import { IconWallet } from "@tabler/icons-react";
|
||||||
import {
|
import {
|
||||||
@ -11,6 +10,7 @@ import {
|
|||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { Trans } from "react-i18next";
|
import { Trans } from "react-i18next";
|
||||||
import { useMst } from "@/views/stores/RootStore";
|
import { useMst } from "@/views/stores/RootStore";
|
||||||
|
import APIUtil from "@/util/APIUtil";
|
||||||
|
|
||||||
const currencyMap = {
|
const currencyMap = {
|
||||||
USD: "$",
|
USD: "$",
|
||||||
@ -28,15 +28,9 @@ function formatCurrency(balance: number | null, currency: string) {
|
|||||||
return `${currencyMap[currency] || currency}${balance}`;
|
return `${currencyMap[currency] || currency}${balance}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const envMap = {
|
const links = {
|
||||||
dev: {
|
dev: "https://webtest.devchat.ai",
|
||||||
requestUrl: "https://apptest.devchat.ai",
|
prod: "https://web.devchat.ai",
|
||||||
link: "https://webtest.devchat.ai",
|
|
||||||
},
|
|
||||||
prod: {
|
|
||||||
requestUrl: "https://app.devchat.ai",
|
|
||||||
link: "https://web.devchat.ai",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function WechatTip() {
|
export default function WechatTip() {
|
||||||
@ -50,23 +44,13 @@ export default function WechatTip() {
|
|||||||
const platform = process.env.platform;
|
const platform = process.env.platform;
|
||||||
|
|
||||||
const getBalance = () => {
|
const getBalance = () => {
|
||||||
if (!envMap[env].requestUrl || !accessKey) {
|
APIUtil.getBalance().then(org => {
|
||||||
return;
|
setLoading(true);
|
||||||
}
|
setBalance(formatBalance(org?.balance));
|
||||||
setLoading(true);
|
setCurrency(org?.currency);
|
||||||
axios
|
}).finally(() => {
|
||||||
.get(`${envMap[env].requestUrl}/api/v1/users/profile`, {
|
setLoading(false);
|
||||||
headers: { Authorization: `Bearer ${accessKey}` },
|
})
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res?.data?.organization?.balance) {
|
|
||||||
setBalance(formatBalance(res?.data?.organization?.balance));
|
|
||||||
setCurrency(res?.data?.organization?.currency);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -93,7 +77,7 @@ export default function WechatTip() {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
messageUtil.sendMessage({
|
messageUtil.sendMessage({
|
||||||
command: "openLink",
|
command: "openLink",
|
||||||
url: envMap[env].link,
|
url: links[env],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -139,7 +123,7 @@ export default function WechatTip() {
|
|||||||
web.devchat.ai{" "}
|
web.devchat.ai{" "}
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<a href={envMap[env].link} target="_blank">
|
<a href={links[env]} target="_blank">
|
||||||
web.devchat.ai{" "}
|
web.devchat.ai{" "}
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { Tooltip, ActionIcon, CopyButton, Flex } from "@mantine/core";
|
import { Tooltip, ActionIcon, CopyButton, Flex } from "@mantine/core";
|
||||||
import { IconCheck, IconGitCommit, IconFileDiff, 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 React, { useState } from "react";
|
||||||
|
import { useMst } from "@/views/stores/RootStore";
|
||||||
|
|
||||||
import messageUtil from '@/util/MessageUtil';
|
import messageUtil from '@/util/MessageUtil';
|
||||||
import language from "react-syntax-highlighter/dist/esm/languages/hljs/1c";
|
import APIUtil from "@/util/APIUtil";
|
||||||
|
|
||||||
const IconButton = ({ label, color = 'gray', onClick, children }) => (
|
const IconButton = ({ label, color = 'gray', onClick, children }) => (
|
||||||
<Tooltip sx={{ padding: '3px', fontSize: 'var(--vscode-editor-font-size)' }} label={label} withArrow position="left" color="gray">
|
<Tooltip sx={{ padding: '3px', fontSize: 'var(--vscode-editor-font-size)' }} label={label} withArrow position="left" color="gray">
|
||||||
@ -31,10 +32,14 @@ const CommitButton = ({ code }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CodeCopyButton = ({ code }) => {
|
const CodeCopyButton = ({ code }) => {
|
||||||
|
const {config} = useMst();
|
||||||
return (
|
return (
|
||||||
<CopyButton value={code} timeout={2000}>
|
<CopyButton value={code} timeout={2000}>
|
||||||
{({ copied, copy }) => (
|
{({ copied, copy }) => (
|
||||||
<IconButton label={copied ? 'Copied' : 'Copy'} color={copied ? 'teal' : 'gray'} onClick={copy}>
|
<IconButton label={copied ? 'Copied' : 'Copy'} color={copied ? 'teal' : 'gray'} onClick={() => {
|
||||||
|
copy();
|
||||||
|
APIUtil.createEvent({name: 'copy', value: 'copy'})
|
||||||
|
}}>
|
||||||
{copied ? <IconCheck size="1rem" /> : <IconCopy size="1rem" />}
|
{copied ? <IconCheck size="1rem" /> : <IconCopy size="1rem" />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
@ -43,11 +48,14 @@ const CodeCopyButton = ({ code }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DiffButton = ({ code }) => {
|
const DiffButton = ({ code }) => {
|
||||||
|
const {config} = useMst();
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
const e = 'show_diff';
|
||||||
messageUtil.sendMessage({
|
messageUtil.sendMessage({
|
||||||
command: 'show_diff',
|
command: e,
|
||||||
content: code
|
content: code
|
||||||
});
|
});
|
||||||
|
APIUtil.createEvent({name: e, value: e})
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<IconButton label='View Diff' onClick={handleClick}>
|
<IconButton label='View Diff' onClick={handleClick}>
|
||||||
@ -57,11 +65,14 @@ const DiffButton = ({ code }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CodeApplyButton = ({ code }) => {
|
const CodeApplyButton = ({ code }) => {
|
||||||
|
const {config} = useMst();
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
const e = 'code_apply';
|
||||||
messageUtil.sendMessage({
|
messageUtil.sendMessage({
|
||||||
command: 'code_apply',
|
command: e,
|
||||||
content: code
|
content: code
|
||||||
});
|
});
|
||||||
|
APIUtil.createEvent({name: e, value: e})
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<IconButton label='Insert Code' onClick={handleClick}>
|
<IconButton label='Insert Code' onClick={handleClick}>
|
||||||
@ -71,11 +82,14 @@ const CodeApplyButton = ({ code }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const FileApplyButton = ({ code }) => {
|
const FileApplyButton = ({ code }) => {
|
||||||
|
const {config} = useMst();
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
const e = 'code_file_apply';
|
||||||
messageUtil.sendMessage({
|
messageUtil.sendMessage({
|
||||||
command: 'code_file_apply',
|
command: e,
|
||||||
content: code
|
content: code
|
||||||
});
|
});
|
||||||
|
APIUtil.createEvent({name: e, value: e})
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<IconButton label='Replace File' onClick={handleClick}>
|
<IconButton label='Replace File' onClick={handleClick}>
|
||||||
@ -86,12 +100,15 @@ const FileApplyButton = ({ code }) => {
|
|||||||
|
|
||||||
// Add a new button to create new file
|
// Add a new button to create new file
|
||||||
const NewFileButton = ({ language,code }) => {
|
const NewFileButton = ({ language,code }) => {
|
||||||
|
const {config} = useMst();
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
const e = 'code_new_file';
|
||||||
messageUtil.sendMessage({
|
messageUtil.sendMessage({
|
||||||
command: 'code_new_file',
|
command: e,
|
||||||
language: language,
|
language: language,
|
||||||
content: code
|
content: code
|
||||||
});
|
});
|
||||||
|
APIUtil.createEvent({name: e, value: e})
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<IconButton label='Create New File' onClick={handleClick}>
|
<IconButton label='Create New File' onClick={handleClick}>
|
||||||
|
@ -17,6 +17,7 @@ import { useSetState } from "@mantine/hooks";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useRouter } from "@/views/router";
|
import { useRouter } from "@/views/router";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
|
import APIUtil from "@/util/APIUtil";
|
||||||
|
|
||||||
(typeof global !== "undefined" ? global : window).Prism = Prism;
|
(typeof global !== "undefined" ? global : window).Prism = Prism;
|
||||||
require("prismjs/components/prism-java");
|
require("prismjs/components/prism-java");
|
||||||
@ -70,7 +71,7 @@ function parseMetaData(string) {
|
|||||||
|
|
||||||
const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
||||||
const { children, activeStep = false, messageDone } = props;
|
const { children, activeStep = false, messageDone } = props;
|
||||||
const { chat } = useMst();
|
const { config, chat } = useMst();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [steps, setSteps] = useState<Step[]>([]);
|
const [steps, setSteps] = useState<Step[]>([]);
|
||||||
const tree = fromMarkdown(children);
|
const tree = fromMarkdown(children);
|
||||||
@ -107,6 +108,13 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCodeCopy = (event) => {
|
||||||
|
const selection = window.getSelection()?.toString();
|
||||||
|
console.log("Copied: ", selection);
|
||||||
|
const e = 'manual_copy';
|
||||||
|
APIUtil.createEvent({name: e, value: selection})
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let previousNode: any = null;
|
let previousNode: any = null;
|
||||||
let chatmarkCount = 0;
|
let chatmarkCount = 0;
|
||||||
@ -328,6 +336,7 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
|
|||||||
whiteSpace: "pre",
|
whiteSpace: "pre",
|
||||||
...props.style,
|
...props.style,
|
||||||
}}
|
}}
|
||||||
|
onCopy={handleCodeCopy}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{tokens.map((line, i) => (
|
{tokens.map((line, i) => (
|
||||||
|
@ -5,6 +5,7 @@ import yaml from "js-yaml";
|
|||||||
import { RootInstance } from "./RootStore";
|
import { RootInstance } from "./RootStore";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import APIUtil from "@/util/APIUtil";
|
||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
content: string;
|
content: string;
|
||||||
@ -171,7 +172,12 @@ export const ChatStore = types
|
|||||||
self.hasDone = false;
|
self.hasDone = false;
|
||||||
self.errorMessage = "";
|
self.errorMessage = "";
|
||||||
self.currentMessage = "";
|
self.currentMessage = "";
|
||||||
const chatModel = getParent<RootInstance>(self).config.getDefaultModel();
|
const config = getParent<RootInstance>(self).config
|
||||||
|
const chatModel = config.getDefaultModel();
|
||||||
|
messageUtil.registerHandler("codeDiffApply", (_: any) => {
|
||||||
|
const e = 'code_diff_apply'
|
||||||
|
APIUtil.createEvent({name: e, value: e})
|
||||||
|
})
|
||||||
messageUtil.sendMessage({
|
messageUtil.sendMessage({
|
||||||
command: "sendMessage",
|
command: "sendMessage",
|
||||||
text: text,
|
text: text,
|
||||||
@ -179,6 +185,7 @@ export const ChatStore = types
|
|||||||
parent_hash: lastNonEmptyHash(),
|
parent_hash: lastNonEmptyHash(),
|
||||||
model: chatModel,
|
model: chatModel,
|
||||||
});
|
});
|
||||||
|
APIUtil.createMessage({content: text, model: chatModel});
|
||||||
};
|
};
|
||||||
|
|
||||||
const helpMessage = (originalMessage = false) => {
|
const helpMessage = (originalMessage = false) => {
|
||||||
@ -390,7 +397,7 @@ Thinking...
|
|||||||
self.messages.push(...messages);
|
self.messages.push(...messages);
|
||||||
},
|
},
|
||||||
updateLastMessage: (message: string) => {
|
updateLastMessage: (message: string) => {
|
||||||
console.log("message: ", message);
|
// console.log("message: ", message);
|
||||||
if (self.messages.length > 0) {
|
if (self.messages.length > 0) {
|
||||||
self.messages[self.messages.length - 1].message = message;
|
self.messages[self.messages.length - 1].message = message;
|
||||||
// if (message === "") {
|
// if (message === "") {
|
||||||
|
@ -2,7 +2,6 @@ import MessageUtil from "@/util/MessageUtil";
|
|||||||
import { types, Instance, flow } from "mobx-state-tree";
|
import { types, Instance, flow } from "mobx-state-tree";
|
||||||
import modelsTemplate from "@/models";
|
import modelsTemplate from "@/models";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import { set } from "mobx";
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
const defaultAPIBase = [
|
const defaultAPIBase = [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user