diff --git a/frontend/src/__tests__/timingClaims.test.ts b/frontend/src/__tests__/timingClaims.test.ts
index 3ef2898c0c..bcee9be227 100644
--- a/frontend/src/__tests__/timingClaims.test.ts
+++ b/frontend/src/__tests__/timingClaims.test.ts
@@ -15,8 +15,8 @@ describe('timing claims in the copy', () => {
// at 2.54 s (install 1.83 + provision 0.63); walk 4: 2.47 s.
// QA report qa-hosted-oob-5, timing table.
test('click to live says "a few seconds", in both languages', () => {
- expect(en.agentByo.mode.hostedMeta).toBe('Live in a few seconds · Beta: {{turns}} turns a day');
- expect(en.agentByo.compact.lead).toBe('Live in a few seconds.');
+ expect(en.agentByo.mode.hostedMeta).toBe('Nothing to install. Live in a few seconds. Beta: {{turns}} turns a day.');
+ expect(en.agentByo.compact.lead).toBe('Live in a few seconds. Beta: {{agents}} hosted agent per account, {{turns}} turns a day.');
expect(zh.agentByo.mode.hostedMeta).toContain('几秒');
expect(zh.agentByo.compact.lead).toContain('几秒');
});
diff --git a/frontend/src/i18n/__tests__/i18n.test.ts b/frontend/src/i18n/__tests__/i18n.test.ts
index 1687a57b2b..a90d183c4e 100644
--- a/frontend/src/i18n/__tests__/i18n.test.ts
+++ b/frontend/src/i18n/__tests__/i18n.test.ts
@@ -133,8 +133,8 @@ describe('i18n configuration', () => {
});
it('uses a Chinese comma in the BYO machine hint', () => {
- const hint = i18n.t('agentByo.mode.machineHint', { lng: 'zh-CN' });
- expect(hint).toBe('在你已添加的电脑上运行,你的文件、你的 CLI');
+ const hint = i18n.t('agentByo.mode.machineMeta', { lng: 'zh-CN' });
+ expect(hint).toBe('在你已添加的电脑上运行,用你的文件和 CLI。需要那台电脑运行 Commonly 守护进程。');
expect(hint).not.toContain('—');
});
});
diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json
index 15d85018a9..fc064be479 100644
--- a/frontend/src/i18n/locales/en.json
+++ b/frontend/src/i18n/locales/en.json
@@ -134,7 +134,7 @@
"avatar": {
"regenerate": "Regenerate",
"uploadPhoto": "Upload photo",
- "description": "No face and no skin tone until you choose one. Regenerate for another, or upload a photo.",
+ "description": "Regenerate for another, or upload a photo.",
"descriptionAfterChoice": "Regenerate for another, or upload a photo.",
"uploadTitle": "Upload photo",
"cropPreview": "Avatar crop preview",
@@ -495,7 +495,7 @@
"people": "Invite people",
"agent": "Add agent"
},
- "peopleHint": "Create a link anyone with a Commonly account can use to join this pod.",
+ "peopleHint": "Anyone with the link can join.",
"podMember": "Pod member",
"options": {
"expiresAfter": "Expires after",
@@ -513,7 +513,6 @@
"generate": "Generate invite link",
"generating": "Generating…",
"newLinkLabel": "New invite link",
- "newLinkHint": "The new link also appears under Active links below.",
"activeLinks": "Active links",
"loadingLinks": "Loading links…",
"noActiveLinks": "No active invite links.",
@@ -541,7 +540,7 @@
"unavailable": "Expiry unavailable",
"expiresOn": "Expires {{date}}"
},
- "agentHint": "Add an agent — Claude Code or Codex, or run one here — and it joins this pod. Takes about two minutes.",
+ "agentHint": "Claude Code, Codex, or run one here.",
"connectOwnAgent": "Connect your own agent →",
"errors": {
"loadFailed": "Could not load invite links.",
@@ -1627,7 +1626,7 @@
"description": "A running agent answers @mentions in its pod.",
"form": {
"nameLabel": "Agent name",
- "nameHint": "Lower-case letters, digits, and dashes. It posts under this name.",
+ "nameHint": "Lowercase letters, digits and dashes.",
"podLabel": "Pod it joins",
"loadingPods": "Loading pods…",
"machineLabel": "Computer",
@@ -1682,7 +1681,7 @@
"cursorInstall": "Add to Cursor",
"cursorInstallHint": "The link adds Commonly with a placeholder key. Replace it in Cursor’s MCP settings with the key above.",
"memory": "Bring its memory (optional)",
- "claudeCodeBody": "Gives your own Claude Code sessions Commonly’s tools. It does not make {{name}} answer @mentions; the command above does.",
+ "claudeCodeBody": "Gives your Claude Code sessions Commonly’s tools. Answering @mentions needs the command above.",
"claudeCodeBodyOwn": "Gives your own Claude Code sessions Commonly’s tools.",
"claudeCodeBodyWindows": "Paste both lines in PowerShell. They give your own Claude Code sessions on this machine Commonly’s tools.",
"claudeCodeMain": "Let Claude Code use the pod in your own sessions"
@@ -1738,17 +1737,12 @@
"mode": {
"label": "Where should it run?",
"hosted": "Run it here",
- "hostedHint": "Commonly runs it for you. Nothing to install. Beta: {{turns}} turns a day.",
"byo": "Use my own agent",
- "byoHint": "Your Claude Code or Codex, on your computer.",
- "onCommonly": "on Commonly",
- "yours": "your machine",
- "hostedMeta": "Live in a few seconds · Beta: {{turns}} turns a day",
- "byoMeta": "One command in Terminal",
+ "hostedMeta": "Nothing to install. Live in a few seconds. Beta: {{turns}} turns a day.",
+ "byoMeta": "Your Claude Code or Codex, on your computer. One command in Terminal.",
"moreAbout": "More about {{mode}}",
"machine": "On my computer",
- "machineHint": "Runs on a computer you've added — your files, your CLIs",
- "machineMeta": "Needs the Commonly daemon running there"
+ "machineMeta": "Runs on a computer you've added, with your files and CLIs. Needs the Commonly daemon running there."
},
"hosted": {
"offerAction": "Run it here instead",
@@ -1798,7 +1792,7 @@
},
"compact": {
"title": "Run an agent here",
- "lead": "Live in a few seconds.",
+ "lead": "Live in a few seconds. Beta: {{agents}} hosted agent per account, {{turns}} turns a day.",
"joins": "It joins {{pod}} as {{name}}.",
"change": "Change",
"limits": "Beta: {{agents}} hosted agent per account, {{turns}} turns a day.",
@@ -2224,7 +2218,7 @@
},
"own": {
"title": "Your own agent",
- "text": "Claude Code, Codex, or anything that speaks the protocol. One command on your machine; it keeps its identity here.",
+ "text": "Claude Code or Codex, from your own machine.",
"command": "npx @commonlyai/cli agent attach claude",
"setup": "Set it up"
}
@@ -2306,7 +2300,6 @@
"codeExpired": "The enable code expired.",
"connect": "Connect",
"connectChannel": "Connect a channel",
- "connectChannelHint": "Choose a channel and the pod it should join.",
"copied": "Copied",
"copyCommand": "Copy command",
"description": "Each channel gets one agent from a pod. It answers there in its own name; the rest of the team stays behind it.",
diff --git a/frontend/src/i18n/locales/zh-CN.json b/frontend/src/i18n/locales/zh-CN.json
index 9984f7b282..73513cecd1 100644
--- a/frontend/src/i18n/locales/zh-CN.json
+++ b/frontend/src/i18n/locales/zh-CN.json
@@ -134,7 +134,7 @@
"avatar": {
"regenerate": "换一个",
"uploadPhoto": "上传照片",
- "description": "选择前不会显示面部或肤色。你可以换一个造型,或上传照片。",
+ "description": "你可以换一个造型,或上传照片。",
"descriptionAfterChoice": "你可以换一个造型,或上传照片。",
"uploadTitle": "上传照片",
"cropPreview": "头像裁剪预览",
@@ -495,7 +495,7 @@
"people": "邀请成员",
"agent": "添加智能体"
},
- "peopleHint": "创建邀请链接,拥有 Commonly 账号的人都能用它加入这个 Pod。",
+ "peopleHint": "拿到链接的人都能加入。",
"podMember": "Pod 成员",
"options": {
"expiresAfter": "有效期",
@@ -513,7 +513,6 @@
"generate": "生成邀请链接",
"generating": "正在生成…",
"newLinkLabel": "新邀请链接",
- "newLinkHint": "新链接也会出现在下方的有效链接中。",
"activeLinks": "有效链接",
"loadingLinks": "正在加载链接…",
"noActiveLinks": "暂无有效邀请链接。",
@@ -541,7 +540,7 @@
"unavailable": "无法查看有效期",
"expiresOn": "{{date}} 到期"
},
- "agentHint": "添加一个智能体 —— Claude Code 或 Codex,或在这里运行一个 —— 它就会加入这个 Pod。大约需要两分钟。",
+ "agentHint": "Claude Code、Codex,或在这里运行一个。",
"connectOwnAgent": "连接你自己的智能体 →",
"errors": {
"loadFailed": "邀请链接加载失败。",
@@ -1686,7 +1685,7 @@
"description": "智能体运行期间会在所在 Pod 回复 @提及。",
"form": {
"nameLabel": "智能体名称",
- "nameHint": "仅支持小写字母、数字和连字符。它会用这个名字发言。",
+ "nameHint": "仅支持小写字母、数字和连字符。",
"podLabel": "要加入的 Pod",
"loadingPods": "正在加载 Pod…",
"machineLabel": "电脑",
@@ -1741,7 +1740,7 @@
"cursorInstall": "添加到 Cursor",
"cursorInstallHint": "该链接会以占位密钥添加 Commonly。请在 Cursor 的 MCP 设置中将其替换为上面的密钥。",
"memory": "导入它的记忆(可选)",
- "claudeCodeBody": "为你自己的 Claude Code 会话加上 Commonly 的工具。它不会让 {{name}} 回复 @提及,上面那条命令才会。",
+ "claudeCodeBody": "为你的 Claude Code 会话加上 Commonly 的工具。回复 @提及需要上面的命令。",
"claudeCodeBodyOwn": "为你自己的 Claude Code 会话加上 Commonly 的工具。",
"claudeCodeBodyWindows": "两行都粘贴到 PowerShell 里运行。这样你在这台电脑上的 Claude Code 会话就能用上 Commonly 的工具。",
"claudeCodeMain": "让 Claude Code 在你自己的会话里使用这个 Pod"
@@ -1797,17 +1796,12 @@
"mode": {
"label": "在哪里运行?",
"hosted": "在这里运行",
- "hostedHint": "由 Commonly 代为运行,无需安装。测试期:每天{{turns}}轮。",
"byo": "用我自己的智能体",
- "byoHint": "你电脑上的 Claude Code 或 Codex。",
- "onCommonly": "在 Commonly 上",
- "yours": "你的电脑",
- "hostedMeta": "几秒即可上线 · 测试期每天{{turns}}轮",
- "byoMeta": "只需一条终端命令",
+ "hostedMeta": "无需安装,几秒即可上线。测试期每天{{turns}}轮。",
+ "byoMeta": "你电脑上的 Claude Code 或 Codex,只需一条终端命令。",
"moreAbout": "更多关于{{mode}}的信息",
"machine": "在我的电脑上",
- "machineHint": "在你已添加的电脑上运行,你的文件、你的 CLI",
- "machineMeta": "需要那台电脑运行 Commonly 守护进程"
+ "machineMeta": "在你已添加的电脑上运行,用你的文件和 CLI。需要那台电脑运行 Commonly 守护进程。"
},
"hosted": {
"offerAction": "改为在这里运行",
@@ -1857,7 +1851,7 @@
},
"compact": {
"title": "在这里运行智能体",
- "lead": "几秒后即可上线。",
+ "lead": "几秒后即可上线。测试期:每个账号{{agents}}个托管智能体,每天{{turns}}轮。",
"joins": "它会以 {{name}} 的身份加入 {{pod}}。",
"change": "更改",
"limits": "测试期:每个账号{{agents}}个托管智能体,每天{{turns}}轮。",
@@ -2214,7 +2208,7 @@
},
"own": {
"title": "你自己的智能体",
- "text": "Claude Code、Codex,或任何支持该协议的工具。在你的机器上运行一条命令,它的身份保留在这里。",
+ "text": "你自己电脑上的 Claude Code 或 Codex。",
"command": "npx @commonlyai/cli agent attach claude",
"setup": "开始设置"
}
@@ -2296,7 +2290,6 @@
"codeExpired": "启用码已过期。",
"connect": "连接",
"connectChannel": "连接频道",
- "connectChannelHint": "选择频道,以及它要加入的 Pod。",
"copied": "已复制",
"copyCommand": "复制命令",
"description": "每个频道从一个 Pod 接入一个智能体。它会在那里以自己的名义回复,其余团队成员都在幕后。",
diff --git a/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx b/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx
index 52b9bb3cf5..a7db131b17 100644
--- a/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx
+++ b/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx
@@ -56,6 +56,11 @@ const mockGet = ({ configured, status }) => {
});
};
+// The hosted mode card is titled "Run it here" too, so the act button is the
+// "Run it here" button that is not a mode toggle (no aria-pressed).
+const actButton = () => screen.queryAllByRole('button', { name: /^run it here$/i })
+ .find((b) => !b.hasAttribute('aria-pressed')) ?? null;
+
const renderPage = () => render(
@@ -79,12 +84,12 @@ describe('V2AgentBYO — Run it here', () => {
await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument());
expect(screen.getByRole('group', { name: 'Where should it run?' })).toBeInTheDocument();
- expect(screen.getByText('Live in a few seconds · Beta: 200 turns a day')).toBeInTheDocument();
+ expect(screen.getByText('Nothing to install. Live in a few seconds. Beta: 200 turns a day.')).toBeInTheDocument();
expect(document.body.textContent).not.toContain('{{');
expect(screen.getByTestId('byo-mode-hosted')).toHaveAttribute('aria-pressed', 'false');
expect(screen.getByTestId('byo-mode-byo')).toHaveAttribute('aria-pressed', 'false');
expect(screen.getByTestId('byo-mode-unchosen')).toHaveTextContent('Choose where it runs first');
- expect(screen.queryByRole('button', { name: /^run it here$/i })).not.toBeInTheDocument();
+ expect(actButton()).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: /get my connect command/i })).not.toBeInTheDocument();
fireEvent.click(screen.getByTestId('byo-mode-byo'));
@@ -148,9 +153,9 @@ describe('V2AgentBYO — Run it here', () => {
renderPage();
await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument());
- expect(screen.getAllByText(/beta: 200 turns a day/i)).toHaveLength(2);
+ expect(screen.getAllByText(/beta: 200 turns a day/i)).toHaveLength(1);
fireEvent.click(screen.getByTestId('byo-mode-hosted'));
- fireEvent.click(screen.getByRole('button', { name: /^run it here$/i }));
+ fireEvent.click(actButton());
await screen.findByTestId('byo-hosted-result');
const installCall = axios.post.mock.calls.find(([url]) => url === '/api/registry/install');
@@ -172,7 +177,7 @@ describe('V2AgentBYO — Run it here', () => {
await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument());
fireEvent.click(screen.getByTestId('byo-mode-hosted'));
- const act = screen.getByRole('button', { name: /^run it here$/i });
+ const act = actButton();
const addComputer = await screen.findByTestId('byo-add-computer');
// eslint-disable-next-line no-bitwise
expect(act.compareDocumentPosition(addComputer) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
@@ -223,12 +228,13 @@ describe('V2AgentBYO — Run it here', () => {
const compact = await screen.findByTestId('byo-hosted-compact');
expect(screen.getByRole('heading', { name: 'Run an agent here' })).toBeInTheDocument();
await waitFor(() => expect(compact).toHaveTextContent('It joins Workspace as sam-agent.'));
- await waitFor(() => expect(compact).toHaveTextContent('Beta: 1 hosted agent per account, 200 turns a day.'));
+ // The beta cap lives in the page-head (i), not in the body.
+ expect(screen.getByText(/Beta: 1 hosted agent per account, 200 turns a day\./)).toBeInTheDocument();
// None of the full page's choice and setup copy is in the way.
expect(screen.queryByTestId('byo-mode-hosted')).not.toBeInTheDocument();
expect(screen.queryByTestId('byo-add-computer')).not.toBeInTheDocument();
- const act = screen.getByRole('button', { name: /^run it here$/i });
+ const act = actButton();
await waitFor(() => expect(act).toBeEnabled());
fireEvent.click(act);
await screen.findByTestId('byo-hosted-result');
@@ -237,6 +243,18 @@ describe('V2AgentBYO — Run it here', () => {
window.history.pushState({}, '', '/');
});
+ // Before availability resolves, or when hosting is not configured, the lead
+ // must not interpolate caps: the (i) would read "Beta: undefined ...".
+ test('the compact hosted lead never shows undefined caps when hosting is not configured', async () => {
+ mockGet({ configured: false, status: { runtime: { lastPollAt: 1 } } });
+ renderAt('/v2/agents/byo?pod=p1&mode=hosted');
+
+ await screen.findByTestId('byo-hosted-compact');
+ expect(screen.queryByText(/undefined/)).not.toBeInTheDocument();
+ expect(screen.queryByText(/Beta:/)).not.toBeInTheDocument();
+ window.history.pushState({}, '', '/');
+ });
+
test('"Other ways to connect" opens the full page with Run it here still chosen', async () => {
mockGet({ configured: true, status: { runtime: { lastPollAt: 1 } } });
renderAt('/v2/agents/byo?pod=p1&mode=hosted');
@@ -266,7 +284,8 @@ describe('V2AgentBYO — Run it here', () => {
,
);
- const act = await screen.findByRole('button', { name: /^run it here$/i });
+ await waitFor(() => expect(actButton()).not.toBeNull());
+ const act = actButton();
await waitFor(() => expect(act).toBeEnabled());
fireEvent.click(act);
fireEvent.click(await screen.findByRole('button', { name: /go to your pod/i }));
@@ -290,10 +309,10 @@ describe('V2AgentBYO — Run it here', () => {
// The compact page shows at once, with the act disabled until the
// instance answers that it can host.
expect(await screen.findByTestId('byo-hosted-compact')).toBeInTheDocument();
- expect(screen.getByRole('button', { name: /^run it here$/i })).toBeDisabled();
+ expect(actButton()).toBeDisabled();
await act(async () => { answerAvailability({ data: { configured: true, caps: { agentsPerUser: 1, turnsPerDay: 30 } } }); });
- await waitFor(() => expect(screen.getByRole('button', { name: /^run it here$/i })).toBeEnabled());
+ await waitFor(() => expect(actButton()).toBeEnabled());
window.history.pushState({}, '', '/');
});
@@ -303,7 +322,7 @@ describe('V2AgentBYO — Run it here', () => {
await waitFor(() => expect(screen.getByRole('button', { name: /get my connect command/i })).toBeEnabled());
expect(screen.queryByTestId('byo-mode-hosted')).not.toBeInTheDocument();
- expect(screen.queryByRole('button', { name: /^run it here$/i })).not.toBeInTheDocument();
+ expect(actButton()).not.toBeInTheDocument();
window.history.pushState({}, '', '/');
});
@@ -323,7 +342,7 @@ describe('V2AgentBYO — Run it here', () => {
// A persona link says what to hire, not where it runs.
expect(screen.getByTestId('byo-mode-hosted')).toHaveAttribute('aria-pressed', 'false');
fireEvent.click(screen.getByTestId('byo-mode-hosted'));
- fireEvent.click(screen.getByRole('button', { name: /^run it here$/i }));
+ fireEvent.click(actButton());
await screen.findByTestId('byo-hosted-result');
const installCall = axios.post.mock.calls.find(([url]) => url === '/api/registry/install');
expect(installCall[1]).toMatchObject({
@@ -341,7 +360,7 @@ describe('V2AgentBYO — Run it here', () => {
renderPage();
await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument());
fireEvent.click(screen.getByTestId('byo-mode-hosted'));
- fireEvent.click(screen.getByRole('button', { name: /^run it here$/i }));
+ fireEvent.click(actButton());
await screen.findByTestId('byo-hosted-starting');
await act(async () => { jest.advanceTimersByTime(4000); });
@@ -361,7 +380,7 @@ describe('V2AgentBYO — Run it here', () => {
renderPage();
await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument());
fireEvent.click(screen.getByTestId('byo-mode-hosted'));
- fireEvent.click(screen.getByRole('button', { name: /^run it here$/i }));
+ fireEvent.click(actButton());
expect(await screen.findByTestId('byo-hosted-running')).toBeInTheDocument();
expect(screen.queryByTestId('byo-hosted-starting')).not.toBeInTheDocument();
@@ -375,7 +394,7 @@ describe('V2AgentBYO — Run it here', () => {
renderPage();
await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument());
fireEvent.click(screen.getByTestId('byo-mode-hosted'));
- fireEvent.click(screen.getByRole('button', { name: /^run it here$/i }));
+ fireEvent.click(actButton());
// No timer advanced: the first check runs on its own.
expect(await screen.findByTestId('byo-hosted-running')).toBeInTheDocument();
@@ -389,7 +408,7 @@ describe('V2AgentBYO — Run it here', () => {
renderPage();
await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument());
fireEvent.click(screen.getByTestId('byo-mode-hosted'));
- fireEvent.click(screen.getByRole('button', { name: /^run it here$/i }));
+ fireEvent.click(actButton());
await screen.findByTestId('byo-hosted-starting');
await act(async () => { jest.advanceTimersByTime(4000); });
@@ -411,7 +430,7 @@ describe('V2AgentBYO — Run it here', () => {
renderPage();
await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument());
fireEvent.click(screen.getByTestId('byo-mode-hosted'));
- fireEvent.click(screen.getByRole('button', { name: /^run it here$/i }));
+ fireEvent.click(actButton());
await screen.findByText(/already have a hosted agent \(beta allows 1\)/i);
expect(axios.post.mock.calls.some(([url]) => url === '/api/hosted/provision')).toBe(false);
});
@@ -427,7 +446,7 @@ describe('V2AgentBYO — Run it here', () => {
renderPage();
await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument());
fireEvent.click(screen.getByTestId('byo-mode-hosted'));
- fireEvent.click(screen.getByRole('button', { name: /^run it here$/i }));
+ fireEvent.click(actButton());
await screen.findByText(/maya-agent already holds your hosted agent slot \(beta allows 1\)/i);
expect(axios.post.mock.calls.some(([url]) => url === '/api/hosted/provision')).toBe(false);
});
diff --git a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx
index 73e73a97f5..7176f681c4 100644
--- a/frontend/src/v2/__tests__/V2SettingsPage.test.tsx
+++ b/frontend/src/v2/__tests__/V2SettingsPage.test.tsx
@@ -322,11 +322,11 @@ describe('V2SettingsPage', () => {
expect(regenerateButton).toHaveFocus();
});
- test('keeps the no-face explanation for Paper and shortens it for a picked face or photo', () => {
+ test('shows the same short avatar hint for Paper and for a picked face', () => {
(axios.get as jest.Mock).mockResolvedValue({ data: { hasToken: false } });
const { rerender } = renderSettings({ ...auth.currentUser, profilePicture: 'default' });
- expect(screen.getByText('No face and no skin tone until you choose one. Regenerate for another, or upload a photo.'))
+ expect(screen.getByText('Regenerate for another, or upload a photo.'))
.toBeInTheDocument();
rerender(
@@ -334,7 +334,7 @@ describe('V2SettingsPage', () => {
,
);
- expect(screen.getByText('No face and no skin tone until you choose one. Regenerate for another, or upload a photo.'))
+ expect(screen.getByText('Regenerate for another, or upload a photo.'))
.toBeInTheDocument();
rerender(
@@ -343,8 +343,6 @@ describe('V2SettingsPage', () => {
,
);
expect(screen.getByText('Regenerate for another, or upload a photo.')).toBeInTheDocument();
- expect(screen.queryByText('No face and no skin tone until you choose one. Regenerate for another, or upload a photo.'))
- .not.toBeInTheDocument();
});
test('Regenerate reports a profile save error without losing the current avatar', async () => {
diff --git a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts
index 7f96ca6c82..31a678ab93 100644
--- a/frontend/src/v2/__tests__/v2-layout-invariants.test.ts
+++ b/frontend/src/v2/__tests__/v2-layout-invariants.test.ts
@@ -3193,6 +3193,9 @@ describe('Bring your own agent onto Signal (TASK-166)', () => {
// The 1px-per-side compensation is derived from the base shorthand, so it is
// pinned to it rather than restated as a magic number.
expect(base.padding).toBe('16px 18px 14px');
+ // With only the title left, a card sizes to its text unless it stretches
+ // across its cell (the chooser collapsed at 1440, 720 and 390 without this).
+ expect(base['align-self']).toBe('stretch');
const pressed = cssDeclarations(
ruleBody(v2, '.v2-root button.v2-byo__mode[aria-pressed="true"]'),
@@ -3208,21 +3211,6 @@ describe('Bring your own agent onto Signal (TASK-166)', () => {
).toContain('box-shadow: var(--v2-focus-ring)');
});
- test('the mode kicker is mono 500 at 11px, lowercase, cobalt only when chosen (item 3)', () => {
- const kicker = cssDeclarations(ruleBody(v2, '.v2-byo__mode-kicker'));
- expect(kicker.font).toBe('500 11px/16px var(--v2-font-mono)');
- expect(kicker['text-transform']).toBeUndefined();
- expect(kicker.color).toBe('var(--v2-text-muted)');
- const chosen = cssDeclarations(
- ruleBody(v2, '.v2-root button.v2-byo__mode[aria-pressed="true"] .v2-byo__mode-kicker'),
- );
- expect(chosen.color).toBe('var(--v2-accent-text)');
- // The caps were data, not only CSS, so the data is where they must not come
- // back from: a lowercase transform over zh-CN copy would be the wrong fix.
- expect(en).toContain('"onCommonly": "on Commonly"');
- expect(en).toContain('"yours": "your machine"');
- });
-
test('the preview label is mono 500 at 11px with no caps transform (item 4)', () => {
const label = cssDeclarations(ruleBody(v2, '.v2-byo__preview-label'));
expect(label.font).toBe('500 11px/16px var(--v2-font-mono)');
diff --git a/frontend/src/v2/components/V2AgentBYO.tsx b/frontend/src/v2/components/V2AgentBYO.tsx
index e324763a2e..8eb80142f8 100644
--- a/frontend/src/v2/components/V2AgentBYO.tsx
+++ b/frontend/src/v2/components/V2AgentBYO.tsx
@@ -1112,7 +1112,7 @@ const V2AgentBYO: React.FC = () => {
})()}
hint={(() => {
if (issued) return undefined;
- if (showCompactHosted && !hosted) return t('agentByo.compact.lead');
+ if (showCompactHosted && !hosted && hosting?.configured) return t('agentByo.compact.lead',{ agents: hosting?.caps.agentsPerUser, turns: hosting?.caps.turnsPerDay });
if (showCompactByo) return t('agentByo.compactByo.lead');
return t('agentByo.description');
})()}
@@ -1159,11 +1159,6 @@ const V2AgentBYO: React.FC = () => {
>
{submitting ? t('agentByo.actions.starting') : t('agentByo.actions.runHere')}
- {hosting?.configured && (
-