chromeflow 0.4.0 → 0.5.0

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.
Files changed (2) hide show
  1. package/CLAUDE.md +36 -4
  2. package/package.json +1 -1
package/CLAUDE.md CHANGED
@@ -126,6 +126,17 @@ use `take_and_copy_screenshot()` — it saves a PNG to ~/Downloads and copies it
126
126
  - `set_file_input` accepts CSS selectors as the hint (e.g. `#import-problem-file`,
127
127
  `.upload-input`) in addition to label text. Use selectors when file inputs are hidden
128
128
  behind custom UIs and have no visible label.
129
+ - **Replacing an already-uploaded file**: after `set_file_input` succeeds, the input
130
+ becomes invisible and a "Remove" span/button typically appears near the upload area.
131
+ To replace the file: `click_element("Remove", nth=N)` (the right `nth` if there are
132
+ multiple), then call `set_file_input(hint, newPath)` again — the same hidden input is
133
+ recycled and accepts the new file. Verify with `get_form_fields()` between the two
134
+ steps so you're sure the input has reappeared.
135
+ - **Forcing auto-save on idempotent text edits** (e.g. keep-alive loop on an
136
+ auto-saving DataAnnotation form): some auto-save logic diffs against the last-saved
137
+ value and skips no-op writes. To force a real save on each tick without changing
138
+ visible content, toggle a trailing space — add when absent, remove when present.
139
+ `fill_input` value comparison handles both directions transparently.
129
140
  - After any radio/checkbox click that reveals new fields, call `get_form_fields()` again —
130
141
  the inventory will include the new fields and warn if more hidden ones still exist.
131
142
  - If a form has collapsible sections, expand them all before calling `get_form_fields()` so
@@ -144,6 +155,12 @@ use `take_and_copy_screenshot()` — it saves a PNG to ~/Downloads and copies it
144
155
  - `switch_to_tab("1")` switches by tab number; `switch_to_tab("form")` matches by URL or title substring.
145
156
  - Before navigating away from a partially-filled form, call `save_page_state()` so the form
146
157
  can be restored if the tab reloads or the page loses its state on return.
158
+ - **In long-lived self-rescheduling loops**, the active tab can silently drift mid-session
159
+ (the user navigates manually while AFK, or another tab steals focus). At the start of
160
+ every loop iteration, call `list_tabs` and verify the active tab's URL matches your
161
+ expected target — if not, `switch_to_tab(<URL or title substring>)` before running
162
+ `execute_script` or any other tab-scoped tool. Without this guard, scripts run on the
163
+ wrong tab and fail with confusing "undefined" errors that look like page bugs.
147
164
 
148
165
  ## Error handling
149
166
 
@@ -270,10 +287,25 @@ document.body.style.zoom = '1';
270
287
  1. Retry the exact same `execute_script` call
271
288
  2. If still failing, use `find_and_highlight` to show the user a download button to click manually
272
289
 
273
- **Shadow DOM `[role=radio]` / custom radios silently no-op**: On sites like Outlier,
274
- `element.click()` on a shadow-DOM radio often doesn't flip `aria-checked`. Two things
275
- must be true: (a) the element must be scrolled into view FIRST (`scrollIntoView({block:'center'})`),
276
- and (b) the full pointer-event chain must firenot just `click()`:
290
+ **React-controlled native radios/checkboxes that don't update `checked`**: `click_element`
291
+ auto-handles this for native `<input type=radio>` and `<input type=checkbox>` inputs
292
+ (including labels that wrap or `for=` reference them). The flow:
293
+ - If a radio is already `checked=true`, `click_element` skips the click re-clicking can
294
+ toggle it OFF on React forms whose `onChange` interprets the click as a deselect. The
295
+ response says `"X — radio already checked, click skipped"`.
296
+ - If the standard click fires but the input's `checked` state didn't change as expected
297
+ (radio still unchecked, or checkbox didn't toggle), `click_element` automatically
298
+ dispatches the full pointer-event chain (`pointerdown → mousedown → pointerup → mouseup
299
+ → click`) on the input. The response says `"now checked (after pointer-chain fallback)"`.
300
+
301
+ You only need to drop into `execute_script` for the no-native-input case below.
302
+
303
+ **Shadow DOM `[role=radio]` / role-only custom radios silently no-op**: On sites like
304
+ Outlier where the radio is a `[role=radio]` div with no underlying `<input>`,
305
+ `click_element`'s native-input fallback can't help — the click target has no `.checked`
306
+ property to verify. Two things must be true: (a) the element must be scrolled into view
307
+ FIRST (`scrollIntoView({block:'center'})`), and (b) the full pointer-event chain must
308
+ fire — not just `click()`:
277
309
  ```js
278
310
  ['pointerdown','mousedown','pointerup','mouseup','click'].forEach(t =>
279
311
  el.dispatchEvent(new MouseEvent(t, {bubbles: true, cancelable: true}))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chromeflow",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
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": {