@import "../styles/animation.scss";
.attach-images {
position: absolute;
left: 30px;
bottom: 32px;
display: flex;
}
.attach-image {
cursor: default;
width: 64px;
height: 64px;
border: rgba($color: #888, $alpha: 0.2) 1px solid;
border-radius: 5px;
margin-right: 10px;
background-size: cover;
background-position: center;
background-color: var(--white);
.attach-image-mask {
width: 100%;
height: 100%;
opacity: 0;
transition: all ease 0.2s;
}
.attach-image-mask:hover {
opacity: 1;
}
.delete-image {
width: 24px;
height: 24px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
float: right;
background-color: var(--white);
}
}
.chat-input-actions {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 5px;
&-end {
display: flex;
margin-left: auto;
gap: 5px;
}
.chat-input-action {
display: inline-flex;
border-radius: 20px;
font-size: 12px;
background-color: var(--white);
color: var(--black);
border: var(--border-in-light);
padding: 4px 10px;
animation: slide-in ease 0.3s;
box-shadow: var(--card-shadow);
transition: width ease 0.3s;
align-items: center;
height: 16px;
width: var(--icon-width);
overflow: hidden;
.text {
white-space: nowrap;
padding-left: 5px;
opacity: 0;
transform: translateX(-5px);
transition: all ease 0.3s;
pointer-events: none;
}
&:hover {
--delay: 0.5s;
width: var(--full-width);
transition-delay: var(--delay);
.text {
transition-delay: var(--delay);
opacity: 1;
transform: translate(0);
}
}
.text,
.icon {
display: flex;
align-items: center;
justify-content: center;
}
}
}
.prompt-toast {
position: absolute;
bottom: -50px;
z-index: 999;
display: flex;
justify-content: center;
width: calc(100% - 40px);
.prompt-toast-inner {
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
background-color: var(--white);
color: var(--black);
border: var(--border-in-light);
box-shadow: var(--card-shadow);
padding: 10px 20px;
border-radius: 100px;
animation: slide-in-from-top ease 0.3s;
.prompt-toast-content {
margin-left: 10px;
}
}
}
.section-title {
font-size: 12px;
font-weight: bold;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
.section-title-action {
display: flex;
align-items: center;
}
}
.context-prompt {
.context-prompt-insert {
display: flex;
justify-content: center;
padding: 4px;
opacity: 0.2;
transition: all ease 0.3s;
background-color: rgba(0, 0, 0, 0);
cursor: pointer;
border-radius: 4px;
margin-top: 4px;
margin-bottom: 4px;
&:hover {
opacity: 1;
background-color: rgba(0, 0, 0, 0.05);
}
}
.context-prompt-row {
display: flex;
justify-content: center;
width: 100%;
&:hover {
.context-drag {
opacity: 1;
}
}
.context-drag {
display: flex;
align-items: center;
opacity: 0.5;
transition: all ease 0.3s;
}
.context-role {
margin-right: 10px;
}
.context-content {
flex: 1;
max-width: 100%;
text-align: left;
}
.context-delete-button {
margin-left: 10px;
}
}
.context-prompt-button {
flex: 1;
}
}
.memory-prompt {
margin: 20px 0;
.memory-prompt-content {
background-color: var(--white);
color: var(--black);
border: var(--border-in-light);
border-radius: 10px;
padding: 10px;
font-size: 12px;
user-select: text;
}
}
.clear-context {
margin: 20px 0 0 0;
padding: 4px 0;
border-top: var(--border-in-light);
border-bottom: var(--border-in-light);
box-shadow: var(--card-shadow) inset;
display: flex;
justify-content: center;
align-items: center;
color: var(--black);
transition: all ease 0.3s;
cursor: pointer;
overflow: hidden;
position: relative;
font-size: 12px;
animation: slide-in ease 0.3s;
$linear: linear-gradient(
to right,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 1),
rgba(0, 0, 0, 0)
);
mask-image: $linear;
@mixin show {
transform: translateY(0);
position: relative;
transition: all ease 0.3s;
opacity: 1;
}
@mixin hide {
transform: translateY(-50%);
position: absolute;
transition: all ease 0.1s;
opacity: 0;
}
&-tips {
@include show;
opacity: 0.5;
}
&-revert-btn {
color: var(--primary);
@include hide;
}
&:hover {
opacity: 1;
border-color: var(--primary);
.clear-context-tips {
@include hide;
}
.clear-context-revert-btn {
@include show;
}
}
}
.chat {
display: flex;
flex-direction: column;
position: relative;
height: 100%;
}
// `chat-body` is Virtuoso's outer scroller. We keep the visual padding
// on this element (so messages aren't flush to the edges) AND set
// `box-sizing: border-box` so the declared 100% width *includes* the
// padding instead of adding to it. Without border-box, the element
// occupied parent_width + 40px and pushed avatars / right-aligned
// timestamps past the viewport.
.chat-body {
flex: 1;
overflow: auto;
overflow-x: hidden;
// Vertical padding stays here (top breathing room + bottom space
// before the input panel). Horizontal padding moves to `.chat-message`
// because Virtuoso sizes its inner sizer using the scroller's
// clientWidth (which INCLUDES this element's padding) and positions
// the sizer starting at padding-left — net effect: items overflow
// padding-right by exactly one padding-right width, clipping
// avatars/timestamps. With horizontal padding moved onto each
// message wrapper, Virtuoso's sizer fits cleanly edge-to-edge.
padding: 20px 0 40px;
position: relative;
overscroll-behavior: none;
box-sizing: border-box;
scrollbar-gutter: stable;
}
.chat-body-main-title {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
@media only screen and (max-width: 600px) {
.chat-body-title {
text-align: center;
}
}
.chat-message {
display: flex;
flex-direction: row;
// Flex items default to `min-width: min-content`, which can exceed the
// parent on long unbreakable strings (URLs, code) and push avatars /
// right-aligned timestamps past the viewport. `min-width: 0` lets
// children shrink correctly; `max-width: 100%` is the safety net.
min-width: 0;
max-width: 100%;
box-sizing: border-box;
// Horizontal inset moved off `.chat-body` so Virtuoso's sizer fits
// cleanly inside the scroller; messages keep the same visual gap from
// the chat edges as before.
padding-left: 20px;
padding-right: 20px;
&:last-child {
animation: slide-in ease 0.3s;
}
}
.chat-message-user {
display: flex;
flex-direction: row-reverse;
.chat-message-header {
flex-direction: row-reverse;
}
}
.chat-message-header {
margin-top: 20px;
display: flex;
align-items: center;
.chat-message-actions {
display: flex;
box-sizing: border-box;
font-size: 12px;
align-items: flex-end;
justify-content: space-between;
transition: all ease 0.3s;
transform: scale(0.9) translateY(5px);
margin: 0 10px;
opacity: 0;
pointer-events: none;
.chat-input-actions {
display: flex;
flex-wrap: nowrap;
}
}
.chat-model-name {
font-size: 12px;
color: var(--black);
margin-left: 6px;
}
}
.chat-message-container {
max-width: var(--message-max-width);
display: flex;
flex-direction: column;
align-items: flex-start;
&:hover {
.chat-message-edit {
opacity: 0.9;
}
.chat-message-actions {
opacity: 1;
pointer-events: all;
transform: scale(1) translateY(0);
}
}
}
.chat-message-user > .chat-message-container {
align-items: flex-end;
}
.chat-message-avatar {
position: relative;
.chat-message-edit {
position: absolute;
height: 100%;
width: 100%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: all ease 0.3s;
button {
padding: 7px;
}
}
/* Specific styles for iOS devices */
@media screen and (max-device-width: 812px) and (-webkit-min-device-pixel-ratio: 2) {
@supports (-webkit-touch-callout: none) {
.chat-message-edit {
top: -8%;
}
}
}
}
.chat-message-status {
font-size: 12px;
color: #aaa;
line-height: 1.5;
margin-top: 5px;
}
.chat-tool-blocks {
margin-top: 6px;
margin-bottom: 4px;
display: flex;
flex-direction: column;
gap: 4px;
}
// Tool block styles live at the top level so they apply both inside
// .chat-tool-blocks (single-tool wrapper) and inside .chat-tool-group-items
// (expanded multi-tool group).
.chat-tool-block {
border-radius: 8px;
overflow: hidden;
font-size: 12px;
border: var(--border-in-light);
.chat-tool-block-header {
display: flex;
align-items: center;
gap: 8px;
padding: 7px 12px;
background: rgba(0, 0, 0, 0.05);
cursor: pointer;
user-select: none;
transition: background 0.15s;
&:hover {
background: rgba(0, 0, 0, 0.09);
}
.chat-tool-block-icon {
display: none;
}
// title column: tool name (dim) + cmd preview (bright) stacked
.chat-tool-block-title {
flex: 1;
display: flex;
flex-direction: column;
gap: 1px;
overflow: hidden;
}
.chat-tool-block-name {
font-size: 10px;
font-family: var(--code-font, "SFMono-Regular", Consolas, monospace);
color: var(--black);
opacity: 0.38;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.chat-tool-block-cmd {
font-size: 12.5px;
font-family: var(--code-font, "SFMono-Regular", Consolas, monospace);
color: var(--black);
opacity: 0.82;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&::before {
content: "$ ";
opacity: 0.4;
}
}
.chat-tool-block-chevron {
flex-shrink: 0;
opacity: 0.28;
color: var(--black);
transition: transform 0.2s ease, opacity 0.15s;
&[data-open="true"] {
transform: rotate(180deg);
opacity: 0.52;
}
}
}
// Terminal-style output area
.chat-tool-block-output {
padding: 10px 14px;
background: #16181d;
border-top: 1px solid rgba(255, 255, 255, 0.06);
max-height: 320px;
overflow-y: auto;
font-family: var(--code-font, "SFMono-Regular", Consolas, monospace);
font-size: 11.5px;
line-height: 1.65;
// command lines e.g. "$ cmd" or "> cmd"
.tool-line-cmd {
color: #93c5fd;
}
// stderr / error lines
.tool-line-err {
color: #fca5a5;
}
// exit code lines
.tool-line-exit {
color: #fdba74;
opacity: 0.9;
}
// normal output lines
.tool-line-out {
color: #c4c9d4;
}
}
}
.chat-video {
display: block;
max-width: 480px;
max-height: 360px;
margin-top: 8px;
border-radius: 10px;
background-color: #000;
outline: none;
}
.chat-video-error {
margin-top: 8px;
padding: 10px 12px;
border-radius: 8px;
border: var(--border-in-light);
background-color: rgba(255, 80, 80, 0.08);
color: var(--black);
font-size: 12px;
line-height: 1.5;
max-width: 480px;
word-break: break-all;
.chat-video-error-path {
margin-top: 4px;
opacity: 0.6;
font-family: var(--font-family-code);
font-size: 11px;
}
}
.chat-file-chips {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
.chat-file-chip {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 9px 3px 7px;
border-radius: 6px;
border: var(--border-in-light);
background-color: var(--white);
color: var(--black);
font-size: 12px;
cursor: pointer;
max-width: 280px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
svg {
flex-shrink: 0;
opacity: 0.5;
}
&:hover {
box-shadow: var(--card-shadow);
svg { opacity: 0.8; }
}
}
}
.chat-message-tools {
font-size: 12px;
color: #aaa;
line-height: 1.5;
margin-top: 5px;
.chat-message-tool {
display: flex;
align-items: flex-end;
svg {
margin-left: 5px;
margin-right: 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;
}
.chat-message-audio {
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.05);
border: var(--border-in-light);
position: relative;
transition: all ease 0.3s;
margin-top: 10px;
font-size: 14px;
user-select: text;
word-break: break-word;
box-sizing: border-box;
audio {
height: 30px; /* 调整高度 */
}
}
.chat-message-item-image {
width: 100%;
margin-top: 10px;
}
.chat-message-item-images {
width: 100%;
display: grid;
justify-content: left;
grid-gap: 10px;
grid-template-columns: repeat(var(--image-count), auto);
margin-top: 10px;
}
.chat-message-item-image-multi {
object-fit: cover;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.chat-message-item-image,
.chat-message-item-image-multi {
box-sizing: border-box;
border-radius: 10px;
border: rgba($color: #888, $alpha: 0.2) 1px solid;
cursor: zoom-in;
}
@media only screen and (max-width: 600px) {
$calc-image-width: calc(100vw / 3 * 2 / var(--image-count));
.chat-message-item-image-multi {
width: $calc-image-width;
height: $calc-image-width;
}
.chat-message-item-image {
max-width: calc(100vw / 3 * 2);
}
}
@media screen and (min-width: 600px) {
$max-image-width: calc(
calc(1200px - var(--sidebar-width)) / 3 * 2 / var(--image-count)
);
$image-width: calc(
calc(var(--window-width) - var(--sidebar-width)) / 3 * 2 /
var(--image-count)
);
.chat-message-item-image-multi {
width: $image-width;
height: $image-width;
max-width: $max-image-width;
max-height: $max-image-width;
}
.chat-message-item-image {
max-width: calc(calc(1200px - var(--sidebar-width)) / 3 * 2);
}
}
.chat-message-action-date {
font-size: 12px;
opacity: 0.2;
white-space: nowrap;
transition: all ease 0.6s;
color: var(--black);
text-align: right;
width: 100%;
box-sizing: border-box;
padding-right: 10px;
pointer-events: none;
z-index: 1;
}
.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);
.chat-input-actions {
.chat-input-action {
margin-bottom: 10px;
}
}
}
@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 {
cursor: text;
display: flex;
flex: 1;
border-radius: 10px;
border: var(--border-in-light);
}
.chat-input-panel-inner-attach {
padding-bottom: 80px;
}
.chat-input-panel-inner:has(.chat-input:focus) {
border: 1px solid rgba(249, 115, 22, 0.35);
}
.chat-input {
height: 100%;
width: 100%;
border-radius: 10px;
border: none;
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;
box-sizing: border-box;
min-height: 68px;
}
.chat-input:focus {
}
.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;
}
}
.shortcut-key-container {
padding: 10px;
overflow-y: auto;
display: flex;
flex-direction: column;
}
.shortcut-key-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 16px;
}
.shortcut-key-item {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
padding: 10px;
background-color: var(--white);
}
.shortcut-key-title {
font-size: 14px;
color: var(--black);
}
.shortcut-key-keys {
display: flex;
gap: 8px;
}
.shortcut-key {
display: flex;
align-items: center;
justify-content: center;
border: var(--border-in-light);
border-radius: 8px;
padding: 4px;
background-color: var(--gray);
min-width: 32px;
}
.shortcut-key span {
font-size: 12px;
color: var(--black);
}
.chat-main {
display: flex;
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
.chat-body-container {
height: 100%;
display: flex;
flex-direction: column;
flex: 1;
width: 100%;
}
.chat-side-panel {
position: absolute;
inset: 0;
background: var(--white);
overflow: hidden;
z-index: 10;
transform: translateX(100%);
transition: all ease 0.3s;
&-show {
transform: translateX(0);
}
}
}
// Muted neutral palette shared between think blocks and intermediate-text
// bubbles so reasoning / progress chatter recedes vs. real assistant replies.
.chat-message-think {
margin-bottom: 10px;
border-radius: 10px;
background: rgba(128, 128, 140, 0.04);
border: 1px solid rgba(128, 128, 140, 0.10);
overflow: hidden;
&.thinking-active {
border-color: rgba(128, 128, 140, 0.18);
}
}
.think-header {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 14px;
cursor: pointer;
user-select: none;
font-size: 13px;
font-weight: 500;
color: #9996a8;
opacity: 0.7;
}
.think-icon {
width: 14px;
height: 14px;
flex-shrink: 0;
opacity: 0.5;
}
.think-title {
flex: 1;
}
.think-chevron {
flex-shrink: 0;
opacity: 0.35;
color: #9996a8;
transition: transform 0.2s ease, opacity 0.15s;
&[data-open="true"] {
transform: rotate(180deg);
opacity: 0.6;
}
}
.chat-message-think-content {
padding: 0 14px 12px;
font-size: 12px;
color: #b3b0c0;
white-space: pre-wrap;
word-break: normal;
overflow-wrap: break-word;
line-height: 1.6;
max-height: 380px;
overflow-y: auto;
scroll-behavior: smooth;
}
// Intermediate-text bubbles ("好的,我来帮你搜索…" / WS notification injected
// messages). Match the think-block muted palette so they recede next to
// real assistant replies.
.chat-message-item-intermediate {
background-color: rgba(128, 128, 140, 0.06) !important;
border: none !important;
color: #b3b0c0 !important;
opacity: 0.85;
font-size: 13px;
}
// 多工具分组折叠
.chat-tool-group {
margin-top: 6px;
margin-bottom: 4px;
border-radius: 8px;
overflow: hidden;
border: var(--border-in-light);
.chat-tool-group-header {
display: flex;
align-items: center;
gap: 8px;
padding: 7px 12px;
background: rgba(0, 0, 0, 0.05);
cursor: pointer;
user-select: none;
font-size: 12px;
transition: background 0.15s;
&:hover {
background: rgba(0, 0, 0, 0.09);
}
.chat-tool-group-label {
flex: 1;
font-weight: 500;
color: var(--black);
opacity: 0.65;
}
.chat-tool-group-chevron {
flex-shrink: 0;
opacity: 0.28;
color: var(--black);
transition: transform 0.2s ease, opacity 0.15s;
&[data-open="true"] {
transform: rotate(180deg);
opacity: 0.52;
}
}
}
// sub-items: remove outer margin/radius, stack cleanly
.chat-tool-group-items {
border-top: var(--border-in-light);
.chat-tool-blocks {
margin: 0;
}
.chat-tool-block {
border-radius: 0;
border-left: none;
border-right: none;
border-top: none;
border-bottom: var(--border-in-light);
&:last-child {
border-bottom: none;
}
}
}
}
// + 按钮下拉菜单
.attach-btn-wrapper {
position: relative;
}
.attach-dropdown {
position: absolute;
bottom: calc(100% + 6px);
left: 0;
background: var(--white);
border: var(--border-in-light);
border-radius: 8px;
box-shadow: var(--card-shadow);
overflow: hidden;
z-index: 20;
min-width: 120px;
animation: slide-in ease 0.15s;
}
.attach-dropdown-item {
display: flex;
align-items: center;
gap: 7px;
width: 100%;
padding: 8px 13px;
background: none;
border: none;
cursor: pointer;
font-size: 13px;
color: var(--black);
text-align: left;
white-space: nowrap;
svg {
opacity: 0.5;
flex-shrink: 0;
}
&:hover {
background: rgba(0, 0, 0, 0.04);
svg { opacity: 0.8; }
}
}
// 输入区文件引用 chips 行
.attach-file-refs {
display: flex;
flex-wrap: wrap;
gap: 5px;
padding: 5px 10px 2px;
border-top: var(--border-in-light);
background: var(--white);
}
// 暂存消息队列指示器:agent 正在跑 turn 时,用户再按 Enter 的消息入队
.pending-queue-indicator {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
border-top: var(--border-in-light);
background: var(--white);
font-size: 12px;
color: var(--black);
opacity: 0.7;
}
.pending-queue-label {
flex: 1;
}
.pending-queue-clear,
.pending-queue-force {
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
padding: 0;
border: none;
border-radius: 4px;
background: transparent;
color: var(--black);
opacity: 0.5;
cursor: pointer;
&:hover {
opacity: 1;
background: var(--hover-color);
}
}
.pending-queue-force {
// Slightly tinted so users can tell it apart from the X button.
color: var(--primary);
}
.attach-file-chip-input {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 6px 3px 8px;
border-radius: 5px;
background: rgba(0, 0, 0, 0.04);
border: var(--border-in-light);
font-size: 12px;
color: var(--black);
max-width: 200px;
svg {
flex-shrink: 0;
opacity: 0.5;
}
.attach-file-chip-input-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
min-width: 0;
}
.attach-file-chip-input-remove {
background: none;
border: none;
cursor: pointer;
padding: 1px;
display: flex;
align-items: center;
opacity: 0.35;
color: var(--black);
flex-shrink: 0;
&:hover {
opacity: 0.8;
}
}
}