diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index 644c917a1f4..54f1dae5cce 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -3,6 +3,7 @@ .chat-input-actions { display: flex; flex-wrap: wrap; + margin-bottom: 10px; .chat-input-action { display: inline-flex; @@ -15,7 +16,6 @@ animation: slide-in ease 0.3s; box-shadow: var(--card-shadow); transition: all ease 0.3s; - margin-bottom: 10px; align-items: center; height: 16px; width: var(--icon-width); @@ -202,3 +202,225 @@ } } } + +.chat { + display: flex; + flex-direction: column; + position: relative; + height: 100%; +} + +.chat-body { + flex: 1; + overflow: auto; + padding: 20px; + padding-bottom: 40px; + position: relative; + overscroll-behavior: none; +} + +.chat-body-title { + cursor: pointer; + + &:hover { + text-decoration: underline; + } +} + +.chat-message { + display: flex; + flex-direction: row; + + &:last-child { + animation: slide-in ease 0.3s; + } +} + +.chat-message-user { + display: flex; + flex-direction: row-reverse; +} + +.chat-message-container { + max-width: var(--message-max-width); + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.chat-message-user > .chat-message-container { + align-items: flex-end; +} + +.chat-message-avatar { + margin-top: 20px; +} + +.chat-message-status { + font-size: 12px; + color: #aaa; + line-height: 1.5; + margin-top: 5px; +} + +.chat-message-item { + box-sizing: border-box; + max-width: 100%; + margin-top: 10px; + border-radius: 10px; + background-color: rgba(0, 0, 0, 0.05); + padding: 10px; + font-size: 14px; + user-select: text; + word-break: break-word; + border: var(--border-in-light); + position: relative; + transition: all ease 0.3s; + min-width: 0; + + &:hover { + min-width: 330px; + + .chat-message-actions { + height: 40px; + opacity: 1; + transform: translateY(0px); + + .chat-message-action-date { + opacity: 0.3; + } + } + } + + .chat-message-actions { + display: flex; + width: 100%; + box-sizing: border-box; + font-size: 12px; + align-items: flex-end; + justify-content: space-between; + transition: all ease 0.3s; + transform: translateY(10px); + opacity: 0; + height: 0; + } + + .chat-message-action-date { + color: var(--black); + opacity: 0; + } +} + +.chat-message-user > .chat-message-container > .chat-message-item { + background-color: var(--second); + + &:hover { + min-width: 0; + } +} + +.chat-input-panel { + position: relative; + width: 100%; + padding: 20px; + padding-top: 10px; + box-sizing: border-box; + flex-direction: column; + border-top: var(--border-in-light); + box-shadow: var(--card-shadow); +} + +@mixin single-line { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.prompt-hints { + min-height: 20px; + width: 100%; + max-height: 50vh; + overflow: auto; + display: flex; + flex-direction: column-reverse; + + background-color: var(--white); + border: var(--border-in-light); + border-radius: 10px; + margin-bottom: 10px; + box-shadow: var(--shadow); + + .prompt-hint { + color: var(--black); + padding: 6px 10px; + animation: slide-in ease 0.3s; + cursor: pointer; + transition: all ease 0.3s; + border: transparent 1px solid; + margin: 4px; + border-radius: 8px; + + &:not(:last-child) { + margin-top: 0; + } + + .hint-title { + font-size: 12px; + font-weight: bolder; + + @include single-line(); + } + .hint-content { + font-size: 12px; + + @include single-line(); + } + + &-selected, + &:hover { + border-color: var(--primary); + } + } +} + +.chat-input-panel-inner { + display: flex; + flex: 1; +} + +.chat-input { + height: 100%; + width: 100%; + border-radius: 10px; + border: var(--border-in-light); + box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.03); + background-color: var(--white); + color: var(--black); + font-family: inherit; + padding: 10px 90px 10px 14px; + resize: none; + outline: none; +} + +.chat-input:focus { + border: 1px solid var(--primary); +} + +.chat-input-send { + background-color: var(--primary); + color: white; + + position: absolute; + right: 30px; + bottom: 32px; +} + +@media only screen and (max-width: 600px) { + .chat-input { + font-size: 16px; + } + + .chat-input-send { + bottom: 30px; + } +} diff --git a/app/components/chat.tsx b/app/components/chat.tsx index e1011e42241..279addf6101 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -21,6 +21,8 @@ import MinIcon from "../icons/min.svg"; import ResetIcon from "../icons/reload.svg"; import BreakIcon from "../icons/break.svg"; import SettingsIcon from "../icons/chat-settings.svg"; +import DeleteIcon from "../icons/clear.svg"; +import PinIcon from "../icons/pin.svg"; import LightIcon from "../icons/light.svg"; import DarkIcon from "../icons/dark.svg"; @@ -57,10 +59,9 @@ import { Prompt, usePromptStore } from "../store/prompt"; import Locale from "../locales"; import { IconButton } from "./button"; -import styles from "./home.module.scss"; -import chatStyle from "./chat.module.scss"; +import styles from "./chat.module.scss"; -import { ListItem, Modal } from "./ui-lib"; +import { ListItem, Modal, showToast } from "./ui-lib"; import { useLocation, useNavigate } from "react-router-dom"; import { LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS } from "../constant"; import { Avatar } from "./emoji"; @@ -148,15 +149,15 @@ function PromptToast(props: { const context = session.mask.context; return ( -
+
{props.showToast && (
props.setShowModal(true)} > - + {Locale.Context.Toast(context.length)}
@@ -270,17 +271,15 @@ function ClearContextDivider() { return (
chatStore.updateCurrentSession( (session) => (session.clearContextIndex = undefined), ) } > -
- {Locale.Context.Clear} -
-
+
{Locale.Context.Clear}
+
{Locale.Context.Revert}
@@ -316,7 +315,7 @@ function ChatAction(props: { return (
{ props.onClick(); setTimeout(updateWidth, 1); @@ -328,10 +327,10 @@ function ChatAction(props: { } as React.CSSProperties } > -
+
{props.icon}
-
+
{props.text}
@@ -400,7 +399,7 @@ export function ChatActions(props: { } return ( -
+
{couldStop && ( { + if (!botMessage.id) return; + const userMessageIndex = findLastUserIndex(botMessage.id); + if (!userMessageIndex) return; + + const userMessage = session.messages[userMessageIndex]; + chatStore.updateCurrentSession((session) => + session.mask.context.push(userMessage, botMessage), + ); + + showToast(Locale.Chat.Actions.PinToastContent, { + text: Locale.Chat.Actions.PinToastAction, + onClick: () => { + setShowPromptModal(true); + }, + }); + }; + const context: RenderMessage[] = session.mask.hideContext ? [] : session.mask.context.slice(); @@ -880,40 +897,6 @@ export function Chat() {
)}
- {showActions && ( -
- {message.streaming ? ( -
onUserStop(message.id ?? i)} - > - {Locale.Chat.Actions.Stop} -
- ) : ( - <> -
onDelete(message.id ?? i)} - > - {Locale.Chat.Actions.Delete} -
-
onResend(message.id ?? i)} - > - {Locale.Chat.Actions.Retry} -
- - )} - -
copyToClipboard(message.content)} - > - {Locale.Chat.Actions.Copy} -
-
- )} = messages.length - 10} /> -
- {!isUser && !message.preview && ( -
-
- {message.date.toLocaleString()} + + {showActions && ( +
+
+ {message.streaming ? ( + } + onClick={() => onUserStop(message.id ?? i)} + /> + ) : ( + <> + } + onClick={() => onDelete(message.id ?? i)} + /> + + } + onClick={() => onResend(message.id ?? i)} + /> + + } + onClick={() => onPinMessage(message)} + /> + + )} + } + onClick={() => copyToClipboard(message.content)} + /> +
+ +
+ {message.date.toLocaleString()} +
-
- )} + )} +
{shouldShowClearContextDivider && } diff --git a/app/components/home.module.scss b/app/components/home.module.scss index 81916d71b3c..49ad2bd2278 100644 --- a/app/components/home.module.scss +++ b/app/components/home.module.scss @@ -313,243 +313,6 @@ margin-right: 15px; } -.chat { - display: flex; - flex-direction: column; - position: relative; - height: 100%; -} - -.chat-body { - flex: 1; - overflow: auto; - padding: 20px; - padding-bottom: 40px; - position: relative; - overscroll-behavior: none; -} - -.chat-body-title { - cursor: pointer; - - &:hover { - text-decoration: underline; - } -} - -.chat-message { - display: flex; - flex-direction: row; - - &:last-child { - animation: slide-in ease 0.3s; - } -} - -.chat-message-user { - display: flex; - flex-direction: row-reverse; -} - -.chat-message-container { - max-width: var(--message-max-width); - display: flex; - flex-direction: column; - align-items: flex-start; - - &:hover { - .chat-message-top-actions { - opacity: 1; - transform: translateX(10px); - pointer-events: all; - } - } -} - -.chat-message-user > .chat-message-container { - align-items: flex-end; -} - -.chat-message-avatar { - margin-top: 20px; -} - -.chat-message-status { - font-size: 12px; - color: #aaa; - line-height: 1.5; - margin-top: 5px; -} - -.chat-message-item { - box-sizing: border-box; - max-width: 100%; - margin-top: 10px; - border-radius: 10px; - background-color: rgba(0, 0, 0, 0.05); - padding: 10px; - font-size: 14px; - user-select: text; - word-break: break-word; - border: var(--border-in-light); - position: relative; -} - -.chat-message-top-actions { - min-width: 120px; - font-size: 12px; - position: absolute; - right: 20px; - top: -26px; - left: 30px; - transition: all ease 0.3s; - opacity: 0; - pointer-events: none; - - display: flex; - flex-direction: row-reverse; - - .chat-message-top-action { - opacity: 0.5; - color: var(--black); - white-space: nowrap; - cursor: pointer; - - &:hover { - opacity: 1; - } - - &:not(:first-child) { - margin-right: 10px; - } - } -} - -.chat-message-user > .chat-message-container > .chat-message-item { - background-color: var(--second); -} - -.chat-message-actions { - display: flex; - flex-direction: row-reverse; - width: 100%; - padding-top: 5px; - box-sizing: border-box; - font-size: 12px; -} - -.chat-message-action-date { - color: #aaa; -} - -.chat-input-panel { - position: relative; - width: 100%; - padding: 20px; - padding-top: 10px; - box-sizing: border-box; - flex-direction: column; - border-top-left-radius: 10px; - border-top-right-radius: 10px; - border-top: var(--border-in-light); - box-shadow: var(--card-shadow); -} - -@mixin single-line { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.prompt-hints { - min-height: 20px; - width: 100%; - max-height: 50vh; - overflow: auto; - display: flex; - flex-direction: column-reverse; - - background-color: var(--white); - border: var(--border-in-light); - border-radius: 10px; - margin-bottom: 10px; - box-shadow: var(--shadow); - - .prompt-hint { - color: var(--black); - padding: 6px 10px; - animation: slide-in ease 0.3s; - cursor: pointer; - transition: all ease 0.3s; - border: transparent 1px solid; - margin: 4px; - border-radius: 8px; - - &:not(:last-child) { - margin-top: 0; - } - - .hint-title { - font-size: 12px; - font-weight: bolder; - - @include single-line(); - } - .hint-content { - font-size: 12px; - - @include single-line(); - } - - &-selected, - &:hover { - border-color: var(--primary); - } - } -} - -.chat-input-panel-inner { - display: flex; - flex: 1; -} - -.chat-input { - height: 100%; - width: 100%; - border-radius: 10px; - border: var(--border-in-light); - box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.03); - background-color: var(--white); - color: var(--black); - font-family: inherit; - padding: 10px 90px 10px 14px; - resize: none; - outline: none; -} - -.chat-input:focus { - border: 1px solid var(--primary); -} - -.chat-input-send { - background-color: var(--primary); - color: white; - - position: absolute; - right: 30px; - bottom: 32px; -} - -@media only screen and (max-width: 600px) { - .chat-input { - font-size: 16px; - } - - .chat-input-send { - bottom: 30px; - } -} - .loading-content { display: flex; flex-direction: column; diff --git a/app/icons/pin.svg b/app/icons/pin.svg new file mode 100644 index 00000000000..caf7b0ee43a --- /dev/null +++ b/app/icons/pin.svg @@ -0,0 +1 @@ + diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 14ee7ec9960..b8161d44247 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -25,6 +25,9 @@ const cn = { Copy: "复制", Stop: "停止", Retry: "重试", + Pin: "固定", + PinToastContent: "已将 2 条对话固定至预设提示词", + PinToastAction: "查看", Delete: "删除", }, Commands: { diff --git a/app/locales/en.ts b/app/locales/en.ts index 1659ddb1f67..e0ccd1f77cd 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -26,6 +26,9 @@ const en: LocaleType = { Copy: "Copy", Stop: "Stop", Retry: "Retry", + Pin: "Pin", + PinToastContent: "Pinned 2 messages to contextual prompts", + PinToastAction: "View", Delete: "Delete", }, Commands: {