@pro-vi/designer 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  const BIN = process.env.DESIGNER_AGENT_BROWSER_BIN || 'agent-browser';
3
3
  const DEFAULT_SESSION = process.env.DESIGNER_SESSION_NAME || 'designer';
4
- const CDP = process.env.DESIGNER_CDP || '';
4
+ const CDP = process.env.DESIGNER_CDP ?? '9222';
5
5
  export function createBrowser({ session = DEFAULT_SESSION, headed = true, timeoutMs = 30_000, cdp = CDP } = {}) {
6
6
  const baseEnv = {
7
7
  ...process.env,
@@ -53,6 +53,17 @@ export function createBrowser({ session = DEFAULT_SESSION, headed = true, timeou
53
53
  close: () => run(['close']).catch(() => null),
54
54
  url: () => run(['get', 'url']),
55
55
  title: () => run(['get', 'title']),
56
+ tabs: async () => {
57
+ const out = await run(['tab', 'list', '--json']);
58
+ const env = JSON.parse(out);
59
+ if (env.success === false) {
60
+ throw new Error(`agent-browser tab list failed: ${JSON.stringify(env.error)}`);
61
+ }
62
+ return env.data?.tabs ?? [];
63
+ },
64
+ activateTab: async (index) => {
65
+ await run(['tab', String(index)]);
66
+ },
56
67
  snapshot: ({ interactive = true, scope } = {}) => {
57
68
  const args = ['snapshot', '--json'];
58
69
  if (interactive)
package/dist/cli.js CHANGED
@@ -517,9 +517,6 @@ async function checkAgentBrowser() {
517
517
  }
518
518
  async function checkCdp() {
519
519
  const port = process.env.DESIGNER_CDP || '9222';
520
- if (!process.env.DESIGNER_CDP) {
521
- return { name: `CDP at port ${port}`, status: 'warn', detail: 'DESIGNER_CDP not set; defaulting to 9222. export DESIGNER_CDP=9222 to silence.' };
522
- }
523
520
  try {
524
521
  const res = await fetch(`http://127.0.0.1:${port}/json/version`);
525
522
  if (!res.ok)
@@ -90,19 +90,55 @@ export class DesignerController {
90
90
  }
91
91
  throw new Error(`Unknown action: ${action}`);
92
92
  }
93
+ async selectMatchingTab() {
94
+ const tabs = await this.browser.tabs().catch(() => []);
95
+ if (tabs.length === 0)
96
+ return { matched: false, candidates: 0 };
97
+ const stored = getSession(this.key);
98
+ const targetRoot = stored?.designUrl?.split('?')[0];
99
+ const candidates = tabs.filter((t) => {
100
+ if (t.type !== 'page' || !t.url)
101
+ return false;
102
+ if (targetRoot)
103
+ return t.url.startsWith(targetRoot);
104
+ return /^https:\/\/claude\.ai\/design(\/|$|\?)/.test(t.url);
105
+ });
106
+ if (candidates.length === 0)
107
+ return { matched: false, candidates: 0 };
108
+ candidates.sort((a, b) => (Number(b.active) - Number(a.active)) || (a.index - b.index));
109
+ for (const cand of candidates) {
110
+ await this.browser.activateTab(cand.index).catch(() => null);
111
+ const composerOk = await this.browser.isVisible(this.selectors.composer.promptTextarea).catch(() => false);
112
+ const homeOk = this.selectors.login.signedInIndicator
113
+ ? await this.browser.isVisible(this.selectors.login.signedInIndicator).catch(() => false)
114
+ : false;
115
+ if (composerOk || homeOk) {
116
+ upsertSession(this.key, { lastUrl: await this.currentUrl() });
117
+ return { matched: true, candidates: candidates.length };
118
+ }
119
+ }
120
+ return { matched: false, candidates: candidates.length };
121
+ }
93
122
  async ensureReady() {
94
123
  await ensureCdpUp();
95
- const u = await this.currentUrl();
96
- if (!/claude\.ai\/design/.test(u)) {
97
- await this.browser.open(DESIGN_HOME);
98
- await this.browser.waitLoad('networkidle').catch(() => null);
124
+ const picked = await this.selectMatchingTab();
125
+ if (picked.matched) {
126
+ return { ok: true, url: await this.currentUrl(), inSession: await this.isInSession() };
127
+ }
128
+ if (picked.candidates === 0) {
129
+ const u = await this.currentUrl();
130
+ if (!/claude\.ai\/design/.test(u)) {
131
+ await this.browser.open(DESIGN_HOME);
132
+ await this.browser.waitLoad('networkidle').catch(() => null);
133
+ }
99
134
  }
100
135
  const homeOk = this.selectors.login.signedInIndicator
101
136
  ? await this.browser.isVisible(this.selectors.login.signedInIndicator).catch(() => false)
102
137
  : false;
103
138
  const sessionOk = await this.browser.isVisible(this.selectors.composer.promptTextarea).catch(() => false);
104
139
  if (!homeOk && !sessionOk) {
105
- throw new Error('Not signed in to claude.ai/design, or on an unrecognized page. Sign in in the CDP-attached Chrome.');
140
+ const suffix = picked.candidates > 0 ? ` (checked ${picked.candidates} tab(s))` : '';
141
+ throw new Error(`Not signed in to claude.ai/design, or on an unrecognized page${suffix}. Sign in in the CDP-attached Chrome.`);
106
142
  }
107
143
  upsertSession(this.key, { lastUrl: await this.currentUrl() });
108
144
  return { ok: true, url: await this.currentUrl(), inSession: await this.isInSession() };
@@ -334,10 +370,12 @@ export class DesignerController {
334
370
  // scraping misses them; text-node walking is resilient.
335
371
  const seen = new Set();
336
372
  const files = [];
373
+ let designFilesLabelVisible = false;
337
374
  const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
338
375
  let node;
339
376
  while ((node = walker.nextNode())) {
340
377
  const t = (node.textContent || '').trim();
378
+ if (t === 'Design Files') designFilesLabelVisible = true;
341
379
  if (!/^[A-Za-z0-9 _.()\\-]+\\.(html|js|css|jsx|tsx|ts|md|json|svg)$/i.test(t)) continue;
342
380
  if (t.length > 80 || seen.has(t)) continue;
343
381
  seen.add(t);
@@ -355,12 +393,15 @@ export class DesignerController {
355
393
  folderSet.add(lines[0]);
356
394
  }
357
395
  }
358
- return { files, folders: Array.from(folderSet) };
359
- })()`).catch(() => ({ files: [], folders: [] }));
396
+ return { files, folders: Array.from(folderSet), designFilesLabelVisible };
397
+ })()`).catch(() => ({ files: [], folders: [], designFilesLabelVisible: false }));
398
+ const files = Array.isArray(result.files) ? result.files : [];
399
+ const folders = Array.isArray(result.folders) ? result.folders : [];
400
+ const emptyButLabelVisible = files.length === 0 && result.designFilesLabelVisible === true;
360
401
  return {
361
- files: Array.isArray(result.files) ? result.files : [],
362
- folders: Array.isArray(result.folders) ? result.folders : [],
363
- authoritative: (result.folders?.length ?? 0) === 0
402
+ files,
403
+ folders,
404
+ authoritative: !emptyButLabelVisible && folders.length === 0
364
405
  };
365
406
  }
366
407
  async openFile(filename) {
@@ -3,9 +3,6 @@ import { createBrowser } from "../browser.js";
3
3
  const cmd = process.argv[2];
4
4
  const arg = process.argv[3];
5
5
  const browser = createBrowser({ headed: true });
6
- if (!process.env.DESIGNER_CDP) {
7
- console.error('[probe] DESIGNER_CDP not set — using agent-browser-managed session (may be blocked by Cloudflare/SSO). Prefer: export DESIGNER_CDP=9222 and relaunch Chrome with --remote-debugging-port=9222.');
8
- }
9
6
  async function main() {
10
7
  switch (cmd) {
11
8
  case 'login':
@@ -43,12 +40,26 @@ async function main() {
43
40
  case 'close':
44
41
  await browser.close();
45
42
  break;
43
+ case 'tabs': {
44
+ const tabs = await browser.tabs();
45
+ const composer = '[data-testid="chat-composer-input"]';
46
+ const signedIn = '[data-testid="create-project-button"]';
47
+ for (const t of tabs) {
48
+ await browser.activateTab(t.index).catch(() => null);
49
+ const composerOk = await browser.isVisible(composer).catch(() => false);
50
+ const signedInOk = await browser.isVisible(signedIn).catch(() => false);
51
+ const flag = composerOk ? 'composer' : signedInOk ? 'home' : 'unrecognized';
52
+ console.log(`[${t.index}] active=${t.active ? 'Y' : 'N'} ${flag.padEnd(12)} ${t.url}`);
53
+ }
54
+ break;
55
+ }
46
56
  default:
47
57
  console.log(`Usage:
48
58
  probe.ts login open headed window for manual login
49
59
  probe.ts open <url> navigate
50
60
  probe.ts url print current url
51
61
  probe.ts title print current title
62
+ probe.ts tabs list CDP tabs with readiness verdict
52
63
  probe.ts snapshot [scope] interactive a11y tree (text)
53
64
  probe.ts snapshot-json [scope] interactive a11y tree (JSON)
54
65
  probe.ts screenshot [path] full-page screenshot
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pro-vi/designer",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "type": "module",
5
5
  "description": "MCP + CLI for autonomous iteration of claude.ai/design — drives the design surface via agent-browser, downloads handoff bundles, and exposes a tasting harness for full-viewport variant comparison.",
6
6
  "license": "MIT",