Skip to content

Commit e78b466

Browse files
chore: Switch from Relative Navigation to Absolute Coordinate Positioning (#3486)
Co-authored-by: Puzhen Zhang <[email protected]>
1 parent bce7af2 commit e78b466

File tree

1 file changed

+64
-57
lines changed

1 file changed

+64
-57
lines changed

camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py

Lines changed: 64 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
1414
# =========
1515

16+
import asyncio
1617
import contextlib
1718
import time
1819
from typing import (
@@ -1368,85 +1369,91 @@ async def _sheet_input_batch_js(
13681369
ws_wrapper: Any,
13691370
system: str,
13701371
) -> Dict[str, Any]:
1371-
r"""Input to sheet using batch keyboard input with relative
1372-
positioning.
1372+
r"""Input to sheet using batch keyboard input with absolute positioning
1373+
via Name Box (Cmd+J).
13731374
1374-
Builds all operations and sends them in ONE command to TypeScript,
1375-
which executes them and only waits for stability once at the end.
1375+
This is more robust than relative navigation (arrow keys) because it
1376+
handles hidden rows/columns and merged cells correctly.
13761377
"""
13771378
operations: List[Dict[str, Any]] = []
13781379

1379-
# Go to A1 to ensure we start from a known position
1380-
if system == "Darwin":
1381-
operations.append({"type": "press", "keys": ["Meta", "Home"]})
1382-
else:
1383-
operations.append({"type": "press", "keys": ["Control", "Home"]})
1384-
operations.append({"type": "wait", "delay": 310})
1385-
1386-
# Start at (0, 0)
1387-
current_row = 0
1388-
current_col = 0
1380+
def col_to_letter(col_idx: int) -> str:
1381+
"""Convert 0-based column index to letter (0->A, 25->Z, 26->AA)."""
1382+
result = ""
1383+
col_idx += 1 # Convert to 1-based for calculation
1384+
while col_idx > 0:
1385+
col_idx, remainder = divmod(col_idx - 1, 26)
1386+
result = chr(65 + remainder) + result
1387+
return result
13891388

13901389
for cell in cells:
13911390
target_row = cell.get("row", 0)
13921391
target_col = cell.get("col", 0)
13931392
text = cell.get("text", "")
13941393

1395-
# Calculate relative movement needed
1396-
row_diff = target_row - current_row
1397-
col_diff = target_col - current_col
1398-
1399-
# Navigate vertically
1400-
if row_diff > 0:
1401-
for _ in range(row_diff):
1402-
operations.append({"type": "press", "keys": ["ArrowDown"]})
1403-
operations.append({"type": "wait", "delay": 50})
1404-
elif row_diff < 0:
1405-
for _ in range(abs(row_diff)):
1406-
operations.append({"type": "press", "keys": ["ArrowUp"]})
1407-
operations.append({"type": "wait", "delay": 50})
1408-
1409-
# Navigate horizontally
1410-
if col_diff > 0:
1411-
for _ in range(col_diff):
1412-
operations.append(
1413-
{"type": "press", "keys": ["ArrowRight"]}
1414-
)
1415-
operations.append({"type": "wait", "delay": 50})
1416-
elif col_diff < 0:
1417-
for _ in range(abs(col_diff)):
1418-
operations.append({"type": "press", "keys": ["ArrowLeft"]})
1419-
operations.append({"type": "wait", "delay": 50})
1394+
# Convert to A1 notation
1395+
col_letter = col_to_letter(target_col)
1396+
row_number = target_row + 1
1397+
cell_address = f"{col_letter}{row_number}"
1398+
1399+
# 1. Focus Name Box
1400+
if system == "Darwin":
1401+
operations.append({"type": "press", "keys": ["Meta", "j"]})
1402+
else:
1403+
# On Windows/Linux, it's usually Ctrl+J or Alt+D
1404+
# The snapshot showed Cmd+J for Mac.
1405+
# Standard Google Sheets shortcut for
1406+
# "Go to range" is F5 or Ctrl+J
1407+
operations.append({"type": "press", "keys": ["Control", "j"]})
1408+
1409+
operations.append({"type": "wait", "delay": 500})
14201410

1421-
# Wait after navigation if moved
1422-
if row_diff != 0 or col_diff != 0:
1423-
operations.append({"type": "wait", "delay": 100})
1411+
# 2. Type Address
1412+
operations.append(
1413+
{"type": "type", "text": cell_address, "delay": 0}
1414+
)
1415+
operations.append({"type": "wait", "delay": 200})
1416+
operations.append({"type": "press", "keys": ["Enter"]})
1417+
operations.append({"type": "wait", "delay": 500})
14241418

1425-
# Clear and input
1419+
# 3. Clear content (Delete/Backspace)
1420+
# Just in case, press Delete to clear existing content
14261421
operations.append({"type": "press", "keys": ["Delete"]})
1427-
operations.append({"type": "wait", "delay": 120})
1422+
operations.append({"type": "wait", "delay": 100})
14281423

1424+
# 4. Type Text
14291425
if text:
14301426
operations.append({"type": "type", "text": text, "delay": 0})
1431-
operations.append({"type": "wait", "delay": 120})
1427+
operations.append({"type": "wait", "delay": 200})
1428+
# Press Enter to confirm input
1429+
operations.append({"type": "press", "keys": ["Enter"]})
1430+
operations.append({"type": "wait", "delay": 300})
14321431

1433-
# Press Enter to confirm
1434-
operations.append({"type": "press", "keys": ["Enter"]})
1435-
operations.append({"type": "wait", "delay": 130})
1436-
1437-
# Update current position (after Enter, cursor moves to next row)
1438-
current_row = target_row + 1
1439-
current_col = target_col
1432+
# Chunk operations to avoid 100-op limit in TypeScript backend
1433+
# Each cell update takes ~10 ops, so 100 ops is only ~10 cells.
1434+
# We split into chunks of 50 ops to be safe.
1435+
CHUNK_SIZE = 50
14401436

14411437
try:
1442-
await ws_wrapper._send_command(
1443-
'batch_keyboard_input',
1444-
{'operations': operations, 'skipStabilityWait': True},
1445-
)
1438+
for i in range(0, len(operations), CHUNK_SIZE):
1439+
chunk = operations[i : i + CHUNK_SIZE]
1440+
await ws_wrapper._send_command(
1441+
'batch_keyboard_input',
1442+
{'operations': chunk, 'skipStabilityWait': True},
1443+
)
1444+
# Small delay between chunks
1445+
await asyncio.sleep(0.2)
1446+
1447+
# Wait a bit for the last input to settle
1448+
await asyncio.sleep(1.0)
1449+
14461450
tab_info = await ws_wrapper.get_tab_info()
14471451

14481452
return {
1449-
"result": f"Successfully input to {len(cells)} cells",
1453+
"result": (
1454+
f"Successfully input to {len(cells)} cells "
1455+
"using absolute navigation"
1456+
),
14501457
"snapshot": "",
14511458
"tabs": tab_info,
14521459
"current_tab": next(

0 commit comments

Comments
 (0)