Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ orbs:
executors:
base:
docker:
- image: docker.io/playerui/bazel-docker:9
- image: docker.io/playerui/bazel-docker
working_directory: ~/devtools
resource_class: large
resource_class: xlarge
environment:
TZ: "/usr/share/zoneinfo/America/Los_Angeles"
minimal:
Expand Down
15 changes: 9 additions & 6 deletions devtools/client/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
# @player-devtools/client
# Player UI Devtools client

The `@player-devtools/client` exposes the Panel with the ReactPlayer, which is responsible for running content sent by Player devtool plugins on the inspected Player UI instance.
The devtools client is split into two packages so headless consumers never load React:

The `Panel` is the shared devtools UI surface, hosted by each client: the [browser extension](https://github.com/player-ui/browser-devtools) for web and the [Flipper plugin](../flipper-plugin) for mobile. The agent-facing [MCP server](../mcp) consumes the same Player devtools instrumentation without rendering the `Panel`.
- [`core/`](./core) — `@player-devtools/client`: the headless client (`createExtensionClient`, state reducer). No React. Consumed by the agent-facing [MCP server](../mcp).
- [`react/`](./react) — `@player-devtools/client-react`: the React `Panel` with the ReactPlayer, responsible for rendering content sent by Player devtool plugins on the inspected Player UI instance.

The `Panel` is the shared devtools UI surface, hosted by each client: the [browser extension](https://github.com/player-ui/browser-devtools) for web and the [Flipper plugin](../flipper-plugin) for mobile. The agent-facing [MCP server](../mcp) consumes the same Player devtools instrumentation via the headless `@player-devtools/client` core, without rendering the `Panel`.

## Installation

The Devtools client is available as an npm package. You can install it using npm or yarn:

```bash
npm install @player-devtools/client
npm install @player-devtools/client-react
```

```bash
yarn add @player-devtools/client
yarn add @player-devtools/client-react
```

## Overview
Expand All @@ -29,7 +32,7 @@ For a more comprehensive understanding of the architecture of the Devtools clien
The Devtools client is a React component that receives content from devtools plugins running in the Player UI used by the inspected page. It can be used in your React application like any other React component.

```jsx
import { Panel } from "@player-devtools/client";
import { Panel } from "@player-devtools/client-react";
import type { MessengerOptions } from "@player-devtools/messenger";
import browser from "webextension-polyfill";

Expand Down
23 changes: 23 additions & 0 deletions devtools/client/core/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
load("@npm//:defs.bzl", "npm_link_all_packages")
load("@rules_player//javascript:defs.bzl", "js_pipeline")
load("//helpers:defs.bzl", "tsup_config", "vitest_config")

npm_link_all_packages(name = "node_modules")

tsup_config(name = "tsup_config")

vitest_config(name = "vitest_config")

js_pipeline(
package_name = "@player-devtools/client",
deps = [
":node_modules/@player-devtools/messenger",
":node_modules/@player-devtools/types",
":node_modules/@player-devtools/utils",
"//:node_modules/immer",
"//:node_modules/dequal",
],
test_deps = [
"//:node_modules/@player-ui/player",
],
)
File renamed without changes.
9 changes: 9 additions & 0 deletions devtools/client/core/src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ExtensionState } from "@player-devtools/types";

export const INITIAL_EXTENSION_STATE: ExtensionState = {
current: {
player: null,
plugin: null,
},
players: {},
};
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { Panel } from "./panel";
export { createExtensionClient, type ExtensionClient } from "./state/client";
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,32 @@ import type {
ExtensionSelectedPlayerEvent,
ExtensionSelectedPluginEvent,
} from "@player-devtools/types";
import type { Flow } from "@player-ui/player";
import { reducer } from "../reducer";
import { INITIAL_EXTENSION_STATE, INITIAL_FLOW } from "../../constants";
import { INITIAL_EXTENSION_STATE } from "../../constants";

/** Minimal flow fixture used to exercise the reducer's flow handling. */
const INITIAL_FLOW: Flow = {
id: "initial-flow",
views: [
{
id: "view-1",
type: "text",
value: "connecting...",
},
],
navigation: {
BEGIN: "FLOW_1",
FLOW_1: {
startState: "VIEW_1",
VIEW_1: {
state_type: "VIEW",
ref: "view-1",
transitions: {},
},
},
},
};

const mockPlayerInitTransaction: Transaction<PlayerInitEvent> = {
id: 1,
Expand Down
20 changes: 20 additions & 0 deletions devtools/client/flipper/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("@npm//:defs.bzl", "npm_link_all_packages")
load("@rules_player//javascript:defs.bzl", "js_pipeline")
load("//helpers:defs.bzl", "tsup_config", "vitest_config")

npm_link_all_packages(name = "node_modules")

tsup_config(name = "tsup_config")

vitest_config(name = "vitest_config")

js_pipeline(
package_name = "@player-devtools/client-flipper",
deps = [
":node_modules/@player-devtools/types",
"//:node_modules/@types/ws",
"//:node_modules/flipper-server",
"//:node_modules/flipper-server-client",
"//:node_modules/ws",
],
)
8 changes: 8 additions & 0 deletions devtools/client/flipper/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@player-devtools/client-flipper",
"version": "0.0.0-PLACEHOLDER",
"main": "src/index.ts",
"dependencies": {
"@player-devtools/types": "workspace:*"
}
}
1 change: 1 addition & 0 deletions devtools/client/flipper/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FlipperServerTransport } from "./transport";
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ import * as net from "net";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import { WebSocket as WsWebSocket } from "ws";
import type {
CommunicationLayerMethods,
ExtensionSupportedEvents,
MessengerEvent,
TransactionMetadata,
Transport,
} from "@player-devtools/types";

// polyfill WebSocket for Node < 22
if (typeof (globalThis as { WebSocket?: unknown }).WebSocket === "undefined") {
(globalThis as { WebSocket?: unknown }).WebSocket =
WsWebSocket as unknown as typeof WebSocket;
}

type MessageCallback = (
message: TransactionMetadata & MessengerEvent<ExtensionSupportedEvents>,
) => void;
Expand All @@ -31,14 +39,6 @@ type FlipperExecuteMessage = {
};
};

/** Transport interface — implemented by each connection adapter */
export interface Transport extends CommunicationLayerMethods {
/** Connect to the underlying transport */
connect(): Promise<void>;
/** Tear down the underlying transport */
close(): Promise<void>;
}

/**
* Flipper headless transport
*
Expand Down
5 changes: 3 additions & 2 deletions devtools/client/BUILD → devtools/client/react/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ tsup_config(name = "tsup_config")
vitest_config(name = "vitest_config")

js_pipeline(
package_name = "@player-devtools/client",
package_name = "@player-devtools/client-react",
deps = [
":node_modules/@player-devtools/client",
":node_modules/@player-devtools/messenger",
":node_modules/@player-devtools/types",
":node_modules/@player-devtools/utils",
Expand All @@ -27,6 +28,6 @@ js_pipeline(
"//:node_modules/react",
"//:node_modules/react-error-boundary",
"//:node_modules/dequal",
"//:node_modules/@devtools-ds/themes"
"//:node_modules/@devtools-ds/themes",
],
)
11 changes: 11 additions & 0 deletions devtools/client/react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@player-devtools/client-react",
"version": "0.0.0-PLACEHOLDER",
"main": "src/index.ts",
"dependencies": {
"@player-devtools/client": "workspace:*",
"@player-devtools/messenger": "workspace:*",
"@player-devtools/types": "workspace:*",
"@player-devtools/utils": "workspace:*"
}
}
1 change: 1 addition & 0 deletions devtools/client/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Panel } from "./panel";
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,33 @@ import {

import { ThemeProvider, useDarkMode } from "@devtools-ds/themes";

import { INITIAL_FLOW } from "../constants";
import { PLAYER_PLUGINS, PUBSUB_PLUGIN } from "../plugins";
import { useExtensionState } from "../state";
import { flowDiff } from "../helpers/flowDiff";

/** Placeholder flow rendered until a real Player flow arrives. */
const INITIAL_FLOW: Flow = {
id: "initial-flow",
views: [
{
id: "view-1",
type: "text",
value: "connecting...",
},
],
navigation: {
BEGIN: "FLOW_1",
FLOW_1: {
startState: "VIEW_1",
VIEW_1: {
state_type: "VIEW",
ref: "view-1",
transitions: {},
},
},
},
};

const fallbackRender: ErrorBoundary["props"]["fallbackRender"] = ({
error,
}) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { CommunicationLayerMethods } from "@player-devtools/types";
import { useEffect, useMemo, useSyncExternalStore } from "react";

import { createExtensionClient } from "./client";
import { createExtensionClient } from "@player-devtools/client";

/**
* Thin React adapter over `createExtensionClient`.
Expand Down
32 changes: 0 additions & 32 deletions devtools/client/src/constants/index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion devtools/flipper-plugin/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ npm_link_all_packages(name = "node_modules")
vitest_config(name = "vitest_config")

deps = [
":node_modules/@player-devtools/client",
":node_modules/@player-devtools/client-react",
":node_modules/@player-devtools/types",
"//:node_modules/dlv",

Expand Down
2 changes: 1 addition & 1 deletion devtools/flipper-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"version": "0.0.0-PLACEHOLDER",
"main": "dist/index.js",
"dependencies": {
"@player-devtools/client": "workspace:*",
"@player-devtools/client-react": "workspace:*",
"@player-devtools/types": "workspace:*"
},
"flipperBundlerEntry": "src/index.tsx",
Expand Down
2 changes: 1 addition & 1 deletion devtools/flipper-plugin/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
MessengerEvent,
TransactionMetadata,
} from "@player-devtools/types";
import { Panel } from "@player-devtools/client";
import { Panel } from "@player-devtools/client-react";
import {
Button,
ChakraProvider,
Expand Down
3 changes: 1 addition & 2 deletions devtools/mcp/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ vitest_config(name = "vitest_config")

deps = [
":node_modules/@player-devtools/client",
":node_modules/@player-devtools/client-flipper",
":node_modules/@player-devtools/messenger",
":node_modules/@player-devtools/types",
"//:node_modules/@modelcontextprotocol/sdk",
"//:node_modules/flipper-server",
"//:node_modules/flipper-server-client",
"//:node_modules/zod",
]

Expand Down
5 changes: 4 additions & 1 deletion devtools/mcp/bin/run
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#!/usr/bin/env node

const { FlipperServerTransport, MCPServer } = require("@player-devtools/mcp");
const { MCPServer } = require("@player-devtools/mcp");
const {
FlipperServerTransport,
} = require("@player-devtools/client-flipper");

const transport = new FlipperServerTransport();
const server = new MCPServer(transport);
Expand Down
1 change: 1 addition & 0 deletions devtools/mcp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
],
"dependencies": {
"@player-devtools/client": "workspace:*",
"@player-devtools/client-flipper": "workspace:*",
"@player-devtools/messenger": "workspace:*",
"@player-devtools/types": "workspace:*"
},
Expand Down
1 change: 0 additions & 1 deletion devtools/mcp/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { MCPServer } from "./server";
export { type Transport, FlipperServerTransport } from "./transport";
2 changes: 1 addition & 1 deletion devtools/mcp/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import {
createExtensionClient,
type ExtensionClient,
} from "@player-devtools/client";
import type { Transport } from "@player-devtools/types";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";

import type { Transport } from "./transport";
import { TOOL_DEFS, type ToolDef } from "./tools";

export class MCPServer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import type {
} from "@player-devtools/types";

// NOTE: this test drives the devtools/MCP side with a real Messenger rather
// than `createExtensionClient`, because that factory currently lives in the
// `@player-devtools/client` barrel alongside the React `Panel`, which can't be
// imported in this node test env (it pulls in UI-only CJS deps). The MCP
// than `createExtensionClient` because it exercises the bus directly — the MCP
// handler's playerId→target resolution is unit-tested in select.test.ts; what
// THIS test proves is the other half: real DevtoolsPlugins on a shared bus only
// handle actions addressed to their own playerID. The frame built below is
Expand Down
Loading