feat: support markdown/html on site notify and fix preset issues

This commit is contained in:
Zhang Minghan 2024-03-09 23:30:19 +08:00
parent 6bf12df847
commit 80fd32add3
15 changed files with 93 additions and 29 deletions

View File

@ -67,7 +67,7 @@ func (c *ChatInstance) CreateRequest(props *ChatProps) *api.ChatReq {
}
func getChoice(choice *api.ChatResp) *globals.Chunk {
if choice == nil {
if choice == nil || choice.Choice == nil || choice.Choice.Message == nil {
return &globals.Chunk{Content: ""}
}
@ -79,7 +79,7 @@ func getChoice(choice *api.ChatResp) *globals.Chunk {
ToolCall: utils.Multi(calls != nil, &globals.ToolCalls{
globals.ToolCall{
Type: "function",
Id: globals.ToolCallId(fmt.Sprintf("%s-%s", calls.Name, choice.ReqId)),
Id: fmt.Sprintf("%s-%s", calls.Name, choice.ReqId),
Function: globals.ToolCallFunction{
Name: calls.Name,
Arguments: calls.Arguments,

View File

@ -79,7 +79,7 @@ func getChoice(form *ChatResponse) *globals.Chunk {
ToolCall: utils.Multi(choice.FunctionCall != nil, &globals.ToolCalls{
globals.ToolCall{
Type: "function",
Id: globals.ToolCallId(fmt.Sprintf("%s-%s", choice.FunctionCall.Name, choice.FunctionCall.Arguments)),
Id: fmt.Sprintf("%s-%s", choice.FunctionCall.Name, choice.FunctionCall.Arguments),
Function: globals.ToolCallFunction{
Name: choice.FunctionCall.Name,
Arguments: choice.FunctionCall.Arguments,

View File

@ -8,7 +8,7 @@
},
"package": {
"productName": "chatnio",
"version": "3.10.4"
"version": "3.10.5"
},
"tauri": {
"allowlist": {

View File

@ -363,3 +363,7 @@ input[type="number"] {
animation: fadeIn 0.5s forwards;
}
.text-common {
color: hsl(var(--text)) !important;
}

View File

@ -4,6 +4,7 @@ import { useToast } from "@/components/ui/use-toast.ts";
import { useTranslation } from "react-i18next";
import { useEffectAsync } from "@/utils/hook.ts";
import { getBroadcast } from "@/api/broadcast.ts";
import Markdown from "@/components/Markdown.tsx";
function Broadcast() {
const { t } = useTranslation();
@ -17,8 +18,12 @@ function Broadcast() {
toast({
title: t("broadcast"),
description: content,
duration: 10000,
description: (
<Markdown className={`text-common`} acceptHtml>
{content}
</Markdown>
),
duration: 30000,
});
}, [init]);

View File

@ -23,6 +23,8 @@ type RichEditorProps = {
onChange: (value: string) => void;
maxLength?: number;
title?: string;
open?: boolean;
setOpen?: (open: boolean) => void;
children?: React.ReactNode;
@ -184,7 +186,7 @@ function EditorProvider(props: RichEditorProps) {
)}
<DialogContent className={`editor-dialog flex-dialog`}>
<DialogHeader>
<DialogTitle>{t("edit")}</DialogTitle>
<DialogTitle>{props.title ?? t("edit")}</DialogTitle>
<DialogDescription asChild>
<RichEditor {...props} />
</DialogDescription>

View File

@ -18,7 +18,7 @@ import {
import { useTranslation } from "react-i18next";
import { extractMessage } from "@/utils/processor.ts";
import { Button } from "@/components/ui/button.tsx";
import { Loader2, Plus, RotateCcw } from "lucide-react";
import { Eye, Loader2, MoreVertical, Plus, RotateCcw } from "lucide-react";
import { useToast } from "@/components/ui/use-toast.ts";
import {
Dialog,
@ -31,6 +31,13 @@ import {
} from "@/components/ui/dialog.tsx";
import { Textarea } from "@/components/ui/textarea.tsx";
import { DialogClose } from "@radix-ui/react-dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu.tsx";
import EditorProvider from "@/components/EditorProvider.tsx";
type CreateBroadcastDialogProps = {
onCreated?: () => void;
@ -96,6 +103,45 @@ function CreateBroadcastDialog(props: CreateBroadcastDialogProps) {
</Dialog>
);
}
function BroadcastItem({ item }: { item: BroadcastInfo }) {
const { t } = useTranslation();
const [open, setOpen] = useState<boolean>(false);
const [value, setValue] = useState<string>("");
return (
<TableRow>
<EditorProvider
title={t("admin.view")}
value={value || item.content}
onChange={setValue}
open={open}
setOpen={setOpen}
/>
<TableCell>{item.index}</TableCell>
<TableCell>{extractMessage(item.content, 25)}</TableCell>
<TableCell>{item.poster}</TableCell>
<TableCell>{item.created_at}</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant={`outline`} size={`icon`}>
<MoreVertical className={`w-4 h-4`} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align={`end`}>
<DropdownMenuItem onClick={() => setOpen(true)}>
<Eye className={`w-4 h-4 mr-1`} />
{t("admin.view")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
);
}
function BroadcastTable() {
const { t } = useTranslation();
const init = useSelector(selectInit);
@ -135,16 +181,12 @@ function BroadcastTable() {
<TableHead>{t("admin.broadcast-content")}</TableHead>
<TableHead>{t("admin.poster")}</TableHead>
<TableHead>{t("admin.post-at")}</TableHead>
<TableHead>{t("admin.action")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((user, idx) => (
<TableRow key={idx}>
<TableCell>{user.index}</TableCell>
<TableCell>{extractMessage(user.content, 25)}</TableCell>
<TableCell>{user.poster}</TableCell>
<TableCell>{user.created_at}</TableCell>
</TableRow>
{data.map((item, idx) => (
<BroadcastItem key={idx} item={item} />
))}
</TableBody>
</Table>

View File

@ -7,7 +7,7 @@ import {
import { syncSiteInfo } from "@/admin/api/info.ts";
import { setAxiosConfig } from "@/conf/api.ts";
export const version = "3.10.4"; // version of the current build
export const version = "3.10.5"; // version of the current build
export const dev: boolean = getDev(); // is in development mode (for debugging, in localhost origin)
export const deploy: boolean = true; // is production environment (for api endpoint)
export const tokenField = getTokenField(deploy); // token field name for storing token

View File

@ -407,6 +407,7 @@
"billing-month": "本月入账",
"subscription-users": "订阅用户",
"seat": "位",
"view": "查看",
"model-chart": "模型使用统计",
"model-chart-tip": "Token 用量",
"model-usage-chart": "模型使用占比",
@ -438,7 +439,7 @@
"post-at": "发布时间",
"broadcast-content": "公告内容",
"create-broadcast": "发布公告",
"broadcast-placeholder": "请输入公告内容",
"broadcast-placeholder": "请输入通知内容 (支持 Markdown / HTML)",
"post": "发布",
"post-success": "发布成功",
"post-success-prompt": "公告发布成功。",

View File

@ -672,7 +672,8 @@
"unban-action-desc": "Are you sure you want to unblock this user?",
"billing": "Income",
"chatnio-format-only": "This format is unique to Chat Nio",
"exit": "Log out of the background"
"exit": "Log out of the background",
"view": "View"
},
"mask": {
"title": "Mask Settings",

View File

@ -672,7 +672,8 @@
"unban-action-desc": "このユーザーのブロックを解除してもよろしいですか?",
"billing": "収入",
"chatnio-format-only": "このフォーマットはChat Nioに固有です",
"exit": "バックグラウンドからログアウト"
"exit": "バックグラウンドからログアウト",
"view": "確認"
},
"mask": {
"title": "プリセット設定",

View File

@ -672,7 +672,8 @@
"unban-action-desc": "Вы уверены, что хотите разблокировать этого пользователя?",
"billing": "Доходы",
"chatnio-format-only": "Этот формат уникален для Chat Nio",
"exit": "Выйти из фонового режима"
"exit": "Выйти из фонового режима",
"view": "проверить"
},
"mask": {
"title": "Настройки маски",

View File

@ -31,7 +31,11 @@ type ProgressProps = {
total: number;
};
function GenerateProgress({ current, total, quota }: ProgressProps & { quota: number }) {
function GenerateProgress({
current,
total,
quota,
}: ProgressProps & { quota: number }) {
const { t } = useTranslation();
return (

View File

@ -136,17 +136,20 @@ const chatSlice = createSlice({
const conversation = state.conversations[id];
if (!conversation) return;
if (id === -1 && state.mask_item && conversation.messages.length === 0) {
conversation.messages = [...state.mask_item.context];
state.mask_item = null;
}
conversation.messages.push({
role: role ?? AssistantRole,
content: content ?? "",
end: role === AssistantRole ? false : undefined,
});
},
fillMaskItem: (state) => {
const conversation = state.conversations[-1];
if (state.mask_item && conversation.messages.length === 0) {
conversation.messages = [...state.mask_item.context];
state.mask_item = null;
}
},
updateMessage: (state, action) => {
const { id, message } = action.payload as {
id: number;
@ -380,6 +383,7 @@ export const {
setSupportModels,
setMaskItem,
clearMaskItem,
fillMaskItem,
createMessage,
updateMessage,
removeMessage,
@ -513,7 +517,7 @@ export function useMessageActions() {
if (current === -1 && mask && mask.context.length > 0) {
conn.sendMaskEvent(t, mask);
dispatch(clearMaskItem());
dispatch(fillMaskItem());
}
}

View File

@ -1,6 +1,5 @@
package globals
type ToolCallId string
type FunctionTools []ToolObject
type ToolObject struct {
Type string `json:"type"`
@ -41,7 +40,7 @@ type ToolCallFunction struct {
type ToolCall struct {
Index *int `json:"index,omitempty"`
Type string `json:"type"`
Id ToolCallId `json:"id"`
Id string `json:"id"`
Function ToolCallFunction `json:"function"`
}
type ToolCalls []ToolCall