agex 0.2.10 → 0.2.12
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/assets/assets/effects.js +77 -35
- package/assets/scripts/ab-click.sh +57 -5
- package/assets/scripts/ab-toggle-animations.sh +15 -0
- package/assets/scripts/ab-type.sh +22 -30
- package/dist/{chunk-HPJHT4WX.js → chunk-IKAOVJCU.js} +53 -25
- package/dist/cli.js +17 -12
- package/dist/index.js +1 -1
- package/package.json +3 -3
package/assets/assets/effects.js
CHANGED
|
@@ -178,6 +178,17 @@
|
|
|
178
178
|
max-width: 80%;
|
|
179
179
|
text-align: center;
|
|
180
180
|
animation: ${EFFECTS_ID}-subtitle-slide-down 0.35s ease-out;
|
|
181
|
+
overflow: hidden;
|
|
182
|
+
}
|
|
183
|
+
.${EFFECTS_ID}-subtitle-progress {
|
|
184
|
+
position: absolute;
|
|
185
|
+
bottom: 0;
|
|
186
|
+
left: 0;
|
|
187
|
+
height: 2px;
|
|
188
|
+
background: color-mix(in srgb, var(--subtitle-fg, white) 15%, transparent);
|
|
189
|
+
border-radius: 0 0 8px 8px;
|
|
190
|
+
width: 0%;
|
|
191
|
+
transition: none;
|
|
181
192
|
}
|
|
182
193
|
.${EFFECTS_ID}-drawing-canvas {
|
|
183
194
|
position: fixed;
|
|
@@ -547,39 +558,13 @@
|
|
|
547
558
|
if (el) el.remove();
|
|
548
559
|
},
|
|
549
560
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
function typeNext() {
|
|
558
|
-
if (i >= len) return;
|
|
559
|
-
var ch = text[i];
|
|
560
|
-
el.dispatchEvent(new KeyboardEvent('keydown', { key: ch, bubbles: true }));
|
|
561
|
-
el.dispatchEvent(new KeyboardEvent('keypress', { key: ch, bubbles: true }));
|
|
562
|
-
// For contenteditable elements, insert text differently
|
|
563
|
-
if (el.isContentEditable) {
|
|
564
|
-
document.execCommand('insertText', false, ch);
|
|
565
|
-
} else {
|
|
566
|
-
// Use InputEvent for proper React/framework compatibility
|
|
567
|
-
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
|
568
|
-
Object.getPrototypeOf(el), 'value'
|
|
569
|
-
);
|
|
570
|
-
if (nativeInputValueSetter && nativeInputValueSetter.set) {
|
|
571
|
-
nativeInputValueSetter.set.call(el, el.value + ch);
|
|
572
|
-
} else {
|
|
573
|
-
el.value = el.value + ch;
|
|
574
|
-
}
|
|
575
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
576
|
-
}
|
|
577
|
-
el.dispatchEvent(new KeyboardEvent('keyup', { key: ch, bubbles: true }));
|
|
578
|
-
i++;
|
|
579
|
-
if (i < len) setTimeout(typeNext, charDelay);
|
|
580
|
-
}
|
|
581
|
-
typeNext();
|
|
582
|
-
return len * charDelay;
|
|
561
|
+
setPosition(x, y) {
|
|
562
|
+
var c = document.getElementById('agex-cursor');
|
|
563
|
+
if (!c) return;
|
|
564
|
+
c.style.transition = 'none';
|
|
565
|
+
c.style.left = x + 'px';
|
|
566
|
+
c.style.top = y + 'px';
|
|
567
|
+
this._lastPos = { x: x, y: y };
|
|
583
568
|
},
|
|
584
569
|
|
|
585
570
|
fadeOut() {
|
|
@@ -1429,10 +1414,29 @@
|
|
|
1429
1414
|
if (fg) subtitle.style.setProperty('--subtitle-fg', fg);
|
|
1430
1415
|
if (bg) subtitle.style.setProperty('--subtitle-bg', bg);
|
|
1431
1416
|
subtitle.textContent = text;
|
|
1417
|
+
|
|
1418
|
+
var bar = document.createElement('div');
|
|
1419
|
+
bar.className = `${EFFECTS_ID}-subtitle-progress`;
|
|
1420
|
+
subtitle.appendChild(bar);
|
|
1421
|
+
|
|
1422
|
+
var startTime = performance.now();
|
|
1423
|
+
var barDuration = duration > 0 ? duration : 30000;
|
|
1424
|
+
var rafId = 0;
|
|
1425
|
+
function tick() {
|
|
1426
|
+
var elapsed = performance.now() - startTime;
|
|
1427
|
+
var pct = Math.min(elapsed / barDuration * 100, 100);
|
|
1428
|
+
bar.style.width = pct + '%';
|
|
1429
|
+
if (pct < 100) rafId = requestAnimationFrame(tick);
|
|
1430
|
+
}
|
|
1431
|
+
rafId = requestAnimationFrame(tick);
|
|
1432
|
+
|
|
1432
1433
|
document.body.appendChild(subtitle);
|
|
1433
1434
|
|
|
1434
1435
|
if (duration > 0) {
|
|
1435
|
-
setTimeout(()
|
|
1436
|
+
setTimeout(function() {
|
|
1437
|
+
cancelAnimationFrame(rafId);
|
|
1438
|
+
subtitle.remove();
|
|
1439
|
+
}, duration);
|
|
1436
1440
|
}
|
|
1437
1441
|
},
|
|
1438
1442
|
|
|
@@ -1636,11 +1640,23 @@
|
|
|
1636
1640
|
scrollElementToCenter(selector) {
|
|
1637
1641
|
var el = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
1638
1642
|
if (!el) return false;
|
|
1639
|
-
// scrollIntoView with block:'center' handles nested scrollable containers
|
|
1640
1643
|
el.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'instant' });
|
|
1641
1644
|
return true;
|
|
1642
1645
|
},
|
|
1643
1646
|
|
|
1647
|
+
scrollElementToCenterSmooth(selector) {
|
|
1648
|
+
var el = typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
1649
|
+
if (!el) return 0;
|
|
1650
|
+
var rect = el.getBoundingClientRect();
|
|
1651
|
+
var viewH = window.innerHeight;
|
|
1652
|
+
var centerY = rect.top + rect.height / 2;
|
|
1653
|
+
if (centerY > viewH * 0.2 && centerY < viewH * 0.8) return 0;
|
|
1654
|
+
var dist = Math.abs(centerY - viewH / 2);
|
|
1655
|
+
var duration = Math.max(600, Math.min(2000, Math.round(dist * 1.5)));
|
|
1656
|
+
el.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'smooth' });
|
|
1657
|
+
return duration;
|
|
1658
|
+
},
|
|
1659
|
+
|
|
1644
1660
|
scrollToCoords(y, viewportHeight, elHeight) {
|
|
1645
1661
|
window.scrollTo({
|
|
1646
1662
|
top: window.scrollY + y - (viewportHeight / 2) + (elHeight / 2),
|
|
@@ -1685,6 +1701,31 @@
|
|
|
1685
1701
|
}
|
|
1686
1702
|
};
|
|
1687
1703
|
|
|
1704
|
+
// Enable or disable page CSS animations & transitions (preserves agex effects)
|
|
1705
|
+
function setPageAnimations(enabled) {
|
|
1706
|
+
var id = EFFECTS_ID + '-disable-animations';
|
|
1707
|
+
var existing = document.getElementById(id);
|
|
1708
|
+
if (enabled) {
|
|
1709
|
+
if (existing) existing.remove();
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
if (existing) return;
|
|
1713
|
+
var style = document.createElement('style');
|
|
1714
|
+
style.id = id;
|
|
1715
|
+
style.textContent = `
|
|
1716
|
+
*:not(.${EFFECTS_ID}):not(.${EFFECTS_ID} *) {
|
|
1717
|
+
animation: none !important;
|
|
1718
|
+
animation-delay: 0s !important;
|
|
1719
|
+
animation-duration: 0s !important;
|
|
1720
|
+
transition: none !important;
|
|
1721
|
+
transition-delay: 0s !important;
|
|
1722
|
+
transition-duration: 0s !important;
|
|
1723
|
+
scroll-behavior: auto !important;
|
|
1724
|
+
}
|
|
1725
|
+
`;
|
|
1726
|
+
document.head.appendChild(style);
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1688
1729
|
// Clear all effects
|
|
1689
1730
|
function clearAll() {
|
|
1690
1731
|
effects.highlight.clear();
|
|
@@ -1720,6 +1761,7 @@
|
|
|
1720
1761
|
window.__agexEffects = {
|
|
1721
1762
|
apply,
|
|
1722
1763
|
clearAll,
|
|
1764
|
+
setPageAnimations,
|
|
1723
1765
|
banner,
|
|
1724
1766
|
proof,
|
|
1725
1767
|
...effects,
|
|
@@ -11,14 +11,38 @@ if [ -z "$SELECTOR" ]; then
|
|
|
11
11
|
exit 1
|
|
12
12
|
fi
|
|
13
13
|
|
|
14
|
-
#
|
|
14
|
+
# Smooth-scroll element into center of viewport so the viewer can follow
|
|
15
15
|
if [[ "$SELECTOR" == @* ]]; then
|
|
16
|
-
|
|
16
|
+
# For @ref: get bounding box first, then smooth-scroll by coordinates
|
|
17
|
+
PRE_STYLES=$(agent-browser get styles "$SELECTOR" --json {{SESSION_ARG}} 2>&1) || true
|
|
18
|
+
PRE_Y=$(echo "$PRE_STYLES" | grep -o '"y":[0-9.-]*' | head -1 | cut -d: -f2)
|
|
19
|
+
PRE_H=$(echo "$PRE_STYLES" | grep -o '"height":[0-9.-]*' | head -1 | cut -d: -f2)
|
|
20
|
+
if [ -n "$PRE_Y" ] && [ -n "$PRE_H" ]; then
|
|
21
|
+
SCROLL_DUR=$(agent-browser eval "(function(){ var viewH=window.innerHeight; var centerY=${PRE_Y}+${PRE_H}/2; if(centerY>viewH*0.2 && centerY<viewH*0.8) return '0'; var dist=Math.abs(centerY-viewH/2); var dur=Math.max(600,Math.min(2000,Math.round(dist*1.5))); window.scrollBy({top:centerY-viewH/2,behavior:'smooth'}); return ''+dur; })()" {{SESSION_ARG}} 2>/dev/null || echo "0")
|
|
22
|
+
SCROLL_DUR=$(echo "$SCROLL_DUR" | tr -d '[:space:]"')
|
|
23
|
+
if [ -n "$SCROLL_DUR" ] && [ "$SCROLL_DUR" != "0" ]; then
|
|
24
|
+
SCROLL_WAIT=$((SCROLL_DUR + 200))
|
|
25
|
+
agent-browser wait "$SCROLL_WAIT" {{SESSION_ARG}} >/dev/null 2>&1
|
|
26
|
+
else
|
|
27
|
+
agent-browser wait 300 {{SESSION_ARG}} >/dev/null 2>&1
|
|
28
|
+
fi
|
|
29
|
+
else
|
|
30
|
+
# Fallback: use instant scrollintoview
|
|
31
|
+
agent-browser scrollintoview "$SELECTOR" {{SESSION_ARG}} >/dev/null 2>&1
|
|
32
|
+
agent-browser wait 300 {{SESSION_ARG}} >/dev/null 2>&1
|
|
33
|
+
fi
|
|
17
34
|
else
|
|
18
35
|
ESCAPED_SELECTOR=$(printf '%s' "$SELECTOR" | sed "s/'/\\\\'/g")
|
|
19
|
-
agent-browser eval "window.__agexEffects.proof.
|
|
36
|
+
SCROLL_DUR=$(agent-browser eval "'' + (window.__agexEffects ? window.__agexEffects.proof.scrollElementToCenterSmooth('${ESCAPED_SELECTOR}') : 0)" {{SESSION_ARG}} 2>/dev/null || echo "0")
|
|
37
|
+
SCROLL_DUR=$(echo "$SCROLL_DUR" | tr -d '[:space:]"')
|
|
38
|
+
if [ -z "$SCROLL_DUR" ] || [ "$SCROLL_DUR" = "0" ]; then
|
|
39
|
+
agent-browser eval "window.__agexEffects.proof.scrollElementToCenter('${ESCAPED_SELECTOR}')" {{SESSION_ARG}} >/dev/null 2>&1
|
|
40
|
+
agent-browser wait 300 {{SESSION_ARG}} >/dev/null 2>&1
|
|
41
|
+
else
|
|
42
|
+
SCROLL_WAIT=$((SCROLL_DUR + 200))
|
|
43
|
+
agent-browser wait "$SCROLL_WAIT" {{SESSION_ARG}} >/dev/null 2>&1
|
|
44
|
+
fi
|
|
20
45
|
fi
|
|
21
|
-
agent-browser wait 300 {{SESSION_ARG}} >/dev/null 2>&1
|
|
22
46
|
|
|
23
47
|
# Get element bounding box without interacting with it
|
|
24
48
|
if [[ "$SELECTOR" == @* ]]; then
|
|
@@ -69,11 +93,39 @@ WAIT_MS=$((ANIM_DURATION + 300))
|
|
|
69
93
|
agent-browser wait "$WAIT_MS" {{SESSION_ARG}} >/dev/null 2>&1
|
|
70
94
|
agent-browser wait 1000 {{SESSION_ARG}} >/dev/null 2>&1
|
|
71
95
|
|
|
96
|
+
# Re-measure element position right before clicking (it may have moved during animation)
|
|
97
|
+
if [[ "$SELECTOR" == @* ]]; then
|
|
98
|
+
STYLES2=$(agent-browser get styles "$SELECTOR" --json {{SESSION_ARG}} 2>&1) || true
|
|
99
|
+
EL_X2=$(echo "$STYLES2" | grep -o '"x":[0-9.-]*' | head -1 | cut -d: -f2)
|
|
100
|
+
EL_Y2=$(echo "$STYLES2" | grep -o '"y":[0-9.-]*' | head -1 | cut -d: -f2)
|
|
101
|
+
EL_W2=$(echo "$STYLES2" | grep -o '"width":[0-9.-]*' | head -1 | cut -d: -f2)
|
|
102
|
+
EL_H2=$(echo "$STYLES2" | grep -o '"height":[0-9.-]*' | head -1 | cut -d: -f2)
|
|
103
|
+
if [ -n "$EL_X2" ] && [ -n "$EL_Y2" ] && [ -n "$EL_W2" ] && [ -n "$EL_H2" ]; then
|
|
104
|
+
CLICK_X=$(echo "$EL_X2 + $EL_W2 / 2" | bc -l)
|
|
105
|
+
CLICK_Y=$(echo "$EL_Y2 + $EL_H2 / 2" | bc -l)
|
|
106
|
+
fi
|
|
107
|
+
else
|
|
108
|
+
BOX2=$(agent-browser eval "JSON.stringify(window.__agexEffects.proof.getBoundingBox('${ESCAPED_SELECTOR}'))" {{SESSION_ARG}} 2>&1) || true
|
|
109
|
+
if [ -n "$BOX2" ] && [[ "$BOX2" != "null" ]] && [[ "$BOX2" != *"error"* ]]; then
|
|
110
|
+
EL_X2=$(echo "$BOX2" | grep -o '"x":[0-9.-]*' | cut -d: -f2)
|
|
111
|
+
EL_Y2=$(echo "$BOX2" | grep -o '"y":[0-9.-]*' | cut -d: -f2)
|
|
112
|
+
EL_W2=$(echo "$BOX2" | grep -o '"width":[0-9.-]*' | cut -d: -f2)
|
|
113
|
+
EL_H2=$(echo "$BOX2" | grep -o '"height":[0-9.-]*' | cut -d: -f2)
|
|
114
|
+
if [ -n "$EL_X2" ] && [ -n "$EL_Y2" ] && [ -n "$EL_W2" ] && [ -n "$EL_H2" ]; then
|
|
115
|
+
CLICK_X=$(echo "$EL_X2 + $EL_W2 / 2" | bc -l)
|
|
116
|
+
CLICK_Y=$(echo "$EL_Y2 + $EL_H2 / 2" | bc -l)
|
|
117
|
+
fi
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# Snap cursor to fresh position so ripple lands on the actual element
|
|
122
|
+
agent-browser eval "window.__agexEffects.cursor.setPosition(${CLICK_X}, ${CLICK_Y})" {{SESSION_ARG}} >/dev/null 2>&1
|
|
123
|
+
|
|
72
124
|
# Show click ripple and perform the actual click
|
|
73
125
|
agent-browser eval "window.__agexEffects.cursor.showClick(${CLICK_X}, ${CLICK_Y})" {{SESSION_ARG}} >/dev/null 2>&1
|
|
74
126
|
agent-browser click "$SELECTOR" {{SESSION_ARG}} >/dev/null 2>&1
|
|
75
127
|
|
|
76
|
-
# Sync _lastPos to the actual
|
|
128
|
+
# Sync _lastPos to the actual click position
|
|
77
129
|
# so the next animateToPosition starts from where the native mouse really is
|
|
78
130
|
agent-browser eval "window.__agexEffects.cursor._lastPos = { x: ${CLICK_X}, y: ${CLICK_Y} }" {{SESSION_ARG}} >/dev/null 2>&1
|
|
79
131
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Enable or disable CSS animations and transitions on the page.
|
|
3
|
+
# Preserves agex visual effects.
|
|
4
|
+
# Usage: ab-toggle-animations true|false
|
|
5
|
+
# true = enable animations (default browser behavior)
|
|
6
|
+
# false = disable animations
|
|
7
|
+
ENABLED="${1:-true}"
|
|
8
|
+
{{FX_INJECT_HELPER}}
|
|
9
|
+
if [ "$ENABLED" = "false" ]; then
|
|
10
|
+
agent-browser eval "window.__agexEffects.setPageAnimations(false)" {{SESSION_ARG}} >/dev/null 2>&1
|
|
11
|
+
echo "page animations disabled"
|
|
12
|
+
else
|
|
13
|
+
agent-browser eval "window.__agexEffects.setPageAnimations(true)" {{SESSION_ARG}} >/dev/null 2>&1
|
|
14
|
+
echo "page animations enabled"
|
|
15
|
+
fi
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Smooth type: moves cursor to input, clicks to focus, then types text
|
|
2
|
+
# Smooth type: moves cursor to input, clicks to focus, then types text slowly
|
|
3
3
|
# Uses ab-click for the animated cursor movement + click
|
|
4
|
-
# Typing happens entirely in JS (one eval call) instead of per-character shell commands
|
|
5
4
|
# Usage: ab-type <selector|@ref> "text"
|
|
6
5
|
SELECTOR="$1"
|
|
7
6
|
TEXT="$2"
|
|
@@ -13,35 +12,28 @@ fi
|
|
|
13
12
|
# Move cursor to input and click to focus (animated)
|
|
14
13
|
ab-click "$SELECTOR"
|
|
15
14
|
|
|
15
|
+
# Show glow focus effect on the input
|
|
16
16
|
ESCAPED_SEL=$(printf '%s' "$SELECTOR" | sed "s/'/\\\\'/g")
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
agent-browser eval "window.__agexEffects.cursor.showInputFocus('${ESCAPED_SEL}')" {{SESSION_ARG}} >/dev/null 2>&1
|
|
18
|
+
|
|
19
|
+
# Lock demo cursor so native mouse moves during typing don't cause jumps
|
|
20
|
+
agent-browser eval "if(window.__agexCursor) window.__agexCursor.setAnimationLock(true)" {{SESSION_ARG}} >/dev/null 2>&1
|
|
21
|
+
|
|
22
|
+
# Type text one character at a time with a human-like delay
|
|
23
|
+
LENGTH=${#TEXT}
|
|
24
|
+
for (( i=0; i<LENGTH; i++ )); do
|
|
25
|
+
CHAR="${TEXT:$i:1}"
|
|
26
|
+
agent-browser type "$SELECTOR" "$CHAR" {{SESSION_ARG}} >/dev/null 2>&1
|
|
27
|
+
agent-browser wait 70 {{SESSION_ARG}} >/dev/null 2>&1
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
# Brief pause so viewer can read the typed text
|
|
31
|
+
agent-browser wait 400 {{SESSION_ARG}} >/dev/null 2>&1
|
|
32
|
+
|
|
33
|
+
# Clear glow focus effect
|
|
34
|
+
agent-browser eval "window.__agexEffects.cursor.clearInputFocus()" {{SESSION_ARG}} >/dev/null 2>&1
|
|
34
35
|
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
agent-browser wait "$WAIT_MS" {{SESSION_ARG}} >/dev/null 2>&1
|
|
38
|
-
|
|
39
|
-
# Clear glow focus effect and unlock cursor
|
|
40
|
-
agent-browser eval "(function(){
|
|
41
|
-
var fx = window.__agexEffects;
|
|
42
|
-
if (!fx) return;
|
|
43
|
-
fx.cursor.clearInputFocus();
|
|
44
|
-
if (window.__agexCursor) window.__agexCursor.setAnimationLock(false);
|
|
45
|
-
})()" {{SESSION_ARG}} >/dev/null 2>&1
|
|
36
|
+
# Unlock demo cursor
|
|
37
|
+
agent-browser eval "if(window.__agexCursor) window.__agexCursor.setAnimationLock(false)" {{SESSION_ARG}} >/dev/null 2>&1
|
|
46
38
|
|
|
47
39
|
echo "typed \"$TEXT\" into $SELECTOR"
|
|
@@ -5475,6 +5475,7 @@ function getScriptEntries(videoEnabled) {
|
|
|
5475
5475
|
{ outputName: "ab-move", scriptFile: "ab-move.sh" },
|
|
5476
5476
|
{ outputName: "ab-click", scriptFile: "ab-click.sh" },
|
|
5477
5477
|
{ outputName: "ab-type", scriptFile: "ab-type.sh" },
|
|
5478
|
+
{ outputName: "ab-toggle-animations", scriptFile: "ab-toggle-animations.sh" },
|
|
5478
5479
|
{ outputName: "agent-browser", scriptFile: "agent-browser-wrapper.sh" }
|
|
5479
5480
|
];
|
|
5480
5481
|
}
|
|
@@ -5485,36 +5486,54 @@ function loadAndRenderScript(scriptFile, vars) {
|
|
|
5485
5486
|
|
|
5486
5487
|
// ../browse/dist/workspace.js
|
|
5487
5488
|
var log8 = createLogger("browse");
|
|
5488
|
-
function
|
|
5489
|
+
function findAgentBrowser() {
|
|
5490
|
+
let binPath;
|
|
5489
5491
|
try {
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5492
|
+
binPath = execSync2("which agent-browser", { encoding: "utf-8" }).trim();
|
|
5493
|
+
} catch {
|
|
5494
|
+
return void 0;
|
|
5495
|
+
}
|
|
5496
|
+
log8.debug(`resolved agent-browser: ${binPath}`);
|
|
5497
|
+
let home;
|
|
5498
|
+
const binDir = path4.dirname(binPath);
|
|
5499
|
+
const npmGlobalCandidate = path4.join(binDir, "..", "lib", "node_modules", "agent-browser");
|
|
5500
|
+
if (fs3.existsSync(path4.join(npmGlobalCandidate, "package.json"))) {
|
|
5501
|
+
home = path4.resolve(npmGlobalCandidate);
|
|
5502
|
+
}
|
|
5503
|
+
if (!home) {
|
|
5504
|
+
try {
|
|
5505
|
+
const realBin = fs3.realpathSync(binPath);
|
|
5506
|
+
let dir = path4.dirname(realBin);
|
|
5507
|
+
for (let i = 0; i < 5; i++) {
|
|
5508
|
+
if (fs3.existsSync(path4.join(dir, "package.json"))) {
|
|
5509
|
+
home = dir;
|
|
5510
|
+
break;
|
|
5508
5511
|
}
|
|
5509
|
-
|
|
5512
|
+
dir = path4.dirname(dir);
|
|
5510
5513
|
}
|
|
5514
|
+
} catch {
|
|
5511
5515
|
}
|
|
5512
|
-
log8.debug(`agent-browser home: ${home ?? "not found"}`);
|
|
5513
|
-
return { bin: binPath, home };
|
|
5514
|
-
} catch {
|
|
5515
|
-
log8.warn("agent-browser not found in PATH, using bare command name");
|
|
5516
|
-
return { bin: "agent-browser" };
|
|
5517
5516
|
}
|
|
5517
|
+
log8.debug(`agent-browser home: ${home ?? "not found"}`);
|
|
5518
|
+
return { bin: binPath, home };
|
|
5519
|
+
}
|
|
5520
|
+
function resolveAgentBrowser() {
|
|
5521
|
+
const found = findAgentBrowser();
|
|
5522
|
+
if (found)
|
|
5523
|
+
return found;
|
|
5524
|
+
log8.info("agent-browser not found in PATH, installing globally...");
|
|
5525
|
+
try {
|
|
5526
|
+
execSync2("npm install -g agent-browser", { encoding: "utf-8", stdio: "pipe" });
|
|
5527
|
+
log8.info("agent-browser installed successfully");
|
|
5528
|
+
} catch (err) {
|
|
5529
|
+
const detail = (err?.stderr ?? err?.stdout)?.toString().trim() || err?.message || "";
|
|
5530
|
+
throw new BrowserError(`Failed to install agent-browser globally: ${detail}`);
|
|
5531
|
+
}
|
|
5532
|
+
const installed = findAgentBrowser();
|
|
5533
|
+
if (!installed) {
|
|
5534
|
+
throw new BrowserError("agent-browser not found in PATH after global install");
|
|
5535
|
+
}
|
|
5536
|
+
return installed;
|
|
5518
5537
|
}
|
|
5519
5538
|
function buildFxInjectHelper(sessionArg, initScriptPath) {
|
|
5520
5539
|
return `
|
|
@@ -5792,6 +5811,15 @@ TYPING INTO INPUTS:
|
|
|
5792
5811
|
ALWAYS use ab-type or agent-browser fill to type into inputs.
|
|
5793
5812
|
NEVER set .value via eval \u2014 it does NOT trigger React/framework events and buttons stay disabled.
|
|
5794
5813
|
ab-type @e1 "text" # Best: animated cursor + slow typing (triggers all events)
|
|
5814
|
+
agent-browser fill @e1 "text" # Instant replace (clears existing text)
|
|
5815
|
+
|
|
5816
|
+
IMPORTANT: ab-type APPENDS text \u2014 it does NOT clear existing content.
|
|
5817
|
+
If the input already has text and you want to REPLACE it, clear first:
|
|
5818
|
+
agent-browser fill @e1 "text" # Easiest: instant replace
|
|
5819
|
+
# OR for animated typing with clear:
|
|
5820
|
+
ab-click @e1 # Focus the input
|
|
5821
|
+
agent-browser key @e1 Meta+a # Select all existing text
|
|
5822
|
+
ab-type @e1 "new text" # First char replaces selection, rest appends
|
|
5795
5823
|
|
|
5796
5824
|
BROWSER:
|
|
5797
5825
|
agent-browser snapshot # Get ALL elements with refs
|
package/dist/cli.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
timestamp,
|
|
24
24
|
validate,
|
|
25
25
|
viewportStringSchema
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-IKAOVJCU.js";
|
|
27
27
|
|
|
28
28
|
// src/cli.ts
|
|
29
29
|
import { defineCommand as defineCommand7, runMain } from "citty";
|
|
@@ -165,11 +165,8 @@ DEMO RULES are behind-the-scenes instructions. They must NOT appear in subtitles
|
|
|
165
165
|
WORKFLOW:
|
|
166
166
|
1. PLAN: Understand the task
|
|
167
167
|
2. OPEN: ab-open "<url>" FIRST (required before any other command)
|
|
168
|
-
3.
|
|
169
|
-
|
|
170
|
-
This prevents tickers, marquees, and other animations from producing duplicate frames in the recording.
|
|
171
|
-
4. EXECUTE: Interact, take screenshots
|
|
172
|
-
5. RECORD: ab-record-start, perform actions, ab-close
|
|
168
|
+
3. EXECUTE: Interact, take screenshots
|
|
169
|
+
4. RECORD: ab-record-start, perform actions, ab-close
|
|
173
170
|
|
|
174
171
|
STRATEGY:
|
|
175
172
|
- Open the URL and explore the page
|
|
@@ -180,7 +177,8 @@ COMMANDS:
|
|
|
180
177
|
ab-open "<url>" # Open URL (ALWAYS quote!)
|
|
181
178
|
ab-record-start # Start recording
|
|
182
179
|
ab-close [1 2 ...] # Stop recording and merge segments
|
|
183
|
-
ab-screenshot <name>.png
|
|
180
|
+
ab-screenshot <NNN-name>.png # Capture screenshot (e.g. 001-homepage.png, 002-menu.png)
|
|
181
|
+
ab-toggle-animations true|false # (optional) Enable/disable CSS animations. WARNING: disabling may hide elements that rely on CSS transitions to render.
|
|
184
182
|
|
|
185
183
|
EFFECTS (optional \u2014 use only if the task asks for subtitles, highlights, etc.):
|
|
186
184
|
fx-subtitle "text" # Show subtitle at bottom (persistent)
|
|
@@ -205,8 +203,16 @@ BROWSER:
|
|
|
205
203
|
agent-browser scrollintoview @e1 # Smooth scroll to element
|
|
206
204
|
agent-browser wait <ms> # Wait milliseconds
|
|
207
205
|
ab-click @e1 # Click element (smooth cursor animation)
|
|
208
|
-
ab-type @e1 "text" # Click input + type text slowly
|
|
209
|
-
agent-browser fill @e2 "text" # Fill input (instant)
|
|
206
|
+
ab-type @e1 "text" # Click input + type text slowly (APPENDS to existing text)
|
|
207
|
+
agent-browser fill @e2 "text" # Fill input (instant, replaces existing text)
|
|
208
|
+
|
|
209
|
+
CLEARING INPUTS BEFORE TYPING:
|
|
210
|
+
ab-type APPENDS text \u2014 it does NOT clear existing content.
|
|
211
|
+
If the input already has text and you want to REPLACE it, clear it first:
|
|
212
|
+
agent-browser eval "var el=document.querySelector('[data-ref]'); el.select && el.select()" # select all text
|
|
213
|
+
agent-browser key @e1 Backspace # delete selected text
|
|
214
|
+
ab-type @e1 "new text" # now type fresh
|
|
215
|
+
Or simply use: agent-browser fill @e1 "text" # instant replace (no animation)
|
|
210
216
|
agent-browser eval '<js>' # Run JavaScript
|
|
211
217
|
|
|
212
218
|
SNAPSHOT TIPS:
|
|
@@ -225,10 +231,9 @@ RULES:
|
|
|
225
231
|
- snapshot -i = interactive only (buttons, links, inputs)
|
|
226
232
|
- snapshot (no flags) = ALL elements including images, text, logos
|
|
227
233
|
- Use ab-record-start BEFORE the actions you want to capture
|
|
228
|
-
- Use ab-screenshot at key moments for
|
|
234
|
+
- Use ab-screenshot with numbered names (001-xxx.png, 002-xxx.png) at key moments for sequential evidence
|
|
229
235
|
- ALWAYS end with ab-close to finalize the recording
|
|
230
|
-
- If you made ANY DOM changes via agent-browser eval BEFORE ab-record-start (e.g.
|
|
231
|
-
agent-browser eval "if(!document.getElementById('__agex-freeze')){var s=document.createElement('style');s.id='__agex-freeze';s.textContent='*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}';document.head.appendChild(s)}"
|
|
236
|
+
- If you made ANY DOM changes via agent-browser eval BEFORE ab-record-start (e.g. hiding elements, injecting styles), you MUST re-apply them AFTER ab-record-start. DOM state does not persist across recording start.
|
|
232
237
|
|
|
233
238
|
FINISHING:
|
|
234
239
|
After completing the task, run ab-close to stop recording.
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agex",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"tsup": "^8.4.0",
|
|
26
26
|
"typescript": "^5.9.3",
|
|
27
27
|
"vitest": "^2.1.9",
|
|
28
|
-
"agex-agent": "0.1.0",
|
|
29
28
|
"agex-browse": "0.1.0",
|
|
30
29
|
"agex-core": "0.1.0",
|
|
31
|
-
"agex-
|
|
30
|
+
"agex-agent": "0.1.0",
|
|
32
31
|
"agex-prove": "0.1.0",
|
|
33
32
|
"agex-prove-pr": "0.1.0",
|
|
33
|
+
"agex-demo": "0.1.0",
|
|
34
34
|
"agex-review": "0.1.0"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|