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
6 changes: 3 additions & 3 deletions web/app/landing.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2057,10 +2057,10 @@
}

/* ── Terminal marquee ────────────────────────────────────────────────────
Three full-bleed rows of agent sessions sliding past. Each track holds two
Three full-bleed rows of agent sessions sliding past. Each track holds four
identical copies of its cards and slides exactly one copy's width, so the
loop is seamless; the spacing lives in each card's margin-right rather than
a flex gap so that -50% lands on a card boundary rather than half a gap
a flex gap so that -25% lands on a card boundary rather than half a gap
short. Rows differ in duration, direction and negative delay (a phase
offset) so the card seams never line up across the band. The rows also hold
different numbers of cards, so their tracks are different lengths and the
Expand Down Expand Up @@ -2170,7 +2170,7 @@
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
transform: translate3d(-25%, 0, 0);
}
}

Expand Down
18 changes: 12 additions & 6 deletions web/components/home/HeroTerminalMarquee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const AGENT_META: Record<
TerminalAgent,
{
activityGlyph: string;
branchPrefix: string;
cycle: string;
label: string;
mode: string;
Expand All @@ -54,6 +55,7 @@ const AGENT_META: Record<
> = {
claude: {
activityGlyph: '✻',
branchPrefix: 'claude',
cycle: '7.8s',
label: 'Claude Code',
mode: 'trusted',
Expand All @@ -63,6 +65,7 @@ const AGENT_META: Record<
},
codex: {
activityGlyph: '◌',
branchPrefix: 'codex',
cycle: '7.2s',
label: 'OpenAI Codex',
mode: 'workspace-write',
Expand All @@ -72,6 +75,7 @@ const AGENT_META: Record<
},
grok: {
activityGlyph: '◆',
branchPrefix: 'grok',
cycle: '7.5s',
label: 'Grok',
mode: 'auto',
Expand All @@ -81,6 +85,7 @@ const AGENT_META: Record<
},
opencode: {
activityGlyph: '▣',
branchPrefix: 'opencode',
cycle: '6.9s',
label: 'OpenCode',
mode: 'build',
Expand Down Expand Up @@ -399,6 +404,7 @@ function RelayNetwork() {
}

function GrokTerminalBody({ card }: { card: TerminalCard }) {
const meta = AGENT_META[card.agent];
const prompt = card.lines.find((line) => line.tone === 'prompt') ?? card.lines[0];
const activity = card.lines.find((line) => line.tone === 'tool') ?? card.lines[1];
const result = card.lines.find((line) => line.tone === 'relay' || line.tone === 'ok');
Expand All @@ -408,9 +414,9 @@ function GrokTerminalBody({ card }: { card: TerminalCard }) {
<div className={s.heroGrokContext}>
<span className={s.heroGrokBranch}>⌁</span>
<span className={s.heroGrokPath}>
codex/{card.repo.replace('/', '-')} ~/{card.repo}
{meta.branchPrefix}/{card.repo.replace('/', '-')} ~/{card.repo}
</span>
<span className={s.heroGrokContextMode}>auto</span>
<span className={s.heroGrokContextMode}>{meta.mode}</span>
</div>
<div
className={`${s.heroGrokPrompt} ${s.heroGrokCycleLine}`}
Expand Down Expand Up @@ -637,10 +643,10 @@ function TerminalRow({
rowClass: string;
rowKey: string;
}) {
// Two identical copies make the loop seamless: the track slides exactly one
// copy's width (translateX(-50%)) and snaps back with the second copy already
// sitting where the first one started.
const copies = ['a', 'b'];
// Four identical copies keep at least three full sequences beyond the loop
// boundary. That prevents wide viewports from outrunning shorter rows while
// the track slides exactly one copy's width (translateX(-25%)).
const copies = ['a', 'b', 'c', 'd'];
const rowPhase = rowKey === 'one' ? 0 : rowKey === 'two' ? 2.2 : 4.4;

return (
Expand Down
24 changes: 24 additions & 0 deletions web/lib/test/hero-terminal-marquee.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it, vi } from 'vitest';

vi.mock('@lobehub/icons/es/Grok', () => ({
default: ({ className }: { className?: string }) => <svg className={className} />,
}));

import { HeroTerminalMarquee } from '../../components/home/HeroTerminalMarquee';

describe('HeroTerminalMarquee', () => {
it('uses Grok metadata for the Grok terminal context', () => {
const markup = renderToStaticMarkup(<HeroTerminalMarquee />);

expect(markup).toContain('grok/relay-docs ~/relay/docs');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This test only guards the branch-prefix half of the fix; the mode-from-metadata half is untested. The Grok context pane renders the mode from AGENT_META (<span>{meta.mode}</span>), and the PR's stated fix is that this mode comes from the agent metadata instead of a hardcoded auto label, but the test never asserts it. Add an assertion that the rendered markup contains the metadata-driven mode (e.g. expect(markup).toContain('auto') scoped to the Grok context) so a regression in the mode no longer slips through.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/lib/test/hero-terminal-marquee.test.tsx, line 15:

<comment>This test only guards the branch-prefix half of the fix; the mode-from-metadata half is untested. The Grok context pane renders the mode from AGENT_META (`<span>{meta.mode}</span>`), and the PR's stated fix is that this mode comes from the agent metadata instead of a hardcoded auto label, but the test never asserts it. Add an assertion that the rendered markup contains the metadata-driven mode (e.g. `expect(markup).toContain('auto')` scoped to the Grok context) so a regression in the mode no longer slips through.</comment>

<file context>
@@ -0,0 +1,18 @@
+  it('uses Grok metadata for the Grok terminal context', () => {
+    const markup = renderToStaticMarkup(<HeroTerminalMarquee />);
+
+    expect(markup).toContain('grok/relay-docs ~/relay/docs');
+    expect(markup).not.toContain('codex/relay-docs ~/relay/docs');
+  });
</file context>

expect(markup).not.toContain('codex/relay-docs ~/relay/docs');
});

it('renders enough repeated sequences to keep wide marquee rows covered', () => {
const markup = renderToStaticMarkup(<HeroTerminalMarquee />);

expect(markup.match(/grok\/relay-docs ~\/relay\/docs/g)).toHaveLength(4);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The test name claims it verifies the marquee keeps wide rows covered, but the assertion only counts occurrences of the literal grok/relay-docs ~/relay/docs string and hardcodes the expected count to 4. That 4 is just a re-derivation of the copies = ['a','b','c','d'] loop in TerminalRow, so it can pass even while wide viewports expose gaps (e.g. if card widths grow or more copies are needed), and it breaks whenever the copy count is tuned. Assert actual coverage or the copy count derived from the rendered rows instead of a hardcoded magic number.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/lib/test/hero-terminal-marquee.test.tsx, line 22:

<comment>The test name claims it verifies the marquee keeps wide rows covered, but the assertion only counts occurrences of the literal `grok/relay-docs ~/relay/docs` string and hardcodes the expected count to 4. That 4 is just a re-derivation of the `copies = ['a','b','c','d']` loop in `TerminalRow`, so it can pass even while wide viewports expose gaps (e.g. if card widths grow or more copies are needed), and it breaks whenever the copy count is tuned. Assert actual coverage or the copy count derived from the rendered rows instead of a hardcoded magic number.</comment>

<file context>
@@ -15,4 +15,10 @@ describe('HeroTerminalMarquee', () => {
+  it('renders enough repeated sequences to keep wide marquee rows covered', () => {
+    const markup = renderToStaticMarkup(<HeroTerminalMarquee />);
+
+    expect(markup.match(/grok\/relay-docs ~\/relay\/docs/g)).toHaveLength(4);
+  });
 });
</file context>

});
});
Loading