forked from XiaoMo/ChatGPT-Next-Web
feat: drag and drop in contextual prompts
This commit is contained in:
parent
b51f7f9a25
commit
fb98050d9f
@ -42,6 +42,20 @@ import { ModelConfigList } from "./model-config";
|
|||||||
import { FileName, Path } from "../constant";
|
import { FileName, Path } from "../constant";
|
||||||
import { BUILTIN_MASK_STORE } from "../masks";
|
import { BUILTIN_MASK_STORE } from "../masks";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
|
import {
|
||||||
|
DragDropContext,
|
||||||
|
Droppable,
|
||||||
|
Draggable,
|
||||||
|
OnDragEndResponder,
|
||||||
|
} from "@hello-pangea/dnd";
|
||||||
|
|
||||||
|
// drag and drop helper function
|
||||||
|
function reorder<T>(list: T[], startIndex: number, endIndex: number): T[] {
|
||||||
|
const result = [...list];
|
||||||
|
const [removed] = result.splice(startIndex, 1);
|
||||||
|
result.splice(endIndex, 0, removed);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export function MaskAvatar(props: { mask: Mask }) {
|
export function MaskAvatar(props: { mask: Mask }) {
|
||||||
return props.mask.avatar !== DEFAULT_MASK_AVATAR ? (
|
return props.mask.avatar !== DEFAULT_MASK_AVATAR ? (
|
||||||
@ -192,6 +206,7 @@ export function MaskConfig(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ContextPromptItem(props: {
|
function ContextPromptItem(props: {
|
||||||
|
index: number;
|
||||||
prompt: ChatMessage;
|
prompt: ChatMessage;
|
||||||
update: (prompt: ChatMessage) => void;
|
update: (prompt: ChatMessage) => void;
|
||||||
remove: () => void;
|
remove: () => void;
|
||||||
@ -199,53 +214,62 @@ function ContextPromptItem(props: {
|
|||||||
const [focusingInput, setFocusingInput] = useState(false);
|
const [focusingInput, setFocusingInput] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={chatStyle["context-prompt-row"]}>
|
<Draggable draggableId={props.prompt.id} index={props.index}>
|
||||||
{!focusingInput && (
|
{(provided) => (
|
||||||
<Select
|
<div
|
||||||
value={props.prompt.role}
|
className={chatStyle["context-prompt-row"]}
|
||||||
className={chatStyle["context-role"]}
|
ref={provided.innerRef}
|
||||||
onChange={(e) =>
|
{...provided.draggableProps}
|
||||||
props.update({
|
{...provided.dragHandleProps}
|
||||||
...props.prompt,
|
|
||||||
role: e.target.value as any,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{ROLES.map((r) => (
|
{!focusingInput && (
|
||||||
<option key={r} value={r}>
|
<Select
|
||||||
{r}
|
value={props.prompt.role}
|
||||||
</option>
|
className={chatStyle["context-role"]}
|
||||||
))}
|
onChange={(e) =>
|
||||||
</Select>
|
props.update({
|
||||||
|
...props.prompt,
|
||||||
|
role: e.target.value as any,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{ROLES.map((r) => (
|
||||||
|
<option key={r} value={r}>
|
||||||
|
{r}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
<Input
|
||||||
|
value={props.prompt.content}
|
||||||
|
type="text"
|
||||||
|
className={chatStyle["context-content"]}
|
||||||
|
rows={focusingInput ? 5 : 1}
|
||||||
|
onFocus={() => setFocusingInput(true)}
|
||||||
|
onBlur={() => {
|
||||||
|
setFocusingInput(false);
|
||||||
|
// If the selection is not removed when the user loses focus, some
|
||||||
|
// extensions like "Translate" will always display a floating bar
|
||||||
|
window?.getSelection()?.removeAllRanges();
|
||||||
|
}}
|
||||||
|
onInput={(e) =>
|
||||||
|
props.update({
|
||||||
|
...props.prompt,
|
||||||
|
content: e.currentTarget.value as any,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{!focusingInput && (
|
||||||
|
<IconButton
|
||||||
|
icon={<DeleteIcon />}
|
||||||
|
className={chatStyle["context-delete-button"]}
|
||||||
|
onClick={() => props.remove()}
|
||||||
|
bordered
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<Input
|
</Draggable>
|
||||||
value={props.prompt.content}
|
|
||||||
type="text"
|
|
||||||
className={chatStyle["context-content"]}
|
|
||||||
rows={focusingInput ? 5 : 1}
|
|
||||||
onFocus={() => setFocusingInput(true)}
|
|
||||||
onBlur={() => {
|
|
||||||
setFocusingInput(false);
|
|
||||||
// If the selection is not removed when the user loses focus, some
|
|
||||||
// extensions like "Translate" will always display a floating bar
|
|
||||||
window?.getSelection()?.removeAllRanges();
|
|
||||||
}}
|
|
||||||
onInput={(e) =>
|
|
||||||
props.update({
|
|
||||||
...props.prompt,
|
|
||||||
content: e.currentTarget.value as any,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{!focusingInput && (
|
|
||||||
<IconButton
|
|
||||||
icon={<DeleteIcon />}
|
|
||||||
className={chatStyle["context-delete-button"]}
|
|
||||||
onClick={() => props.remove()}
|
|
||||||
bordered
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,17 +291,41 @@ export function ContextPrompts(props: {
|
|||||||
props.updateContext((context) => (context[i] = prompt));
|
props.updateContext((context) => (context[i] = prompt));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onDragEnd: OnDragEndResponder = (result) => {
|
||||||
|
if (!result.destination) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newContext = reorder(
|
||||||
|
context,
|
||||||
|
result.source.index,
|
||||||
|
result.destination.index,
|
||||||
|
);
|
||||||
|
props.updateContext((context) => {
|
||||||
|
context.splice(0, context.length, ...newContext);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={chatStyle["context-prompt"]} style={{ marginBottom: 20 }}>
|
<div className={chatStyle["context-prompt"]} style={{ marginBottom: 20 }}>
|
||||||
{context.map((c, i) => (
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
<ContextPromptItem
|
<Droppable droppableId="context-prompt-list">
|
||||||
key={i}
|
{(provided) => (
|
||||||
prompt={c}
|
<div ref={provided.innerRef} {...provided.droppableProps}>
|
||||||
update={(prompt) => updateContextPrompt(i, prompt)}
|
{context.map((c, i) => (
|
||||||
remove={() => removeContextPrompt(i)}
|
<ContextPromptItem
|
||||||
/>
|
index={i}
|
||||||
))}
|
key={c.id}
|
||||||
|
prompt={c}
|
||||||
|
update={(prompt) => updateContextPrompt(i, prompt)}
|
||||||
|
remove={() => removeContextPrompt(i)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{provided.placeholder}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</DragDropContext>
|
||||||
|
|
||||||
<div className={chatStyle["context-prompt-row"]}>
|
<div className={chatStyle["context-prompt-row"]}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
Loading…
Reference in New Issue
Block a user