Skip to content

Commit 68cdbe1

Browse files
committed
dev bar
1 parent 4962bc8 commit 68cdbe1

9 files changed

Lines changed: 104 additions & 36 deletions

File tree

apps/code/src/main/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import { MAIN_TOKENS } from "./di/tokens";
1212
import { registerMcpSandboxProtocol } from "./protocols/mcp-sandbox";
1313
import type { AppLifecycleService } from "./services/app-lifecycle/service";
1414
import type { AuthService } from "./services/auth/service";
15-
import type { DevLogsService } from "./services/dev-logs/service";
16-
import type { DevNetworkService } from "./services/dev-network/service";
15+
import { initDevToolbar } from "./services/dev-toolbar";
1716
import type { ExternalAppsService } from "./services/external-apps/service";
1817
import type { GitHubIntegrationService } from "./services/github-integration/service";
1918
import type { InboxLinkService } from "./services/inbox-link/service";
@@ -146,8 +145,7 @@ app.on("child-process-gone", (_event, details) => {
146145
});
147146

148147
async function initializeServices(): Promise<void> {
149-
container.get<DevNetworkService>(MAIN_TOKENS.DevNetworkService).install();
150-
container.get<DevLogsService>(MAIN_TOKENS.DevLogsService).install();
148+
initDevToolbar(container);
151149

152150
container.get<DatabaseService>(MAIN_TOKENS.DatabaseService);
153151
container.get<OAuthService>(MAIN_TOKENS.OAuthService);

apps/code/src/main/services/dev-logs/service.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import type ElectronLog from "electron-log";
22
import log from "electron-log/main";
3-
import { injectable } from "inversify";
3+
import { inject, injectable } from "inversify";
4+
import { MAIN_TOKENS } from "../../di/tokens";
45
import { TypedEventEmitter } from "../../utils/typed-event-emitter";
5-
import {
6-
DevLogsEvent,
7-
type DevLogsEvents,
8-
type LogEntry,
9-
} from "./schemas";
6+
import type { DevFlagsService } from "../dev-flags/service";
7+
import { DevLogsEvent, type DevLogsEvents, type LogEntry } from "./schemas";
108

119
const RING_BUFFER_SIZE = 1000;
1210

@@ -16,20 +14,27 @@ export class DevLogsService extends TypedEventEmitter<DevLogsEvents> {
1614
private nextId = 1;
1715
private installed = false;
1816

17+
constructor(
18+
@inject(MAIN_TOKENS.DevFlagsService)
19+
private readonly flags: DevFlagsService,
20+
) {
21+
super();
22+
}
23+
1924
install(): void {
2025
if (this.installed) return;
2126
this.installed = true;
2227

2328
const transport = ((message: ElectronLog.LogMessage) => {
29+
if (!this.flags.getFlags().devMode) return;
2430
const entry: LogEntry = {
2531
id: this.nextId++,
2632
level: message.level ?? "info",
2733
scope: message.scope,
2834
message: formatMessage(message.data),
2935
capturedAt: (message.date ?? new Date()).getTime(),
30-
source: message.variables?.processType === "renderer"
31-
? "renderer"
32-
: "main",
36+
source:
37+
message.variables?.processType === "renderer" ? "renderer" : "main",
3338
};
3439
this.entries.push(entry);
3540
if (this.entries.length > RING_BUFFER_SIZE) {

apps/code/src/main/services/dev-network/service.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { injectable } from "inversify";
1+
import { inject, injectable } from "inversify";
2+
import { MAIN_TOKENS } from "../../di/tokens";
23
import { logger } from "../../utils/logger";
34
import { TypedEventEmitter } from "../../utils/typed-event-emitter";
5+
import type { DevFlagsService } from "../dev-flags/service";
46
import {
57
DevNetworkEvent,
68
type DevNetworkEvents,
@@ -19,13 +21,24 @@ export class DevNetworkService extends TypedEventEmitter<DevNetworkEvents> {
1921
private sim: NetworkSim = { offline: false, slowDelayMs: 0 };
2022
private installed = false;
2123

24+
constructor(
25+
@inject(MAIN_TOKENS.DevFlagsService)
26+
private readonly flags: DevFlagsService,
27+
) {
28+
super();
29+
}
30+
2231
install(): void {
2332
if (this.installed) return;
2433
this.installed = true;
2534
this.wrapFetch();
2635
log.info("Network instrumentation installed");
2736
}
2837

38+
private capturing(): boolean {
39+
return this.installed && this.flags.getFlags().devMode;
40+
}
41+
2942
getSnapshot(): NetworkRequest[] {
3043
return [...this.requests];
3144
}
@@ -60,6 +73,9 @@ export class DevNetworkService extends TypedEventEmitter<DevNetworkEvents> {
6073
input: RequestInfo | URL,
6174
init?: RequestInit,
6275
): Promise<Response> => {
76+
if (!this.capturing()) {
77+
return original(input, init);
78+
}
6379
const startedAt = Date.now();
6480
const start = performance.now();
6581
const method = (init?.method ?? "GET").toUpperCase();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { Container } from "inversify";
2+
import { MAIN_TOKENS } from "../di/tokens";
3+
import { DevFlagsEvent } from "./dev-flags/schemas";
4+
import type { DevFlagsService } from "./dev-flags/service";
5+
import type { DevLogsService } from "./dev-logs/service";
6+
import type { DevNetworkService } from "./dev-network/service";
7+
8+
export function initDevToolbar(container: Container): void {
9+
const flags = container.get<DevFlagsService>(MAIN_TOKENS.DevFlagsService);
10+
const network = container.get<DevNetworkService>(
11+
MAIN_TOKENS.DevNetworkService,
12+
);
13+
const logs = container.get<DevLogsService>(MAIN_TOKENS.DevLogsService);
14+
15+
const installCapture = () => {
16+
network.install();
17+
logs.install();
18+
};
19+
20+
if (flags.getFlags().devMode) installCapture();
21+
22+
flags.on(DevFlagsEvent.Changed, (next) => {
23+
if (next.devMode) installCapture();
24+
});
25+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* This file is auto-generated by vite-plugin-auto-services.ts
44
*/
55

6+
import "./dev-toolbar.js";
67
import "./integration-flow-schemas.js";
78
import "./posthog-analytics.js";
89
import "./settingsStore.js";

apps/code/src/renderer/App.tsx

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useAuthSession } from "@features/auth/hooks/useAuthSession";
1414
import { useIsOrgAdmin } from "@features/auth/hooks/useOrgRole";
1515
import { registerBillingSubscriptions } from "@features/billing/subscriptions";
1616
import { DevToolbar } from "@features/dev-toolbar/components/DevToolbar";
17-
import { installMainThreadHealth } from "@features/dev-toolbar/mainThreadHealth";
17+
import { useDevToolbarIntegration } from "@features/dev-toolbar/integration";
1818
import { AddDirectoryDialog } from "@features/folder-picker/components/AddDirectoryDialog";
1919
import { OnboardingFlow } from "@features/onboarding/components/OnboardingFlow";
2020
import { useOnboardingStore } from "@features/onboarding/stores/onboardingStore";
@@ -82,21 +82,7 @@ function App() {
8282
return initializeUpdateStore();
8383
}, []);
8484

85-
// Install main-thread health observers (longtasks + FPS) for the dev toolbar.
86-
useEffect(() => installMainThreadHealth(), []);
87-
88-
// Surface dev-toolbar triggered toasts (e.g. quick actions test toasts).
89-
useSubscription(
90-
trpcReact.dev.onDevToast.subscriptionOptions(undefined, {
91-
onData: (data) => {
92-
if (data.variant === "error") {
93-
toast.error(data.message);
94-
} else {
95-
toast.info(data.message);
96-
}
97-
},
98-
}),
99-
);
85+
useDevToolbarIntegration();
10086

10187
// Dev-only inbox demo command for local QA from the renderer console.
10288
useEffect(() => {

apps/code/src/renderer/features/dev-toolbar/components/DevToolbar.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ import {
5151
X,
5252
ZapOff,
5353
} from "lucide-react";
54-
import { useEffect, useMemo, useRef, useState } from "react";
54+
import { useMemo, useRef, useState } from "react";
5555
import type { MetricsSample } from "../../../../main/services/dev-metrics/schemas";
5656
import { REGION_LABELS } from "../../../../shared/types/regions";
57-
import { subscribeDevFlagsFromMain, useDevFlagsStore } from "../devFlagsStore";
57+
import { useDevFlagsStore } from "../devFlagsStore";
5858
import { useIpcMetricsStore } from "../ipcMetricsStore";
5959
import { useMainThreadHealthStore } from "../mainThreadHealth";
6060
import { AgentsPanel } from "./AgentsPanel";
@@ -86,8 +86,6 @@ export function DevToolbar() {
8686
const [openPanel, setOpenPanel] = useState<DetailPanel>(null);
8787
const [panelHeight, setPanelHeight] = useState(480);
8888

89-
useEffect(() => subscribeDevFlagsFromMain(), []);
90-
9189
if (!devMode) return null;
9290

9391
const togglePanel = (panel: Exclude<DetailPanel, null>) => {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useTRPC } from "@renderer/trpc/client";
2+
import { useSubscription } from "@trpc/tanstack-react-query";
3+
import { toast } from "@utils/toast";
4+
import { useEffect } from "react";
5+
import { subscribeDevFlagsFromMain, useDevFlagsStore } from "./devFlagsStore";
6+
import { installMainThreadHealth } from "./mainThreadHealth";
7+
8+
export function useDevToolbarIntegration(): void {
9+
const trpcReact = useTRPC();
10+
const devMode = useDevFlagsStore((s) => s.devMode);
11+
12+
useEffect(() => {
13+
if (!devMode) return;
14+
const stopHealth = installMainThreadHealth();
15+
const stopFlags = subscribeDevFlagsFromMain();
16+
return () => {
17+
stopHealth();
18+
stopFlags();
19+
};
20+
}, [devMode]);
21+
22+
useSubscription(
23+
trpcReact.dev.onDevToast.subscriptionOptions(undefined, {
24+
enabled: devMode,
25+
onData: (data) => {
26+
if (data.variant === "error") {
27+
toast.error(data.message);
28+
} else {
29+
toast.info(data.message);
30+
}
31+
},
32+
}),
33+
);
34+
}

apps/code/src/renderer/features/dev-toolbar/ipcInstrumentationLink.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { TRPCLink } from "@trpc/client";
22
import { observable, tap } from "@trpc/server/observable";
33
import type { AnyRouter } from "@trpc/server/unstable-core-do-not-import";
4+
import { useDevFlagsStore } from "./devFlagsStore";
45
import { type IpcOpType, useIpcMetricsStore } from "./ipcMetricsStore";
56

67
function byteLength(value: unknown): number {
@@ -16,8 +17,11 @@ export function ipcInstrumentationLink<
1617
TRouter extends AnyRouter = AnyRouter,
1718
>(): TRPCLink<TRouter> {
1819
return () =>
19-
({ op, next }) =>
20-
observable((observer) => {
20+
({ op, next }) => {
21+
if (!useDevFlagsStore.getState().devMode) {
22+
return next(op);
23+
}
24+
return observable((observer) => {
2125
const start = performance.now();
2226
const startedAt = Date.now();
2327
const inputBytes = byteLength(op.input);
@@ -81,4 +85,5 @@ export function ipcInstrumentationLink<
8185
subscription.unsubscribe();
8286
};
8387
});
88+
};
8489
}

0 commit comments

Comments
 (0)