Merge task attempt f8a712e0-423c-4f72-b6e6-bbdac2088c19 into main
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
import { Navbar } from "@/components/layout/navbar";
|
||||
import { Projects } from "@/pages/projects";
|
||||
@@ -5,12 +6,77 @@ import { ProjectTasks } from "@/pages/project-tasks";
|
||||
import { TaskDetailsPage } from "@/pages/task-details";
|
||||
import { TaskAttemptComparePage } from "@/pages/task-attempt-compare";
|
||||
import { Settings } from "@/pages/Settings";
|
||||
import { DisclaimerDialog } from "@/components/DisclaimerDialog";
|
||||
import type { Config, ApiResponse } from "shared/types";
|
||||
|
||||
function AppContent() {
|
||||
const [config, setConfig] = useState<Config | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showDisclaimer, setShowDisclaimer] = useState(false);
|
||||
const showNavbar = true;
|
||||
|
||||
useEffect(() => {
|
||||
const loadConfig = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/config");
|
||||
const data: ApiResponse<Config> = await response.json();
|
||||
|
||||
if (data.success && data.data) {
|
||||
setConfig(data.data);
|
||||
setShowDisclaimer(!data.data.disclaimer_acknowledged);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error loading config:", err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadConfig();
|
||||
}, []);
|
||||
|
||||
const handleDisclaimerAccept = async () => {
|
||||
if (!config) return;
|
||||
|
||||
const updatedConfig = { ...config, disclaimer_acknowledged: true };
|
||||
|
||||
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) {
|
||||
setConfig(updatedConfig);
|
||||
setShowDisclaimer(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error saving config:", err);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
|
||||
<p className="mt-2 text-muted-foreground">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<DisclaimerDialog
|
||||
open={showDisclaimer}
|
||||
onAccept={handleDisclaimerAccept}
|
||||
/>
|
||||
{showNavbar && <Navbar />}
|
||||
<div className={showNavbar ? "max-w-7xl mx-auto p-6 sm:p-8" : ""}>
|
||||
<Routes>
|
||||
|
||||
87
frontend/src/components/DisclaimerDialog.tsx
Normal file
87
frontend/src/components/DisclaimerDialog.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
|
||||
interface DisclaimerDialogProps {
|
||||
open: boolean;
|
||||
onAccept: () => void;
|
||||
}
|
||||
|
||||
export function DisclaimerDialog({ open, onAccept }: DisclaimerDialogProps) {
|
||||
const [acknowledged, setAcknowledged] = useState(false);
|
||||
|
||||
const handleAccept = () => {
|
||||
if (acknowledged) {
|
||||
onAccept();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={() => {}}>
|
||||
<DialogContent className="sm:max-w-[600px]">
|
||||
<DialogHeader>
|
||||
<div className="flex items-center gap-3">
|
||||
<AlertTriangle className="h-6 w-6 text-destructive" />
|
||||
<DialogTitle>Important Safety Warning</DialogTitle>
|
||||
</div>
|
||||
<DialogDescription className="text-left space-y-4 pt-4">
|
||||
<p className="font-semibold text-foreground">
|
||||
Please read and acknowledge the following before proceeding:
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
<p>
|
||||
<strong>Coding agents have full access to your computer</strong> and can execute any terminal commands, including:
|
||||
</p>
|
||||
<ul className="list-disc list-inside space-y-1 ml-4">
|
||||
<li>Installing, modifying, or deleting software</li>
|
||||
<li>Accessing, creating, or removing files and directories</li>
|
||||
<li>Making network requests and connections</li>
|
||||
<li>Running system-level commands with your permissions</li>
|
||||
</ul>
|
||||
<p>
|
||||
<strong>This software is experimental and may cause catastrophic damage</strong> to your system, data, or projects. By using this software, you acknowledge that:
|
||||
</p>
|
||||
<ul className="list-disc list-inside space-y-1 ml-4">
|
||||
<li>You use this software entirely at your own risk</li>
|
||||
<li>The developers are not responsible for any damage, data loss, or security issues</li>
|
||||
<li>You should have proper backups of important data before using this software</li>
|
||||
<li>You understand the potential consequences of granting unrestricted system access</li>
|
||||
</ul>
|
||||
</div>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex items-center space-x-2 py-4">
|
||||
<Checkbox
|
||||
id="acknowledge"
|
||||
checked={acknowledged}
|
||||
onCheckedChange={(checked: boolean) => setAcknowledged(checked === true)}
|
||||
/>
|
||||
<label
|
||||
htmlFor="acknowledge"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
I understand and acknowledge the risks described above. I am aware that coding agents have full access to my computer and may cause catastrophic damage.
|
||||
</label>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
onClick={handleAccept}
|
||||
disabled={!acknowledged}
|
||||
variant="destructive"
|
||||
>
|
||||
I Accept the Risks and Want to Proceed
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
41
frontend/src/components/ui/checkbox.tsx
Normal file
41
frontend/src/components/ui/checkbox.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import * as React from "react"
|
||||
import { Check } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
interface CheckboxProps {
|
||||
id?: string;
|
||||
checked?: boolean;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(
|
||||
({ className, checked = false, onCheckedChange, disabled, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
role="checkbox"
|
||||
aria-checked={checked}
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
checked && "bg-primary text-primary-foreground",
|
||||
className
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={() => onCheckedChange?.(!checked)}
|
||||
{...props}
|
||||
>
|
||||
{checked && (
|
||||
<div className="flex items-center justify-center text-current">
|
||||
<Check className="h-4 w-4" />
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
)
|
||||
Checkbox.displayName = "Checkbox"
|
||||
|
||||
export { Checkbox }
|
||||
@@ -78,6 +78,12 @@ export function Settings() {
|
||||
setConfig((prev: Config | null) => prev ? { ...prev, ...updates } : null);
|
||||
};
|
||||
|
||||
const resetDisclaimer = async () => {
|
||||
if (!config) return;
|
||||
|
||||
updateConfig({ disclaimer_acknowledged: false });
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
@@ -183,6 +189,40 @@ export function Settings() {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Safety & Disclaimers</CardTitle>
|
||||
<CardDescription>
|
||||
Manage safety warnings and acknowledgments.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>Disclaimer Status</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{config.disclaimer_acknowledged
|
||||
? "You have acknowledged the safety disclaimer."
|
||||
: "The safety disclaimer has not been acknowledged."}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
onClick={resetDisclaimer}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={!config.disclaimer_acknowledged}
|
||||
>
|
||||
Reset Disclaimer
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Resetting the disclaimer will require you to acknowledge the safety warning again on next app start.
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
|
||||
Reference in New Issue
Block a user