n8n-nodes-nvk-browser 1.0.5 → 1.0.6

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.
@@ -22,7 +22,7 @@ exports.moveAndClickFields = [
22
22
  type: 'string',
23
23
  required: true,
24
24
  default: '',
25
- description: 'For Puppeteer: CSS selector, XPath (with >XPATH> prefix), or paste the full Puppeteer code from Chrome Recorder (export as "Puppeteer"). The node will automatically extract the first Locator.race().click() block. Examples: "textarea", ">XPATH>/html/body/div", or full code with puppeteer.Locator.race([...]).click(). For Javascript: CSS selector only.',
25
+ description: 'For Puppeteer: CSS selector, XPath (with >XPATH> prefix), or paste the full Puppeteer code from Chrome Recorder (export as "Puppeteer"). The node will automatically detect and execute .click() or .fill() actions from Locator.race() blocks. Examples: "textarea", ">XPATH>/html/body/div", or full code with puppeteer.Locator.race([...]).click()/.fill(). For Javascript: CSS selector only. Note: The node only executes click/fill actions, not goto() or other navigation actions.',
26
26
  typeOptions: {
27
27
  rows: 4,
28
28
  },
@@ -99,6 +99,8 @@ class MoveAndClick {
99
99
  const timeout = this.getNodeParameter('timeout', i) || 30000;
100
100
  const tabIndex = this.getNodeParameter('tabIndex', i) || 0;
101
101
  const autoStart = this.getNodeParameter('autoStart', i) || false;
102
+ // Track action type for return value (default to click)
103
+ let actionType = 'click';
102
104
  let instance = browserManager.getInstance(profileId);
103
105
  // Auto start profile nếu chưa chạy và option được bật
104
106
  if (!instance && autoStart) {
@@ -146,20 +148,35 @@ class MoveAndClick {
146
148
  try {
147
149
  // Parse selectors from the code pattern
148
150
  const locators = [];
149
- // Find Locator.race blocks - prioritize blocks that end with .click()
151
+ // Find Locator.race blocks - check for both .click() and .fill() actions
150
152
  // Improved regex to handle multiline code from Chrome Recorder
151
- // Match pattern: puppeteer.Locator.race([...]).setTimeout(...).click(...)
153
+ // Match pattern: puppeteer.Locator.race([...]).setTimeout(...).click(...) or .fill(...)
152
154
  const clickBlockPattern = /puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)[\s\S]*?\.click\s*\(/;
155
+ const fillBlockPattern = /puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)[\s\S]*?\.fill\s*\(/;
153
156
  const clickBlockMatch = selector.match(clickBlockPattern);
157
+ const fillBlockMatch = selector.match(fillBlockPattern);
154
158
  let raceBlockContent = null;
155
- if (clickBlockMatch && clickBlockMatch[1]) {
159
+ let fillText = null;
160
+ // Prioritize fill if both exist (fill usually comes after click)
161
+ if (fillBlockMatch && fillBlockMatch[1]) {
162
+ raceBlockContent = fillBlockMatch[1];
163
+ actionType = 'fill';
164
+ // Extract fill text value
165
+ const fillTextMatch = selector.match(/\.fill\s*\(\s*['"]([^'"]*(?:\\.[^'"]*)*)['"]\s*\)/);
166
+ if (fillTextMatch && fillTextMatch[1]) {
167
+ fillText = fillTextMatch[1].replace(/\\(.)/g, '$1'); // Unescape
168
+ }
169
+ }
170
+ else if (clickBlockMatch && clickBlockMatch[1]) {
156
171
  raceBlockContent = clickBlockMatch[1];
172
+ actionType = 'click';
157
173
  }
158
174
  else {
159
- // Fallback: find any Locator.race block (even without .click())
175
+ // Fallback: find any Locator.race block (even without .click() or .fill())
160
176
  const anyRaceBlockMatch = selector.match(/puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)/);
161
177
  if (anyRaceBlockMatch && anyRaceBlockMatch[1]) {
162
178
  raceBlockContent = anyRaceBlockMatch[1];
179
+ actionType = 'click'; // Default to click
163
180
  }
164
181
  }
165
182
  if (raceBlockContent) {
@@ -244,30 +261,43 @@ class MoveAndClick {
244
261
  if (waitForClick > 0) {
245
262
  await page.waitForTimeout(waitForClick);
246
263
  }
247
- // Parse offset from code if present (handle multiline)
248
- let offset;
249
- // Match offset in click options, handling multiline and whitespace
250
- const offsetMatch = selector.match(/\.click\s*\(\s*\{[\s\S]*?offset:\s*\{[\s\S]*?x:\s*(\d+)[\s\S]*?y:\s*(\d+)[\s\S]*?\}[\s\S]*?\}/);
251
- if (offsetMatch && offsetMatch[1] && offsetMatch[2]) {
252
- offset = {
253
- x: parseInt(offsetMatch[1], 10),
254
- y: parseInt(offsetMatch[2], 10),
255
- };
256
- }
257
- // Click with options
258
- const clickOptions = {
259
- button: button,
260
- clickCount: clickCount,
261
- };
262
- // Add offset if found in code
263
- if (offset) {
264
- clickOptions.offset = offset;
265
- }
266
- if (typeof locator.click === 'function') {
267
- await locator.click(clickOptions);
264
+ // Execute action based on detected type
265
+ if (actionType === 'fill' && fillText) {
266
+ // Fill action
267
+ if (typeof locator.fill === 'function') {
268
+ await locator.fill(fillText);
269
+ }
270
+ else {
271
+ throw new Error('Locator.fill is not available');
272
+ }
268
273
  }
269
274
  else {
270
- throw new Error('Locator.click is not available');
275
+ // Click action (default)
276
+ // Parse offset from code if present (handle multiline)
277
+ let offset;
278
+ // Match offset in click options, handling multiline and whitespace
279
+ const offsetMatch = selector.match(/\.click\s*\(\s*\{[\s\S]*?offset:\s*\{[\s\S]*?x:\s*(\d+)[\s\S]*?y:\s*(\d+)[\s\S]*?\}[\s\S]*?\}/);
280
+ if (offsetMatch && offsetMatch[1] && offsetMatch[2]) {
281
+ offset = {
282
+ x: parseInt(offsetMatch[1], 10),
283
+ y: parseInt(offsetMatch[2], 10),
284
+ };
285
+ }
286
+ // Click with options
287
+ const clickOptions = {
288
+ button: button,
289
+ clickCount: clickCount,
290
+ };
291
+ // Add offset if found in code
292
+ if (offset) {
293
+ clickOptions.offset = offset;
294
+ }
295
+ if (typeof locator.click === 'function') {
296
+ await locator.click(clickOptions);
297
+ }
298
+ else {
299
+ throw new Error('Locator.click is not available');
300
+ }
271
301
  }
272
302
  }
273
303
  else {
@@ -345,7 +375,8 @@ class MoveAndClick {
345
375
  success: true,
346
376
  selector,
347
377
  method: clickMethod,
348
- message: 'Click performed successfully',
378
+ action: actionType || 'click',
379
+ message: actionType === 'fill' ? 'Fill performed successfully' : 'Click performed successfully',
349
380
  },
350
381
  });
351
382
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-nvk-browser",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "n8n nodes for managing Chrome browser profiles and page interactions",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",