Normalise styles
This commit is contained in:
@@ -8,6 +8,7 @@ import { TaskAttemptComparePage } from "@/pages/task-attempt-compare";
|
||||
import { Settings } from "@/pages/Settings";
|
||||
import { DisclaimerDialog } from "@/components/DisclaimerDialog";
|
||||
import { ConfigProvider, useConfig } from "@/components/config-provider";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import type { Config, ApiResponse } from "shared/types";
|
||||
|
||||
function AppContent() {
|
||||
@@ -57,30 +58,32 @@ function AppContent() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen flex flex-col bg-background">
|
||||
<DisclaimerDialog
|
||||
open={showDisclaimer}
|
||||
onAccept={handleDisclaimerAccept}
|
||||
/>
|
||||
{showNavbar && <Navbar />}
|
||||
<div className="flex-1 overflow-y-scroll">
|
||||
<Routes>
|
||||
<Route path="/" element={<Projects />} />
|
||||
<Route path="/projects" element={<Projects />} />
|
||||
<Route path="/projects/:projectId" element={<Projects />} />
|
||||
<Route path="/projects/:projectId/tasks" element={<ProjectTasks />} />
|
||||
<Route
|
||||
path="/projects/:projectId/tasks/:taskId"
|
||||
element={<TaskDetailsPage />}
|
||||
/>
|
||||
<Route
|
||||
path="/projects/:projectId/tasks/:taskId/attempts/:attemptId/compare"
|
||||
element={<TaskAttemptComparePage />}
|
||||
/>
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
</Routes>
|
||||
<ThemeProvider initialTheme={config?.theme || "system"}>
|
||||
<div className="h-screen flex flex-col bg-background">
|
||||
<DisclaimerDialog
|
||||
open={showDisclaimer}
|
||||
onAccept={handleDisclaimerAccept}
|
||||
/>
|
||||
{showNavbar && <Navbar />}
|
||||
<div className="flex-1 overflow-y-scroll">
|
||||
<Routes>
|
||||
<Route path="/" element={<Projects />} />
|
||||
<Route path="/projects" element={<Projects />} />
|
||||
<Route path="/projects/:projectId" element={<Projects />} />
|
||||
<Route path="/projects/:projectId/tasks" element={<ProjectTasks />} />
|
||||
<Route
|
||||
path="/projects/:projectId/tasks/:taskId"
|
||||
element={<TaskDetailsPage />}
|
||||
/>
|
||||
<Route
|
||||
path="/projects/:projectId/tasks/:taskId/attempts/:attemptId/compare"
|
||||
element={<TaskAttemptComparePage />}
|
||||
/>
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
import {
|
||||
PersonStanding,
|
||||
Brain,
|
||||
Wrench as Tool,
|
||||
ChevronDown,
|
||||
@@ -408,11 +407,11 @@ export function ConversationViewer({ jsonlOutput }: ConversationViewerProps) {
|
||||
return (
|
||||
<Card
|
||||
key={`error-${index}`}
|
||||
className="bg-yellow-50 border-yellow-200"
|
||||
className="bg-yellow-100/50 dark:bg-yellow-900/20 border"
|
||||
>
|
||||
<CardContent className="p-3">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<AlertTriangle className="h-4 w-4 text-yellow-600" />
|
||||
<AlertTriangle className="h-4 w-4 text-yellow-600 dark:text-yellow-400" />
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
Parse Error
|
||||
</Badge>
|
||||
@@ -421,7 +420,7 @@ export function ConversationViewer({ jsonlOutput }: ConversationViewerProps) {
|
||||
<p className="text-xs text-muted-foreground mb-1">
|
||||
Raw JSONL:
|
||||
</p>
|
||||
<pre className="text-xs bg-white p-2 rounded border overflow-x-auto whitespace-pre-wrap">
|
||||
<pre className="text-xs bg-background p-2 rounded border overflow-x-auto whitespace-pre-wrap">
|
||||
{safeRenderString(item.rawLine)}
|
||||
</pre>
|
||||
</div>
|
||||
@@ -443,20 +442,17 @@ export function ConversationViewer({ jsonlOutput }: ConversationViewerProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={`unknown-${index}`}
|
||||
className="bg-gray-50 border-gray-200"
|
||||
>
|
||||
<Card key={`unknown-${index}`} className="bg-muted/30 border">
|
||||
<CardContent className="p-3">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<FileText className="h-4 w-4 text-gray-600" />
|
||||
<FileText className="h-4 w-4 text-muted-foreground" />
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
Unknown
|
||||
</Badge>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground mb-1">JSONL:</p>
|
||||
<pre className="text-xs bg-white p-2 rounded border overflow-x-auto whitespace-pre-wrap">
|
||||
<pre className="text-xs bg-background p-2 rounded border overflow-x-auto whitespace-pre-wrap">
|
||||
{safeRenderString(prettyJson)}
|
||||
</pre>
|
||||
</div>
|
||||
@@ -469,11 +465,11 @@ export function ConversationViewer({ jsonlOutput }: ConversationViewerProps) {
|
||||
return (
|
||||
<Card
|
||||
key={`rejection-${index}`}
|
||||
className="bg-red-50 border-red-200"
|
||||
className="bg-red-100/50 dark:bg-red-900/20 border"
|
||||
>
|
||||
<CardContent className="p-3">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<AlertTriangle className="h-4 w-4 text-red-600" />
|
||||
<AlertTriangle className="h-4 w-4 text-red-600 dark:text-red-400" />
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
Tool Rejected
|
||||
</Badge>
|
||||
@@ -486,7 +482,7 @@ export function ConversationViewer({ jsonlOutput }: ConversationViewerProps) {
|
||||
<p className="text-xs text-muted-foreground mb-1">
|
||||
Command:
|
||||
</p>
|
||||
<pre className="text-xs bg-white p-2 rounded border overflow-x-auto">
|
||||
<pre className="text-xs bg-background p-2 rounded border overflow-x-auto">
|
||||
{safeRenderString(item.command)}
|
||||
</pre>
|
||||
</div>
|
||||
@@ -494,7 +490,7 @@ export function ConversationViewer({ jsonlOutput }: ConversationViewerProps) {
|
||||
<p className="text-xs text-muted-foreground mb-1">
|
||||
Message:
|
||||
</p>
|
||||
<p className="text-xs bg-white p-2 rounded border">
|
||||
<p className="text-xs bg-background p-2 rounded border">
|
||||
{safeRenderString(item.message)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -516,8 +512,8 @@ export function ConversationViewer({ jsonlOutput }: ConversationViewerProps) {
|
||||
key={messageId}
|
||||
className={`${
|
||||
item.role === "user"
|
||||
? "bg-blue-50 border-blue-200 ml-12"
|
||||
: "bg-gray-50 border-gray-200 mr-12"
|
||||
? "bg-blue-100/50 dark:bg-blue-900/20 border ml-12"
|
||||
: "bg-muted/50 border mr-12"
|
||||
}`}
|
||||
>
|
||||
<CardContent className="p-4">
|
||||
@@ -587,7 +583,7 @@ export function ConversationViewer({ jsonlOutput }: ConversationViewerProps) {
|
||||
return (
|
||||
<div key={contentIndex} className="mt-3">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Tool className="h-4 w-4 text-green-600" />
|
||||
<Tool className="h-4 w-4 text-green-600 dark:text-green-400" />
|
||||
<span className="text-sm font-medium">
|
||||
{safeRenderString(content.name)}
|
||||
</span>
|
||||
|
||||
@@ -62,7 +62,7 @@ export function ExecutionOutputViewer({
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="bg-muted border-none">
|
||||
<Card className="">
|
||||
<CardContent className="p-3">
|
||||
<div className="space-y-3">
|
||||
{/* View mode toggle for Amp executor with valid JSONL */}
|
||||
|
||||
@@ -1,45 +1,30 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from "react";
|
||||
import type { Config, ThemeMode, ApiResponse } from "shared/types";
|
||||
import type { ThemeMode } from "shared/types";
|
||||
|
||||
type ThemeProviderProps = {
|
||||
children: React.ReactNode;
|
||||
initialTheme?: ThemeMode;
|
||||
};
|
||||
|
||||
type ThemeProviderState = {
|
||||
theme: ThemeMode;
|
||||
setTheme: (theme: ThemeMode) => void;
|
||||
loadThemeFromConfig: () => Promise<void>;
|
||||
};
|
||||
|
||||
const initialState: ThemeProviderState = {
|
||||
theme: "system",
|
||||
setTheme: () => null,
|
||||
loadThemeFromConfig: async () => {},
|
||||
};
|
||||
|
||||
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
||||
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
const [theme, setThemeState] = useState<ThemeMode>("system");
|
||||
export function ThemeProvider({ children, initialTheme = "system", ...props }: ThemeProviderProps) {
|
||||
const [theme, setThemeState] = useState<ThemeMode>(initialTheme);
|
||||
|
||||
// Load theme from backend config
|
||||
const loadThemeFromConfig = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/config");
|
||||
const data: ApiResponse<Config> = await response.json();
|
||||
|
||||
if (data.success && data.data) {
|
||||
setThemeState(data.data.theme);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error loading theme from config:", err);
|
||||
}
|
||||
};
|
||||
|
||||
// Load theme on mount
|
||||
// Update theme when initialTheme changes
|
||||
useEffect(() => {
|
||||
loadThemeFromConfig();
|
||||
}, []);
|
||||
setThemeState(initialTheme);
|
||||
}, [initialTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement;
|
||||
@@ -66,7 +51,6 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
const value = {
|
||||
theme,
|
||||
setTheme,
|
||||
loadThemeFromConfig,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,13 +3,10 @@ import ReactDOM from "react-dom/client";
|
||||
import App from "./App.tsx";
|
||||
import "./index.css";
|
||||
import { ClickToComponent } from "click-to-react-component";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<ThemeProvider>
|
||||
<ClickToComponent />
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
<ClickToComponent />
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState } from "react";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
@@ -7,40 +7,17 @@ import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import type { Config, ThemeMode, EditorType, ApiResponse } from "shared/types";
|
||||
import type { ThemeMode, EditorType } from "shared/types";
|
||||
import { useTheme } from "@/components/theme-provider";
|
||||
import { useConfig } from "@/components/config-provider";
|
||||
|
||||
export function Settings() {
|
||||
const [config, setConfig] = useState<Config | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { config, updateConfig, saveConfig, loading } = useConfig();
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
// Load initial config
|
||||
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);
|
||||
} else {
|
||||
setError(data.message || "Failed to load configuration");
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to load configuration");
|
||||
console.error("Error loading config:", err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadConfig();
|
||||
}, []);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!config) return;
|
||||
|
||||
@@ -49,24 +26,16 @@ export function Settings() {
|
||||
setSuccess(false);
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/config", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(config),
|
||||
});
|
||||
const success = await saveConfig();
|
||||
|
||||
const data: ApiResponse<Config> = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
if (success) {
|
||||
setSuccess(true);
|
||||
// Update theme provider to reflect the saved theme
|
||||
setTheme(config.theme);
|
||||
|
||||
setTimeout(() => setSuccess(false), 3000);
|
||||
} else {
|
||||
setError(data.message || "Failed to save configuration");
|
||||
setError("Failed to save configuration");
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to save configuration");
|
||||
@@ -76,10 +45,6 @@ export function Settings() {
|
||||
}
|
||||
};
|
||||
|
||||
const updateConfig = (updates: Partial<Config>) => {
|
||||
setConfig((prev: Config | null) => prev ? { ...prev, ...updates } : null);
|
||||
};
|
||||
|
||||
const resetDisclaimer = async () => {
|
||||
if (!config) return;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ArrowLeft, FileText, Code /* , Monitor, Braces */ } from "lucide-react"
|
||||
import { makeRequest } from "@/lib/api";
|
||||
import { TaskFormDialog } from "@/components/tasks/TaskFormDialog";
|
||||
import { useKeyboardShortcuts } from "@/lib/keyboard-shortcuts";
|
||||
import { useConfig } from "@/components/config-provider";
|
||||
import type {
|
||||
TaskStatus,
|
||||
TaskAttempt,
|
||||
@@ -23,7 +24,6 @@ import type {
|
||||
ExecutionProcess,
|
||||
ExecutionProcessStatus,
|
||||
ExecutionProcessType,
|
||||
Config,
|
||||
ApiResponse,
|
||||
} from "shared/types";
|
||||
|
||||
@@ -118,6 +118,7 @@ export function TaskDetailsPage() {
|
||||
>([]);
|
||||
const [executionProcessesLoading, setExecutionProcessesLoading] = useState(false);
|
||||
const [selectedExecutor, setSelectedExecutor] = useState<string>("claude");
|
||||
const { config } = useConfig();
|
||||
const [creatingAttempt, setCreatingAttempt] = useState(false);
|
||||
const [stoppingProcess, setStoppingProcess] = useState<string | null>(null);
|
||||
const [openingEditor, setOpeningEditor] = useState(false);
|
||||
@@ -164,23 +165,12 @@ export function TaskDetailsPage() {
|
||||
}
|
||||
}, [projectId, taskId]);
|
||||
|
||||
// Load config to get default executor
|
||||
// Set default executor from config
|
||||
useEffect(() => {
|
||||
const loadConfig = async () => {
|
||||
try {
|
||||
const response = await makeRequest("/api/config");
|
||||
if (response.ok) {
|
||||
const result: ApiResponse<Config> = await response.json();
|
||||
if (result.success && result.data) {
|
||||
setSelectedExecutor(result.data.executor.type);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to load config:", err);
|
||||
}
|
||||
};
|
||||
loadConfig();
|
||||
}, []);
|
||||
if (config) {
|
||||
setSelectedExecutor(config.executor.type);
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
useEffect(() => {
|
||||
if (task) {
|
||||
|
||||
Reference in New Issue
Block a user