diff --git a/frontend/src/v2/__tests__/V2ActivityPage.test.tsx b/frontend/src/v2/__tests__/V2ActivityPage.test.tsx index cf0540fd3..5355a6b52 100644 --- a/frontend/src/v2/__tests__/V2ActivityPage.test.tsx +++ b/frontend/src/v2/__tests__/V2ActivityPage.test.tsx @@ -1706,6 +1706,9 @@ describe('V2ActivityPage', () => { // The shape agent posts arrive in: whitespace already folded by the recap. expect(plainLine('Done: - a - b - c ## Shipped **ok**')).toBe('Done: a - b - c Shipped ok'); expect(plainLine('```\nlog line\n```')).toBe('log line'); + // Folded, as agent posts arrive: the fence ends where its closing marks are, not at the end. + expect(plainLine('Before ```js code ``` after')).toBe('Before after'); + expect(plainLine('```sh npm test```')).toBe('npm test'); expect(plainLine('中_文_字 and __init__.py stay')).toBe('中_文_字 and __init__.py stay'); const link = `${'y'.repeat(160)} see [the doc](https://example.com/aaaaaaaaaaaa`.slice(0, 180); expect(plainLine(link)).toMatch(/ see the doc…$/); diff --git a/frontend/src/v2/__tests__/V2ConnectPage.test.tsx b/frontend/src/v2/__tests__/V2ConnectPage.test.tsx index 05eba8cb5..2e361ad66 100644 --- a/frontend/src/v2/__tests__/V2ConnectPage.test.tsx +++ b/frontend/src/v2/__tests__/V2ConnectPage.test.tsx @@ -164,6 +164,8 @@ describe('V2ConnectPage in zh-CN', () => { expect(container.querySelector('.v2-connect__rule .v2-connect__code-head > span')) .toHaveTextContent(`Cursor 规则 · 在项目中保存为 ${CURSOR_PROJECT_RULE_PATH}`); expect(container.querySelector('.v2-connect__cursor')).toHaveTextContent('在 Cursor 的 MCP 设置里填入你的智能体密钥。'); + // The intro line that replaced the head's hint renders translated, not just present in source. + expect(container.querySelector('p.v2-connect__intro')).toHaveTextContent('MCP 用的是接入时创建的智能体密钥。'); expect(chrome.join('\n')).not.toMatch(/\b(Copy|Copied|Connect|Manage|Add|Set up|Open|Instance keys|save as)\b/); fireEvent.click(screen.getAllByRole('button', { name: '复制' })[0]); expect(await screen.findByRole('button', { name: '已复制' })).toBeInTheDocument(); diff --git a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts index c3a50b30c..10acb301f 100644 --- a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts +++ b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts @@ -6133,6 +6133,19 @@ describe('secondary pages on the reference grammar (ui-audit-1104 findings 2–8 expect(css.lastIndexOf('@media (max-width: 760px)')).toBeLessThan(css.indexOf('.v2-feature--aprofile .v2-aprofile__avatar-button::after')); }); + test('the public bar\'s keys (Sign up) reach 44px to touch on a phone', () => { + const css = read('../agents/v2-agent-profile.css'); + expect(ruleBody(css, '.v2-aprofile .v2-aprofile__btn')).toContain('height: 28px'); + // Both bar keys carry a 1px border, and an absolute ::before measures from the padding box, + // so the overlay starts from 26, not 28 (measured in Chromium: -8px gave 42). + expect(ruleBody(css, '.v2-aprofile .v2-aprofile__btn--primary')).toContain('border: 1px solid'); + const phone = css.slice(css.lastIndexOf('@media (max-width: 760px)')); + const hits = [...phone.matchAll(/\.v2-aprofile \.v2-aprofile__bar \.v2-aprofile__btn::before \{([^}]*)\}/g)].map((m) => m[1]); + // The last inset wins: its vertical reach must make 44 from the 26px padding box. + const inset = hits.map((body) => body.match(/inset:\s*(-?\d+)px/)).filter(Boolean).pop(); + expect(28 - 2 - 2 * Number(inset?.[1])).toBeGreaterThanOrEqual(44); + }); + test('finding 6: the board header icon keys never shrink on a phone', () => { const phone = mediaBlockContaining(boardCss, '.v2-feature--board .v2-board__key--icon { flex: none; }'); expect(ruleBody(boardCss, '.v2-feature--board .v2-board__key--icon', { within: phone })).toContain('flex: none'); @@ -6197,6 +6210,7 @@ describe('Activity built to the d215 signal frames (#s-activity-signal)', () => const text = rule('.v2-activity__moved-text'); expect(text).toContain('-webkit-line-clamp: 2'); expect(text).toContain('display: -webkit-box'); + expect(text).toContain('-webkit-box-orient: vertical'); expect(text).toContain('overflow: hidden'); }); diff --git a/frontend/src/v2/agents/v2-agent-profile.css b/frontend/src/v2/agents/v2-agent-profile.css index 4b78418d9..2b9bc2d66 100644 --- a/frontend/src/v2/agents/v2-agent-profile.css +++ b/frontend/src/v2/agents/v2-agent-profile.css @@ -156,6 +156,9 @@ .v2-aprofile .v2-aprofile__bar .v2-aprofile__btn { position: relative; } .v2-aprofile .v2-aprofile__navlink::before, .v2-aprofile .v2-aprofile__bar .v2-aprofile__btn::before { content: ''; position: absolute; inset: -5px -6px; } + /* The bar key draws at the v2p small key's 28 with a 1px border, and an absolute overlay + measures from inside the border (26), so it reaches 9px each way: 26 + 18 = 44. */ + .v2-aprofile .v2-aprofile__bar .v2-aprofile__btn::before { inset: -9px -6px; } .v2-aprofile .v2-aprofile__navlink::before { inset: -14px -7px; } .v2-aprofile .v2-agent-settings__tabs > * { box-sizing: border-box; min-height: 44px; display: inline-flex; align-items: center; } .v2-aprofile .v2-aprofile__pod-remove { height: 44px; line-height: 44px; } diff --git a/frontend/src/v2/components/V2ActivityPage.tsx b/frontend/src/v2/components/V2ActivityPage.tsx index 876574771..97c3e1579 100644 --- a/frontend/src/v2/components/V2ActivityPage.tsx +++ b/frontend/src/v2/components/V2ActivityPage.tsx @@ -167,7 +167,8 @@ const RECAP_CLIP = 180; export const plainLine = (value: string): string => { const clipped = value.length >= RECAP_CLIP; const fold = (text: string) => text.replace(/\s+/g, ' ').trim(); - const fence = /```[^\n]*\n?([\s\S]*?)(?:```|$)/g; + // The info string is a bare word (js, ts, sh), so a folded post's fence keeps the text after it. + const fence = /```[\w+-]*[ \t]*\n?([\s\S]*?)(?:```|$)/g; const prose = value.replace(fence, ' '); let text = (fold(prose) ? prose : value.replace(fence, ' $1 ')) .replace(/!?\[([^\]]*)\]\([^)]*\)/g, '$1');