clean up proposal logic

This commit is contained in:
Will Chen
2025-04-18 13:23:26 -07:00
parent 639b3a320c
commit a4702f90b0
7 changed files with 74 additions and 61 deletions

View File

@@ -274,10 +274,11 @@ function mapActionToButton(action: SuggestedAction) {
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
className="rounded-xl"
key={action.id} key={action.id}
onClick={restartApp} onClick={restartApp}
> >
Restart App Restart app
</Button> </Button>
); );
default: default:
@@ -292,8 +293,21 @@ function mapActionToButton(action: SuggestedAction) {
function ActionProposalActions({ proposal }: { proposal: ActionProposal }) { function ActionProposalActions({ proposal }: { proposal: ActionProposal }) {
return ( return (
<div className="p-2 pb-0"> <div className="border-b border-border p-2 flex items-center justify-between">
{proposal.actions.map((action) => mapActionToButton(action))} <div className="flex items-center space-x-2">
{proposal.actions.map((action) => mapActionToButton(action))}
</div>
<AutoApproveSwitch />
</div>
);
}
function AutoApproveSwitch() {
// const [autoApprove, setAutoApprove] = useAtom(autoApproveAtom);
return (
<div className="flex items-center space-x-2">
<Switch id="auto-approve" />
<Label htmlFor="auto-approve">Auto-approve</Label>
</div> </div>
); );
} }
@@ -376,17 +390,7 @@ function ChatInputActions({
Reject Reject
</Button> </Button>
<div className="flex items-center space-x-1 ml-auto"> <div className="flex items-center space-x-1 ml-auto">
{/* Basic HTML checkbox styled to look like a toggle */} <AutoApproveSwitch />
<input
type="checkbox"
id="auto-approve"
checked={autoApprove}
onChange={(e) => setAutoApprove(e.target.checked)}
className="relative peer shrink-0 appearance-none w-8 h-4 border border-input rounded-full bg-input checked:bg-primary cursor-pointer after:absolute after:w-3 after:h-3 after:top-[1px] after:left-[2px] after:bg-background after:rounded-full after:transition-all checked:after:translate-x-4 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
/>
<label htmlFor="auto-approve" className="text-xs cursor-pointer">
Auto-approve
</label>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -11,7 +11,9 @@ export function HomeChatInput({ onSubmit }: { onSubmit: () => void }) {
const [inputValue, setInputValue] = useAtom(homeChatInputValueAtom); const [inputValue, setInputValue] = useAtom(homeChatInputValueAtom);
const textareaRef = useRef<HTMLTextAreaElement>(null); const textareaRef = useRef<HTMLTextAreaElement>(null);
const { settings, updateSettings, isAnyProviderSetup } = useSettings(); const { settings, updateSettings, isAnyProviderSetup } = useSettings();
const { streamMessage, isStreaming, setIsStreaming } = useStreamChat(); // eslint-disable-line @typescript-eslint/no-unused-vars const { streamMessage, isStreaming, setIsStreaming } = useStreamChat({
hasChatId: false,
}); // eslint-disable-line @typescript-eslint/no-unused-vars
const adjustHeight = () => { const adjustHeight = () => {
const textarea = textareaRef.current; const textarea = textareaRef.current;

View File

@@ -8,35 +8,39 @@ export function useProposal(chatId?: number | undefined) {
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const fetchProposal = useCallback(async () => { const fetchProposal = useCallback(
if (chatId === undefined) { async (overrideChatId?: number) => {
setProposalResult(null); chatId = overrideChatId ?? chatId;
setIsLoading(false); if (chatId === undefined) {
setError(null); setProposalResult(null);
return; setIsLoading(false);
} setError(null);
setIsLoading(true); return;
setError(null);
setProposalResult(null); // Reset on new fetch
try {
// Type assertion might be needed depending on how IpcClient is typed
const result = (await IpcClient.getInstance().getProposal(
chatId
)) as ProposalResult | null;
if (result) {
setProposalResult(result);
} else {
setProposalResult(null); // Explicitly set to null if IPC returns null
} }
} catch (err: any) { setIsLoading(true);
console.error("Error fetching proposal:", err); setError(null);
setError(err.message || "Failed to fetch proposal"); setProposalResult(null); // Reset on new fetch
setProposalResult(null); // Clear proposal data on error try {
} finally { // Type assertion might be needed depending on how IpcClient is typed
setIsLoading(false); const result = (await IpcClient.getInstance().getProposal(
} chatId
}, [chatId]); // Depend on chatId )) as ProposalResult | null;
if (result) {
setProposalResult(result);
} else {
setProposalResult(null); // Explicitly set to null if IPC returns null
}
} catch (err: any) {
console.error("Error fetching proposal:", err);
setError(err.message || "Failed to fetch proposal");
setProposalResult(null); // Clear proposal data on error
} finally {
setIsLoading(false);
}
},
[chatId]
); // Depend on chatId
useEffect(() => { useEffect(() => {
fetchProposal(); fetchProposal();

View File

@@ -22,7 +22,9 @@ export function getRandomString() {
return Math.random().toString(36).substring(2, 15); return Math.random().toString(36).substring(2, 15);
} }
export function useStreamChat() { export function useStreamChat({
hasChatId = true,
}: { hasChatId?: boolean } = {}) {
const [messages, setMessages] = useAtom(chatMessagesAtom); const [messages, setMessages] = useAtom(chatMessagesAtom);
const [isStreaming, setIsStreaming] = useAtom(isStreamingAtom); const [isStreaming, setIsStreaming] = useAtom(isStreamingAtom);
const [error, setError] = useAtom(chatErrorAtom); const [error, setError] = useAtom(chatErrorAtom);
@@ -32,8 +34,14 @@ export function useStreamChat() {
const { refreshApp } = useLoadApp(selectedAppId); const { refreshApp } = useLoadApp(selectedAppId);
const setStreamCount = useSetAtom(chatStreamCountAtom); const setStreamCount = useSetAtom(chatStreamCountAtom);
const { refreshVersions } = useLoadVersions(selectedAppId); const { refreshVersions } = useLoadVersions(selectedAppId);
const { id: chatId } = useSearch({ from: "/chat" }); let chatId: number | undefined;
const { refreshProposal } = useProposal(chatId);
if (hasChatId) {
const { id } = useSearch({ from: "/chat" });
chatId = id;
}
let { refreshProposal } = hasChatId ? useProposal(chatId) : useProposal();
const streamMessage = useCallback( const streamMessage = useCallback(
async ({ async ({
prompt, prompt,
@@ -93,7 +101,7 @@ export function useStreamChat() {
if (response.updatedFiles) { if (response.updatedFiles) {
setIsPreviewOpen(true); setIsPreviewOpen(true);
} }
refreshProposal(); refreshProposal(chatId);
// Keep the same as below // Keep the same as below
setIsStreaming(false); setIsStreaming(false);

View File

@@ -51,18 +51,7 @@ const getProposalHandler = async (
return null; return null;
} }
if (latestAssistantMessage?.approvalState === "approved") { if (latestAssistantMessage?.approvalState === "approved") {
return { return null;
proposal: {
type: "action-proposal",
actions: [
{
id: "restart-app",
},
],
},
chatId: chatId,
messageId: latestAssistantMessage.id,
};
} }
if (latestAssistantMessage?.content && latestAssistantMessage.id) { if (latestAssistantMessage?.content && latestAssistantMessage.id) {

View File

@@ -127,7 +127,13 @@ export interface ActionProposal {
actions: SuggestedAction[]; actions: SuggestedAction[];
} }
export type Proposal = CodeProposal | ActionProposal; export interface TipProposal {
type: "tip-proposal";
title: string;
description: string;
}
export type Proposal = CodeProposal | ActionProposal | TipProposal;
export interface ProposalResult { export interface ProposalResult {
proposal: Proposal; proposal: Proposal;

View File

@@ -20,7 +20,7 @@ export default function HomePage() {
const { settings, isAnyProviderSetup } = useSettings(); const { settings, isAnyProviderSetup } = useSettings();
const setIsPreviewOpen = useSetAtom(isPreviewOpenAtom); const setIsPreviewOpen = useSetAtom(isPreviewOpenAtom);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { streamMessage } = useStreamChat(); const { streamMessage } = useStreamChat({ hasChatId: false });
// Get the appId from search params // Get the appId from search params
const appId = search.appId ? Number(search.appId) : null; const appId = search.appId ? Number(search.appId) : null;