Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit 0db03b8

Browse files
feat: Add dev toolbar for live diagnostics and actions (#2276)
Co-authored-by: Jonathan Mieloo <32547391+jonathanlab@users.noreply.github.com> Co-authored-by: JonathanLab <jonathanmieloo@gmail.com>
1 parent be89b85 commit 0db03b8

64 files changed

Lines changed: 6200 additions & 540 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/code/electron.vite.config.ts‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { builtinModules, createRequire } from "node:module";
33
import path from "node:path";
44
import { fileURLToPath } from "node:url";
55
import tailwindcss from "@tailwindcss/vite";
6-
import { devtools } from "@tanstack/devtools-vite";
76
import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
87
import react from "@vitejs/plugin-react";
98
import { defineConfig } from "electron-vite";
@@ -199,7 +198,6 @@ export default defineConfig(({ mode }) => {
199198
renderer: {
200199
root: __dirname,
201200
plugins: [
202-
isDev && devtools(),
203201
TanStackRouterVite({
204202
target: "react",
205203
autoCodeSplitting: true,

‎apps/code/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
"@storybook/addon-a11y": "10.4.1",
4848
"@storybook/addon-docs": "10.4.1",
4949
"@storybook/react-vite": "10.4.1",
50-
"@tanstack/devtools-vite": "^0.7.0",
5150
"@testing-library/jest-dom": "^6.9.1",
5251
"@testing-library/react": "^16.3.0",
5352
"@testing-library/user-event": "^14.6.1",
@@ -138,6 +137,7 @@
138137
"react": "19.2.6",
139138
"react-dom": "19.2.6",
140139
"react-hotkeys-hook": "^4.4.4",
140+
"react-scan": "^0.5.6",
141141
"reflect-metadata": "^0.2.2",
142142
"semver": "^7.8.1",
143143
"shadcn": "^4.1.2",

‎apps/code/src/main/di/bindings.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,13 @@ import type {
107107
} from "@posthog/platform/analytics";
108108
import type { APP_LIFECYCLE_SERVICE } from "@posthog/platform/app-lifecycle";
109109
import type { APP_META_SERVICE } from "@posthog/platform/app-meta";
110+
import type { APP_METRICS_SERVICE } from "@posthog/platform/app-metrics";
110111
import type { BUNDLED_RESOURCES_SERVICE } from "@posthog/platform/bundled-resources";
111112
import type { CLIPBOARD_SERVICE } from "@posthog/platform/clipboard";
112113
import type { CONTEXT_MENU_SERVICE } from "@posthog/platform/context-menu";
113114
import type { CRYPTO_SERVICE } from "@posthog/platform/crypto";
114115
import type { DEEP_LINK_SERVICE } from "@posthog/platform/deep-link";
116+
import type { DEV_HOST_ACTIONS_SERVICE } from "@posthog/platform/dev-host-actions";
115117
import type { DIALOG_SERVICE } from "@posthog/platform/dialog";
116118
import type { FILE_ICON_SERVICE } from "@posthog/platform/file-icon";
117119
import type { IMAGE_PROCESSOR_SERVICE } from "@posthog/platform/image-processor";
@@ -126,11 +128,13 @@ import type { WORKSPACE_SETTINGS_SERVICE } from "@posthog/platform/workspace-set
126128
import type { WorkspaceClient } from "@posthog/workspace-client/client";
127129
import type { DatabaseService } from "@posthog/workspace-server/db/service";
128130
import type { GIT_SERVICE as WS_GIT_SERVICE } from "@posthog/workspace-server/di/tokens";
131+
import type { AgentService } from "@posthog/workspace-server/services/agent/agent";
129132
import type {
130133
AGENT_AUTH,
131134
AGENT_LOGGER,
132135
AGENT_MCP_APPS,
133136
AGENT_REPO_FILES,
137+
AGENT_SERVICE,
134138
AGENT_SLEEP_COORDINATOR,
135139
} from "@posthog/workspace-server/services/agent/identifiers";
136140
import type {
@@ -207,10 +211,12 @@ import type { WorkspaceService } from "@posthog/workspace-server/services/worksp
207211
import type { FileWatcherBridge } from "../index";
208212
import type { ElectronAppLifecycle } from "../platform-adapters/electron-app-lifecycle";
209213
import type { ElectronAppMeta } from "../platform-adapters/electron-app-meta";
214+
import type { ElectronAppMetrics } from "../platform-adapters/electron-app-metrics";
210215
import type { ElectronBundledResources } from "../platform-adapters/electron-bundled-resources";
211216
import type { ElectronClipboard } from "../platform-adapters/electron-clipboard";
212217
import type { ElectronContextMenu } from "../platform-adapters/electron-context-menu";
213218
import type { ElectronCrypto } from "../platform-adapters/electron-crypto";
219+
import type { ElectronDevHostActions } from "../platform-adapters/electron-dev-host-actions";
214220
import type { ElectronDialog } from "../platform-adapters/electron-dialog";
215221
import type { ElectronFileIcon } from "../platform-adapters/electron-file-icon";
216222
import type { ElectronImageProcessor } from "../platform-adapters/electron-image-processor";
@@ -231,6 +237,11 @@ import type {
231237
TokenCipherPortAdapter,
232238
} from "../services/auth/port-adapters";
233239
import type { DeepLinkService } from "../services/deep-link/service";
240+
import type { DevActionsService } from "../services/dev-actions/service";
241+
import type { DevFlagsService } from "../services/dev-flags/service";
242+
import type { DevLogsService } from "../services/dev-logs/service";
243+
import type { DevMetricsService } from "../services/dev-metrics/service";
244+
import type { DevNetworkService } from "../services/dev-network/service";
234245
import type { DiscordPresenceService } from "../services/discord-presence/service";
235246
import type { EncryptionService } from "../services/encryption/service";
236247
import type { SecureStoreService } from "../services/secure-store/service";
@@ -250,6 +261,11 @@ import type {
250261
DATABASE_SERVICE as MAIN_DATABASE_SERVICE,
251262
DEEP_LINK_SERVICE as MAIN_DEEP_LINK_SERVICE,
252263
DEFAULT_ADDITIONAL_DIRECTORY_REPOSITORY as MAIN_DEFAULT_ADDITIONAL_DIRECTORY_REPOSITORY,
264+
DEV_ACTIONS_SERVICE as MAIN_DEV_ACTIONS_SERVICE,
265+
DEV_FLAGS_SERVICE as MAIN_DEV_FLAGS_SERVICE,
266+
DEV_LOGS_SERVICE as MAIN_DEV_LOGS_SERVICE,
267+
DEV_METRICS_SERVICE as MAIN_DEV_METRICS_SERVICE,
268+
DEV_NETWORK_SERVICE as MAIN_DEV_NETWORK_SERVICE,
253269
DISCORD_PRESENCE_SERVICE as MAIN_DISCORD_PRESENCE_SERVICE,
254270
ENCRYPTION_SERVICE as MAIN_ENCRYPTION_SERVICE,
255271
EXTERNAL_APPS_SERVICE as MAIN_EXTERNAL_APPS_SERVICE,
@@ -301,6 +317,8 @@ export interface MainBindings {
301317
[BUNDLED_RESOURCES_SERVICE]: ElectronBundledResources;
302318
[IMAGE_PROCESSOR_SERVICE]: ElectronImageProcessor;
303319
[WORKSPACE_SETTINGS_SERVICE]: ElectronWorkspaceSettings;
320+
[APP_METRICS_SERVICE]: ElectronAppMetrics;
321+
[DEV_HOST_ACTIONS_SERVICE]: ElectronDevHostActions;
304322

305323
// Database (main aliases + ws-server source tokens via toService)
306324
[MAIN_DATABASE_SERVICE]: DatabaseService;
@@ -441,6 +459,13 @@ export interface MainBindings {
441459
[MAIN_ENCRYPTION_SERVICE]: EncryptionService;
442460
[MAIN_DISCORD_PRESENCE_SERVICE]: DiscordPresenceService;
443461

462+
// Dev toolbar diagnostics
463+
[MAIN_DEV_FLAGS_SERVICE]: DevFlagsService;
464+
[MAIN_DEV_METRICS_SERVICE]: DevMetricsService;
465+
[MAIN_DEV_NETWORK_SERVICE]: DevNetworkService;
466+
[MAIN_DEV_LOGS_SERVICE]: DevLogsService;
467+
[MAIN_DEV_ACTIONS_SERVICE]: DevActionsService;
468+
444469
// ws-server git service (bound to(GitService))
445470
[WS_GIT_SERVICE]: GitService;
446471

@@ -458,6 +483,7 @@ export interface MainBindings {
458483
[FS_SERVICE]: FsCapability;
459484

460485
// Typed container.get-only tokens (bound via loaded modules)
486+
[AGENT_SERVICE]: AgentService;
461487
[OAUTH_SERVICE]: OAuthService;
462488
[GITHUB_INTEGRATION_SERVICE]: GitHubIntegrationService;
463489
[SLACK_INTEGRATION_SERVICE]: SlackIntegrationService;

‎apps/code/src/main/di/container.ts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,13 @@ import {
9999
import { ANALYTICS_SERVICE } from "@posthog/platform/analytics";
100100
import { APP_LIFECYCLE_SERVICE } from "@posthog/platform/app-lifecycle";
101101
import { APP_META_SERVICE } from "@posthog/platform/app-meta";
102+
import { APP_METRICS_SERVICE } from "@posthog/platform/app-metrics";
102103
import { BUNDLED_RESOURCES_SERVICE } from "@posthog/platform/bundled-resources";
103104
import { CLIPBOARD_SERVICE } from "@posthog/platform/clipboard";
104105
import { CONTEXT_MENU_SERVICE } from "@posthog/platform/context-menu";
105106
import { CRYPTO_SERVICE } from "@posthog/platform/crypto";
106107
import { DEEP_LINK_SERVICE } from "@posthog/platform/deep-link";
108+
import { DEV_HOST_ACTIONS_SERVICE } from "@posthog/platform/dev-host-actions";
107109
import { DIALOG_SERVICE } from "@posthog/platform/dialog";
108110
import { FILE_ICON_SERVICE } from "@posthog/platform/file-icon";
109111
import { IMAGE_PROCESSOR_SERVICE } from "@posthog/platform/image-processor";
@@ -217,10 +219,12 @@ import ExternalAppsStoreImpl from "electron-store";
217219
import type { FileWatcherBridge } from "../index";
218220
import { ElectronAppLifecycle } from "../platform-adapters/electron-app-lifecycle";
219221
import { ElectronAppMeta } from "../platform-adapters/electron-app-meta";
222+
import { ElectronAppMetrics } from "../platform-adapters/electron-app-metrics";
220223
import { ElectronBundledResources } from "../platform-adapters/electron-bundled-resources";
221224
import { ElectronClipboard } from "../platform-adapters/electron-clipboard";
222225
import { ElectronContextMenu } from "../platform-adapters/electron-context-menu";
223226
import { ElectronCrypto } from "../platform-adapters/electron-crypto";
227+
import { ElectronDevHostActions } from "../platform-adapters/electron-dev-host-actions";
224228
import { ElectronDialog } from "../platform-adapters/electron-dialog";
225229
import { ElectronFileIcon } from "../platform-adapters/electron-file-icon";
226230
import { ElectronImageProcessor } from "../platform-adapters/electron-image-processor";
@@ -243,6 +247,11 @@ import {
243247
TokenCipherPortAdapter,
244248
} from "../services/auth/port-adapters";
245249
import { DeepLinkService } from "../services/deep-link/service";
250+
import { DevActionsService } from "../services/dev-actions/service";
251+
import { DevFlagsService } from "../services/dev-flags/service";
252+
import { DevLogsService } from "../services/dev-logs/service";
253+
import { DevMetricsService } from "../services/dev-metrics/service";
254+
import { DevNetworkService } from "../services/dev-network/service";
246255
import { DiscordPresenceService } from "../services/discord-presence/service";
247256
import { EncryptionService } from "../services/encryption/service";
248257
import { SecureStoreService } from "../services/secure-store/service";
@@ -265,6 +274,11 @@ import {
265274
DATABASE_SERVICE as MAIN_DATABASE_SERVICE,
266275
DEEP_LINK_SERVICE as MAIN_DEEP_LINK_SERVICE,
267276
DEFAULT_ADDITIONAL_DIRECTORY_REPOSITORY as MAIN_DEFAULT_ADDITIONAL_DIRECTORY_REPOSITORY,
277+
DEV_ACTIONS_SERVICE as MAIN_DEV_ACTIONS_SERVICE,
278+
DEV_FLAGS_SERVICE as MAIN_DEV_FLAGS_SERVICE,
279+
DEV_LOGS_SERVICE as MAIN_DEV_LOGS_SERVICE,
280+
DEV_METRICS_SERVICE as MAIN_DEV_METRICS_SERVICE,
281+
DEV_NETWORK_SERVICE as MAIN_DEV_NETWORK_SERVICE,
268282
DISCORD_PRESENCE_SERVICE as MAIN_DISCORD_PRESENCE_SERVICE,
269283
ENCRYPTION_SERVICE as MAIN_ENCRYPTION_SERVICE,
270284
EXTERNAL_APPS_SERVICE as MAIN_EXTERNAL_APPS_SERVICE,
@@ -318,6 +332,8 @@ container.bind(CONTEXT_MENU_SERVICE).to(ElectronContextMenu);
318332
container.bind(BUNDLED_RESOURCES_SERVICE).to(ElectronBundledResources);
319333
container.bind(IMAGE_PROCESSOR_SERVICE).to(ElectronImageProcessor);
320334
container.bind(WORKSPACE_SETTINGS_SERVICE).to(ElectronWorkspaceSettings);
335+
container.bind(APP_METRICS_SERVICE).to(ElectronAppMetrics);
336+
container.bind(DEV_HOST_ACTIONS_SERVICE).to(ElectronDevHostActions);
321337

322338
container.load(databaseModule);
323339
container.load(repositoriesModule);
@@ -728,3 +744,9 @@ container.load(canvasCoreModule);
728744
// Browser tabs for the Channels canvas surface. Authoritative sqlite-backed
729745
// service in the main process; resolved by the host-router browserTabs router.
730746
container.load(browserTabsModule);
747+
748+
container.bind(MAIN_DEV_FLAGS_SERVICE).to(DevFlagsService);
749+
container.bind(MAIN_DEV_METRICS_SERVICE).to(DevMetricsService);
750+
container.bind(MAIN_DEV_NETWORK_SERVICE).to(DevNetworkService);
751+
container.bind(MAIN_DEV_LOGS_SERVICE).to(DevLogsService);
752+
container.bind(MAIN_DEV_ACTIONS_SERVICE).to(DevActionsService);

‎apps/code/src/main/di/tokens.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,18 @@ export const WORKSPACE_SERVER_SERVICE = Symbol.for(
127127
export const DISCORD_PRESENCE_SERVICE = Symbol.for(
128128
"posthog.host.main.discord-presence.service",
129129
);
130+
export const DEV_FLAGS_SERVICE = Symbol.for(
131+
"posthog.host.main.dev-flags.service",
132+
);
133+
export const DEV_METRICS_SERVICE = Symbol.for(
134+
"posthog.host.main.dev-metrics.service",
135+
);
136+
export const DEV_NETWORK_SERVICE = Symbol.for(
137+
"posthog.host.main.dev-network.service",
138+
);
139+
export const DEV_LOGS_SERVICE = Symbol.for(
140+
"posthog.host.main.dev-logs.service",
141+
);
142+
export const DEV_ACTIONS_SERVICE = Symbol.for(
143+
"posthog.host.main.dev-actions.service",
144+
);

‎apps/code/src/main/index.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import {
7272
import { posthogNodeAnalytics } from "./platform-adapters/posthog-analytics";
7373
import { registerMcpSandboxProtocol } from "./protocols/mcp-sandbox";
7474
import type { AppLifecycleService } from "./services/app-lifecycle/service";
75+
import { initDevToolbar } from "./services/dev-toolbar";
7576
import type { DiscordPresenceService } from "./services/discord-presence/service";
7677
import {
7778
focusSessionStore,
@@ -239,6 +240,8 @@ app.on("child-process-gone", (_event, details) => {
239240
});
240241

241242
async function initializeServices(): Promise<void> {
243+
initDevToolbar();
244+
242245
container.get<DatabaseService>(DATABASE_SERVICE);
243246
container.get<OAuthService>(OAUTH_SERVICE);
244247
const authService = container.get<AuthService>(AUTH_SERVICE);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type {
2+
AppProcessMetric,
3+
IAppMetrics,
4+
} from "@posthog/platform/app-metrics";
5+
import { app } from "electron";
6+
import { injectable } from "inversify";
7+
8+
@injectable()
9+
export class ElectronAppMetrics implements IAppMetrics {
10+
public getAppMetrics(): AppProcessMetric[] {
11+
return app.getAppMetrics().map((m) => ({
12+
pid: m.pid,
13+
type: m.type,
14+
name: m.name,
15+
cpu: m.cpu ? { percentCPUUsage: m.cpu.percentCPUUsage } : undefined,
16+
memory: m.memory
17+
? { workingSetSize: m.memory.workingSetSize }
18+
: undefined,
19+
}));
20+
}
21+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { IDevHostActions } from "@posthog/platform/dev-host-actions";
2+
import { app, BrowserWindow, shell } from "electron";
3+
import { injectable } from "inversify";
4+
5+
@injectable()
6+
export class ElectronDevHostActions implements IDevHostActions {
7+
public async openPath(path: string): Promise<void> {
8+
await shell.openPath(path);
9+
}
10+
11+
public reloadAllWindows(): void {
12+
for (const window of BrowserWindow.getAllWindows()) {
13+
window.webContents.reload();
14+
}
15+
}
16+
17+
public relaunch(): void {
18+
app.relaunch();
19+
app.exit(0);
20+
}
21+
22+
public crash(): void {
23+
process.crash();
24+
}
25+
}

‎apps/code/src/main/preload.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@ import { contextBridge, webUtils } from "electron";
33
import "electron-log/preload";
44
import { parseSessionIdArg } from "./posthog-session-arg";
55

6+
const DEV_FLAGS_CLI_PREFIX = "--posthog-code-flags=";
7+
8+
function readDevFlags(): { devMode: boolean } {
9+
const arg = process.argv.find((a) => a.startsWith(DEV_FLAGS_CLI_PREFIX));
10+
if (!arg) return { devMode: false };
11+
try {
12+
const payload = decodeURIComponent(arg.slice(DEV_FLAGS_CLI_PREFIX.length));
13+
const parsed = JSON.parse(payload);
14+
return { devMode: parsed?.devMode === true };
15+
} catch {
16+
return { devMode: false };
17+
}
18+
}
19+
20+
const devFlags = readDevFlags();
21+
622
contextBridge.exposeInMainWorld("electronUtils", {
723
getPathForFile: (file: File) => webUtils.getPathForFile(file),
824
});
@@ -11,6 +27,8 @@ contextBridge.exposeInMainWorld("__posthogBootstrap", {
1127
sessionId: parseSessionIdArg(process.argv),
1228
});
1329

30+
contextBridge.exposeInMainWorld("__posthogCodeDevFlags", devFlags);
31+
1432
if (process.argv.includes("--posthog-code-dev")) {
1533
contextBridge.exposeInMainWorld("__posthogCodeTest", {
1634
crash: () => {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { z } from "zod";
2+
3+
export const devToastInput = z.object({
4+
variant: z.enum(["info", "error"]),
5+
message: z.string(),
6+
});
7+
8+
export const devToastSchema = z.object({
9+
id: z.number(),
10+
variant: z.enum(["info", "error"]),
11+
message: z.string(),
12+
});
13+
14+
export type DevToast = z.infer<typeof devToastSchema>;
15+
16+
export const DevActionsEvent = {
17+
Toast: "toast",
18+
} as const;
19+
20+
export interface DevActionsEvents {
21+
[DevActionsEvent.Toast]: DevToast;
22+
}

0 commit comments

Comments
 (0)