Skip to content

Commit 11f11ff

Browse files
talzucursoragent
andcommitted
v0.2.4: --headed flag actually works on browser-driving subcommands; dismiss new "Skip intro" overlay
Two bug reports from a first-time user, both confirmed and fixed: #1 (Commander option resolution) `--headed` had no effect on `submit`, `resume`, `fetch`, `loop`. The flag was defined on each subcommand without a default, so `opts.headed` resolved to `undefined` (falsy) and the browser always launched headless regardless of CLI intent. The wizard itself was unaffected because the program-level `--headed` had a `true` default. Fixed by giving each browser-driving subcommand the same `--headed (default: true)` + `--no-headed` pattern as the program level. So `pnpm design submit <id>` now launches headed by default; `--no-headed` flips for CI / smoke tests. Verified via a Commander parser smoke test before shipping. #2 (Live UI regression) `fillNewProject` timed out clicking the Create button on first project creation per session. claude.ai/design started shipping a "Skip intro" onboarding overlay that intercepts pointer events on the form behind it. Added `dismissOnboardingOverlay()` at the start of `fillNewProject` — best-effort 1.5s wait, click if visible, silently fall through if not. Tries multiple locators ("Skip intro", "Skip", text fallback) so a future copy rotation doesn't silently regress us. Once dismissed, the persistent profile never sees it again; on subsequent runs the dismissal is a silent no-op. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 4278fd1 commit 11f11ff

4 files changed

Lines changed: 72 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

77
## [Unreleased]
88

9+
## [0.2.4] - 2026-05-15
10+
11+
### Fixed
12+
- **`--headed` had no effect on `submit`, `resume`, `fetch`, and `loop`
13+
subcommands** — the flag was defined on each subcommand without a
14+
default value, so `opts.headed` resolved to `undefined` (falsy) and
15+
the browser always launched headless, ignoring the user's `--headed`
16+
intent. The program-level `--headed` flag (used by the wizard) was
17+
unaffected. Each browser-driving subcommand now defines `--headed`
18+
with `default: true` and a companion `--no-headed` to flip it,
19+
matching the program-level pattern. So `pnpm design submit <id>`
20+
now launches headed by default; pass `--no-headed` for CI / quick
21+
smoke tests.
22+
- **`fillNewProject` timed out clicking the Create button on first
23+
project creation per session** — claude.ai/design started shipping a
24+
"Skip intro" onboarding overlay that intercepts pointer events on
25+
the form behind it. We now best-effort dismiss the overlay
26+
(1.5s wait + click) at the start of `fillNewProject`. Once
27+
dismissed, the persistent Chromium profile never sees it again,
28+
so the dismissal is a silent no-op on subsequent runs.
29+
930
## [0.2.3] - 2026-05-15
1031

1132
### Fixed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ekolabs/claude-design-loop",
3-
"version": "0.2.3",
3+
"version": "0.2.4",
44
"description": "Round-trip design loop between your IDE and claude.ai/design. Capture a route, send a brief, iterate visually with Claude, fetch the handoff bundle, translate it into framework-native scaffolds, and verify the result.",
55
"license": "SEE LICENSE IN LICENSE",
66
"private": false,

src/cli.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const program = new Command();
6767
program
6868
.name('design-loop')
6969
.description('Round-trip design loop between your IDE and claude.ai/design')
70-
.version('0.2.3');
70+
.version('0.2.4');
7171

7272
// Default action: when no subcommand is supplied, drop into the wizard.
7373
program
@@ -106,7 +106,8 @@ program
106106
'Drive claude.ai/design end-to-end for an existing loop: open project, attach screenshots, send brief, wait for the first design pass, then hand control to an interactive terminal prompt for review/iterate/fetch.',
107107
)
108108
.argument('<loopId>', 'loop id (the directory name under design-loops/)')
109-
.option('--headed', 'show the browser window (recommended for review)')
109+
.option('--headed', 'show the browser window (recommended for review)', true)
110+
.option('--no-headed', 'run the browser headless (CI / quick smoke tests)')
110111
.option(
111112
'--fidelity <fidelity>',
112113
'wireframe | high-fidelity (default: high-fidelity)',
@@ -159,7 +160,8 @@ program
159160
'--project-url <url>',
160161
'override the manifest-saved project URL (claude.ai/design/p/...)',
161162
)
162-
.option('--headed', 'show the browser window (recommended)')
163+
.option('--headed', 'show the browser window (recommended)', true)
164+
.option('--no-headed', 'run the browser headless (CI / quick smoke tests)')
163165
.option(
164166
'--no-interactive',
165167
'skip the terminal review prompt \u2014 exit after the first settle',
@@ -201,7 +203,8 @@ program
201203
'--project-url <url>',
202204
'override the manifest-saved project URL (claude.ai/design/p/...)',
203205
)
204-
.option('--headed', 'show the browser window')
206+
.option('--headed', 'show the browser window', true)
207+
.option('--no-headed', 'run the browser headless (CI / quick smoke tests)')
205208
.option(
206209
'--no-pull',
207210
'just capture the bundle URL, don\'t auto-pull (you\'ll need to run pull yourself)',
@@ -365,7 +368,8 @@ program
365368
.argument('<route>', 'route path, e.g. / or /canonical')
366369
.option('--breakpoints <list>', 'comma-separated viewport widths in px')
367370
.option('--intent <text>', 'optional one-line intent for this round')
368-
.option('--headed', 'show the browser window')
371+
.option('--headed', 'show the browser window', true)
372+
.option('--no-headed', 'run the browser headless (CI / quick smoke tests)')
369373
.option('--fidelity <fidelity>', 'wireframe | high-fidelity (default: high-fidelity)')
370374
.option(
371375
'--no-interactive',

src/lib/claude-design.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,16 @@ async function assertLoggedIn(page: Page, headed: boolean): Promise<void> {
617617

618618
async function fillNewProject(page: Page, opts: SubmitOptions): Promise<void> {
619619
console.log(`[submit] creating project "${opts.projectName}" ...`);
620+
621+
// claude.ai/design ships a "Skip intro" onboarding overlay on first
622+
// project creation per session. It intercepts pointer events on the
623+
// form below it (most visibly: the Create button click times out).
624+
// Dismiss it eagerly so every later interaction in this function
625+
// works against the real form. Once dismissed it stays dismissed for
626+
// the persistent profile, so on subsequent runs the locator never
627+
// resolves and we fall through silently.
628+
await dismissOnboardingOverlay(page);
629+
620630
const nameBox = page.getByRole('textbox', { name: 'Project name' });
621631
await nameBox.fill(opts.projectName);
622632

@@ -662,6 +672,37 @@ function createButtonLocator(page: Page): import('playwright').Locator {
662672
return page.locator('[data-testid="create-project-button"]');
663673
}
664674

675+
/**
676+
* Best-effort dismissal of the "Skip intro" onboarding overlay that
677+
* claude.ai/design started shipping in mid-2026. It pops up on first
678+
* project creation per session and intercepts pointer events on the
679+
* form behind it — most visibly causing the Create button click to
680+
* time out. We try a 1.5s wait and silently fall through if it never
681+
* appears (subsequent runs in the persistent profile never see it).
682+
*
683+
* Multiple selectors because Anthropic has rotated the button copy in
684+
* past UI revisions and a single role-name match is brittle.
685+
*/
686+
async function dismissOnboardingOverlay(page: Page): Promise<void> {
687+
const candidates = [
688+
page.getByRole('button', { name: /^Skip intro$/i }),
689+
page.getByRole('button', { name: /^Skip$/i }),
690+
page.locator('button:has-text("Skip intro")'),
691+
];
692+
for (const locator of candidates) {
693+
try {
694+
await locator.first().waitFor({ state: 'visible', timeout: 1500 });
695+
console.log('[submit] dismissing onboarding overlay');
696+
await locator.first().click({ timeout: 2000 });
697+
// Let the overlay animate out before any subsequent interaction.
698+
await page.waitForTimeout(400);
699+
return;
700+
} catch {
701+
/* not present on this run — try the next candidate */
702+
}
703+
}
704+
}
705+
665706
/**
666707
* Locator for a fidelity card button. String matching is critical here —
667708
* the buttons render with an icon prefix that shows up as a leading space

0 commit comments

Comments
 (0)