Normalise styles

This commit is contained in:
Louis Knight-Webb
2025-06-21 22:33:33 +01:00
parent 290d128220
commit 6031904a07
7 changed files with 63 additions and 128 deletions

View File

@@ -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>
);
}

View File

@@ -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>

View File

@@ -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 */}

View File

@@ -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 (

View File

@@ -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>
);

View File

@@ -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;

View File

@@ -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) {