Add a keep going suggestion (#201)

This commit is contained in:
Will Chen
2025-05-19 15:38:33 -07:00
committed by GitHub
parent 68cb6b3d7d
commit b4b9556e2c
3 changed files with 126 additions and 140 deletions

View File

@@ -362,10 +362,39 @@ export function ChatInput({ chatId }: { chatId?: number }) {
); );
} }
function SuggestionButton({
children,
onClick,
tooltipText,
}: {
onClick: () => void;
children: React.ReactNode;
tooltipText: string;
}) {
const { isStreaming } = useStreamChat();
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
disabled={isStreaming}
variant="outline"
size="sm"
onClick={onClick}
>
{children}
</Button>
</TooltipTrigger>
<TooltipContent>{tooltipText}</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
function SummarizeInNewChatButton() { function SummarizeInNewChatButton() {
const chatId = useAtomValue(selectedChatIdAtom); const chatId = useAtomValue(selectedChatIdAtom);
const appId = useAtomValue(selectedAppIdAtom); const appId = useAtomValue(selectedAppIdAtom);
const { streamMessage, isStreaming } = useStreamChat(); const { streamMessage } = useStreamChat();
const navigate = useNavigate(); const navigate = useNavigate();
const onClick = async () => { const onClick = async () => {
if (!appId) { if (!appId) {
@@ -385,106 +414,71 @@ function SummarizeInNewChatButton() {
} }
}; };
return ( return (
<TooltipProvider> <SuggestionButton
<Tooltip> onClick={onClick}
<TooltipTrigger asChild> tooltipText="Creating a new chat makes the AI more focused and efficient"
<Button >
disabled={isStreaming} Summarize to new chat
variant="outline" </SuggestionButton>
size="sm"
onClick={onClick}
>
Summarize to new chat
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Creating a new chat makes the AI more focused and efficient</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
); );
} }
function RefactorFileButton({ path }: { path: string }) { function RefactorFileButton({ path }: { path: string }) {
const chatId = useAtomValue(selectedChatIdAtom); const chatId = useAtomValue(selectedChatIdAtom);
const { streamMessage, isStreaming } = useStreamChat(); const { streamMessage } = useStreamChat();
const onClick = () => {
if (!chatId) {
console.error("No chat id found");
return;
}
streamMessage({
prompt: `Refactor ${path} and make it more modular`,
chatId,
redo: false,
});
};
return ( return (
<TooltipProvider> <SuggestionButton
<Tooltip> onClick={onClick}
<TooltipTrigger asChild> tooltipText="Refactor the file to improve maintainability"
<Button >
disabled={isStreaming} <span className="max-w-[180px] overflow-hidden whitespace-nowrap text-ellipsis">
variant="outline" Refactor {path.split("/").slice(-2).join("/")}
size="sm" </span>
onClick={() => { </SuggestionButton>
if (!chatId) {
console.error("No chat id found");
return;
}
streamMessage({
prompt: `Refactor ${path} and make it more modular`,
chatId,
redo: false,
});
}}
>
<span className="max-w-[180px] overflow-hidden whitespace-nowrap text-ellipsis">
Refactor {path.split("/").slice(-2).join("/")}
</span>
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Refactor {path} to improve maintainability</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
); );
} }
function WriteCodeProperlyButton() { function WriteCodeProperlyButton() {
const chatId = useAtomValue(selectedChatIdAtom); const chatId = useAtomValue(selectedChatIdAtom);
const { streamMessage, isStreaming } = useStreamChat(); const { streamMessage } = useStreamChat();
const onClick = () => {
if (!chatId) {
console.error("No chat id found");
return;
}
streamMessage({
prompt: `Write the code in the previous message in the correct format using \`<dyad-write>\` tags!`,
chatId,
redo: false,
});
};
return ( return (
<TooltipProvider> <SuggestionButton
<Tooltip> onClick={onClick}
<TooltipTrigger asChild> tooltipText="Write code properly (useful when AI generates the code in the wrong format)"
<Button >
disabled={isStreaming} Write code properly
variant="outline" </SuggestionButton>
size="sm"
onClick={() => {
if (!chatId) {
console.error("No chat id found");
return;
}
streamMessage({
prompt: `Write the code in the previous message in the correct format using \`<dyad-write>\` tags!`,
chatId,
redo: false,
});
}}
>
Write code properly
</Button>
</TooltipTrigger>
<TooltipContent>
<p>
Write code properly (useful when AI generates the code in the wrong
format)
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
); );
} }
function RebuildButton() { function RebuildButton() {
const { restartApp } = useRunApp(); const { restartApp } = useRunApp();
const { isStreaming } = useStreamChat();
const posthog = usePostHog(); const posthog = usePostHog();
const selectedAppId = useAtomValue(selectedAppIdAtom); const selectedAppId = useAtomValue(selectedAppIdAtom);
const handleRebuild = useCallback(async () => { const onClick = useCallback(async () => {
if (!selectedAppId) return; if (!selectedAppId) return;
posthog.capture("action:rebuild"); posthog.capture("action:rebuild");
@@ -492,33 +486,18 @@ function RebuildButton() {
}, [selectedAppId, posthog, restartApp]); }, [selectedAppId, posthog, restartApp]);
return ( return (
<TooltipProvider> <SuggestionButton onClick={onClick} tooltipText="Rebuild the application">
<Tooltip> Rebuild app
<TooltipTrigger asChild> </SuggestionButton>
<Button
disabled={isStreaming}
variant="outline"
size="sm"
onClick={handleRebuild}
>
Rebuild app
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Rebuild the application</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
); );
} }
function RestartButton() { function RestartButton() {
const { isStreaming } = useStreamChat();
const { restartApp } = useRunApp(); const { restartApp } = useRunApp();
const posthog = usePostHog(); const posthog = usePostHog();
const selectedAppId = useAtomValue(selectedAppIdAtom); const selectedAppId = useAtomValue(selectedAppIdAtom);
const handleRestart = useCallback(async () => { const onClick = useCallback(async () => {
if (!selectedAppId) return; if (!selectedAppId) return;
posthog.capture("action:restart"); posthog.capture("action:restart");
@@ -526,54 +505,51 @@ function RestartButton() {
}, [selectedAppId, posthog, restartApp]); }, [selectedAppId, posthog, restartApp]);
return ( return (
<TooltipProvider> <SuggestionButton
<Tooltip> onClick={onClick}
<TooltipTrigger asChild> tooltipText="Restart the development server"
<Button >
disabled={isStreaming} Restart app
variant="outline" </SuggestionButton>
size="sm"
onClick={handleRestart}
>
Restart app
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Restart the development server</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
); );
} }
function RefreshButton() { function RefreshButton() {
const { refreshAppIframe } = useRunApp(); const { refreshAppIframe } = useRunApp();
const { isStreaming } = useStreamChat();
const posthog = usePostHog(); const posthog = usePostHog();
const handleRefresh = useCallback(() => { const onClick = useCallback(() => {
posthog.capture("action:refresh"); posthog.capture("action:refresh");
refreshAppIframe(); refreshAppIframe();
}, [posthog, refreshAppIframe]); }, [posthog, refreshAppIframe]);
return ( return (
<TooltipProvider> <SuggestionButton
<Tooltip> onClick={onClick}
<TooltipTrigger asChild> tooltipText="Refresh the application preview"
<Button >
disabled={isStreaming} Refresh app
variant="outline" </SuggestionButton>
size="sm" );
onClick={handleRefresh} }
>
Refresh app function KeepGoingButton() {
</Button> const { streamMessage } = useStreamChat();
</TooltipTrigger> const chatId = useAtomValue(selectedChatIdAtom);
<TooltipContent> const onClick = () => {
<p>Refresh the application preview</p> if (!chatId) {
</TooltipContent> console.error("No chat id found");
</Tooltip> return;
</TooltipProvider> }
streamMessage({
prompt: "Keep going",
chatId,
});
};
return (
<SuggestionButton onClick={onClick} tooltipText="Keep going">
Keep going
</SuggestionButton>
); );
} }
@@ -591,6 +567,8 @@ function mapActionToButton(action: SuggestedAction) {
return <RestartButton />; return <RestartButton />;
case "refresh": case "refresh":
return <RefreshButton />; return <RefreshButton />;
case "keep-going":
return <KeepGoingButton />;
default: default:
console.error(`Unsupported action: ${action.id}`); console.error(`Unsupported action: ${action.id}`);
return ( return (
@@ -603,8 +581,8 @@ function mapActionToButton(action: SuggestedAction) {
function ActionProposalActions({ proposal }: { proposal: ActionProposal }) { function ActionProposalActions({ proposal }: { proposal: ActionProposal }) {
return ( return (
<div className="border-b border-border p-2 flex items-center justify-between"> <div className="border-b border-border p-2 pb-0 flex items-center justify-between">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2 overflow-x-auto pb-2">
{proposal.actions.map((action) => mapActionToButton(action))} {proposal.actions.map((action) => mapActionToButton(action))}
</div> </div>
</div> </div>

View File

@@ -297,7 +297,10 @@ const getProposalHandler = async (
}); });
} }
} }
if (actions.length > 0 && latestAssistantMessage) { if (latestAssistantMessage) {
actions.push({
id: "keep-going",
});
return { return {
proposal: { proposal: {
type: "action-proposal", type: "action-proposal",

View File

@@ -166,7 +166,8 @@ export type SuggestedAction =
| WriteCodeProperlyAction | WriteCodeProperlyAction
| RebuildAction | RebuildAction
| RestartAction | RestartAction
| RefreshAction; | RefreshAction
| KeepGoingAction;
export interface RestartAppAction { export interface RestartAppAction {
id: "restart-app"; id: "restart-app";
@@ -197,6 +198,10 @@ export interface RefreshAction {
id: "refresh"; id: "refresh";
} }
export interface KeepGoingAction {
id: "keep-going";
}
export interface ActionProposal { export interface ActionProposal {
type: "action-proposal"; type: "action-proposal";
actions: SuggestedAction[]; actions: SuggestedAction[];