diff --git a/adapter/chatgpt/chat.go b/adapter/chatgpt/chat.go
index 653615d..d71f2ce 100644
--- a/adapter/chatgpt/chat.go
+++ b/adapter/chatgpt/chat.go
@@ -122,10 +122,14 @@ func (c *ChatInstance) Test() bool {
return err == nil && len(result) > 0
}
-func FilterKeys(v string) string {
+func FilterKeys(v string) []string {
endpoint := viper.GetString(fmt.Sprintf("openai.%s.endpoint", v))
keys := strings.Split(viper.GetString(fmt.Sprintf("openai.%s.apikey", v)), "|")
+ return FilterKeysNative(endpoint, keys)
+}
+
+func FilterKeysNative(endpoint string, keys []string) []string {
stack := make(chan string, len(keys))
for _, key := range keys {
go func(key string) {
@@ -140,5 +144,5 @@ func FilterKeys(v string) string {
result = append(result, res)
}
}
- return strings.Join(result, "|")
+ return result
}
diff --git a/app/src/assets/markdown/all.less b/app/src/assets/markdown/all.less
index 666155e..94d9fe2 100644
--- a/app/src/assets/markdown/all.less
+++ b/app/src/assets/markdown/all.less
@@ -27,18 +27,54 @@
}
}
-.markdown-body pre code {
- color: #c9d1d9;
-}
+.markdown-body {
+ pre code {
+ color: #c9d1d9;
+ }
-.markdown-body pre.file-block,
-.markdown-body pre.file-block > div {
- padding: 0;
- margin: 0;
- background: none !important;
- box-shadow: none !important;
+ pre.file-block,
+ pre.file-block > div {
+ padding: 0;
+ margin: 0;
+ background: none !important;
+ box-shadow: none !important;
- &:before {
- content: none !important;
+ &:before {
+ content: none !important;
+ }
+ }
+
+ .markdown-syntax {
+ position: relative;
+
+ .markdown-syntax-header {
+ position: absolute;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ z-index: 1;
+ top: -30px;
+ right: 2px;
+ user-select: none;
+
+ p {
+ font-size: 12px;
+ line-height: 1;
+ margin: 0 0 0 6px;
+ padding: 0;
+ transform: translateY(-2px);
+ }
+
+ svg {
+ cursor: pointer;
+ color: hsl(var(--text-secondary));
+ transition: .2s;
+
+ &:hover {
+ color: hsl(var(--text));
+ }
+ }
+ }
}
}
diff --git a/app/src/components/Markdown.tsx b/app/src/components/Markdown.tsx
index 275cc9d..f3f705f 100644
--- a/app/src/components/Markdown.tsx
+++ b/app/src/components/Markdown.tsx
@@ -11,6 +11,10 @@ import { useDispatch } from "react-redux";
import { openDialog as openQuotaDialog } from "../store/quota.ts";
import { openDialog as openSubscriptionDialog } from "../store/subscription.ts";
import { AppDispatch } from "../store";
+import {Copy} from "lucide-react";
+import {copyClipboard} from "../utils.ts";
+import {useToast} from "./ui/use-toast.ts";
+import {useTranslation} from "react-i18next";
type MarkdownProps = {
children: string;
@@ -30,6 +34,9 @@ function doAction(dispatch: AppDispatch, url: string): boolean {
function Markdown({ children, className }: MarkdownProps) {
const dispatch = useDispatch();
+ const { t } = useTranslation();
+ const { toast } = useToast();
+
useEffect(() => {
document.querySelectorAll(".file-instance").forEach((el) => {
@@ -67,17 +74,27 @@ function Markdown({ children, className }: MarkdownProps) {
if (match && match[1] === "file")
return parseFile(children.toString());
return !inline && match ? (
-
{match[1]}
+
{children}
diff --git a/app/src/conf.ts b/app/src/conf.ts
index f686cd1..b56a972 100644
--- a/app/src/conf.ts
+++ b/app/src/conf.ts
@@ -1,7 +1,7 @@
import axios from "axios";
import { Model } from "./conversation/types.ts";
-export const version = "3.5.19";
+export const version = "3.5.20";
export const dev: boolean = window.location.hostname === "localhost";
export const deploy: boolean = true;
export let rest_api: string = "http://localhost:8094";
diff --git a/app/src/store/api.ts b/app/src/store/api.ts
index 5ad7fd8..fc0984e 100644
--- a/app/src/store/api.ts
+++ b/app/src/store/api.ts
@@ -22,6 +22,7 @@ export const apiSlice = createSlice({
state.dialog = false;
},
setKey: (state, action) => {
+ if (!action.payload.length) return;
state.key = action.payload as string;
},
},
diff --git a/cli/exec.go b/cli/exec.go
index 6a33a7a..b791c7a 100644
--- a/cli/exec.go
+++ b/cli/exec.go
@@ -13,6 +13,9 @@ func Run() bool {
case "invite":
CreateInvitationCommand(args[1:])
return true
+ case "filter":
+ FilterApiKeyCommand(args[1:])
+ return true
default:
return false
}
diff --git a/cli/filter.go b/cli/filter.go
new file mode 100644
index 0000000..4b5a433
--- /dev/null
+++ b/cli/filter.go
@@ -0,0 +1,18 @@
+package cli
+
+import (
+ "chat/adapter/chatgpt"
+ "fmt"
+ "github.com/spf13/viper"
+ "strings"
+)
+
+func FilterApiKeyCommand(args []string) {
+ data := strings.Trim(strings.TrimSpace(GetArgString(args, 0)), "\"")
+ endpoint := viper.GetString("openai.test")
+ keys := strings.Split(data, "|")
+
+ available := chatgpt.FilterKeysNative(endpoint, keys)
+ fmt.Println(fmt.Sprintf("[cli] filtered %d keys, %d available, %d unavailable", len(keys), len(available), len(keys)-len(available)))
+ fmt.Println(strings.Join(available, "|"))
+}