feat(marketing): redesign the T3 Code website - #5452
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| } | ||
| } | ||
| }); | ||
| request.addEventListener("blocked", () => settle(false)); |
There was a problem hiding this comment.
🟡 Medium demo/seed.ts:138
When the blocked event fires, settle(false) marks the seed as failed but leaves the indexedDB.open request alive. If another tab then closes its connection and the blocked request proceeds, the success handler sees settled === true, calls database.close() on the newly-upgraded database, and returns without writing the catalog. The database is now upgraded but empty, so the app boots with no demo remote machines registered, and they only appear after a reload retries seeding. Consider calling request.abort() (or ignoring the result without closing in the already-settled branch) so a late success does not close an empty upgraded database.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/demo/seed.ts around line 138:
When the `blocked` event fires, `settle(false)` marks the seed as failed but leaves the `indexedDB.open` request alive. If another tab then closes its connection and the blocked request proceeds, the `success` handler sees `settled === true`, calls `database.close()` on the newly-upgraded database, and returns without writing the catalog. The database is now upgraded but empty, so the app boots with no demo remote machines registered, and they only appear after a reload retries seeding. Consider calling `request.abort()` (or ignoring the result without closing in the already-settled branch) so a late `success` does not close an empty upgraded database.
| index 3f1c2aa..9e84b71 100644 | ||
| --- a/apps/web/src/components/chat/ChatComposer.tsx | ||
| +++ b/apps/web/src/components/chat/ChatComposer.tsx | ||
| @@ -41,6 +41,9 @@ export function ChatComposer(props: ChatComposerProps) { |
There was a problem hiding this comment.
🟠 High demo/fixtures.ts:676
The hunk headers in DEMO_COMPOSER_DIFF are inconsistent with their bodies. The first hunk declares @@ -41,6 +41,9 @@ (6 old lines, 9 new lines) but the body contains only 3 old lines and 6 new lines, and the new-file hunk declares 11 lines but supplies 7. A unified-diff parser relying on the header counts will reject or truncate the diff or assign incorrect line numbers when the composer demo's diff panel opens. Consider correcting the line counts in the hunk headers to match the actual body.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/demo/fixtures.ts around line 676:
The hunk headers in `DEMO_COMPOSER_DIFF` are inconsistent with their bodies. The first hunk declares `@@ -41,6 +41,9 @@` (6 old lines, 9 new lines) but the body contains only 3 old lines and 6 new lines, and the new-file hunk declares 11 lines but supplies 7. A unified-diff parser relying on the header counts will reject or truncate the diff or assign incorrect line numbers when the composer demo's diff panel opens. Consider correcting the line counts in the hunk headers to match the actual body.
|
|
||
| // The browser preview surface needs the Electron desktop bridge, so the web | ||
| // demo showcases the right panel with the diff surface instead. | ||
| const DEMO_COMPOSER_DIFF = `diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx |
There was a problem hiding this comment.
🟡 Medium demo/fixtures.ts:672
The DEMO_COMPOSER_DIFF fixture only contains changes for ChatComposer.tsx and DropOverlay.tsx, but the thread-composer checkpoint metadata advertises ChatComposer.tsx, ChatView.tsx, and composerDraftStore.ts. When demoThreadDiff selects DEMO_COMPOSER_DIFF for thread-composer, the resulting diff omits ChatView.tsx and composerDraftStore.ts, so the displayed file summary disagrees with the actual diff content. Update DEMO_COMPOSER_DIFF so its changed files match the checkpoint's advertised file list.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/demo/fixtures.ts around line 672:
The `DEMO_COMPOSER_DIFF` fixture only contains changes for `ChatComposer.tsx` and `DropOverlay.tsx`, but the `thread-composer` checkpoint metadata advertises `ChatComposer.tsx`, `ChatView.tsx`, and `composerDraftStore.ts`. When `demoThreadDiff` selects `DEMO_COMPOSER_DIFF` for `thread-composer`, the resulting diff omits `ChatView.tsx` and `composerDraftStore.ts`, so the displayed file summary disagrees with the actual diff content. Update `DEMO_COMPOSER_DIFF` so its changed files match the checkpoint's advertised file list.
| } | ||
| return this.removeProject(command.projectId); | ||
| } | ||
| default: { |
There was a problem hiding this comment.
🟠 High demo/server.ts:458
dispatch silently no-ops on thread.pin and thread.unpin commands: both fall into the default case and return the current sequence without updating pinnedAt or emitting a stream event. SidebarV2's pin/unpin controls therefore do nothing in the demo. Consider adding cases that set/clear pinnedAt (and any related fields) and call upsertThread.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/demo/server.ts around line 458:
`dispatch` silently no-ops on `thread.pin` and `thread.unpin` commands: both fall into the `default` case and return the current sequence without updating `pinnedAt` or emitting a stream event. SidebarV2's pin/unpin controls therefore do nothing in the demo. Consider adding cases that set/clear `pinnedAt` (and any related fields) and call `upsertThread`.
| const isDev = stage === "dev"; | ||
| devSource?.toggleAttribute("hidden", !isDev); | ||
| desktopSections.forEach((section) => section.toggleAttribute("hidden", isDev)); | ||
| mobileSection?.toggleAttribute("hidden", isDev); |
There was a problem hiding this comment.
🟠 High pages/download.astro:228
When the user selects the Dev stage, applyStage hides the entire mobile section, so the App Store and Google Play download links disappear even though they are channel-independent. The picker is labeled "Desktop release channel," so selecting Dev should only replace the desktop platform cards with the build-from-source section — mobile downloads should remain available. Consider removing the mobileSection hide so only the desktop sections are toggled for the Dev stage.
| mobileSection?.toggleAttribute("hidden", isDev); | |
| desktopSections.forEach((section) => section.toggleAttribute("hidden", isDev)); |
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/marketing/src/pages/download.astro around line 228:
When the user selects the Dev stage, `applyStage` hides the entire mobile section, so the App Store and Google Play download links disappear even though they are channel-independent. The picker is labeled "Desktop release channel," so selecting Dev should only replace the desktop platform cards with the build-from-source section — mobile downloads should remain available. Consider removing the `mobileSection` hide so only the desktop sections are toggled for the Dev stage.
| return data; | ||
| } | ||
|
|
||
| export async function fetchNightlyRelease(): Promise<Release> { |
There was a problem hiding this comment.
🟡 Medium lib/releases.ts:154
fetchNightlyRelease searches only the first 100 releases returned by RELEASES_API_URL and never follows GitHub pagination, so it throws No nightly release was found when the newest matching nightly falls outside that first page. Consider following Link/page pagination, or use a tagged nightly/latest endpoint to avoid relying on the first 100 entries.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/marketing/src/lib/releases.ts around line 154:
`fetchNightlyRelease` searches only the first 100 releases returned by `RELEASES_API_URL` and never follows GitHub pagination, so it throws `No nightly release was found` when the newest matching nightly falls outside that first page. Consider following `Link`/`page` pagination, or use a tagged nightly/latest endpoint to avoid relying on the first 100 entries.
| settle(true); | ||
| return; | ||
| } | ||
| const staleStores = ["shell", "thread", "server-config", "vcs-refs"].filter((store) => |
There was a problem hiding this comment.
🟠 High demo/seed.ts:167
When force is true, seedConnectionCatalog clears all stores (shell, thread, server-config, vcs-refs) and overwrites the entire catalog document with demoCatalogDocument(). Any visitor-added connections or credentials — and cached state for non-fixture environments — are silently deleted. The fix is to update only the demo-owned records instead of wholesale replacing the catalog and clearing every store.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/demo/seed.ts around line 167:
When `force` is `true`, `seedConnectionCatalog` clears all stores (`shell`, `thread`, `server-config`, `vcs-refs`) and overwrites the entire catalog document with `demoCatalogDocument()`. Any visitor-added connections or credentials — and cached state for non-fixture environments — are silently deleted. The fix is to update only the demo-owned records instead of wholesale replacing the catalog and clearing every store.
| checkpoints.flatMap((checkpoint) => checkpoint.files).map((file) => [file.path, file]), | ||
| ); | ||
| const diff = | ||
| threadId === "thread-composer" && fromTurnCount >= 1 && filesByPath.size > 0 |
There was a problem hiding this comment.
🟡 Medium demo/fixtures.ts:822
When demoReviewDiffPreview calls demoThreadDiff with fromTurnCount = 0, the DEMO_COMPOSER_DIFF fixture is skipped because the guard requires fromTurnCount >= 1. The composer full-review preview then falls back to generated diffs that omit DropOverlay.tsx, so opening the full review for the featured composer demo shows an incomplete diff. Consider dropping the fromTurnCount >= 1 condition (or handling 0 explicitly) so the full-diff path uses DEMO_COMPOSER_DIFF as intended.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/demo/fixtures.ts around line 822:
When `demoReviewDiffPreview` calls `demoThreadDiff` with `fromTurnCount = 0`, the `DEMO_COMPOSER_DIFF` fixture is skipped because the guard requires `fromTurnCount >= 1`. The composer full-review preview then falls back to generated diffs that omit `DropOverlay.tsx`, so opening the full review for the featured composer demo shows an incomplete diff. Consider dropping the `fromTurnCount >= 1` condition (or handling `0` explicitly) so the full-diff path uses `DEMO_COMPOSER_DIFF` as intended.
| * Opens the right panel (on the diff surface — the browser preview needs the | ||
| * desktop bridge) on the showcase threads for first-time visitors. | ||
| */ | ||
| function seedRightPanelState(force: boolean): boolean { |
There was a problem hiding this comment.
🟡 Medium demo/seed.ts:203
When the fixture marker is stale, seedRightPanelState overwrites the entire persisted byThreadKey document — including all existing entries — instead of only replacing the fixture-owned showcase entries. A fixture-version change therefore deletes a returning visitor's panel visibility/layout for every thread (including unrelated or still-valid demo threads), despite seeding being intended to preserve visitor changes. The same wholesale-replace problem occurs in seedDiffPanelSelection, which discards all visitor-selected turns/files and branchBaseRefByThreadKey values. Both functions should merge the new fixture entries into the existing persisted state rather than replacing the whole document.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/demo/seed.ts around line 203:
When the fixture marker is stale, `seedRightPanelState` overwrites the entire persisted `byThreadKey` document — including all existing entries — instead of only replacing the fixture-owned showcase entries. A fixture-version change therefore deletes a returning visitor's panel visibility/layout for every thread (including unrelated or still-valid demo threads), despite seeding being intended to preserve visitor changes. The same wholesale-replace problem occurs in `seedDiffPanelSelection`, which discards all visitor-selected turns/files and `branchBaseRefByThreadKey` values. Both functions should merge the new fixture entries into the existing persisted state rather than replacing the whole document.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 50e470c. Configure here.
| "dev": "npm run prepare:assets && astro dev", | ||
| "build": "npm run prepare:assets && astro build", | ||
| "prepare:assets": "npm run build:sidebar-demo", | ||
| "build:sidebar-demo": "npm run --prefix ../web build:sidebar-demo && node scripts/capture-demo-screenshots.mjs", |
There was a problem hiding this comment.
Demo build missing web deps
High Severity
Marketing build/dev now run build:sidebar-demo, which builds @t3tools/web into the gitignored public/sidebar-demo tree, but @t3tools/marketing does not depend on @t3tools/web. The Vercel install filter @t3tools/marketing... therefore never installs web’s packages, so the interactive hero demo cannot be produced in that environment and the marketing build fails.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 50e470c. Configure here.
ApprovabilityVerdict: Needs human review 9 blocking correctness issues found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. You can customize Macroscope's approvability policy. Learn more. |
|
Superseded by #5457, now published from agent/marketing-core-value against main. |


The current marketing site does not clearly show how T3 Code works across desktop, web, and mobile, and its download flow does not distinguish release channels well.
This redesign gives the site a product-led story with a real interactive app demo, stage-aware Latest/Nightly/Dev presentation, dedicated desktop and mobile download paths, T3 Connect and multi-device sections, and refreshed legal and mobile documentation. The demo fixtures are isolated from normal app state, and the footer wordmark now follows the selected release channel.
Visual reference:
Verification:
vp run --filter @t3tools/marketing typecheckvp test run apps/marketing/src/lib/releases.test.ts apps/web/src/branding.test.ts apps/web/src/demo/fixtures.test.ts apps/web/src/demo/seedState.test.ts apps/web/src/demo/server.test.ts packages/client-runtime/src/state/server.test.ts(56 tests)Generated with GPT-5.6 in T3 Code through the Codex harness.
Note
Medium Risk
Marketing-only surface area is large (layout, download logic, build scripts), and the new workflow pushes commits to the default branch with a GitHub App token—worth validating permissions and that automated commits do not race with human merges.
Overview
Redesigns the T3 Code marketing site around a product-led story, Latest / Nightly / Dev release channels, and richer download and social metadata.
Site shell and download —
Layout.astroadds canonical/OG/Twitter tags, stage-aware favicons and accent colors, a layered blur nav, and a footer wordmark that follows the selected channel. The download page gains a channel picker (latest, nightly, or build-from-source dev), GitHub-backed asset resolution viafindReleaseAssetUrl/fetchNightlyRelease, and explicit mobile store links.Marketing assets pipeline —
dev/buildrunprepare:assets: build the web sidebar demo, thencapture-demo-screenshots.mjs(Playwright) writesdemo-statescrops,og-source.png, and a WebGL-compositedog-image.pngfromshowcase-spec.jsonandog-studio-setup.json.sidebar-demois gitignored; demo and mobile preview paths get cache headers inastro.config.mjs.CI and docs —
mobile-showcase-screenshots.ymladdspublish_marketing_preview, which on successful iOS+Android dark captures on the default branch copies screenshots intoapps/marketing/public/mobile-states, writessource.json, and commits when images change. README documents mobile app links;LegalPagesupports asimplelayout variant.Reviewed by Cursor Bugbot for commit 50e470c. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Redesign the T3 Code marketing website with a live demo iframe, release channel switching, and mobile app support
apps/web/src/demo/that serves snapshots, threads, diffs, and VCS state over a fake WebSocket, allowing the embedded demo to run without a real server.localStorageand communicated to the demo iframe viapostMessage.dev/buildscripts now runprepare:assets(Vite build + Playwright screenshot capture) before Astro starts, which significantly increases cold-start time.📊 Macroscope summarized 50e470c. 34 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.