Skip to content

Commit becf79a

Browse files
committed
chore: sync Qwen Code v0.21.13 (9/9)
1 parent 3d4b136 commit becf79a

95 files changed

Lines changed: 15586 additions & 4439 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/web-shell/client/components/messages/SystemMessage.module.css

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,10 @@
2323
margin-top: 0.55em;
2424
}
2525

26-
.noMarker::before {
27-
display: none;
28-
}
29-
3026
.content {
3127
min-width: 0;
3228
}
3329

34-
.notificationContent {
35-
display: flex;
36-
align-items: flex-start;
37-
gap: 8px;
38-
}
39-
4030
.notificationIcon {
4131
display: inline-flex;
4232
width: 14px;
@@ -62,6 +52,42 @@
6252
min-width: 0;
6353
}
6454

55+
/* Left-aligned bubble layout, matching UserMessage's chat mode. */
56+
.notificationBubbleRow {
57+
display: flex;
58+
width: 100%;
59+
justify-content: flex-start;
60+
margin: 12px 0;
61+
}
62+
63+
.notificationBubbleColumn {
64+
display: flex;
65+
max-width: 80%;
66+
flex-direction: column;
67+
align-items: flex-start;
68+
}
69+
70+
.notificationBubble {
71+
display: inline-flex;
72+
max-width: 100%;
73+
align-items: flex-start;
74+
gap: 8px;
75+
padding: 8px 12px;
76+
border-radius: 6px;
77+
background: var(--accent);
78+
color: var(--foreground);
79+
font-family: var(--font-sans, system-ui, sans-serif);
80+
font-size: 14px;
81+
line-height: 1.5;
82+
text-align: left;
83+
white-space: pre-wrap;
84+
word-break: break-word;
85+
}
86+
87+
.notificationBubble .notificationIcon {
88+
margin-top: calc((1.5em - 14px) / 2);
89+
}
90+
6591
.message pre {
6692
display: inline;
6793
margin: 0;

packages/web-shell/client/components/messages/SystemMessage.test.tsx

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,220 @@ describe('SystemMessage — background notification label', () => {
134134
});
135135
});
136136

137+
describe('SystemMessage — background notification i18n body', () => {
138+
it('renders shell notifications with structured command via i18n', () => {
139+
const container = render(
140+
<SystemMessage
141+
content='Background shell "npm test" completed.'
142+
variant="info"
143+
source="background_notification"
144+
data={{
145+
status: 'completed',
146+
kind: 'shell',
147+
commandLabel: 'npm test',
148+
}}
149+
/>,
150+
'zh-CN',
151+
);
152+
153+
expect(container.textContent).toContain('后台 Shell 已完成:npm test');
154+
expect(container.textContent).not.toContain(
155+
'Background shell "npm test" completed.',
156+
);
157+
});
158+
159+
it('renders agent notifications with structured description via i18n', () => {
160+
const container = render(
161+
<SystemMessage
162+
content='Background agent "worker" completed.'
163+
variant="info"
164+
source="background_notification"
165+
data={{
166+
status: 'completed',
167+
kind: 'agent',
168+
description: 'worker',
169+
}}
170+
/>,
171+
'zh-CN',
172+
);
173+
174+
expect(container.textContent).toContain('后台智能体已完成:worker');
175+
});
176+
177+
it('renders monitor notifications with the event count in English', () => {
178+
const container = render(
179+
<SystemMessage
180+
content="raw daemon text"
181+
variant="info"
182+
source="background_notification"
183+
data={{
184+
status: 'completed',
185+
kind: 'monitor',
186+
description: 'logs',
187+
eventCount: 5,
188+
}}
189+
/>,
190+
);
191+
192+
expect(container.textContent).toContain(
193+
'Monitor "logs" completed. (5 events)',
194+
);
195+
expect(container.textContent).not.toContain('raw daemon text');
196+
});
197+
198+
it('renders the dropped-lines clause when throttling dropped output', () => {
199+
const container = render(
200+
<SystemMessage
201+
content="raw daemon text"
202+
variant="info"
203+
source="background_notification"
204+
data={{
205+
status: 'completed',
206+
kind: 'monitor',
207+
description: 'logs',
208+
eventCount: 5,
209+
droppedLines: 2,
210+
}}
211+
/>,
212+
);
213+
214+
expect(container.textContent).toContain(
215+
'Monitor "logs" completed. (5 events, 2 lines dropped due to throttling)',
216+
);
217+
});
218+
219+
it('renders the dropped-lines clause in zh-CN when throttling dropped output', () => {
220+
const container = render(
221+
<SystemMessage
222+
content="raw daemon text"
223+
variant="info"
224+
source="background_notification"
225+
data={{
226+
status: 'completed',
227+
kind: 'monitor',
228+
description: 'logs',
229+
eventCount: 5,
230+
droppedLines: 2,
231+
}}
232+
/>,
233+
'zh-CN',
234+
);
235+
236+
expect(container.textContent).toContain(
237+
'监控器已完成(5 个事件,因限流丢弃 2 行):logs',
238+
);
239+
});
240+
241+
it('renders failed monitor notifications with the event count in zh-CN', () => {
242+
const container = render(
243+
<SystemMessage
244+
content="raw daemon text"
245+
variant="info"
246+
source="background_notification"
247+
data={{
248+
status: 'failed',
249+
kind: 'monitor',
250+
description: 'logs',
251+
eventCount: 42,
252+
}}
253+
/>,
254+
'zh-CN',
255+
);
256+
257+
expect(container.textContent).toContain(
258+
'监控器执行失败(42 个事件):logs',
259+
);
260+
});
261+
262+
it('renders cancelled shell notifications via i18n', () => {
263+
const container = render(
264+
<SystemMessage
265+
content="raw daemon text"
266+
variant="info"
267+
source="background_notification"
268+
data={{
269+
status: 'cancelled',
270+
kind: 'shell',
271+
commandLabel: 'npm test',
272+
}}
273+
/>,
274+
'zh-CN',
275+
);
276+
277+
expect(container.textContent).toContain('后台 Shell 已取消:npm test');
278+
});
279+
280+
it('renders failed agent notifications via i18n in English', () => {
281+
const container = render(
282+
<SystemMessage
283+
content="raw daemon text"
284+
variant="info"
285+
source="background_notification"
286+
data={{ status: 'failed', kind: 'agent', description: 'worker' }}
287+
/>,
288+
);
289+
290+
expect(container.textContent).toContain(
291+
'Background agent "worker" failed.',
292+
);
293+
});
294+
295+
it('falls back to raw content for unknown terminal statuses', () => {
296+
const container = render(
297+
<SystemMessage
298+
content='Monitor "logs" timed out.'
299+
variant="info"
300+
source="background_notification"
301+
data={{ status: 'timeout', kind: 'monitor', description: 'logs' }}
302+
/>,
303+
);
304+
305+
expect(container.textContent).toContain('Monitor "logs" timed out.');
306+
expect(container.textContent).not.toContain('notification.monitor.timeout');
307+
});
308+
309+
it('falls back to raw content when structured fields are absent', () => {
310+
const container = render(
311+
<SystemMessage
312+
content='Background shell "npm test" completed.'
313+
variant="info"
314+
source="background_notification"
315+
data={{ status: 'completed', kind: 'shell' }}
316+
/>,
317+
);
318+
319+
expect(container.textContent).toContain(
320+
'Background shell "npm test" completed.',
321+
);
322+
});
323+
324+
it('renders the fallback through Markdown so session links stay clickable', () => {
325+
const seen: unknown[] = [];
326+
const handler = (e: Event) => seen.push((e as CustomEvent).detail);
327+
window.addEventListener('qwen:open-session', handler);
328+
const container = render(
329+
<SystemMessage
330+
content="Sub-session [🧵 abc12345](qwen-session://abc12345-full) completed."
331+
variant="info"
332+
source="background_notification"
333+
data={{ status: 'completed', kind: 'agent' }}
334+
/>,
335+
);
336+
337+
const link = container.querySelector('a[role="button"]');
338+
expect(link).not.toBeNull();
339+
expect(link?.textContent).toBe('🧵 abc12345');
340+
expect(container.textContent).not.toContain('](');
341+
act(() => {
342+
link?.dispatchEvent(
343+
new MouseEvent('click', { bubbles: true, cancelable: true }),
344+
);
345+
});
346+
expect(seen).toEqual(['abc12345-full']);
347+
window.removeEventListener('qwen:open-session', handler);
348+
});
349+
});
350+
137351
describe('SystemMessage — goal status activation', () => {
138352
const content = serializeGoalStatusMessage({
139353
kind: 'set',

0 commit comments

Comments
 (0)