mirror of
https://github.com/coaidev/coai.git
synced 2025-05-20 13:30:13 +09:00
add markdown code copy feature
This commit is contained in:
parent
cf67af6df4
commit
83213a60fb
@ -122,10 +122,14 @@ func (c *ChatInstance) Test() bool {
|
|||||||
return err == nil && len(result) > 0
|
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))
|
endpoint := viper.GetString(fmt.Sprintf("openai.%s.endpoint", v))
|
||||||
keys := strings.Split(viper.GetString(fmt.Sprintf("openai.%s.apikey", 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))
|
stack := make(chan string, len(keys))
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
go func(key string) {
|
go func(key string) {
|
||||||
@ -140,5 +144,5 @@ func FilterKeys(v string) string {
|
|||||||
result = append(result, res)
|
result = append(result, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return strings.Join(result, "|")
|
return result
|
||||||
}
|
}
|
||||||
|
@ -27,12 +27,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body pre code {
|
.markdown-body {
|
||||||
|
pre code {
|
||||||
color: #c9d1d9;
|
color: #c9d1d9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body pre.file-block,
|
pre.file-block,
|
||||||
.markdown-body pre.file-block > div {
|
pre.file-block > div {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: none !important;
|
background: none !important;
|
||||||
@ -41,4 +42,39 @@
|
|||||||
&:before {
|
&:before {
|
||||||
content: none !important;
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@ import { useDispatch } from "react-redux";
|
|||||||
import { openDialog as openQuotaDialog } from "../store/quota.ts";
|
import { openDialog as openQuotaDialog } from "../store/quota.ts";
|
||||||
import { openDialog as openSubscriptionDialog } from "../store/subscription.ts";
|
import { openDialog as openSubscriptionDialog } from "../store/subscription.ts";
|
||||||
import { AppDispatch } from "../store";
|
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 = {
|
type MarkdownProps = {
|
||||||
children: string;
|
children: string;
|
||||||
@ -30,6 +34,9 @@ function doAction(dispatch: AppDispatch, url: string): boolean {
|
|||||||
|
|
||||||
function Markdown({ children, className }: MarkdownProps) {
|
function Markdown({ children, className }: MarkdownProps) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.querySelectorAll(".file-instance").forEach((el) => {
|
document.querySelectorAll(".file-instance").forEach((el) => {
|
||||||
@ -67,6 +74,16 @@ function Markdown({ children, className }: MarkdownProps) {
|
|||||||
if (match && match[1] === "file")
|
if (match && match[1] === "file")
|
||||||
return parseFile(children.toString());
|
return parseFile(children.toString());
|
||||||
return !inline && match ? (
|
return !inline && match ? (
|
||||||
|
<div className={`markdown-syntax`}>
|
||||||
|
<div className={`markdown-syntax-header`}>
|
||||||
|
<Copy className={`h-3 w-3`} onClick={async () => {
|
||||||
|
await copyClipboard(children.toString());
|
||||||
|
toast({
|
||||||
|
title: t("share.copied"),
|
||||||
|
});
|
||||||
|
}} />
|
||||||
|
<p>{match[1]}</p>
|
||||||
|
</div>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
{...props}
|
{...props}
|
||||||
children={String(children).replace(/\n$/, "")}
|
children={String(children).replace(/\n$/, "")}
|
||||||
@ -76,8 +93,8 @@ function Markdown({ children, className }: MarkdownProps) {
|
|||||||
wrapLongLines={true}
|
wrapLongLines={true}
|
||||||
wrapLines={true}
|
wrapLines={true}
|
||||||
className={`code-block`}
|
className={`code-block`}
|
||||||
lang={match[1]}
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<code className={`code-inline ${className}`} {...props}>
|
<code className={`code-inline ${className}`} {...props}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { Model } from "./conversation/types.ts";
|
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 dev: boolean = window.location.hostname === "localhost";
|
||||||
export const deploy: boolean = true;
|
export const deploy: boolean = true;
|
||||||
export let rest_api: string = "http://localhost:8094";
|
export let rest_api: string = "http://localhost:8094";
|
||||||
|
@ -22,6 +22,7 @@ export const apiSlice = createSlice({
|
|||||||
state.dialog = false;
|
state.dialog = false;
|
||||||
},
|
},
|
||||||
setKey: (state, action) => {
|
setKey: (state, action) => {
|
||||||
|
if (!action.payload.length) return;
|
||||||
state.key = action.payload as string;
|
state.key = action.payload as string;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -13,6 +13,9 @@ func Run() bool {
|
|||||||
case "invite":
|
case "invite":
|
||||||
CreateInvitationCommand(args[1:])
|
CreateInvitationCommand(args[1:])
|
||||||
return true
|
return true
|
||||||
|
case "filter":
|
||||||
|
FilterApiKeyCommand(args[1:])
|
||||||
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
18
cli/filter.go
Normal file
18
cli/filter.go
Normal file
@ -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, "|"))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user