chromeflow 0.2.0 → 0.2.2

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/CLAUDE.md CHANGED
@@ -48,7 +48,8 @@ Do NOT ask "should I open the browser?" — just do it. The user expects seamles
48
48
  scroll_page("down") — reveal off-screen content when target location is unknown
49
49
  b. Check results with text, not vision:
50
50
  get_page_text() — read errors/status after actions
51
- wait_for_selector(".success") — wait for async changes (builds, modals)
51
+ wait_for_selector(".success") — wait for a new element to appear
52
+ wait_for_change(".toast") — wait for an existing element's content to mutate, then read it (uses MutationObserver, cheaper than polling)
52
53
  execute_script("document.title") — query DOM state programmatically
53
54
  c. When an element can't be found or clicked:
54
55
  scroll_page("down") and retry — always try this first
@@ -167,6 +168,8 @@ screenshot to check what happened.
167
168
 
168
169
  **Waiting for async results** (build, save, deploy): `wait_for_selector(selector, timeout)` — never poll with screenshots.
169
170
 
171
+ **Waiting for an existing region to update** (e.g. click Save, then get the confirmation toast; send a chat message, then get the reply): `wait_for_change(selector)` uses a MutationObserver on the element's subtree and returns its new text content as soon as the mutation settles. Prefer this over `wait_for_selector` + `get_page_text` when the element already exists and you just need its next state — one call instead of two, no polling.
172
+
170
173
  **Pre-filling `prompt()` and `confirm()` dialogs**: When a page action will trigger a JS
171
174
  dialog (e.g. "Save As" calling `prompt()`), call `set_dialog_response` BEFORE the action:
172
175
  ```
package/dist/setup.js CHANGED
@@ -175,7 +175,9 @@ const CHROMEFLOW_TOOLS = [
175
175
  // v0.1.46+
176
176
  "type_text",
177
177
  // v0.1.57+
178
- "inspect_request_headers"
178
+ "inspect_request_headers",
179
+ // v0.2.1+
180
+ "wait_for_change"
179
181
  ].map((t) => `mcp__chromeflow__${t}`);
180
182
  function patchSettingsLocalJson(cwd) {
181
183
  const claudeDir = join(cwd, ".claude");
@@ -97,6 +97,53 @@ to 15 seconds so the page is checked gently rather than hammered every 500ms.`,
97
97
  };
98
98
  }
99
99
  );
100
+ server.tool(
101
+ "wait_for_change",
102
+ `Block until the element matching \`selector\` mutates, then return its text content.
103
+ Uses a MutationObserver \u2014 no polling, no screenshots. Ideal after an action where you expect
104
+ a specific UI region to update: click Save, then wait_for_change(".toast") to capture the
105
+ confirmation. wait_for_change(".chat-messages") after sending a message to get the reply.
106
+
107
+ The element must exist at call time (use wait_for_selector first if needed). After the first
108
+ mutation fires, waits a brief settle window (default 150ms) for the update to batch, then
109
+ returns the element's current text with secrets redacted.
110
+
111
+ Only observes changes within the matched element's subtree. Mutations in deeper shadow roots
112
+ or in sibling elements are not detected. For form inputs whose \`value\` changes without a
113
+ DOM mutation, this won't fire \u2014 use execute_script to read the value directly.`,
114
+ {
115
+ selector: z.string().describe(
116
+ `CSS selector of the element whose changes you want to observe (e.g. '.toast', '.chat-messages', '[role="alert"]')`
117
+ ),
118
+ timeout: z.number().optional().describe("Max seconds to wait for a mutation (default 30)"),
119
+ settle: z.number().optional().describe(
120
+ "Milliseconds to wait AFTER the first mutation for subsequent mutations to batch (default 150). Increase to 500-1000 if the page renders in multiple rapid steps."
121
+ )
122
+ },
123
+ async ({ selector, timeout = 30, settle }) => {
124
+ const timeoutMs = timeout * 1e3;
125
+ const settleMs = settle ?? 150;
126
+ const response = await bridge.request(
127
+ { type: "wait_for_change", selector, timeout: timeoutMs, settle: settleMs },
128
+ timeoutMs + 5e3
129
+ );
130
+ const r = response;
131
+ if (!r.ok) {
132
+ return { content: [{ type: "text", text: r.message ?? `wait_for_change timed out on "${selector}"` }] };
133
+ }
134
+ const preview = (r.text ?? "").slice(0, 5e3);
135
+ return {
136
+ content: [
137
+ {
138
+ type: "text",
139
+ text: `Element "${selector}" changed.
140
+
141
+ ${preview}`
142
+ }
143
+ ]
144
+ };
145
+ }
146
+ );
100
147
  server.tool(
101
148
  "scroll_to_element",
102
149
  `Scroll an element into view by CSS selector or label/text match.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chromeflow",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Browser guidance MCP server for Claude Code — highlights, clicks, fills, and captures from the web so you don't have to.",
5
5
  "type": "module",
6
6
  "bin": {