Unverified 提交 77fb0ecb authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Fix duplicate server log messages in console (#2359)

## Summary - Multiple components calling `useRunApp()` each registered their own event listener for `onAppOutput`, causing duplicate log entries in the console - Extracted the event subscription into a new `useAppOutputSubscription()` hook - The new hook is called only once in `layout.tsx` at the root level, eliminating duplicates ## Test plan - Run an app and observe the System Messages console - Verify that server log messages (stdout/stderr) appear only once instead of multiple times - Verify HMR updates still trigger iframe refreshes correctly 🤖 Generated with [Claude Code](https://claude.com/claude-code) #skip-bugbot <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2359"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Fix duplicate server log messages by subscribing to app output events only once at the app root. Prevents repeated stdout/stderr entries while keeping HMR iframe refreshes working. - **Bug Fixes** - Extracted event subscription from useRunApp into a new useAppOutputSubscription hook. - Call useAppOutputSubscription once in layout.tsx to avoid duplicate listeners. - Preserved proxy URL processing and hot module reload behavior. <sup>Written for commit 0f4df3f539c818e2100228e23602f31fe1d07742. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Eliminates duplicate server logs by centralizing app output subscription. > > - Extracts event listener logic from `useRunApp` into new `useAppOutputSubscription()` and subscribes once in `src/app/layout.tsx` > - Preserves HMR handling (refresh via preview panel key) and proxy URL parsing in `processProxyServerOutput` > - Refactors internals with `useCallback` dependencies to avoid duplicate processing while keeping existing `useRunApp` API intact > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 0f4df3f539c818e2100228e23602f31fe1d07742. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: 's avatarClaude Opus 4.5 <noreply@anthropic.com>
上级 1dae4f9e
......@@ -5,7 +5,7 @@ import { DeepLinkProvider } from "../contexts/DeepLinkContext";
import { Toaster } from "sonner";
import { TitleBar } from "./TitleBar";
import { useEffect, type ReactNode } from "react";
import { useRunApp } from "@/hooks/useRunApp";
import { useRunApp, useAppOutputSubscription } from "@/hooks/useRunApp";
import { useAtomValue, useSetAtom } from "jotai";
import {
appConsoleEntriesAtom,
......@@ -21,6 +21,8 @@ const DEFAULT_ZOOM_LEVEL: ZoomLevel = "100";
export default function RootLayout({ children }: { children: ReactNode }) {
const { refreshAppIframe } = useRunApp();
// Subscribe to app output events once at the root level to avoid duplicates
useAppOutputSubscription();
const previewMode = useAtomValue(previewModeAtom);
const { settings } = useSettings();
const setSelectedComponentsPreview = useSetAtom(
......
......@@ -14,16 +14,19 @@ import { showInputRequest } from "@/lib/toast";
const useRunAppLoadingAtom = atom(false);
export function useRunApp() {
const [loading, setLoading] = useAtom(useRunAppLoadingAtom);
const [app, setApp] = useAtom(currentAppAtom);
/**
* Hook to subscribe to app output events from the main process.
* IMPORTANT: This hook should only be called ONCE in the app (in layout.tsx)
* to avoid duplicate event subscriptions causing duplicate log entries.
*/
export function useAppOutputSubscription() {
const setConsoleEntries = useSetAtom(appConsoleEntriesAtom);
const [, setAppUrlObj] = useAtom(appUrlAtom);
const setPreviewPanelKey = useSetAtom(previewPanelKeyAtom);
const appId = useAtomValue(selectedAppIdAtom);
const setPreviewErrorMessage = useSetAtom(previewErrorMessageAtom);
const processProxyServerOutput = (output: AppOutput) => {
const processProxyServerOutput = useCallback(
(output: AppOutput) => {
const matchesProxyServerStart = output.message.includes(
"[dyad-proxy-server]started=[",
);
......@@ -44,7 +47,9 @@ export function useRunApp() {
});
}
}
};
},
[setAppUrlObj],
);
const onHotModuleReload = useCallback(() => {
setPreviewPanelKey((prevKey) => prevKey + 1);
......@@ -93,7 +98,7 @@ export function useRunApp() {
// Process proxy server output
processProxyServerOutput(output);
},
[setConsoleEntries],
[setConsoleEntries, processProxyServerOutput],
);
// Subscribe to app output events from main process
......@@ -114,6 +119,16 @@ export function useRunApp() {
return unsubscribe;
}, [appId, processAppOutput, onHotModuleReload]);
}
export function useRunApp() {
const [loading, setLoading] = useAtom(useRunAppLoadingAtom);
const [app, setApp] = useAtom(currentAppAtom);
const setConsoleEntries = useSetAtom(appConsoleEntriesAtom);
const [, setAppUrlObj] = useAtom(appUrlAtom);
const setPreviewPanelKey = useSetAtom(previewPanelKeyAtom);
const appId = useAtomValue(selectedAppIdAtom);
const setPreviewErrorMessage = useSetAtom(previewErrorMessageAtom);
const runApp = useCallback(async (appId: number) => {
setLoading(true);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论