mirror of
https://github.com/coaidev/coai.git
synced 2025-05-19 21:10:18 +09:00
add copy selection text feature
This commit is contained in:
parent
67b574033b
commit
0014ce0818
@ -8,7 +8,7 @@ import {
|
||||
Loader2,
|
||||
MousePointerSquare,
|
||||
Power,
|
||||
RotateCcw,
|
||||
RotateCcw, ScanText,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
ContextMenu,
|
||||
@ -18,7 +18,7 @@ import {
|
||||
} from "./ui/context-menu.tsx";
|
||||
import {
|
||||
copyClipboard,
|
||||
filterMessage,
|
||||
filterMessage, getSelectionTextInArea,
|
||||
saveAsFile,
|
||||
useInputValue,
|
||||
} from "../utils.ts";
|
||||
@ -29,21 +29,29 @@ import {
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "./ui/tooltip.tsx";
|
||||
import {Ref, useRef, useState} from "react";
|
||||
|
||||
type MessageProps = {
|
||||
message: Message;
|
||||
end?: boolean;
|
||||
onEvent?: (event: string) => void;
|
||||
ref?: Ref<HTMLElement>;
|
||||
};
|
||||
|
||||
function MessageSegment(props: MessageProps) {
|
||||
const [ copied, setCopied ] = useState<string>("");
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef(null);
|
||||
const { message } = props;
|
||||
|
||||
function updateSelection(): void {
|
||||
if (ref.current) setCopied(getSelectionTextInArea(ref.current));
|
||||
}
|
||||
|
||||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu onOpenChange={updateSelection}>
|
||||
<ContextMenuTrigger asChild>
|
||||
<div className={`message ${message.role}`}>
|
||||
<div className={`message ${message.role}`} ref={ref}>
|
||||
<MessageContent {...props} />
|
||||
{message.quota && message.quota !== 0 ? (
|
||||
<TooltipProvider>
|
||||
@ -66,6 +74,15 @@ function MessageSegment(props: MessageProps) {
|
||||
</div>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
{
|
||||
copied.length > 0 && (
|
||||
<ContextMenuItem
|
||||
onClick={() => copyClipboard(copied)}
|
||||
>
|
||||
<ScanText className={`h-4 w-4 mr-2`} /> {t("message.copy-area")}
|
||||
</ContextMenuItem>
|
||||
)
|
||||
}
|
||||
<ContextMenuItem
|
||||
onClick={() => copyClipboard(filterMessage(message.content))}
|
||||
>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import axios from "axios";
|
||||
import { Model } from "./conversation/types.ts";
|
||||
|
||||
export const version = "3.5.18";
|
||||
export const version = "3.5.19";
|
||||
export const dev: boolean = window.location.hostname === "localhost";
|
||||
export const deploy: boolean = true;
|
||||
export let rest_api: string = "http://localhost:8094";
|
||||
|
@ -59,6 +59,7 @@ const resources = {
|
||||
copy: "Copy",
|
||||
save: "Save as File",
|
||||
use: "Use Message",
|
||||
"copy-area": "Copy Selected Area",
|
||||
},
|
||||
"quota-description": "spending quota for the message",
|
||||
buy: {
|
||||
@ -261,6 +262,7 @@ const resources = {
|
||||
copy: "复制",
|
||||
save: "保存为文件",
|
||||
use: "使用消息",
|
||||
"copy-area": "复制选中区域",
|
||||
},
|
||||
"quota-description": "消息的配额支出",
|
||||
buy: {
|
||||
@ -464,6 +466,7 @@ const resources = {
|
||||
copy: "Копировать",
|
||||
save: "Сохранить как файл",
|
||||
use: "Использовать сообщение",
|
||||
"copy-area": "Копировать выбранную область",
|
||||
},
|
||||
"quota-description": "квота расходов на сообщение",
|
||||
buy: {
|
||||
|
@ -270,3 +270,15 @@ export function getSelectionText(): string {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// browser compatibility issue
|
||||
export function getSelectionTextInArea(el: HTMLElement): string {
|
||||
const selection = window.getSelection();
|
||||
if (!selection) return "";
|
||||
const range = selection.getRangeAt(0);
|
||||
const preSelectionRange = range.cloneRange();
|
||||
preSelectionRange.selectNodeContents(el);
|
||||
preSelectionRange.setEnd(range.startContainer, range.startOffset);
|
||||
const start = preSelectionRange.toString().length;
|
||||
return el.innerText.slice(start, start + range.toString().length);
|
||||
}
|
||||
|
@ -20,9 +20,13 @@ func GetErrorQuota(model string) float32 {
|
||||
return utils.Multi[float32](globals.IsGPT4Model(model), -0xe, 0) // special value for error
|
||||
}
|
||||
|
||||
func CollectQuota(c *gin.Context, user *auth.User, quota float32, reversible bool) {
|
||||
func CollectQuota(c *gin.Context, user *auth.User, buffer *utils.Buffer, uncountable bool) {
|
||||
db := utils.GetDBFromContext(c)
|
||||
if !reversible && quota > 0 && user != nil {
|
||||
quota := buffer.GetQuota()
|
||||
if buffer.IsEmpty() {
|
||||
return
|
||||
}
|
||||
if !uncountable && quota > 0 && user != nil {
|
||||
user.UseQuota(db, quota)
|
||||
}
|
||||
}
|
||||
@ -130,7 +134,7 @@ func ChatHandler(conn *Connection, user *auth.User, instance *conversation.Conve
|
||||
if err != nil && err.Error() != "signal" {
|
||||
globals.Warn(fmt.Sprintf("caught error from chat handler: %s (instance: %s, client: %s)", err, model, conn.GetCtx().ClientIP()))
|
||||
|
||||
CollectQuota(conn.GetCtx(), user, buffer.GetQuota(), plan)
|
||||
CollectQuota(conn.GetCtx(), user, buffer, plan)
|
||||
conn.Send(globals.ChatSegmentResponse{
|
||||
Message: err.Error(),
|
||||
Quota: GetErrorQuota(model),
|
||||
@ -139,7 +143,7 @@ func ChatHandler(conn *Connection, user *auth.User, instance *conversation.Conve
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
CollectQuota(conn.GetCtx(), user, buffer.GetQuota(), plan)
|
||||
CollectQuota(conn.GetCtx(), user, buffer, plan)
|
||||
|
||||
if buffer.IsEmpty() {
|
||||
conn.Send(globals.ChatSegmentResponse{
|
||||
|
@ -46,11 +46,11 @@ func NativeChatHandler(c *gin.Context, user *auth.User, model string, message []
|
||||
buffer.Write(resp)
|
||||
return nil
|
||||
}); err != nil {
|
||||
CollectQuota(c, user, buffer.GetQuota(), plan)
|
||||
CollectQuota(c, user, buffer, plan)
|
||||
return keyword, err.Error(), GetErrorQuota(model)
|
||||
}
|
||||
|
||||
CollectQuota(c, user, buffer.GetQuota(), plan)
|
||||
CollectQuota(c, user, buffer, plan)
|
||||
|
||||
SaveCacheData(c, &CacheProps{
|
||||
Message: segment,
|
||||
|
@ -125,7 +125,7 @@ func sendTranshipmentResponse(c *gin.Context, form TranshipmentForm, id string,
|
||||
globals.Warn(fmt.Sprintf("error from chat request api: %s", err.Error()))
|
||||
}
|
||||
|
||||
CollectQuota(c, user, buffer.GetQuota(), plan)
|
||||
CollectQuota(c, user, buffer, plan)
|
||||
c.JSON(http.StatusOK, TranshipmentResponse{
|
||||
Id: id,
|
||||
Object: "chat.completion",
|
||||
@ -186,13 +186,13 @@ func sendStreamTranshipmentResponse(c *gin.Context, form TranshipmentForm, id st
|
||||
return nil
|
||||
}); err != nil {
|
||||
channel <- getStreamTranshipmentForm(id, created, form, fmt.Sprintf("Error: %s", err.Error()), buffer, true)
|
||||
CollectQuota(c, user, buffer.GetQuota(), plan)
|
||||
CollectQuota(c, user, buffer, plan)
|
||||
close(channel)
|
||||
return
|
||||
}
|
||||
|
||||
channel <- getStreamTranshipmentForm(id, created, form, "", buffer, true)
|
||||
CollectQuota(c, user, buffer.GetQuota(), plan)
|
||||
CollectQuota(c, user, buffer, plan)
|
||||
close(channel)
|
||||
return
|
||||
}()
|
||||
|
Loading…
Reference in New Issue
Block a user