Skip to content

Commit 1058934

Browse files
authored
Merge pull request #3221 from bobleer/bob/fix-steering-presentation
fix(chat): align mobile controls and suppress steering completion warning
2 parents 3590e01 + 98b754a commit 1058934

7 files changed

Lines changed: 48 additions & 17 deletions

File tree

‎src/mobile-web/src/styles/components/chat-input.scss‎

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -315,8 +315,8 @@
315315

316316
.chat-page__action-btn {
317317
flex-shrink: 0;
318-
width: var(--openbitfun-space-12);
319-
height: var(--openbitfun-space-12);
318+
width: 44px;
319+
height: 44px;
320320
border-radius: 50%;
321321
border: none;
322322
background: transparent;
@@ -339,8 +339,8 @@
339339

340340
.chat-page__send-btn {
341341
position: relative;
342-
width: var(--openbitfun-space-12);
343-
height: var(--openbitfun-space-12);
342+
width: 44px;
343+
height: 44px;
344344
border-radius: 50%;
345345
border: 1px solid var(--openbitfun-color-border-subtle);
346346
background: var(--openbitfun-color-action-neutral-surface);
@@ -554,7 +554,7 @@
554554

555555
// Conversation composer layout and skin. Shared geometry belongs to MobileComposer.
556556
.chat-page__composer {
557-
padding: 0 8px;
557+
padding: 5px;
558558
border-color: var(--openbitfun-color-border-subtle);
559559
border-radius: 28px;
560560
background: var(--openbitfun-color-surface-panel);
@@ -564,7 +564,7 @@
564564
.chat-page__composer[data-expanded='true'] {
565565
height: auto;
566566
max-height: none;
567-
padding: 8px;
567+
padding: 5px;
568568
border-radius: 18px;
569569
}
570570

@@ -575,9 +575,9 @@
575575
.chat-page__composer-leading {
576576
display: grid;
577577
place-items: center;
578-
flex: 0 0 48px;
579-
width: 48px;
580-
height: 48px;
578+
flex: 0 0 44px;
579+
width: 44px;
580+
height: 44px;
581581
padding: 0;
582582
border: 0;
583583
border-radius: 20px;
@@ -608,8 +608,8 @@
608608
}
609609

610610
.chat-page__input-wrap.is-expanded .chat-page__action-btn {
611-
width: 48px;
612-
height: 48px;
611+
width: 44px;
612+
height: 44px;
613613
color: var(--openbitfun-color-content-primary);
614614
}
615615

@@ -645,3 +645,8 @@
645645
aspect-ratio: 1;
646646
&:hover, &:active, &:focus { border-radius: 50%; }
647647
}
648+
649+
// Keep the action row and collapsed pill on the same inset in both modes.
650+
.chat-page__composer [data-openbitfun-part='toolbar'] {
651+
min-block-size: 44px;
652+
}

‎src/mobile-web/src/styles/components/harmony-native.scss‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1361,7 +1361,9 @@
13611361
.chat-msg__assistant-content {
13621362
width: 100%;
13631363
max-width: 100%;
1364-
padding: 0 2px 10px 0;
1364+
padding: 0 var(--size-gap-3) 10px;
1365+
box-sizing: border-box;
1366+
min-width: 0;
13651367
border: 0;
13661368
border-radius: 0;
13671369
background: transparent;

‎src/mobile-web/src/styles/host-queue.scss‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
font-size: var(--openbitfun-type-body-sm-font-size);
1111
line-height: var(--openbitfun-type-body-sm-line-height);
1212

13-
&__header { display: flex; align-items: center; padding: 0 12px; }
13+
// 7px + half a 40px queue control matches the composer: 5px + half of 44px.
14+
&__header { display: flex; align-items: center; padding: 0 7px 0 12px; }
1415
&__toggle {
1516
display: flex;
1617
align-items: center;
@@ -53,7 +54,7 @@
5354
&__list {
5455
list-style: none;
5556
margin: 0;
56-
padding: 0 12px;
57+
padding: 0 7px 0 12px;
5758
li { padding: 4px 0; border-top: 1px solid var(--openbitfun-color-border-subtle); }
5859
p { margin: 0 0 4px; }
5960
}

‎src/mobile-web/tests/fixtures/host-queue.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import '@openbitfun/theme-openbitfun/default.css';
44
import '@openbitfun/ui/mobile.css';
55
import '../../src/styles/index.scss';
66
import { createRoot } from 'react-dom/client';
7+
import { ThinkingBlock } from '../../src/components/ChatTranscript';
8+
import { MarkdownContent } from '../../src/components/ChatMarkdown';
79
import ChatComposerBar from '../../src/components/ChatComposerBar';
810
import { MobileHostQueue } from '../../src/components/MobileHostQueue';
911
import { useMobileViewport } from '../../src/hooks/useMobileViewport';
@@ -37,7 +39,7 @@ export function mountHostQueueFixture({ count = 1, expanded = true } = {}) {
3739
}, []);
3840
return <ThemeProvider><I18nProvider><div className={`chat-page${window.innerWidth >= 900 ? ' chat-page--wide' : ''}`} style={{ '--chat-composer-height': `${height}px` } as React.CSSProperties}>
3941
<header className="chat-page__header">项目介绍</header>
40-
<div className="chat-page__messages"><p>正在检查代码和测试覆盖。</p><p>ExecCommand</p><p>分析当前项目的实现。</p></div>
42+
<div className="chat-page__messages"><div className="chat-msg chat-msg--assistant"><ThinkingBlock thinking="检查代码和测试覆盖。" /><div className="chat-msg__assistant-content"><MarkdownContent content={"我先看一下工作区,再继续分析实现。\n\nhttps://example.com/very/long/path/that/should/wrap/without/overflowing/the/mobile/viewport"} /></div></div></div>
4143
<ChatComposerBar queueContent={<MobileHostQueue queue={queue} onRestore={noop} />}
4244
cancelling={false} containerRef={ref} expanded={expanded} imageAnalyzing={false} sending={false}
4345
input="继续检查" inputRef={null} modelControls={null} onActivate={noop} onAttach={noop} onCancel={() => calls.push('stop')}

‎src/mobile-web/tests/host-dialog-queue-browser.test.mjs‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ test('queue stays above the measured composer across phone, keyboard-height and
6060
const page=await browser.newPage();
6161
for(const [width,height] of [[320,568],[390,844],[390,420],[768,800],[1200,900]]) {
6262
await page.setViewport({width,height});
63-
await page.goto(server.origin+'/tests/fixtures/host-queue.html?count=8');
63+
await page.goto(server.origin+'/tests/fixtures/host-queue.html?count=8&theme='+ (width===768 ? 'light' : 'dark'));
6464
await page.waitForSelector('.host-message-queue li');
6565
await page.waitForFunction(()=>{
6666
const wrap=document.querySelector('.chat-page__input-wrap');
@@ -99,6 +99,21 @@ test('queue stays above the measured composer across phone, keyboard-height and
9999
await page.goto(server.origin+'/tests/fixtures/host-queue.html?count=2&expanded=false');
100100
await page.waitForSelector('.host-message-queue li');
101101
assert.equal(await page.$('.chat-page__input'),null,'queue controls also work alongside the collapsed composer');
102+
const geometry=await page.evaluate(()=>{
103+
const box=s=>document.querySelector(s).getBoundingClientRect();
104+
const composer=box('.chat-page__composer'),stop=box('.chat-page__send-btn.is-stop');
105+
const plus=box('.chat-page__composer-leading');
106+
const text=box('.chat-msg__assistant-content');
107+
const icon=box('.chat-thinking [data-openbitfun-part="leading"]');
108+
return {top:stop.top-composer.top,bottom:composer.bottom-stop.bottom,right:composer.right-stop.right,
109+
left:plus.left-composer.left,width:stop.width,plusWidth:plus.width,
110+
textLeft:text.left+parseFloat(getComputedStyle(document.querySelector('.chat-msg__assistant-content')).paddingLeft),iconLeft:icon.left};
111+
});
112+
assert.equal(geometry.width,44);
113+
assert.equal(geometry.plusWidth,44);
114+
for(const edge of ['top','bottom','left']) assert.ok(Math.abs(geometry[edge]-geometry.right)<1,JSON.stringify(geometry));
115+
assert.ok(Math.abs(geometry.textLeft-geometry.iconLeft)<1,JSON.stringify(geometry));
116+
await page.screenshot({path:'/tmp/mobile-queue-aligned.png',fullPage:true});
102117
await page.click('[aria-label="排队消息说明"]');
103118
await page.waitForSelector('.host-message-queue__help');
104119
await page.click('[aria-label="排队消息说明"]');

‎src/web-ui/src/flow_chat/utils/turnCompletionNotice.test.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ describe('getTurnCompletionNotice', () => {
99
} as any)).toBeNull();
1010
});
1111

12+
it.each([true, false, undefined])('does not warn on user steering (final response: %s)', (hasFinalResponse) => {
13+
expect(getTurnCompletionNotice({
14+
status: 'completed', finishReason: 'user_steering', hasFinalResponse,
15+
})).toBeNull();
16+
});
17+
1218
it('returns null for non-completed turns', () => {
1319
expect(getTurnCompletionNotice({
1420
status: 'processing',

‎src/web-ui/src/flow_chat/utils/turnCompletionNotice.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface NormalizedTurnCompletionNoticeState {
1919
hasFinalResponse?: boolean;
2020
}
2121

22-
const NORMAL_FINISH_REASONS = new Set(['complete']);
22+
const NORMAL_FINISH_REASONS = new Set(['complete', 'user_steering']);
2323

2424
const TURN_COMPLETION_NOTICE_CONFIG: Record<string, TurnCompletionNoticeConfig> = {
2525
repeated_tool_failures: {

0 commit comments

Comments
 (0)