Skip to content

Commit 6ecc34a

Browse files
authored
feat: add back animation (#41)
1 parent 10d46da commit 6ecc34a

File tree

16 files changed

+285
-126
lines changed

16 files changed

+285
-126
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ https://github.com/user-attachments/assets/1fbf87fd-06d1-42bf-a06f-cc2bbdf375a8
1919
"mcpServers": {
2020
"browser-kit": {
2121
"command": "npx",
22-
"args": ["@mcp-browser-kit/server"]
22+
"args": ["@mcp-browser-kit/server@latest"]
2323
}
2424
}
2525
}

apps/m2/src/services/mbk-tab.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class MbkTab {
3030
.to(DrivenLoggerFactoryConsolaBrowser);
3131

3232
// Setup M2 container with required services
33-
DrivenBrowserDriverM2.setupContainer(container);
33+
DrivenBrowserDriverM2.setupTabContainer(container);
3434

3535
// Register KeepAlive service
3636
container.bind<KeepAlive>(KeepAlive).to(KeepAlive);

apps/m3/src/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"permissions": [
88
"tabs",
99
"activeTab",
10+
"tabCapture",
1011
"scripting",
1112
"alarms",
1213
"storage"

apps/m3/src/services/mbk-tab.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class MbkTab {
3030
.to(DrivenLoggerFactoryConsolaBrowser);
3131

3232
// Setup M3 container with required services
33-
DrivenBrowserDriverM3.setupContainer(container);
33+
DrivenBrowserDriverM3.setupTabContainer(container);
3434

3535
// Register KeepAlive service
3636
container.bind<KeepAlive>(KeepAlive).to(KeepAlive);

packages/extension-driven-browser-driver/src/services/driven-browser-driver-m2.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,28 @@ import type { Container } from "inversify";
1616
import { inject, injectable } from "inversify";
1717
import * as backgroundToolsM2 from "../utils/background-tools-m2";
1818
import * as backgroundToolsM3 from "../utils/background-tools-m3";
19-
import { TabAnimationTools } from "./tab-animation-tools";
20-
import { TabContextStore } from "./tab-context-store";
21-
import { TabDomTools } from "./tab-dom-tools";
2219
import { TabRpcService } from "./tab-rpc-service";
23-
import { TabTools } from "./tab-tools";
2420
import { TabToolsSetup } from "./tab-tools-setup";
2521

2622
@injectable()
2723
export class DrivenBrowserDriverM2 implements BrowserDriverOutputPort {
2824
/**
29-
* Setup container bindings for M2 environment - includes tab services and M2 driver
25+
* Setup container bindings for M2 environment
3026
*/
3127
static setupContainer(container: Container): void {
32-
// Tab services
33-
container.bind<TabTools>(TabTools).to(TabTools);
34-
container.bind<TabDomTools>(TabDomTools).to(TabDomTools);
35-
container.bind<TabAnimationTools>(TabAnimationTools).to(TabAnimationTools);
36-
container.bind<TabContextStore>(TabContextStore).to(TabContextStore);
37-
container.bind<TabToolsSetup>(TabToolsSetup).to(TabToolsSetup);
28+
// Setup TabRpcService and its dependencies
29+
container.bind<TabRpcService>(TabRpcService).to(TabRpcService);
3830

3931
// M2 browser driver
40-
container.bind<TabRpcService>(TabRpcService).to(TabRpcService);
4132
container
4233
.bind<BrowserDriverOutputPort>(BrowserDriverOutputPort)
4334
.to(DrivenBrowserDriverM2);
4435
}
4536

37+
static setupTabContainer(container: Container): void {
38+
TabToolsSetup.setupContainer(container);
39+
}
40+
4641
private readonly logger;
4742

4843
constructor(
@@ -113,7 +108,7 @@ export class DrivenBrowserDriverM2 implements BrowserDriverOutputPort {
113108

114109
captureTab = (tabId: string): Promise<Screenshot> => {
115110
this.logger.verbose(`Capturing tab: ${tabId}`);
116-
return backgroundToolsM3.captureTab(tabId);
111+
return backgroundToolsM2.captureTab(tabId);
117112
};
118113

119114
// DOM Query Methods

packages/extension-driven-browser-driver/src/services/driven-browser-driver-m3.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,28 @@ import type { Func } from "@mcp-browser-kit/types";
1515
import type { Container } from "inversify";
1616
import { inject, injectable } from "inversify";
1717
import * as backgroundToolsM3 from "../utils/background-tools-m3";
18-
import { TabAnimationTools } from "./tab-animation-tools";
19-
import { TabContextStore } from "./tab-context-store";
20-
import { TabDomTools } from "./tab-dom-tools";
2118
import { TabRpcService } from "./tab-rpc-service";
22-
import { TabTools } from "./tab-tools";
2319
import { TabToolsSetup } from "./tab-tools-setup";
2420

2521
@injectable()
2622
export class DrivenBrowserDriverM3 implements BrowserDriverOutputPort {
2723
/**
28-
* Setup container bindings for M3 environment - includes tab services and M3 driver
24+
* Setup container bindings for M3 environment
2925
*/
3026
static setupContainer(container: Container): void {
31-
// Tab services
32-
container.bind<TabTools>(TabTools).to(TabTools);
33-
container.bind<TabDomTools>(TabDomTools).to(TabDomTools);
34-
container.bind<TabAnimationTools>(TabAnimationTools).to(TabAnimationTools);
35-
container.bind<TabContextStore>(TabContextStore).to(TabContextStore);
36-
container.bind<TabToolsSetup>(TabToolsSetup).to(TabToolsSetup);
37-
38-
// M3 browser driver services
27+
// Setup TabRpcService and its dependencies
3928
container.bind<TabRpcService>(TabRpcService).to(TabRpcService);
29+
30+
// M3 browser driver
4031
container
4132
.bind<BrowserDriverOutputPort>(BrowserDriverOutputPort)
4233
.to(DrivenBrowserDriverM3);
4334
}
4435

36+
static setupTabContainer(container: Container): void {
37+
TabToolsSetup.setupContainer(container);
38+
}
39+
4540
private readonly logger;
4641

4742
constructor(
@@ -110,9 +105,9 @@ export class DrivenBrowserDriverM3 implements BrowserDriverOutputPort {
110105
this.logger.info(`Tab closed: ${tabId}`);
111106
};
112107

113-
captureTab = (tabId: string): Promise<Screenshot> => {
114-
this.logger.verbose(`Capturing tab: ${tabId}`);
115-
return backgroundToolsM3.captureTab(tabId);
108+
captureTab = (_tabId: string): Promise<Screenshot> => {
109+
this.logger.verbose("captureTab called (not supported in M3 driver)");
110+
return Promise.reject("captureTab is not supported in M3 driver");
116111
};
117112

118113
// DOM Query Methods

packages/extension-driven-browser-driver/src/services/tab-animation-tools.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,38 @@ export class TabAnimationTools {
1717
this.logger = this.loggerFactory.create("TabAnimationTools");
1818
}
1919

20-
playClickAnimationAdvance = (
20+
playClickAnimationAdvance = async (
2121
x: number,
2222
y: number,
2323
options: ClickAnimationOptions = {},
24-
): void => {
24+
): Promise<void> => {
2525
this.logger.info(
2626
`Playing advanced click animation at (${x}, ${y})`,
2727
options,
2828
);
29-
animation.playClickAnimationAdvance(x, y, options);
29+
await animation.playClickAnimationAdvance(x, y, options);
3030
this.logger.verbose("Advanced click animation completed");
3131
};
3232

33-
playClickAnimation = (x: number, y: number): void => {
33+
playClickAnimation = async (x: number, y: number): Promise<void> => {
3434
this.logger.info(`Playing click animation at (${x}, ${y})`);
35-
animation.playClickAnimation(x, y);
35+
await animation.playClickAnimation(x, y);
3636
this.logger.verbose("Click animation completed");
3737
};
3838

39-
playClickAnimationOnElementByReadablePath = (readablePath: string): void => {
39+
playClickAnimationOnElement = async (element: Element): Promise<void> => {
40+
this.logger.info("Playing click animation on element");
41+
if (element instanceof HTMLElement) {
42+
await animation.playClickAnimationOnElement(element);
43+
this.logger.verbose("Click animation on element completed");
44+
} else {
45+
this.logger.warn("Element is not an HTMLElement, animation skipped");
46+
}
47+
};
48+
49+
playClickAnimationOnElementByReadablePath = async (
50+
readablePath: string,
51+
): Promise<void> => {
4052
this.logger.info(
4153
`Playing click animation on element at path: ${readablePath}`,
4254
);
@@ -47,7 +59,12 @@ export class TabAnimationTools {
4759
);
4860
return;
4961
}
50-
animation.playClickAnimationOnElement(element);
51-
this.logger.verbose("Click animation on element completed");
62+
await this.playClickAnimationOnElement(element);
63+
};
64+
65+
playScanAnimation = async (): Promise<void> => {
66+
this.logger.info("Playing scan animation");
67+
await animation.playScanAnimation();
68+
this.logger.verbose("Scan animation completed");
5269
};
5370
}

packages/extension-driven-browser-driver/src/services/tab-dom-tools.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { LoggerFactoryOutputPort } from "@mcp-browser-kit/core-extension/ou
33
import { LoggerFactoryOutputPort as LoggerFactoryOutputPortSymbol } from "@mcp-browser-kit/core-extension/output-ports";
44
import { inject, injectable } from "inversify";
55
import * as dom from "../utils/dom-tools";
6+
import { TabAnimationTools } from "./tab-animation-tools";
67
import { TabContextStore } from "./tab-context-store";
78

89
@injectable()
@@ -13,19 +14,24 @@ export class TabDomTools {
1314
@inject(LoggerFactoryOutputPortSymbol)
1415
private readonly loggerFactory: LoggerFactoryOutputPort,
1516
@inject(TabContextStore) private readonly contextStore: TabContextStore,
17+
@inject(TabAnimationTools) private readonly animation: TabAnimationTools,
1618
) {
1719
this.logger = this.loggerFactory.create("TabDomTools");
1820
}
1921

20-
clickOnCoordinates = (x: number, y: number) => {
22+
clickOnCoordinates = async (x: number, y: number) => {
2123
this.logger.info(`Clicking on coordinates (${x}, ${y})`);
24+
await this.animation.playClickAnimation(x, y);
2225
const result = dom.clickOnCoordinates(x, y);
2326
this.logger.verbose("Click on coordinates completed");
2427
return result;
2528
};
2629

2730
clickOnElementByReadablePath = async (readablePath: string) => {
2831
this.logger.info(`Clicking on element at path: ${readablePath}`);
32+
await this.animation.playClickAnimationOnElementByReadablePath(
33+
readablePath,
34+
);
2935
const element = this.contextStore.getElementFromPath(readablePath);
3036
if (!element) {
3137
this.logger.warn(`Element not found at path: ${readablePath}`);
@@ -36,31 +42,45 @@ export class TabDomTools {
3642
return result;
3743
};
3844

39-
fillTextToElementByReadablePath = (readablePath: string, value: string) => {
45+
fillTextToElementByReadablePath = async (
46+
readablePath: string,
47+
value: string,
48+
) => {
4049
this.logger.info(
4150
`Filling text to element at path: ${readablePath}, value length: ${value.length}`,
4251
);
52+
await this.animation.playClickAnimationOnElementByReadablePath(
53+
readablePath,
54+
);
4355
const element = this.contextStore.getElementFromPath(readablePath);
4456
if (!element) {
4557
this.logger.warn(`Element not found at path: ${readablePath}`);
4658
return;
4759
}
48-
const result = dom.fillTextToElementByReadablePath(element, value);
60+
const result = await dom.fillTextToElementByReadablePath(element, value);
4961
this.logger.verbose("Fill text completed");
5062
return result;
5163
};
5264

53-
fillTextToFocusedElement = (value: string) => {
65+
fillTextToFocusedElement = async (value: string) => {
5466
this.logger.info(
5567
`Filling text to focused element, value length: ${value.length}`,
5668
);
69+
// Play animation on the currently focused element
70+
const focusedElement = document.activeElement;
71+
if (focusedElement) {
72+
await this.animation.playClickAnimationOnElement(focusedElement);
73+
}
5774
const result = dom.fillTextToFocusedElement(value);
5875
this.logger.verbose("Fill text to focused element completed");
5976
return result;
6077
};
6178

62-
focusOnElement = (readablePath: string) => {
79+
focusOnElement = async (readablePath: string) => {
6380
this.logger.info(`Focusing on element at path: ${readablePath}`);
81+
await this.animation.playClickAnimationOnElementByReadablePath(
82+
readablePath,
83+
);
6484
const element = this.contextStore.getElementFromPath(readablePath);
6585
if (!element) {
6686
this.logger.warn(`Element not found at path: ${readablePath}`);
@@ -71,22 +91,27 @@ export class TabDomTools {
7191
return result;
7292
};
7393

74-
focusOnCoordinates = (x: number, y: number) => {
94+
focusOnCoordinates = async (x: number, y: number) => {
7595
this.logger.info(`Focusing on coordinates (${x}, ${y})`);
96+
await this.animation.playClickAnimation(x, y);
7697
const result = dom.focusOnCoordinates(x, y);
7798
this.logger.verbose("Focus on coordinates completed");
7899
return result;
79100
};
80101

81-
getInnerText = () => {
102+
getInnerText = async () => {
82103
this.logger.verbose("Getting inner text");
104+
await this.animation.playScanAnimation();
83105
const result = dom.getInnerText();
84106
this.logger.verbose(`Retrieved inner text (${result.length} characters)`);
85107
return result;
86108
};
87109

88110
hitEnterOnElementByReadablePath = async (readablePath: string) => {
89111
this.logger.info(`Hitting enter on element at path: ${readablePath}`);
112+
await this.animation.playClickAnimationOnElementByReadablePath(
113+
readablePath,
114+
);
90115
const element = this.contextStore.getElementFromPath(readablePath);
91116
if (!element) {
92117
this.logger.warn(`Element not found at path: ${readablePath}`);
@@ -99,13 +124,19 @@ export class TabDomTools {
99124

100125
hitEnterOnFocusedElement = async () => {
101126
this.logger.info("Hitting enter on focused element");
127+
// Play animation on the currently focused element
128+
const focusedElement = document.activeElement;
129+
if (focusedElement) {
130+
await this.animation.playClickAnimationOnElement(focusedElement);
131+
}
102132
const result = await dom.hitEnterOnFocusedElement();
103133
this.logger.verbose("Hit enter on focused element completed");
104134
return result;
105135
};
106136

107-
getSelection = (): Selection => {
137+
getSelection = async (): Promise<Selection> => {
108138
this.logger.verbose("Getting text selection");
139+
await this.animation.playScanAnimation();
109140
const result = dom.getSerializableSelection();
110141
this.logger.verbose(
111142
`Retrieved selection: ${result.selectedText ? `"${result.selectedText.substring(0, 50)}..."` : "none"}`,

packages/extension-driven-browser-driver/src/services/tab-tools-setup.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ import {
88
type ResolveMessage,
99
} from "@mcp-browser-kit/core-utils";
1010
import type { Func, Logger, LoggerFactory } from "@mcp-browser-kit/types";
11+
import type { Container } from "inversify";
1112
import { inject, injectable } from "inversify";
1213
import type { Get, Paths } from "type-fest";
1314
import browser, { type Runtime } from "webextension-polyfill";
15+
import { TabAnimationTools } from "./tab-animation-tools";
16+
import { TabContextStore } from "./tab-context-store";
17+
import { TabDomTools } from "./tab-dom-tools";
1418
import { TabTools } from "./tab-tools";
1519

1620
export type ToolKeys = Paths<
@@ -51,6 +55,18 @@ export interface CallToolArgs<T extends ToolKeys> {
5155
*/
5256
@injectable()
5357
export class TabToolsSetup {
58+
/**
59+
* Setup container bindings for TabToolsSetup and its dependencies
60+
*/
61+
static setupContainer(container: Container): void {
62+
// Tab service dependencies
63+
container.bind<TabDomTools>(TabDomTools).to(TabDomTools);
64+
container.bind<TabAnimationTools>(TabAnimationTools).to(TabAnimationTools);
65+
container.bind<TabContextStore>(TabContextStore).to(TabContextStore);
66+
container.bind<TabTools>(TabTools).to(TabTools);
67+
container.bind<TabToolsSetup>(TabToolsSetup).to(TabToolsSetup);
68+
}
69+
5470
private readonly logger: Logger;
5571
private readonly channel: EmitteryMessageChannel<DeferData, ResolveData>;
5672
private readonly rpcServer: MessageChannelRpcServer<TabTools>;

0 commit comments

Comments
 (0)