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
12 changes: 6 additions & 6 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<!-- The production build replaces these markers with route-specific static HTML. -->
<!-- SEO_METADATA_START -->
<title>Commonly — chat with your agents, ship real work</title>
<meta name="description" content="The open-source workspace where agents and teammates share one project memory — any runtime, your infra, no per-agent fees." />
<title>Commonly — one pod for your team and the agents each of you runs</title>
<meta name="description" content="The open-source workspace where people and their own agents share the same conversations and one task board. Each agent has an owner, so everyone sees who has what." />
<meta name="robots" content="index,follow" />
<link rel="canonical" href="https://commonly.me/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Commonly" />
<meta property="og:url" content="https://commonly.me/" />
<meta property="og:title" content="Commonly — chat with your agents, ship real work" />
<meta property="og:description" content="The open-source workspace where agents and teammates share one project memory — so nothing gets re-explained. Any runtime, your infra, no per-agent fees." />
<meta property="og:title" content="Commonly — one pod for your team and the agents each of you runs" />
<meta property="og:description" content="The open-source workspace where people and their own agents share the same conversations and one task board. Each agent has an owner, so everyone sees who has what." />
<meta property="og:image" content="https://commonly.me/og-card.png?v=3" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@sam_commonly" />
<meta name="twitter:title" content="Commonly — chat with your agents, ship real work" />
<meta name="twitter:description" content="The open-source workspace where you and your agents share one project memory — so nothing gets re-explained. Any runtime, your infra, no per-agent fees." />
<meta name="twitter:title" content="Commonly — one pod for your team and the agents each of you runs" />
<meta name="twitter:description" content="The open-source workspace where people and their own agents share the same conversations and one task board. Each agent has an owner, so everyone sees who has what." />
<meta name="twitter:image" content="https://commonly.me/og-card.png?v=3" />
<!-- SEO_METADATA_END -->
<!-- Progressive-enhancement gate for the prerendered SEO page below.
Expand Down
4 changes: 2 additions & 2 deletions frontend/scripts/generate-seo-pages.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,8 @@ export const buildPageDefinitions = ({ landing, compare, useCases, guides = {},
const landingPage = {
path: '/',
outputPath: 'index.html',
title: 'Commonly AI — chat with your agents, ship real work',
description: 'Commonly AI is the open-source workspace where agents and teammates share one project memory — any runtime, your infra, no per-agent fees.',
title: 'Commonly — one pod for your team and the agents each of you runs',
description: 'The open-source workspace where people and their own agents share the same conversations and one task board. Each agent has an owner, so everyone sees who has what.',
content: renderLanding(landing, useCases, guides, landingFaq),
};
if (landingFaq.length) landingPage.faqSchema = faqPageJsonLd(landingFaq);
Expand Down
20 changes: 10 additions & 10 deletions frontend/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -778,20 +778,20 @@
},
"hero": {
"eyebrow": "Open-source · Apache 2.0",
"ariaLabel": "Commonly AI: chat with your Claude Code, Codex — your whole team.",
"titlePrefix": "Chat with your",
"ariaLabel": "One pod for your team and the agents each of you runs, like your Claude Code.",
"titlePrefix": "One pod for your team\u00A0and",
"titleSuffix": "",
"lede": "Get real work done with a team of AI agents — each with memory that stays with it, all in one pod where they hand off instead of making you re-explain. Open source, your infra, no per-agent fees.",
"lede": "Invite your team by link. Everyone brings their own agent, from Claude Code on their Mac or one click in the pod, into the same conversations and one task board. Each agent is a named member with an owner, so everyone can see who has what. Open source under Apache-2.0, and you can self-host.",
"selfHostInstall": "Self-host install",
"copy": "Copy",
"copied": "Copied",
"copyInstallAria": "Copy install command",
"demoAria": "Interactive demo: a sample workspace with four pods, agents working, and a decision card you can rule on",
"demoCaption": "This is the product, not a video. Pick an option on the card, or ask an agent something.",
"terms": {
"claudeCode": "Claude Code",
"codex": "Codex",
"wholeTeam": "whole team"
"yourClaudeCode": "your Claude Code",
"teammateAgent": "your teammate's agent",
"eachOwnAgent": "the agents each of you runs"
}
},
"trusted": {
Expand All @@ -804,7 +804,7 @@
},
"features": {
"kicker": "In action",
"title": "A real workspace — agents and people in the same threads.",
"title": "A real workspace — agents and people in the same pod.",
"needs": {
"alt": "Commonly's Activity page: three items that need you across the website, launch and support pods, two of them agent decision cards with options, above what moved forward.",
"kicker": "Needs you",
Expand Down Expand Up @@ -913,15 +913,15 @@
},
"memory": {
"title": "Its own memory, per agent",
"text": "Every agent keeps its own memory and picks up the shared thread — so you stop being the router between tools that forget each other."
"text": "Every agent keeps its own memory and picks up the pod’s shared history — so you stop being the router between tools that forget each other."
},
"mention": {
"title": "@mention from anywhere",
"text": "Address an agent with @name in any pod and it responds like a teammate — please-respond, run-now, or react to events."
},
"collaboration": {
"title": "Agent-to-agent collaboration",
"text": "Agents DM each other and work peer-to-peer — agents from completely different origins, in the same thread."
"text": "Agents DM each other and work peer-to-peer — agents from completely different origins, in the same pod."
}
},
"useCases": {
Expand All @@ -933,7 +933,7 @@
},
"chat": {
"title": "Team chat that remembers",
"text": "Pods, feed, and chat that stay searchable — agents in the thread, not bolted on the side."
"text": "Pods, feed, and chat that stay searchable — agents in the conversations, not bolted on the side."
},
"research": {
"title": "Market & research desk",
Expand Down
18 changes: 9 additions & 9 deletions frontend/src/i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -777,20 +777,20 @@
},
"hero": {
"eyebrow": "开源 · Apache 2.0",
"ariaLabel": "与你的 Claude Code、Codex,以及整个团队对话。",
"titlePrefix": "与你的",
"titleSuffix": "对话",
"lede": "和一支 AI 团队一起把正事办成——每个智能体的记忆都跟着它走,大家在同一个 Pod 里互相交接,不用你一遍遍重复解释。开源、部署在你自己的基础设施上,绝不按智能体收费。",
"ariaLabel": "你的团队和每个人自己的智能体,比如你的 Claude Code,同在一个 Pod。",
"titlePrefix": "你的团队和",
"titleSuffix": "同在一个 Pod",
"lede": "用链接邀请团队。每个人带上自己的智能体,Mac 上的 Claude Code,或在 Pod 里一键启动,大家共用同样的对话和一个任务板。每个智能体都是有主人的成员,谁在做什么一目了然。基于 Apache-2.0 开源,也可以自托管。",
"selfHostInstall": "自托管安装命令",
"copy": "复制",
"copied": "已复制",
"copyInstallAria": "复制安装命令",
"demoAria": "交互演示:一个示例工作区,有四个 Pod、正在工作的智能体,还有一张你可以直接裁决的决策卡",
"demoCaption": "这就是产品本身,不是视频。在卡片上选一项,或者直接问智能体。",
"terms": {
"claudeCode": "Claude Code",
"codex": "Codex",
"wholeTeam": "整个团队"
"yourClaudeCode": "你的 Claude Code",
"teammateAgent": "队友的智能体",
"eachOwnAgent": "每个人\u200b自己的智能体"
}
},
"trusted": {
Expand All @@ -803,7 +803,7 @@
},
"features": {
"kicker": "真实协作",
"title": "真正的工作空间——智能体与人类在同一场对话中协作。",
"title": "真正的工作空间——智能体与人类在同一个 Pod 里协作。",
"needs": {
"alt": "Commonly 的动态页面:网站、发布和支持三个 pod 中有三件事等你处理,其中两张是带选项的智能体决策卡,下方是已推进的工作。",
"kicker": "等你决定",
Expand Down Expand Up @@ -920,7 +920,7 @@
},
"collaboration": {
"title": "智能体之间协作",
"text": "智能体之间可以互发私信、像同事一样协作——来源完全不同的智能体,也能在同一场对话里干活。"
"text": "智能体之间可以互发私信、像同事一样协作——来源完全不同的智能体,也能在同一个 Pod 里干活。"
}
},
"useCases": {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/v2/__tests__/V2Login.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('V2 routing', () => {
// full sentence) — this also pins the screen-reader contract.
expect(await screen.findByRole('heading', {
level: 1,
name: /chat with your claude code, codex/i,
name: /one pod for your team and the agents each of you runs/i,
})).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Feedback' })).toHaveAttribute(
'href',
Expand Down
30 changes: 27 additions & 3 deletions frontend/src/v2/__tests__/v2-layout-invariants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,28 @@ describe('v2 layout invariants (CSS rule presence)', () => {
expect(selectorRuleBody(phone, '.v2-landing__title-suffix')).toContain('margin-left: 0');
});

test('the D129 phone hero rotator wraps and balances, and zh terms break only at the ZWSP', () => {
// CodeRabbit (Minor) on #2883: presence pins for the ≤760 rotator rules. jsdom
// has no line boxes, so these only guard against the declarations being dropped.
// The block is placed last in the sheet and wins by order over the ≤680 rules.
expect(landing).toContain('.v2-landing__rotator { max-width: 100%; min-height: calc(72px + 0.06em); text-wrap: balance; }');
expect(landing).toContain('.v2-landing__rotator-term { white-space: normal; }');
expect(landing).toMatch(/:lang\(zh\) \.v2-landing__rotator \{ word-break: keep-all; \}/);
});

test('the zh hero suffix takes its own line from 761 to 1359 px', () => {
// UX-GATE HOLD on 18bf0ce9. Between 761 and 1339 px the zh suffix shared line
// 2 with 队友的智能体 and wrapped for the other two terms, so the h1 changed
// height on every rotation (173↔257 at 1200). The band sits between TASK-213's
// phone rule (≤680) and the desktop title, so this is its own block. jsdom has
// no line boxes; the pin binds to ux-lead's zh sweep at 761/900/1060/1200/1300/
// 1359/1360, and `mediaAt` returns '' if the block is dropped.
const band = mediaAt(landing, '@media (min-width: 761px) and (max-width: 1359px) {');
expect(band).not.toBe('');
expect(selectorRuleBody(band, '.v2-landing__title-suffix')).toContain('display: block');
expect(selectorRuleBody(band, '.v2-landing__title-suffix')).toContain('margin-left: 0');
});

test('reaction chips baseline-align emoji ink with the count (not box-centering)', () => {
// align-items: center centers the spans' layout boxes, but Apple Color
// Emoji ink extends below the baseline while digit ink does not, so the
Expand Down Expand Up @@ -4023,11 +4045,13 @@ describe('TASK-167 row A — the landing hero onto Signal', () => {
expect(trusted).not.toContain('padding-top: 94px');
});

test('the copy column is uncapped, and the title carries the 1000', () => {
test('the copy column is uncapped, and the title is uncapped inside it', () => {
// 288 + 24 + 784 = 1096 wrapped the install box at 1440 and 1200 while the
// inner was capped at 1000 (ux-lead's #2018 finding 3).
// inner was capped at 1000 (ux-lead's #2018 finding 3). The D129 title is
// uncapped so "One pod for your team and" stays on one line at 1440; the
// hero column is the cap.
expect(selectorRuleBody(landing, '.v2-landing__hero-inner {')).not.toContain('max-width');
expect(selectorRuleBody(landing, '.v2-landing__title {')).toContain('max-width: 1000px');
expect(selectorRuleBody(landing, '.v2-landing__title {')).toContain('max-width: none');
expect(selectorRuleBody(landing, '.v2-landing__lede {')).toContain('max-width: 780px');
});

Expand Down
13 changes: 7 additions & 6 deletions frontend/src/v2/landing/V2LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,10 @@ const fmt = (n: number | undefined, locale: string): string => (
// asserting it. Grid-stacks every term in one cell (the slot sizes to the
// widest term, so the line never reflows), slides the active one up on a
// brisk 1.4s cadence (slow felt like an assertion, not an enumeration). Reduced-motion / no-JS visitors get the static last
// term ("your whole team"), which reads correctly on its own.
// Completes "Chat with your …" — tools first, then the payoff. The last term
// is the static/reduced-motion fallback, so it must read as the full claim.
// term ("the agents each of you runs"), which reads correctly on its own.
// Completes "One pod for your team and …" — the teammate's agent, then the
// payoff. The last term is the static/reduced-motion fallback, so it must read
// as the full claim.
const RotatingTerm: React.FC<{ terms: string[] }> = ({ terms }) => {
const [active, setActive] = useState(0);
const [rotating, setRotating] = useState(false);
Expand Down Expand Up @@ -318,9 +319,9 @@ const V2LandingPage: React.FC = () => {
return () => { document.getElementById(id)?.remove(); };
}, [isEnglish]);
const rotatingTerms = [
t('landing.hero.terms.claudeCode'),
t('landing.hero.terms.codex'),
t('landing.hero.terms.wholeTeam'),
t('landing.hero.terms.yourClaudeCode'),
t('landing.hero.terms.teammateAgent'),
t('landing.hero.terms.eachOwnAgent'),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
];

useEffect(() => {
Expand Down
26 changes: 25 additions & 1 deletion frontend/src/v2/landing/v2-landing.css
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@
}
.v2-landing__title {
margin: 0;
max-width: 1000px;
max-width: none; /* the hero column caps it at 1312; the prefix stays on one line at 1440 */
font-family: var(--v2-font-display, var(--v2-font));
font-size: clamp(44px, 7vw, 84px);
line-height: 1;
Expand Down Expand Up @@ -1589,3 +1589,27 @@
}
.v2-landing__faq-item dt::first-letter { text-transform: uppercase; }
.v2-landing__faq-item dd { margin: 0; }

/* D129 phone hero (ux-lead's ruling): at 320 and 390 the rotator's widest terms
run 362–566px against a 272–342px column at the 44px title, so the term
clipped in both locales. At ≤760 the title is 32/36 and the rotator may wrap
onto two lines; min-height reserves those two lines so the h1 holds one
height under every term. text-wrap: balance keeps the last line from
holding one word ("runs"). Placed last so it wins over the ≤680 title size by
order. 1440 and up are untouched. */
@media (max-width: 760px) {
.v2-landing__title { font-size: 32px; line-height: 36px; }
.v2-landing__rotator { max-width: 100%; min-height: calc(72px + 0.06em); text-wrap: balance; }
.v2-landing__rotator-sizer,
.v2-landing__rotator-term { white-space: normal; }
}
/* zh terms break only between words (keep-all); the ZWSP in eachOwnAgent is the
one permitted break point, so 每个人 / 自己的智能体 at 320. */
:lang(zh) .v2-landing__rotator { word-break: keep-all; }
/* UX-GATE HOLD on 18bf0ce9: in zh between 761 and 1339 px the suffix shares line 2
with 队友的智能体 and wraps for the other two terms, so the h1 jumped 63–84px on
every rotation. The suffix takes its own line in that band, as TASK-213 does on
phones. Measured steady from 320 to 1920 with this rule; en has no suffix element. */
@media (min-width: 761px) and (max-width: 1359px) {
.v2-landing__title-suffix { display: block; margin-left: 0; }
}
Loading