Skip to content

Commit 6db818e

Browse files
authored
Implement Web Inspector (#16)
1 parent 0ad2c92 commit 6db818e

File tree

23 files changed

+1881
-7
lines changed

23 files changed

+1881
-7
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ out/
2727
build
2828
dist
2929

30+
# Generated Tailwind assets
31+
packages/web-inspector/src/styles/generated.css
32+
3033

3134
# Debug
3235
npm-debug.log*
@@ -42,4 +45,4 @@ storybook-static
4245

4346
# Angular
4447
.angular/
45-
.playwright-mcp/
48+
.playwright-mcp/

apps/react/storybook/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
"private": true,
44
"version": "0.0.0",
55
"scripts": {
6-
"dev": "pnpm --filter @copilotkitnext/react run build:css && sleep 1 && concurrently \"pnpm --filter @copilotkitnext/react run dev:css\" \"storybook dev -p 6006 --no-open\"",
6+
"dev": "pnpm --filter @copilotkitnext/react run build:css && sleep 1 && concurrently \"pnpm --filter @copilotkitnext/react run dev\" \"pnpm --filter @copilotkitnext/web-inspector run dev\" \"storybook dev -p 6006 --no-open\"",
77
"build": "storybook build",
8-
"storybook:dev": "pnpm --filter @copilotkitnext/react run build:css && sleep 1 && concurrently \"pnpm --filter @copilotkitnext/react run dev:css\" \"storybook dev -p 6006 --no-open\"",
8+
"storybook:dev": "pnpm --filter @copilotkitnext/react run build:css && sleep 1 && concurrently \"pnpm --filter @copilotkitnext/react run dev\" \"pnpm --filter @copilotkitnext/web-inspector run dev\" \"storybook dev -p 6006 --no-open\"",
99
"storybook:build": "storybook build"
1010
},
1111
"dependencies": {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { WebInspector } from "@copilotkitnext/react";
3+
4+
const meta: Meta<typeof WebInspector> = {
5+
title: "Components/Web Inspector",
6+
component: WebInspector,
7+
};
8+
9+
export default meta;
10+
11+
type Story = StoryObj<typeof WebInspector>;
12+
13+
export const Default: Story = {
14+
args: {},
15+
};

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"name": "CopilotKitNext",
33
"private": true,
44
"scripts": {
5-
"predev": "turbo run build --filter='@copilotkitnext/core' --filter='@copilotkitnext/shared' --filter='@copilotkitnext/react' --filter='@copilotkitnext/angular' --filter='@copilotkitnext/agent'",
5+
"predev": "turbo run build --filter='@copilotkitnext/core' --filter='@copilotkitnext/shared' --filter='@copilotkitnext/web-inspector' --filter='@copilotkitnext/react' --filter='@copilotkitnext/angular' --filter='@copilotkitnext/agent'",
66
"build": "turbo run build",
77
"clean": "turbo run clean",
8-
"dev": "turbo run dev --filter='@copilotkitnext/core' --filter='@copilotkitnext/shared' --filter='@copilotkitnext/runtime' --filter='@copilotkitnext/react' --filter='@copilotkitnext/angular' --filter='@copilotkitnext/agent' --concurrency=15",
9-
"dev:packages": "turbo run dev --filter='@copilotkitnext/core' --filter='@copilotkitnext/shared' --filter='@copilotkitnext/runtime' --filter='@copilotkitnext/react' --filter='@copilotkitnext/angular' --filter='@copilotkitnext/agent'",
8+
"dev": "turbo run dev --filter='@copilotkitnext/core' --filter='@copilotkitnext/shared' --filter='@copilotkitnext/web-inspector' --filter='@copilotkitnext/runtime' --filter='@copilotkitnext/react' --filter='@copilotkitnext/angular' --filter='@copilotkitnext/agent' --concurrency=15",
9+
"dev:packages": "turbo run dev --filter='@copilotkitnext/core' --filter='@copilotkitnext/shared' --filter='@copilotkitnext/web-inspector' --filter='@copilotkitnext/runtime' --filter='@copilotkitnext/react' --filter='@copilotkitnext/angular' --filter='@copilotkitnext/agent'",
1010
"demo:angular": "turbo run dev --filter='@copilotkitnext/angular-demo-server' --filter='@copilotkitnext/angular-demo'",
1111
"storybook:angular": "pnpm -C apps/angular/storybook dev",
1212
"demo:react": "pnpm -C apps/react/demo dev",

packages/react/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
"@ag-ui/core": "0.0.40-alpha.3",
5858
"@copilotkitnext/core": "workspace:*",
5959
"@copilotkitnext/shared": "workspace:*",
60+
"@copilotkitnext/web-inspector": "workspace:*",
61+
"@lit-labs/react": "^2.0.2",
6062
"@radix-ui/react-dropdown-menu": "^2.1.15",
6163
"@radix-ui/react-slot": "^1.2.3",
6264
"@radix-ui/react-tooltip": "^1.2.7",
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as React from "react";
2+
import { createComponent } from "@lit-labs/react";
3+
import {
4+
WEB_INSPECTOR_TAG,
5+
WebInspectorElement,
6+
defineWebInspector,
7+
} from "@copilotkitnext/web-inspector";
8+
import type { CopilotKitCore } from "@copilotkitnext/core";
9+
10+
defineWebInspector();
11+
12+
const WebInspectorBase = createComponent({
13+
tagName: WEB_INSPECTOR_TAG,
14+
elementClass: WebInspectorElement,
15+
react: React,
16+
});
17+
18+
export type WebInspectorBaseProps = React.ComponentProps<typeof WebInspectorBase>;
19+
20+
export interface WebInspectorProps extends Omit<WebInspectorBaseProps, "core"> {
21+
core?: CopilotKitCore | null;
22+
}
23+
24+
export const WebInspector = React.forwardRef<WebInspectorElement, WebInspectorProps>(
25+
({ core, ...rest }, ref) => {
26+
const innerRef = React.useRef<WebInspectorElement>(null);
27+
28+
React.useImperativeHandle(ref, () => innerRef.current as WebInspectorElement, []);
29+
30+
React.useEffect(() => {
31+
if (innerRef.current) {
32+
innerRef.current.core = core ?? null;
33+
}
34+
}, [core]);
35+
36+
return <WebInspectorBase {...(rest as WebInspectorBaseProps)} ref={innerRef} />; // eslint-disable-line react/jsx-props-no-spreading
37+
},
38+
);
39+
40+
WebInspector.displayName = "WebInspector";

packages/react/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33

44
export * from "./chat";
55
export * from "./WildcardToolCallRender";
6+
export * from "./WebInspector";

packages/react/src/providers/CopilotKitProvider.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
"use client";
22

3-
import React, { createContext, useContext, ReactNode, useMemo, useEffect, useReducer, useRef } from "react";
3+
import React, {
4+
createContext,
5+
useContext,
6+
ReactNode,
7+
useMemo,
8+
useEffect,
9+
useReducer,
10+
useRef,
11+
useState,
12+
} from "react";
413
import { ReactToolCallRenderer } from "../types/react-tool-call-renderer";
514
import { ReactCustomMessageRenderer } from "../types/react-custom-message-renderer";
615
import { ReactFrontendTool } from "../types/frontend-tool";
@@ -9,6 +18,7 @@ import { z } from "zod";
918
import { FrontendTool } from "@copilotkitnext/core";
1019
import { AbstractAgent } from "@ag-ui/client";
1120
import { CopilotKitCoreReact } from "../lib/react-core";
21+
import { WebInspector } from "../components/WebInspector";
1222

1323
// Define the context value interface - idiomatic React naming
1424
export interface CopilotKitContextValue {
@@ -68,6 +78,18 @@ export const CopilotKitProvider: React.FC<CopilotKitProviderProps> = ({
6878
frontendTools,
6979
humanInTheLoop,
7080
}) => {
81+
const [shouldRenderInspector, setShouldRenderInspector] = useState(false);
82+
83+
useEffect(() => {
84+
if (typeof window === "undefined") {
85+
return;
86+
}
87+
const localhostHosts = new Set(["localhost", "127.0.0.1"]);
88+
if (localhostHosts.has(window.location.hostname)) {
89+
setShouldRenderInspector(true);
90+
}
91+
}, []);
92+
7193
// Normalize array props to stable references with clear dev warnings
7294
const renderToolCallsList = useStableArrayProp<ReactToolCallRenderer<any>>(
7395
renderToolCalls,
@@ -223,6 +245,7 @@ export const CopilotKitProvider: React.FC<CopilotKitProviderProps> = ({
223245
}}
224246
>
225247
{children}
248+
{shouldRenderInspector ? <WebInspector core={copilotkit} /> : null}
226249
</CopilotKitContext.Provider>
227250
);
228251
};

packages/react/tsconfig.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
"@copilotkitnext/shared": [
1515
"../shared/dist/index.d.ts",
1616
"../shared/src/index.ts"
17+
],
18+
"@copilotkitnext/web-inspector": [
19+
"../web-inspector/dist/index.d.ts",
20+
"../web-inspector/src/index.ts"
1721
]
1822
},
1923
"types": ["vitest/globals"]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { config as baseConfig } from "@copilotkitnext/eslint-config/base";
2+
3+
export default [...baseConfig];

0 commit comments

Comments
 (0)