Merge pull request #2541 from Yidadaa/yifei-bugfix-0802

feat: improve chat action, auto scroll and edit model title
This commit is contained in:
Yifei Zhang 2023-08-02 23:40:50 +08:00 committed by GitHub
commit 56904ad6b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 18 deletions

View File

@ -14,10 +14,11 @@
padding: 4px 10px; padding: 4px 10px;
animation: slide-in ease 0.3s; animation: slide-in ease 0.3s;
box-shadow: var(--card-shadow); box-shadow: var(--card-shadow);
transition: all ease 0.3s; transition: width ease 0.3s;
align-items: center; align-items: center;
height: 16px; height: 16px;
width: var(--icon-width); width: var(--icon-width);
overflow: hidden;
&:not(:last-child) { &:not(:last-child) {
margin-right: 5px; margin-right: 5px;
@ -29,14 +30,16 @@
opacity: 0; opacity: 0;
transform: translateX(-5px); transform: translateX(-5px);
transition: all ease 0.3s; transition: all ease 0.3s;
transition-delay: 0.1s;
pointer-events: none; pointer-events: none;
} }
&:hover { &:hover {
--delay: 0.5s;
width: var(--full-width); width: var(--full-width);
transition-delay: var(--delay);
.text { .text {
transition-delay: var(--delay);
opacity: 1; opacity: 1;
transform: translate(0); transform: translate(0);
} }

View File

@ -370,18 +370,27 @@ function ChatAction(props: {
function useScrollToBottom() { function useScrollToBottom() {
// for auto-scroll // for auto-scroll
const scrollRef = useRef<HTMLDivElement>(null); const scrollRef = useRef<HTMLDivElement>(null);
const [autoScroll, setAutoScroll] = useState(true); const autoScroll = useRef(true);
const scrollToBottom = useCallback(() => { const scrollToBottom = useCallback(() => {
const dom = scrollRef.current; const dom = scrollRef.current;
if (dom) { if (dom) {
requestAnimationFrame(() => dom.scrollTo(0, dom.scrollHeight)); requestAnimationFrame(() => dom.scrollTo(0, dom.scrollHeight));
} }
}, []); }, []);
const setAutoScroll = (enable: boolean) => {
autoScroll.current = enable;
};
// auto scroll // auto scroll
useEffect(() => { useEffect(() => {
autoScroll && scrollToBottom(); const intervalId = setInterval(() => {
}); if (autoScroll.current) {
scrollToBottom();
}
}, 100);
return () => clearInterval(intervalId);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return { return {
scrollRef, scrollRef,
@ -504,6 +513,7 @@ export function ChatActions(props: {
{showModelSelector && ( {showModelSelector && (
<Selector <Selector
defaultSelectedValue={currentModel}
items={models.map((m) => ({ items={models.map((m) => ({
title: m, title: m,
value: m, value: m,
@ -531,7 +541,7 @@ export function EditMessageModal(props: { onClose: () => void }) {
return ( return (
<div className="modal-mask"> <div className="modal-mask">
<Modal <Modal
title={Locale.UI.Edit} title={Locale.Chat.EditMessage.Title}
onClose={props.onClose} onClose={props.onClose}
actions={[ actions={[
<IconButton <IconButton
@ -589,10 +599,7 @@ export function Chat() {
type RenderMessage = ChatMessage & { preview?: boolean }; type RenderMessage = ChatMessage & { preview?: boolean };
const chatStore = useChatStore(); const chatStore = useChatStore();
const [session, sessionIndex] = useChatStore((state) => [ const session = chatStore.currentSession();
state.currentSession(),
state.currentSessionIndex,
]);
const config = useAppConfig(); const config = useAppConfig();
const fontSize = config.fontSize; const fontSize = config.fontSize;
@ -607,9 +614,14 @@ export function Chat() {
const isMobileScreen = useMobileScreen(); const isMobileScreen = useMobileScreen();
const navigate = useNavigate(); const navigate = useNavigate();
const lastBodyScroolTop = useRef(0);
const onChatBodyScroll = (e: HTMLElement) => { const onChatBodyScroll = (e: HTMLElement) => {
const isTouchBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 10; const isTouchBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 10;
setHitBottom(isTouchBottom); setHitBottom(isTouchBottom);
// only enable auto scroll when scroll down and touched bottom
setAutoScroll(e.scrollTop >= lastBodyScroolTop.current && isTouchBottom);
lastBodyScroolTop.current = e.scrollTop;
}; };
// prompt hints // prompt hints
@ -1035,7 +1047,6 @@ export function Chat() {
ref={scrollRef} ref={scrollRef}
onScroll={(e) => onChatBodyScroll(e.currentTarget)} onScroll={(e) => onChatBodyScroll(e.currentTarget)}
onMouseDown={() => inputRef.current?.blur()} onMouseDown={() => inputRef.current?.blur()}
onWheel={(e) => setAutoScroll(hitBottom && e.deltaY > 0)}
onTouchStart={() => { onTouchStart={() => {
inputRef.current?.blur(); inputRef.current?.blur();
setAutoScroll(false); setAutoScroll(false);
@ -1147,7 +1158,7 @@ export function Chat() {
}} }}
fontSize={fontSize} fontSize={fontSize}
parentRef={scrollRef} parentRef={scrollRef}
defaultShow={i >= messages.length - 10} defaultShow={i >= messages.length - 6}
/> />
</div> </div>
@ -1192,7 +1203,6 @@ export function Chat() {
value={userInput} value={userInput}
onKeyDown={onInputKeyDown} onKeyDown={onInputKeyDown}
onFocus={() => setAutoScroll(true)} onFocus={() => setAutoScroll(true)}
onBlur={() => setAutoScroll(false)}
rows={inputRows} rows={inputRows}
autoFocus={autoFocus} autoFocus={autoFocus}
style={{ style={{

View File

@ -76,7 +76,7 @@ export function ModelConfigList(props: {
<input <input
type="number" type="number"
min={100} min={100}
max={32000} max={100000}
value={props.modelConfig.max_tokens} value={props.modelConfig.max_tokens}
onChange={(e) => onChange={(e) =>
props.updateConfig( props.updateConfig(
@ -169,7 +169,7 @@ export function ModelConfigList(props: {
title={props.modelConfig.historyMessageCount.toString()} title={props.modelConfig.historyMessageCount.toString()}
value={props.modelConfig.historyMessageCount} value={props.modelConfig.historyMessageCount}
min="0" min="0"
max="32" max="64"
step="1" step="1"
onChange={(e) => onChange={(e) =>
props.updateConfig( props.updateConfig(

View File

@ -443,6 +443,7 @@ export function Selector<T>(props: {
subTitle?: string; subTitle?: string;
value: T; value: T;
}>; }>;
defaultSelectedValue?: T;
onSelection?: (selection: T[]) => void; onSelection?: (selection: T[]) => void;
onClose?: () => void; onClose?: () => void;
multiple?: boolean; multiple?: boolean;
@ -452,6 +453,7 @@ export function Selector<T>(props: {
<div className={styles["selector-content"]}> <div className={styles["selector-content"]}>
<List> <List>
{props.items.map((item, i) => { {props.items.map((item, i) => {
const selected = props.defaultSelectedValue === item.value;
return ( return (
<ListItem <ListItem
className={styles["selector-item"]} className={styles["selector-item"]}
@ -462,7 +464,20 @@ export function Selector<T>(props: {
props.onSelection?.([item.value]); props.onSelection?.([item.value]);
props.onClose?.(); props.onClose?.();
}} }}
></ListItem> >
{selected ? (
<div
style={{
height: 10,
width: 10,
backgroundColor: "var(--primary)",
borderRadius: 10,
}}
></div>
) : (
<></>
)}
</ListItem>
); );
})} })}
</List> </List>

View File

@ -19,6 +19,7 @@ const cn = {
Chat: { Chat: {
SubTitle: (count: number) => `${count} 条对话`, SubTitle: (count: number) => `${count} 条对话`,
EditMessage: { EditMessage: {
Title: "编辑消息记录",
Topic: { Topic: {
Title: "聊天主题", Title: "聊天主题",
SubTitle: "更改当前聊天主题", SubTitle: "更改当前聊天主题",
@ -274,7 +275,7 @@ const cn = {
Context: { Context: {
Toast: (x: any) => `包含 ${x} 条预设提示词`, Toast: (x: any) => `包含 ${x} 条预设提示词`,
Edit: "当前对话设置", Edit: "当前对话设置",
Add: "新增预设对话", Add: "新增一条对话",
Clear: "上下文已清除", Clear: "上下文已清除",
Revert: "恢复上下文", Revert: "恢复上下文",
}, },

View File

@ -21,6 +21,7 @@ const en: LocaleType = {
Chat: { Chat: {
SubTitle: (count: number) => `${count} messages`, SubTitle: (count: number) => `${count} messages`,
EditMessage: { EditMessage: {
Title: "Edit All Messages",
Topic: { Topic: {
Title: "Topic", Title: "Topic",
SubTitle: "Change the current topic", SubTitle: "Change the current topic",

View File

@ -81,7 +81,7 @@ export const ModalConfigValidator = {
return x as ModelType; return x as ModelType;
}, },
max_tokens(x: number) { max_tokens(x: number) {
return limitNumber(x, 0, 32000, 2000); return limitNumber(x, 0, 100000, 2000);
}, },
presence_penalty(x: number) { presence_penalty(x: number) {
return limitNumber(x, -2, 2, 0); return limitNumber(x, -2, 2, 0);