Task attempt ec641adb-5bf7-43ae-91d5-39e9dd4caa3a - Final changes
This commit is contained in:
@@ -6,18 +6,23 @@ import { ProjectTasks } from "@/pages/project-tasks";
|
||||
import { TaskAttemptComparePage } from "@/pages/task-attempt-compare";
|
||||
import { Settings } from "@/pages/Settings";
|
||||
import { DisclaimerDialog } from "@/components/DisclaimerDialog";
|
||||
import { OnboardingDialog } from "@/components/OnboardingDialog";
|
||||
import { ConfigProvider, useConfig } from "@/components/config-provider";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import type { Config, ApiResponse } from "shared/types";
|
||||
import type { Config, ApiResponse, ExecutorConfig, EditorType } from "shared/types";
|
||||
|
||||
function AppContent() {
|
||||
const { config, updateConfig, loading } = useConfig();
|
||||
const [showDisclaimer, setShowDisclaimer] = useState(false);
|
||||
const [showOnboarding, setShowOnboarding] = useState(false);
|
||||
const showNavbar = true;
|
||||
|
||||
useEffect(() => {
|
||||
if (config) {
|
||||
setShowDisclaimer(!config.disclaimer_acknowledged);
|
||||
if (config.disclaimer_acknowledged) {
|
||||
setShowOnboarding(!config.onboarding_acknowledged);
|
||||
}
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
@@ -39,6 +44,38 @@ function AppContent() {
|
||||
|
||||
if (data.success) {
|
||||
setShowDisclaimer(false);
|
||||
setShowOnboarding(!config.onboarding_acknowledged);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error saving config:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnboardingComplete = async (onboardingConfig: { executor: ExecutorConfig; editor: { editor_type: EditorType; custom_command: string | null } }) => {
|
||||
if (!config) return;
|
||||
|
||||
const updatedConfig = {
|
||||
...config,
|
||||
onboarding_acknowledged: true,
|
||||
executor: onboardingConfig.executor,
|
||||
editor: onboardingConfig.editor,
|
||||
};
|
||||
|
||||
updateConfig(updatedConfig);
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/config", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(updatedConfig),
|
||||
});
|
||||
|
||||
const data: ApiResponse<Config> = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
setShowOnboarding(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error saving config:", err);
|
||||
@@ -63,6 +100,10 @@ function AppContent() {
|
||||
open={showDisclaimer}
|
||||
onAccept={handleDisclaimerAccept}
|
||||
/>
|
||||
<OnboardingDialog
|
||||
open={showOnboarding}
|
||||
onComplete={handleOnboardingComplete}
|
||||
/>
|
||||
{showNavbar && <Navbar />}
|
||||
<div className="flex-1 overflow-y-scroll">
|
||||
<Routes>
|
||||
|
||||
145
frontend/src/components/OnboardingDialog.tsx
Normal file
145
frontend/src/components/OnboardingDialog.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Sparkles, Code } from "lucide-react";
|
||||
import type { EditorType, ExecutorConfig } from "shared/types";
|
||||
import { EXECUTOR_TYPES, EDITOR_TYPES, EXECUTOR_LABELS, EDITOR_LABELS } from "shared/types";
|
||||
|
||||
interface OnboardingDialogProps {
|
||||
open: boolean;
|
||||
onComplete: (config: { executor: ExecutorConfig; editor: { editor_type: EditorType; custom_command: string | null } }) => void;
|
||||
}
|
||||
|
||||
export function OnboardingDialog({ open, onComplete }: OnboardingDialogProps) {
|
||||
const [executor, setExecutor] = useState<ExecutorConfig>({ type: "claude" });
|
||||
const [editorType, setEditorType] = useState<EditorType>("vscode");
|
||||
const [customCommand, setCustomCommand] = useState<string>("");
|
||||
|
||||
const handleComplete = () => {
|
||||
onComplete({
|
||||
executor,
|
||||
editor: {
|
||||
editor_type: editorType,
|
||||
custom_command: editorType === "custom" ? customCommand || null : null,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const isValid = editorType !== "custom" || (editorType === "custom" && customCommand.trim() !== "");
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={() => {}}>
|
||||
<DialogContent className="sm:max-w-[600px]">
|
||||
<DialogHeader>
|
||||
<div className="flex items-center gap-3">
|
||||
<Sparkles className="h-6 w-6 text-primary" />
|
||||
<DialogTitle>Welcome to Mission Control</DialogTitle>
|
||||
</div>
|
||||
<DialogDescription className="text-left pt-2">
|
||||
Let's set up your coding preferences. You can always change these later in Settings.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-6 py-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Sparkles className="h-4 w-4" />
|
||||
Choose Your Coding Agent
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="executor">Default Executor</Label>
|
||||
<Select
|
||||
value={executor.type}
|
||||
onValueChange={(value: "echo" | "claude" | "amp") => setExecutor({ type: value })}
|
||||
>
|
||||
<SelectTrigger id="executor">
|
||||
<SelectValue placeholder="Select your preferred coding agent" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{EXECUTOR_TYPES.map((type) => (
|
||||
<SelectItem key={type} value={type}>
|
||||
{EXECUTOR_LABELS[type]}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{executor.type === "claude" && "Claude will handle your coding tasks with advanced reasoning."}
|
||||
{executor.type === "amp" && "Amp provides powerful code generation and debugging capabilities."}
|
||||
{executor.type === "echo" && "Echo is a simple testing executor that repeats your commands."}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Code className="h-4 w-4" />
|
||||
Choose Your Code Editor
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="editor">Preferred Editor</Label>
|
||||
<Select
|
||||
value={editorType}
|
||||
onValueChange={(value: EditorType) => setEditorType(value)}
|
||||
>
|
||||
<SelectTrigger id="editor">
|
||||
<SelectValue placeholder="Select your preferred editor" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{EDITOR_TYPES.map((type) => (
|
||||
<SelectItem key={type} value={type}>
|
||||
{EDITOR_LABELS[type]}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
This editor will be used to open task attempts and project files.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{editorType === "custom" && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="custom-command">Custom Command</Label>
|
||||
<Input
|
||||
id="custom-command"
|
||||
placeholder="e.g., code, subl, vim"
|
||||
value={customCommand}
|
||||
onChange={(e) => setCustomCommand(e.target.value)}
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Enter the command to run your custom editor. Use spaces for arguments (e.g., "code --wait").
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button onClick={handleComplete} disabled={!isValid} className="w-full">
|
||||
Complete Setup
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import type { ThemeMode, EditorType } from "shared/types";
|
||||
import { EXECUTOR_TYPES, EDITOR_TYPES, EXECUTOR_LABELS, EDITOR_LABELS } from "shared/types";
|
||||
import { useTheme } from "@/components/theme-provider";
|
||||
import { useConfig } from "@/components/config-provider";
|
||||
|
||||
@@ -51,6 +52,12 @@ export function Settings() {
|
||||
updateConfig({ disclaimer_acknowledged: false });
|
||||
};
|
||||
|
||||
const resetOnboarding = async () => {
|
||||
if (!config) return;
|
||||
|
||||
updateConfig({ onboarding_acknowledged: false });
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
@@ -145,9 +152,11 @@ export function Settings() {
|
||||
<SelectValue placeholder="Select executor" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="claude">Claude</SelectItem>
|
||||
<SelectItem value="amp">Amp</SelectItem>
|
||||
<SelectItem value="echo">Echo</SelectItem>
|
||||
{EXECUTOR_TYPES.map((type) => (
|
||||
<SelectItem key={type} value={type}>
|
||||
{EXECUTOR_LABELS[type]}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
@@ -181,12 +190,11 @@ export function Settings() {
|
||||
<SelectValue placeholder="Select editor" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="vscode">VS Code</SelectItem>
|
||||
<SelectItem value="cursor">Cursor</SelectItem>
|
||||
<SelectItem value="windsurf">Windsurf</SelectItem>
|
||||
<SelectItem value="intellij">IntelliJ IDEA</SelectItem>
|
||||
<SelectItem value="zed">Zed</SelectItem>
|
||||
<SelectItem value="custom">Custom Command</SelectItem>
|
||||
{EDITOR_TYPES.map((type) => (
|
||||
<SelectItem key={type} value={type}>
|
||||
{EDITOR_LABELS[type]}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
@@ -260,7 +268,7 @@ export function Settings() {
|
||||
Manage safety warnings and acknowledgments.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<CardContent className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
@@ -284,6 +292,29 @@ export function Settings() {
|
||||
Resetting the disclaimer will require you to acknowledge the safety warning again on next app start.
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>Onboarding Status</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{config.onboarding_acknowledged
|
||||
? "You have completed the onboarding process."
|
||||
: "The onboarding process has not been completed."}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
onClick={resetOnboarding}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={!config.onboarding_acknowledged}
|
||||
>
|
||||
Reset Onboarding
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Resetting the onboarding will show the setup screen again on next app start.
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
"shared": path.resolve(__dirname, "../shared"),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
|
||||
Reference in New Issue
Block a user