Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 45 additions & 42 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { motion, AnimatePresence } from "framer-motion";
import { Plus, Loader2, Bot, FolderCode } from "lucide-react";
import { api, type Project, type Session, type ClaudeMdFile } from "@/lib/api";
import { OutputCacheProvider } from "@/lib/outputCache";
import { ThemeProvider } from "@/contexts/ThemeContext";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { ProjectList } from "@/components/ProjectList";
Expand Down Expand Up @@ -449,49 +450,51 @@ function App() {
};

return (
<OutputCacheProvider>
<div className="h-screen bg-background flex flex-col">
{/* Topbar */}
<Topbar
onClaudeClick={() => handleViewChange("editor")}
onSettingsClick={() => handleViewChange("settings")}
onUsageClick={() => handleViewChange("usage-dashboard")}
onMCPClick={() => handleViewChange("mcp")}
onInfoClick={() => setShowNFO(true)}
/>

{/* Main Content */}
<div className="flex-1 overflow-y-auto">
{renderContent()}
<ThemeProvider>
<OutputCacheProvider>
<div className="h-screen bg-background flex flex-col">
{/* Topbar */}
<Topbar
onClaudeClick={() => handleViewChange("editor")}
onSettingsClick={() => handleViewChange("settings")}
onUsageClick={() => handleViewChange("usage-dashboard")}
onMCPClick={() => handleViewChange("mcp")}
onInfoClick={() => setShowNFO(true)}
/>

{/* Main Content */}
<div className="flex-1 overflow-y-auto">
{renderContent()}
</div>

{/* NFO Credits Modal */}
{showNFO && <NFOCredits onClose={() => setShowNFO(false)} />}

{/* Claude Binary Dialog */}
<ClaudeBinaryDialog
open={showClaudeBinaryDialog}
onOpenChange={setShowClaudeBinaryDialog}
onSuccess={() => {
setToast({ message: "Claude binary path saved successfully", type: "success" });
// Trigger a refresh of the Claude version check
window.location.reload();
}}
onError={(message) => setToast({ message, type: "error" })}
/>

{/* Toast Container */}
<ToastContainer>
{toast && (
<Toast
message={toast.message}
type={toast.type}
onDismiss={() => setToast(null)}
/>
)}
</ToastContainer>
</div>

{/* NFO Credits Modal */}
{showNFO && <NFOCredits onClose={() => setShowNFO(false)} />}

{/* Claude Binary Dialog */}
<ClaudeBinaryDialog
open={showClaudeBinaryDialog}
onOpenChange={setShowClaudeBinaryDialog}
onSuccess={() => {
setToast({ message: "Claude binary path saved successfully", type: "success" });
// Trigger a refresh of the Claude version check
window.location.reload();
}}
onError={(message) => setToast({ message, type: "error" })}
/>

{/* Toast Container */}
<ToastContainer>
{toast && (
<Toast
message={toast.message}
type={toast.type}
onDismiss={() => setToast(null)}
/>
)}
</ToastContainer>
</div>
</OutputCacheProvider>
</OutputCacheProvider>
</ThemeProvider>
);
}

Expand Down
4 changes: 3 additions & 1 deletion src/components/ClaudeFileEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button";
import { Toast, ToastContainer } from "@/components/ui/toast";
import { api, type ClaudeMdFile } from "@/lib/api";
import { cn } from "@/lib/utils";
import { useTheme } from "@/contexts/ThemeContext";

interface ClaudeFileEditorProps {
/**
Expand Down Expand Up @@ -36,6 +37,7 @@ export const ClaudeFileEditor: React.FC<ClaudeFileEditorProps> = ({
onBack,
className,
}) => {
const { theme } = useTheme();
const [content, setContent] = useState<string>("");
const [originalContent, setOriginalContent] = useState<string>("");
const [loading, setLoading] = useState(true);
Expand Down Expand Up @@ -151,7 +153,7 @@ export const ClaudeFileEditor: React.FC<ClaudeFileEditorProps> = ({
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
) : (
<div className="h-full rounded-lg border border-border overflow-hidden shadow-sm" data-color-mode="dark">
<div className="h-full rounded-lg border border-border overflow-hidden shadow-sm" data-color-mode={theme}>
<MDEditor
value={content}
onChange={(val) => setContent(val || "")}
Expand Down
17 changes: 17 additions & 0 deletions src/components/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { ClaudeVersionSelector } from "./ClaudeVersionSelector";
import { StorageTab } from "./StorageTab";
import { HooksEditor } from "./HooksEditor";
import { SlashCommandsManager } from "./SlashCommandsManager";
import { useTheme } from "@/contexts/ThemeContext";

interface SettingsProps {
/**
Expand Down Expand Up @@ -56,6 +57,7 @@ export const Settings: React.FC<SettingsProps> = ({
onBack,
className,
}) => {
const { theme, setTheme } = useTheme();
const [settings, setSettings] = useState<ClaudeSettings | null>(null);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
Expand Down Expand Up @@ -375,6 +377,21 @@ export const Settings: React.FC<SettingsProps> = ({
<h3 className="text-base font-semibold mb-4">General Settings</h3>

<div className="space-y-4">
{/* Theme Selection */}
<div className="flex items-center justify-between">
<div className="space-y-0.5 flex-1">
<Label htmlFor="theme">Light Theme</Label>
<p className="text-xs text-muted-foreground">
Enable light mode appearance
</p>
</div>
<Switch
id="theme"
checked={theme === 'light'}
onCheckedChange={(checked) => setTheme(checked ? 'light' : 'dark')}
/>
</div>

{/* Include Co-authored By */}
<div className="flex items-center justify-between">
<div className="space-y-0.5 flex-1">
Expand Down
7 changes: 4 additions & 3 deletions src/components/StreamMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { cn } from "@/lib/utils";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { claudeSyntaxTheme } from "@/lib/claudeSyntaxTheme";
import { useSyntaxTheme } from "@/hooks/useSyntaxTheme";
import type { ClaudeStreamMessage } from "./AgentExecution";
import {
TodoWidget,
Expand Down Expand Up @@ -51,6 +51,7 @@ interface StreamMessageProps {
* Component to render a single Claude Code stream message
*/
const StreamMessageComponent: React.FC<StreamMessageProps> = ({ message, className, streamMessages, onLinkDetected }) => {
const syntaxTheme = useSyntaxTheme();
// State to track tool results mapped by tool call ID
const [toolResults, setToolResults] = useState<Map<string, any>>(new Map());

Expand Down Expand Up @@ -131,7 +132,7 @@ const StreamMessageComponent: React.FC<StreamMessageProps> = ({ message, classNa
const match = /language-(\w+)/.exec(className || '');
return !inline && match ? (
<SyntaxHighlighter
style={claudeSyntaxTheme}
style={syntaxTheme}
language={match[1]}
PreTag="div"
{...props}
Expand Down Expand Up @@ -660,7 +661,7 @@ const StreamMessageComponent: React.FC<StreamMessageProps> = ({ message, classNa
const match = /language-(\w+)/.exec(className || '');
return !inline && match ? (
<SyntaxHighlighter
style={claudeSyntaxTheme}
style={syntaxTheme}
language={match[1]}
PreTag="div"
{...props}
Expand Down
22 changes: 14 additions & 8 deletions src/components/ToolWidgets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import {
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { claudeSyntaxTheme } from "@/lib/claudeSyntaxTheme";
import { useSyntaxTheme } from "@/hooks/useSyntaxTheme";
import { Button } from "@/components/ui/button";
import { createPortal } from "react-dom";
import * as Diff from 'diff';
Expand Down Expand Up @@ -399,6 +399,7 @@ export const ReadWidget: React.FC<{ filePath: string; result?: any }> = ({ fileP
* Widget for Read tool result - shows file content with line numbers
*/
export const ReadResultWidget: React.FC<{ content: string; filePath?: string }> = ({ content, filePath }) => {
const syntaxTheme = useSyntaxTheme();
const [isExpanded, setIsExpanded] = useState(false);

// Extract file extension for syntax highlighting
Expand Down Expand Up @@ -530,7 +531,7 @@ export const ReadResultWidget: React.FC<{ content: string; filePath?: string }>
<div className="relative overflow-x-auto">
<SyntaxHighlighter
language={language}
style={claudeSyntaxTheme}
style={syntaxTheme}
showLineNumbers
startingLineNumber={startLineNumber}
wrapLongLines={false}
Expand Down Expand Up @@ -694,6 +695,7 @@ export const BashWidget: React.FC<{
* Widget for Write tool
*/
export const WriteWidget: React.FC<{ filePath: string; content: string; result?: any }> = ({ filePath, content, result: _result }) => {
const syntaxTheme = useSyntaxTheme();
const [isMaximized, setIsMaximized] = useState(false);

// Extract file extension for syntax highlighting
Expand Down Expand Up @@ -776,7 +778,7 @@ export const WriteWidget: React.FC<{ filePath: string; content: string; result?:
<div className="flex-1 overflow-auto">
<SyntaxHighlighter
language={language}
style={claudeSyntaxTheme}
style={syntaxTheme}
customStyle={{
margin: 0,
padding: '1.5rem',
Expand Down Expand Up @@ -827,7 +829,7 @@ export const WriteWidget: React.FC<{ filePath: string; content: string; result?:
<div className="overflow-auto flex-1">
<SyntaxHighlighter
language={language}
style={claudeSyntaxTheme}
style={syntaxTheme}
customStyle={{
margin: 0,
padding: '1rem',
Expand Down Expand Up @@ -1121,6 +1123,7 @@ export const EditWidget: React.FC<{
new_string: string;
result?: any;
}> = ({ file_path, old_string, new_string, result: _result }) => {
const syntaxTheme = useSyntaxTheme();

const diffResult = Diff.diffLines(old_string || '', new_string || '', {
newlineIsToken: true,
Expand Down Expand Up @@ -1165,7 +1168,7 @@ export const EditWidget: React.FC<{
<div className="flex-1">
<SyntaxHighlighter
language={language}
style={claudeSyntaxTheme}
style={syntaxTheme}
PreTag="div"
wrapLongLines={false}
customStyle={{
Expand Down Expand Up @@ -1196,6 +1199,7 @@ export const EditWidget: React.FC<{
* Widget for Edit tool result - shows a diff view
*/
export const EditResultWidget: React.FC<{ content: string }> = ({ content }) => {
const syntaxTheme = useSyntaxTheme();
// Parse the content to extract file path and code snippet
const lines = content.split('\n');
let filePath = '';
Expand Down Expand Up @@ -1245,7 +1249,7 @@ export const EditResultWidget: React.FC<{ content: string }> = ({ content }) =>
<div className="overflow-x-auto max-h-[440px]">
<SyntaxHighlighter
language={language}
style={claudeSyntaxTheme}
style={syntaxTheme}
showLineNumbers
startingLineNumber={startLineNumber}
wrapLongLines={false}
Expand Down Expand Up @@ -1281,6 +1285,7 @@ export const MCPWidget: React.FC<{
input?: any;
result?: any;
}> = ({ toolName, input, result: _result }) => {
const syntaxTheme = useSyntaxTheme();
const [isExpanded, setIsExpanded] = useState(false);

// Parse the tool name to extract components
Expand Down Expand Up @@ -1396,7 +1401,7 @@ export const MCPWidget: React.FC<{
)}>
<SyntaxHighlighter
language="json"
style={claudeSyntaxTheme}
style={syntaxTheme}
customStyle={{
margin: 0,
padding: '0.75rem',
Expand Down Expand Up @@ -1583,6 +1588,7 @@ export const MultiEditWidget: React.FC<{
edits: Array<{ old_string: string; new_string: string }>;
result?: any;
}> = ({ file_path, edits, result: _result }) => {
const syntaxTheme = useSyntaxTheme();
const [isExpanded, setIsExpanded] = useState(false);
const language = getLanguage(file_path);

Expand Down Expand Up @@ -1645,7 +1651,7 @@ export const MultiEditWidget: React.FC<{
<div className="flex-1">
<SyntaxHighlighter
language={language}
style={claudeSyntaxTheme}
style={syntaxTheme}
PreTag="div"
wrapLongLines={false}
customStyle={{
Expand Down
11 changes: 4 additions & 7 deletions src/components/ui/switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,17 @@ const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
className={cn(
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors",
"disabled:cursor-not-allowed disabled:opacity-50",
checked
? "bg-blue-600 dark:bg-blue-500"
: "bg-gray-300 dark:bg-gray-600",
className
)}
style={{
backgroundColor: checked ? "var(--color-primary)" : "var(--color-muted)"
}}
>
<span
className={cn(
"pointer-events-none block h-4 w-4 rounded-full shadow-lg ring-0 transition-transform",
"pointer-events-none block h-4 w-4 rounded-full bg-white shadow-lg ring-0 transition-transform",
checked ? "translate-x-4" : "translate-x-0"
)}
style={{
backgroundColor: "var(--color-background)"
}}
/>
<input
ref={ref}
Expand Down
Loading