Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions src/mobile-web/src/styles/components/chat-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,8 @@

.chat-page__action-btn {
flex-shrink: 0;
width: var(--openbitfun-space-12);
height: var(--openbitfun-space-12);
width: 44px;
height: 44px;
border-radius: 50%;
border: none;
background: transparent;
Expand All @@ -339,8 +339,8 @@

.chat-page__send-btn {
position: relative;
width: var(--openbitfun-space-12);
height: var(--openbitfun-space-12);
width: 44px;
height: 44px;
border-radius: 50%;
border: 1px solid var(--openbitfun-color-border-subtle);
background: var(--openbitfun-color-action-neutral-surface);
Expand Down Expand Up @@ -554,7 +554,7 @@

// Conversation composer layout and skin. Shared geometry belongs to MobileComposer.
.chat-page__composer {
padding: 0 8px;
padding: 5px;
border-color: var(--openbitfun-color-border-subtle);
border-radius: 28px;
background: var(--openbitfun-color-surface-panel);
Expand All @@ -564,7 +564,7 @@
.chat-page__composer[data-expanded='true'] {
height: auto;
max-height: none;
padding: 8px;
padding: 5px;
border-radius: 18px;
}

Expand All @@ -575,9 +575,9 @@
.chat-page__composer-leading {
display: grid;
place-items: center;
flex: 0 0 48px;
width: 48px;
height: 48px;
flex: 0 0 44px;
width: 44px;
height: 44px;
padding: 0;
border: 0;
border-radius: 20px;
Expand Down Expand Up @@ -608,8 +608,8 @@
}

.chat-page__input-wrap.is-expanded .chat-page__action-btn {
width: 48px;
height: 48px;
width: 44px;
height: 44px;
color: var(--openbitfun-color-content-primary);
}

Expand Down Expand Up @@ -645,3 +645,8 @@
aspect-ratio: 1;
&:hover, &:active, &:focus { border-radius: 50%; }
}

// Keep the action row and collapsed pill on the same inset in both modes.
.chat-page__composer [data-openbitfun-part='toolbar'] {
min-block-size: 44px;
}
4 changes: 3 additions & 1 deletion src/mobile-web/src/styles/components/harmony-native.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,9 @@
.chat-msg__assistant-content {
width: 100%;
max-width: 100%;
padding: 0 2px 10px 0;
padding: 0 var(--size-gap-3) 10px;
box-sizing: border-box;
min-width: 0;
border: 0;
border-radius: 0;
background: transparent;
Expand Down
5 changes: 3 additions & 2 deletions src/mobile-web/src/styles/host-queue.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
font-size: var(--openbitfun-type-body-sm-font-size);
line-height: var(--openbitfun-type-body-sm-line-height);

&__header { display: flex; align-items: center; padding: 0 12px; }
// 7px + half a 40px queue control matches the composer: 5px + half of 44px.
&__header { display: flex; align-items: center; padding: 0 7px 0 12px; }
&__toggle {
display: flex;
align-items: center;
Expand Down Expand Up @@ -53,7 +54,7 @@
&__list {
list-style: none;
margin: 0;
padding: 0 12px;
padding: 0 7px 0 12px;
li { padding: 4px 0; border-top: 1px solid var(--openbitfun-color-border-subtle); }
p { margin: 0 0 4px; }
}
Expand Down
4 changes: 3 additions & 1 deletion src/mobile-web/tests/fixtures/host-queue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import '@openbitfun/theme-openbitfun/default.css';
import '@openbitfun/ui/mobile.css';
import '../../src/styles/index.scss';
import { createRoot } from 'react-dom/client';
import { ThinkingBlock } from '../../src/components/ChatTranscript';
import { MarkdownContent } from '../../src/components/ChatMarkdown';
import ChatComposerBar from '../../src/components/ChatComposerBar';
import { MobileHostQueue } from '../../src/components/MobileHostQueue';
import { useMobileViewport } from '../../src/hooks/useMobileViewport';
Expand Down Expand Up @@ -37,7 +39,7 @@ export function mountHostQueueFixture({ count = 1, expanded = true } = {}) {
}, []);
return <ThemeProvider><I18nProvider><div className={`chat-page${window.innerWidth >= 900 ? ' chat-page--wide' : ''}`} style={{ '--chat-composer-height': `${height}px` } as React.CSSProperties}>
<header className="chat-page__header">项目介绍</header>
<div className="chat-page__messages"><p>正在检查代码和测试覆盖。</p><p>ExecCommand</p><p>分析当前项目的实现。</p></div>
<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>
<ChatComposerBar queueContent={<MobileHostQueue queue={queue} onRestore={noop} />}
cancelling={false} containerRef={ref} expanded={expanded} imageAnalyzing={false} sending={false}
input="继续检查" inputRef={null} modelControls={null} onActivate={noop} onAttach={noop} onCancel={() => calls.push('stop')}
Expand Down
17 changes: 16 additions & 1 deletion src/mobile-web/tests/host-dialog-queue-browser.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ test('queue stays above the measured composer across phone, keyboard-height and
const page=await browser.newPage();
for(const [width,height] of [[320,568],[390,844],[390,420],[768,800],[1200,900]]) {
await page.setViewport({width,height});
await page.goto(server.origin+'/tests/fixtures/host-queue.html?count=8');
await page.goto(server.origin+'/tests/fixtures/host-queue.html?count=8&theme='+ (width===768 ? 'light' : 'dark'));
await page.waitForSelector('.host-message-queue li');
await page.waitForFunction(()=>{
const wrap=document.querySelector('.chat-page__input-wrap');
Expand Down Expand Up @@ -99,6 +99,21 @@ test('queue stays above the measured composer across phone, keyboard-height and
await page.goto(server.origin+'/tests/fixtures/host-queue.html?count=2&expanded=false');
await page.waitForSelector('.host-message-queue li');
assert.equal(await page.$('.chat-page__input'),null,'queue controls also work alongside the collapsed composer');
const geometry=await page.evaluate(()=>{
const box=s=>document.querySelector(s).getBoundingClientRect();
const composer=box('.chat-page__composer'),stop=box('.chat-page__send-btn.is-stop');
const plus=box('.chat-page__composer-leading');
const text=box('.chat-msg__assistant-content');
const icon=box('.chat-thinking [data-openbitfun-part="leading"]');
return {top:stop.top-composer.top,bottom:composer.bottom-stop.bottom,right:composer.right-stop.right,
left:plus.left-composer.left,width:stop.width,plusWidth:plus.width,
textLeft:text.left+parseFloat(getComputedStyle(document.querySelector('.chat-msg__assistant-content')).paddingLeft),iconLeft:icon.left};
});
assert.equal(geometry.width,44);
assert.equal(geometry.plusWidth,44);
for(const edge of ['top','bottom','left']) assert.ok(Math.abs(geometry[edge]-geometry.right)<1,JSON.stringify(geometry));
assert.ok(Math.abs(geometry.textLeft-geometry.iconLeft)<1,JSON.stringify(geometry));
await page.screenshot({path:'/tmp/mobile-queue-aligned.png',fullPage:true});
await page.click('[aria-label="排队消息说明"]');
await page.waitForSelector('.host-message-queue__help');
await page.click('[aria-label="排队消息说明"]');
Expand Down
6 changes: 6 additions & 0 deletions src/web-ui/src/flow_chat/utils/turnCompletionNotice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ describe('getTurnCompletionNotice', () => {
} as any)).toBeNull();
});

it.each([true, false, undefined])('does not warn on user steering (final response: %s)', (hasFinalResponse) => {
expect(getTurnCompletionNotice({
status: 'completed', finishReason: 'user_steering', hasFinalResponse,
})).toBeNull();
});

it('returns null for non-completed turns', () => {
expect(getTurnCompletionNotice({
status: 'processing',
Expand Down
2 changes: 1 addition & 1 deletion src/web-ui/src/flow_chat/utils/turnCompletionNotice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface NormalizedTurnCompletionNoticeState {
hasFinalResponse?: boolean;
}

const NORMAL_FINISH_REASONS = new Set(['complete']);
const NORMAL_FINISH_REASONS = new Set(['complete', 'user_steering']);

const TURN_COMPLETION_NOTICE_CONFIG: Record<string, TurnCompletionNoticeConfig> = {
repeated_tool_failures: {
Expand Down
Loading