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
39 changes: 0 additions & 39 deletions .github/workflows/chromatic.yml

This file was deleted.

47 changes: 47 additions & 0 deletions .github/workflows/pixel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Pixel

on:
push:
branches:
- main
pull_request:
branches: ["**"]
workflow_dispatch:

jobs:
pixel:
name: Visual Regression Testing
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
# Skip for forked PRs since they don't have access to secrets
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'
env:
PIXEL_KEY: ${{ secrets.PIXEL_KEY }}
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: false

- uses: ./.github/actions/setup-mux

- uses: ./.github/actions/setup-playwright

- name: Generate version file
run: ./scripts/generate-version.sh

- name: Mock version for stable visual testing
run: cp src/browser/stories/mocks/version.ts src/version.ts

- name: Snapshot
if: env.PIXEL_KEY != ''
run: bun x pixel-storybook
env:
# On pull_request events GITHUB_SHA is the merge commit; report the
# head SHA so the Pixel commit status lands on the PR.
PIXEL_COMMIT: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
# Auto-approve main builds so they become baselines without review.
PIXEL_AUTO_REVIEW: ${{ github.event_name == 'push' && 'true' || 'false' }}

- name: Notice when Pixel is not configured
if: env.PIXEL_KEY == ''
run: echo "PIXEL_KEY secret not configured; skipping Pixel snapshot upload."
8 changes: 4 additions & 4 deletions .mux/skills/generate-readme-screenshots/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
name: generate-readme-screenshots
description: >-
Regenerate high-resolution README screenshots from Storybook stories.
Use this skill when Chromatic detects visual diffs in any story under
Use this skill when visual diffs are detected in any story under
"Docs/README Screenshots", or when story data/layout changes require
updated documentation assets. Triggers on: Chromatic visual regressions
updated documentation assets. Triggers on: visual regressions
in readme screenshot stories, changes to App.readmeScreenshots.stories.tsx,
changes to mockFactory.ts that affect screenshot stories, or explicit
user request to update README images.
Expand All @@ -17,7 +17,7 @@ description: >-
The README screenshots in `docs/img/*.webp` are **not manually captured** — they are
deterministically generated from Storybook stories defined in
`src/browser/stories/App.readmeScreenshots.stories.tsx`. When any of these stories
change visually (detected by Chromatic or local inspection), the corresponding WebP
change visually (detected in review or local inspection), the corresponding WebP
assets must be regenerated and committed.

## Quick Reference
Expand Down Expand Up @@ -174,7 +174,7 @@ git commit -m "regenerate README screenshots at 3800px"

## When to Run This

1. **Chromatic flags visual changes** in any `Docs/README Screenshots` story
1. **Visual changes are flagged** in any `Docs/README Screenshots` story
2. **Story data changes** in `App.readmeScreenshots.stories.tsx` or `mockFactory.ts`
3. **UI component changes** that affect the visual appearance of screenshot stories
4. **Explicit user request** to refresh README images
Expand Down
22 changes: 3 additions & 19 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { NOW } from "../src/browser/stories/storyTime";
import { updatePersistedState } from "../src/browser/hooks/usePersistedState";
import { configure } from "storybook/test";

// Signal Storybook runtime to modules that need to stabilize for Chromatic
// Signal Storybook runtime to modules that need to stabilize for visual snapshots
// (e.g. the ChatInput placeholder tip carousel pins to its lead tip so
// tip-list reorders don't cascade into baseline diffs across every story).
// Set as early as possible so it precedes any story-module import that might
Expand All @@ -39,7 +39,7 @@ function ensureStorybookFontsReady(): Promise<void> {

const fonts = document.fonts;

// Trigger load of layout-affecting fonts so Chromatic doesn't snapshot mid font-swap.
// Trigger load of layout-affecting fonts so snapshots aren't captured mid font-swap.
await Promise.allSettled([
fonts.load("400 14px 'Geist'"),
fonts.load("600 14px 'Geist'"),
Expand Down Expand Up @@ -130,10 +130,9 @@ const preview: Preview = {
decorators: [
// Theme provider
(Story, context) => {
// Default to dark if mode not set (e.g., Chromatic headless browser defaults to light)
const mode = (context.globals.theme as ThemeMode | undefined) ?? "dark";

// Apply theme synchronously before React renders - critical for Chromatic snapshots
// Apply theme synchronously before React renders - critical for visual snapshots
if (typeof document !== "undefined") {
document.documentElement.dataset.theme = mode;
document.documentElement.style.colorScheme = mode;
Expand Down Expand Up @@ -208,21 +207,6 @@ const preview: Preview = {
},
},
},
// Chromatic modes STACK across project → component → story levels (they are
// NOT overridden by more specific levels). A project-level `modes` map here
// would therefore be added to EVERY story's snapshot set on top of whatever
// each meta/story declares, multiplying the whole budget. Concretely, a
// global { dark, light } stacked onto appMeta's `dark-desktop` produced 3
// snapshots per App story (dark + light + dark-desktop) instead of the 1 the
// meta intended — silently inflating the Chromatic snapshot budget.
//
// We intentionally declare NO project-level modes. Stories then snapshot in:
// - the modes their meta/story explicitly declare (e.g. CHROMATIC_SINGLE_MODE,
// CHROMATIC_SMOKE_MODES), or
// - a single default snapshot (the default `theme: "dark"` global) when none
// are declared.
// Dual-theme coverage is opt-in per file/story via CHROMATIC_SMOKE_MODES, which
// matches the documented policy in src/browser/stories/meta.tsx.
},
};

Expand Down
6 changes: 1 addition & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ include fmt.mk
.PHONY: dist dist-mac dist-win dist-linux install-mac-arm64 check-appimage-icons check-mac-attach-file-runtime
.PHONY: vscode-ext vscode-ext-install
.PHONY: docs-server check-docs-links
.PHONY: storybook storybook-run storybook-build test-storybook chromatic
.PHONY: storybook storybook-run storybook-build test-storybook
.PHONY: benchmark-terminal
.PHONY: ensure-deps rebuild-native mux
.PHONY: check-eager-imports check-bundle-size check-startup
Expand Down Expand Up @@ -530,10 +530,6 @@ test-storybook: node_modules/.installed ## Run Storybook interaction tests (requ
@# Storybook story transitions can exceed Jest's default 15s timeout on loaded CI runners.
@bun x test-storybook --testTimeout 30000

chromatic: node_modules/.installed ## Run Chromatic for visual regression testing
$(check_node_version)
@bun x chromatic --exit-zero-on-changes

## Benchmarks
benchmark-terminal: ## Run Terminal-Bench 2.0 with Harbor (use TB_HARBOR_PACKAGE/TB_HARBOR_DAYTONA_PACKAGE/TB_DATASET/TB_CONCURRENCY/TB_TIMEOUT/TB_ENV/TB_MODEL/TB_ARGS to customize)
@# Pin Harbor with the Daytona extra so scheduled ingestion does not break on future CLI or adapter API drift.
Expand Down
31 changes: 29 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 0 additions & 8 deletions chromatic.config.json

This file was deleted.

Loading
Loading