Files
kandev/apps/web/components/task/dev-server-preview-button.tsx
T
2026-08-17 23:54:44 +00:00

71 lines
2.3 KiB
TypeScript

"use client";
import { useCallback } from "react";
import { IconDeviceDesktop, IconDeviceDesktopOff } from "@tabler/icons-react";
import { Button } from "@kandev/ui/button";
import { useTranslation } from "react-i18next";
import { useAppStore } from "@/components/state-provider";
import { useDockviewStore } from "@/lib/state/dockview-store";
import { useDevServerPreview } from "@/hooks/domains/session/use-dev-server-preview";
type DevServerPreviewButtonProps = {
/** Dockview group the dev-server output panel should open in. */
outputGroupId?: string | null;
className: string;
iconClassName: string;
/** Distinguishes the terminal-group control from the chat-group one in specs. */
testId?: string;
};
/**
* Start/stop toggle for the repository dev script.
*
* Starting also opens the browser preview and the Dev Server output panel, so
* the script's stdout/stderr is visible somewhere. Stopping leaves both panels
* in place: the buffered output is usually the reason the user wants to stop.
*/
export function DevServerPreviewButton({
outputGroupId,
className,
iconClassName,
testId = "dev-server-preview-toggle",
}: DevServerPreviewButtonProps) {
const { t } = useTranslation();
const activeSessionId = useAppStore((state) => state.tasks.activeSessionId);
const focusOrAddBrowserPanel = useDockviewStore((s) => s.focusOrAddBrowserPanel);
const addDevServerPanel = useDockviewStore((s) => s.addDevServerPanel);
const dev = useDevServerPreview(activeSessionId);
const handleClick = useCallback(async () => {
if (!activeSessionId) return;
if (dev.isRunning) {
await dev.stop();
return;
}
focusOrAddBrowserPanel();
addDevServerPanel(outputGroupId ?? undefined);
await dev.start();
}, [activeSessionId, dev, focusOrAddBrowserPanel, addDevServerPanel, outputGroupId]);
const label = dev.isRunning ? t("task:stopDevServerPreview") : t("task:startDevServerPreview");
return (
<Button
size="sm"
variant="ghost"
className={className}
onClick={handleClick}
disabled={dev.isPending || !activeSessionId}
aria-label={label}
title={label}
data-testid={testId}
>
{dev.isRunning ? (
<IconDeviceDesktopOff className={iconClassName} />
) : (
<IconDeviceDesktop className={iconClassName} />
)}
</Button>
);
}