mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
feat: improve prompt management (#7853)
This commit is contained in:
@@ -9,12 +9,23 @@ import { useRightPanel } from '../layout';
|
||||
import type { Prompt } from './prompts';
|
||||
import { usePrompt } from './use-prompt';
|
||||
|
||||
export function EditPrompt({ item }: { item: Prompt }) {
|
||||
export function EditPrompt({
|
||||
item,
|
||||
setCanSave,
|
||||
}: {
|
||||
item: Prompt;
|
||||
setCanSave: (changed: boolean) => void;
|
||||
}) {
|
||||
const { closePanel } = useRightPanel();
|
||||
|
||||
const [messages, setMessages] = useState(item.messages);
|
||||
const { updatePrompt } = usePrompt();
|
||||
|
||||
const disableSave = useMemo(
|
||||
() => JSON.stringify(messages) === JSON.stringify(item.messages),
|
||||
[item.messages, messages]
|
||||
);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLTextAreaElement>, index: number) => {
|
||||
const newMessages = [...messages];
|
||||
@@ -23,8 +34,9 @@ export function EditPrompt({ item }: { item: Prompt }) {
|
||||
content: e.target.value,
|
||||
};
|
||||
setMessages(newMessages);
|
||||
setCanSave(!disableSave);
|
||||
},
|
||||
[messages]
|
||||
[disableSave, messages, setCanSave]
|
||||
);
|
||||
const handleClose = useCallback(() => {
|
||||
setMessages(item.messages);
|
||||
@@ -32,14 +44,11 @@ export function EditPrompt({ item }: { item: Prompt }) {
|
||||
}, [closePanel, item.messages]);
|
||||
|
||||
const onConfirm = useCallback(() => {
|
||||
updatePrompt({ name: item.name, messages });
|
||||
if (!disableSave) {
|
||||
updatePrompt({ name: item.name, messages });
|
||||
}
|
||||
handleClose();
|
||||
}, [handleClose, item.name, messages, updatePrompt]);
|
||||
|
||||
const disableSave = useMemo(
|
||||
() => JSON.stringify(messages) === JSON.stringify(item.messages),
|
||||
[item.messages, messages]
|
||||
);
|
||||
}, [disableSave, handleClose, item.name, messages, updatePrompt]);
|
||||
|
||||
useEffect(() => {
|
||||
setMessages(item.messages);
|
||||
@@ -71,74 +80,83 @@ export function EditPrompt({ item }: { item: Prompt }) {
|
||||
</div>
|
||||
<Separator />
|
||||
<ScrollArea>
|
||||
<div className="px-5 py-4 overflow-y-auto space-y-[10px] flex flex-col gap-5">
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-medium">Name</div>
|
||||
<div className="text-sm font-normal text-zinc-500">{item.name}</div>
|
||||
</div>
|
||||
{item.action ? (
|
||||
<div className="grid">
|
||||
<div className="px-5 py-4 overflow-y-auto space-y-[10px] flex flex-col gap-5">
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-medium">Action</div>
|
||||
<div className="text-sm font-medium">Name</div>
|
||||
<div className="text-sm font-normal text-zinc-500">
|
||||
{item.action}
|
||||
{item.name}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-medium">Model</div>
|
||||
<div className="text-sm font-normal text-zinc-500">
|
||||
{item.model}
|
||||
</div>
|
||||
</div>
|
||||
{item.config ? (
|
||||
<div className="flex flex-col border rounded p-3">
|
||||
<div className="text-sm font-medium">Config</div>
|
||||
{Object.entries(item.config).map(([key, value], index) => (
|
||||
<div key={key} className="flex flex-col">
|
||||
{index !== 0 && <Separator />}
|
||||
<span className="text-sm font-normal">{key}</span>
|
||||
<span className="text-sm font-normal text-zinc-500">
|
||||
{value?.toString()}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="px-5 py-4 overflow-y-auto space-y-[10px] flex flex-col">
|
||||
<div className="text-sm font-medium">Messages</div>
|
||||
{messages.map((message, index) => (
|
||||
<div key={index} className="flex flex-col gap-3">
|
||||
{index !== 0 && <Separator />}
|
||||
<div>
|
||||
<div className="text-sm font-normal">Role</div>
|
||||
{item.action ? (
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-medium">Action</div>
|
||||
<div className="text-sm font-normal text-zinc-500">
|
||||
{message.role}
|
||||
{item.action}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{message.params ? (
|
||||
<div>
|
||||
<div className="text-sm font-medium">Params</div>
|
||||
{Object.entries(message.params).map(([key, value], index) => (
|
||||
<div key={key} className="flex flex-col">
|
||||
{index !== 0 && <Separator />}
|
||||
<span className="text-sm font-normal">{key}</span>
|
||||
<span className="text-sm font-normal text-zinc-500">
|
||||
{value.toString()}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="text-sm font-normal">Content</div>
|
||||
<Textarea
|
||||
className=" min-h-48"
|
||||
value={message.content}
|
||||
onChange={e => handleChange(e, index)}
|
||||
/>
|
||||
) : null}
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-medium">Model</div>
|
||||
<div className="text-sm font-normal text-zinc-500">
|
||||
{item.model}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{item.config ? (
|
||||
<div className="flex flex-col border rounded p-3">
|
||||
<div className="text-sm font-medium">Config</div>
|
||||
{Object.entries(item.config).map(([key, value], index) => (
|
||||
<div key={key} className="flex flex-col">
|
||||
{index !== 0 && <Separator />}
|
||||
<span className="text-sm font-normal">{key}</span>
|
||||
<span className="text-sm font-normal text-zinc-500">
|
||||
{value?.toString()}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="px-5 py-4 overflow-y-auto space-y-[10px] flex flex-col">
|
||||
<div className="text-sm font-medium">Messages</div>
|
||||
{messages.map((message, index) => (
|
||||
<div key={index} className="flex flex-col gap-3">
|
||||
{index !== 0 && <Separator />}
|
||||
<div>
|
||||
<div className="text-sm font-normal">Role</div>
|
||||
<div className="text-sm font-normal text-zinc-500">
|
||||
{message.role}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{message.params ? (
|
||||
<div>
|
||||
<div className="text-sm font-medium">Params</div>
|
||||
{Object.entries(message.params).map(
|
||||
([key, value], index) => (
|
||||
<div key={key} className="flex flex-col">
|
||||
{index !== 0 && <Separator />}
|
||||
<span className="text-sm font-normal">{key}</span>
|
||||
<span
|
||||
className="text-sm font-normal text-zinc-500"
|
||||
style={{ overflowWrap: 'break-word' }}
|
||||
>
|
||||
{value.toString()}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="text-sm font-normal">Content</div>
|
||||
<Textarea
|
||||
className=" min-h-48"
|
||||
value={message.content}
|
||||
onChange={e => handleChange(e, index)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
|
||||
@@ -4,14 +4,7 @@ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
||||
|
||||
import { Prompts } from './prompts';
|
||||
|
||||
export function Ai() {
|
||||
return null;
|
||||
|
||||
// hide ai config in admin until it's ready
|
||||
// return <AiPage />;
|
||||
}
|
||||
|
||||
export function AiPage() {
|
||||
function AiPage() {
|
||||
return (
|
||||
<div className=" h-screen flex-1 flex-col flex">
|
||||
<div className="flex items-center justify-between px-6 py-3 my-[2px] max-md:ml-9 max-md:mt-[2px]">
|
||||
@@ -38,4 +31,5 @@ export function AiPage() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export { Ai as Component };
|
||||
|
||||
export { AiPage as Component };
|
||||
|
||||
@@ -54,14 +54,16 @@ export function Prompts() {
|
||||
export const PromptRow = ({ item, index }: { item: Prompt; index: number }) => {
|
||||
const { setRightPanelContent, openPanel, isOpen } = useRightPanel();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [canSave, setCanSave] = useState(false);
|
||||
|
||||
const handleDiscardChangesCancel = useCallback(() => {
|
||||
setDialogOpen(false);
|
||||
setCanSave(false);
|
||||
}, []);
|
||||
|
||||
const handleConfirm = useCallback(
|
||||
(item: Prompt) => {
|
||||
setRightPanelContent(<EditPrompt item={item} />);
|
||||
setRightPanelContent(<EditPrompt item={item} setCanSave={setCanSave} />);
|
||||
if (dialogOpen) {
|
||||
handleDiscardChangesCancel();
|
||||
}
|
||||
@@ -81,13 +83,13 @@ export const PromptRow = ({ item, index }: { item: Prompt; index: number }) => {
|
||||
|
||||
const handleEdit = useCallback(
|
||||
(item: Prompt) => {
|
||||
if (isOpen) {
|
||||
if (isOpen && canSave) {
|
||||
setDialogOpen(true);
|
||||
} else {
|
||||
handleConfirm(item);
|
||||
}
|
||||
},
|
||||
[handleConfirm, isOpen]
|
||||
[canSave, handleConfirm, isOpen]
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -7,7 +7,12 @@ import {
|
||||
import { buttonVariants } from '@affine/admin/components/ui/button';
|
||||
import { cn } from '@affine/admin/utils';
|
||||
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
||||
import { ClipboardListIcon, SettingsIcon, UsersIcon } from 'lucide-react';
|
||||
import {
|
||||
ClipboardListIcon,
|
||||
CpuIcon,
|
||||
SettingsIcon,
|
||||
UsersIcon,
|
||||
} from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
@@ -56,8 +61,7 @@ export function Nav() {
|
||||
<UsersIcon className="mr-2 h-4 w-4" />
|
||||
Accounts
|
||||
</Link>
|
||||
{/* hide ai config in admin until it's ready */}
|
||||
{/* <Link
|
||||
<Link
|
||||
to={'/admin/ai'}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
@@ -72,7 +76,7 @@ export function Nav() {
|
||||
>
|
||||
<CpuIcon className="mr-2 h-4 w-4" />
|
||||
AI
|
||||
</Link> */}
|
||||
</Link>
|
||||
<Link
|
||||
to={'/admin/config'}
|
||||
className={cn(
|
||||
|
||||
Reference in New Issue
Block a user