Skip to content

Commit d09590d

Browse files
authored
Merge pull request #156 from GCWing/gcwing/dev
fix: refine collapsed chat input processing controls
2 parents f1881a7 + 401f270 commit d09590d

File tree

4 files changed

+120
-4
lines changed

4 files changed

+120
-4
lines changed

src/web-ui/src/flow_chat/components/ChatInput.scss

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,43 @@
192192
margin: 0;
193193
}
194194

195+
&.bitfun-chat-input--processing {
196+
.bitfun-chat-input__input-area {
197+
left: 18px;
198+
right: 118px;
199+
width: auto;
200+
transform: translateY(-50%);
201+
justify-content: flex-start;
202+
}
203+
204+
.rich-text-input {
205+
display: none;
206+
}
207+
208+
.bitfun-chat-input__actions {
209+
position: absolute;
210+
top: 50%;
211+
right: 14px;
212+
transform: translateY(-50%);
213+
z-index: 2;
214+
}
215+
216+
.bitfun-chat-input__actions-right {
217+
display: flex !important;
218+
align-items: center;
219+
gap: 8px;
220+
}
221+
222+
.bitfun-chat-input__space-hint {
223+
justify-content: flex-start;
224+
width: 100%;
225+
}
226+
227+
.bitfun-chat-input__action-button {
228+
display: none !important;
229+
}
230+
}
231+
195232
&:hover .bitfun-chat-input__space-hint {
196233
opacity: 0.9;
197234
}
@@ -212,6 +249,35 @@
212249
border: none;
213250
box-shadow: none;
214251
}
252+
253+
&__cancel-shortcut {
254+
display: inline-flex;
255+
align-items: center;
256+
flex-shrink: 0;
257+
gap: 4px;
258+
font-size: 13px;
259+
line-height: 24px;
260+
color: var(--color-text-muted);
261+
opacity: 0.72;
262+
white-space: nowrap;
263+
pointer-events: none;
264+
}
265+
266+
&__capsule-divider {
267+
width: 1px;
268+
height: 14px;
269+
flex-shrink: 0;
270+
background: color-mix(in srgb, var(--color-text-muted) 28%, transparent);
271+
pointer-events: none;
272+
}
273+
274+
:root[data-theme="light"] &,
275+
:root[data-theme-type="light"] &,
276+
.light & {
277+
.bitfun-chat-input__capsule-divider {
278+
background: rgba(15, 23, 42, 0.2);
279+
}
280+
}
215281

216282
&--active {
217283
.bitfun-chat-input__box {
@@ -1068,6 +1134,26 @@
10681134
background: rgba(234, 179, 8, 0.6); // yellow-500 with opacity
10691135
animation: breathing-pulse 2.5s ease-in-out infinite;
10701136
}
1137+
1138+
&__queued-badge {
1139+
position: absolute;
1140+
top: -4px;
1141+
right: -4px;
1142+
min-width: 11px;
1143+
height: 11px;
1144+
padding: 0 3px;
1145+
border-radius: 999px;
1146+
background: #ef4444;
1147+
color: white;
1148+
display: inline-flex;
1149+
align-items: center;
1150+
justify-content: center;
1151+
font-size: 8px;
1152+
font-weight: 700;
1153+
line-height: 1;
1154+
box-shadow: 0 0 0 1px rgba(18, 18, 28, 0.85);
1155+
pointer-events: none;
1156+
}
10711157
}
10721158

10731159
@keyframes spin {

src/web-ui/src/flow_chat/components/ChatInput.tsx

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -741,8 +741,11 @@ export const ChatInput: React.FC<ChatInputProps> = ({
741741
expand: true,
742742
});
743743
setInputTarget('btw');
744+
dispatchInput({ type: 'DEACTIVATE' });
744745
} catch (e) {
745746
log.error('Failed to start /btw thread', { e });
747+
dispatchInput({ type: 'ACTIVATE' });
748+
dispatchInput({ type: 'SET_VALUE', payload: message });
746749
}
747750
}, [currentSessionId, derivedState, inputState.value, isBtwSession, setQueuedInput, t, workspacePath]);
748751

@@ -782,8 +785,10 @@ export const ChatInput: React.FC<ChatInputProps> = ({
782785
try {
783786
await sendMessage(message);
784787
dispatchInput({ type: 'CLEAR_VALUE' });
788+
dispatchInput({ type: 'DEACTIVATE' });
785789
} catch (error) {
786790
log.error('Failed to send message', { error });
791+
dispatchInput({ type: 'ACTIVATE' });
787792
dispatchInput({ type: 'SET_VALUE', payload: message });
788793
}
789794
}, [inputState.value, derivedState, transition, sendMessage, addToHistory, effectiveTargetSessionId, setQueuedInput, submitBtwFromInput]);
@@ -1265,15 +1270,21 @@ export const ChatInput: React.FC<ChatInputProps> = ({
12651270
if (inputState.isActive) return;
12661271

12671272
const handleGlobalKeyDown = (e: KeyboardEvent) => {
1268-
if (e.key !== ' ') return;
1269-
12701273
const target = e.target as HTMLElement;
12711274
const isEditable =
12721275
target.tagName === 'INPUT' ||
12731276
target.tagName === 'TEXTAREA' ||
12741277
target.isContentEditable ||
12751278
target.closest('[contenteditable="true"]') !== null;
12761279

1280+
if (e.key === 'Escape' && derivedState?.canCancel) {
1281+
if (isEditable) return;
1282+
e.preventDefault();
1283+
void transition(SessionExecutionEvent.USER_CANCEL);
1284+
return;
1285+
}
1286+
1287+
if (e.key !== ' ') return;
12771288
if (isEditable) return;
12781289

12791290
e.preventDefault();
@@ -1287,7 +1298,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({
12871298

12881299
document.addEventListener('keydown', handleGlobalKeyDown);
12891300
return () => document.removeEventListener('keydown', handleGlobalKeyDown);
1290-
}, [inputState.isActive]);
1301+
}, [derivedState?.canCancel, inputState.isActive, transition]);
12911302

12921303
const containerRef = useRef<HTMLDivElement>(null);
12931304

@@ -1318,7 +1329,11 @@ export const ChatInput: React.FC<ChatInputProps> = ({
13181329
if (sendButtonMode === 'cancel') {
13191330
return (
13201331
<Tooltip content={t('input.stopGeneration')}>
1321-
<div className="bitfun-chat-input__send-button bitfun-chat-input__send-button--breathing" onClick={handleSendOrCancel}>
1332+
<div
1333+
className="bitfun-chat-input__send-button bitfun-chat-input__send-button--breathing"
1334+
onClick={handleSendOrCancel}
1335+
data-testid="chat-input-cancel-btn"
1336+
>
13221337
<div className="bitfun-chat-input__breathing-circle" />
13231338
{hasQueuedInput && <span className="bitfun-chat-input__queued-badge">1</span>}
13241339
</div>
@@ -1353,6 +1368,8 @@ export const ChatInput: React.FC<ChatInputProps> = ({
13531368
);
13541369
};
13551370

1371+
const isCollapsedProcessing = !inputState.isActive && !!derivedState?.isProcessing;
1372+
13561373
return (
13571374
<>
13581375
<TemplatePickerPanel
@@ -1627,6 +1644,15 @@ export const ChatInput: React.FC<ChatInputProps> = ({
16271644
)}
16281645
</div>
16291646
<div className="bitfun-chat-input__actions-right">
1647+
{isCollapsedProcessing && (
1648+
<>
1649+
<span className="bitfun-chat-input__capsule-divider" />
1650+
<span className="bitfun-chat-input__cancel-shortcut">
1651+
<span className="bitfun-chat-input__space-key">Esc</span>
1652+
<span>{t('input.cancelShortcut')}</span>
1653+
</span>
1654+
</>
1655+
)}
16301656
{canSwitchModes && (
16311657
<div
16321658
className="bitfun-chat-input__mode-selector"

src/web-ui/src/locales/en-US/flow-chat.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@
106106
"sendShortcut": "Send (⏎)",
107107
"stop": "Stop",
108108
"stopGeneration": "Stop Generation (Esc)",
109+
"processingCapsule": "Processing",
110+
"cancelShortcut": "Cancel",
109111
"clear": "Clear",
110112
"attach": "Attach File",
111113
"collapseInput": "Collapse input",

src/web-ui/src/locales/zh-CN/flow-chat.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@
106106
"sendShortcut": "发送 (⏎)",
107107
"stop": "停止",
108108
"stopGeneration": "停止生成 (Esc)",
109+
"processingCapsule": "正在处理",
110+
"cancelShortcut": "取消",
109111
"clear": "清空",
110112
"attach": "附加文件",
111113
"collapseInput": "收起输入框",

0 commit comments

Comments
 (0)