Skip to content
Merged
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
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<title>Trufos</title>
</head>
<body class="dark">
<body>
<div id="root"></div>
<script type="module" src="./src/renderer/index.tsx"></script>
</body>
Expand Down
32 changes: 17 additions & 15 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import '@/styles/tailwind.css';
import '@/styles/global.css';
import '@/styles/index.css';
import { Menubar } from '@/view/Menubar';
import { RequestWindow } from '@/view/RequestWindow';
import { SidebarProvider } from '@/components/ui/sidebar';
import { TooltipProvider } from '@/components/ui/tooltip';
import { ResizablePanel, ResizablePanelGroup, ResizableHandle } from '@/components/ui/resizable';
import { ThemeProvider } from '@/contexts/ThemeContext';
import { useEffect, useState } from 'react';

const MIN_SIDEBAR_PIXELS = 300;
Expand Down Expand Up @@ -32,18 +32,20 @@ export const App = () => {
}, []);

return (
<TooltipProvider delayDuration={750}>
<SidebarProvider className="grid">
<ResizablePanelGroup direction="horizontal" className="h-full w-full">
<ResizablePanel defaultSize={minSidebarSize} minSize={minSidebarSize}>
<Menubar />
</ResizablePanel>
<ResizableHandle />
<ResizablePanel minSize={minRequestWindowSize}>
<RequestWindow />
</ResizablePanel>
</ResizablePanelGroup>
</SidebarProvider>
</TooltipProvider>
<ThemeProvider defaultTheme="dark">
<TooltipProvider delayDuration={750}>
<SidebarProvider className="grid">
<ResizablePanelGroup direction="horizontal" className="h-full w-full">
<ResizablePanel defaultSize={minSidebarSize} minSize={minSidebarSize}>
<Menubar />
</ResizablePanel>
<ResizableHandle />
<ResizablePanel minSize={minRequestWindowSize}>
<RequestWindow />
</ResizablePanel>
</ResizablePanelGroup>
</SidebarProvider>
</TooltipProvider>
</ThemeProvider>
);
};
59 changes: 59 additions & 0 deletions src/renderer/components/shared/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { Button } from '@/components/ui/button';
import { useTheme } from '@/contexts/ThemeContext';

const SunIcon = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="12" cy="12" r="4" />
<path d="M12 2v2" />
<path d="M12 20v2" />
<path d="m4.93 4.93 1.41 1.41" />
<path d="m17.66 17.66 1.41 1.41" />
<path d="M2 12h2" />
<path d="M20 12h2" />
<path d="m6.34 17.66-1.41 1.41" />
<path d="m19.07 4.93-1.41 1.41" />
</svg>
);

const MoonIcon = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
</svg>
);

export const ThemeToggle: React.FC = () => {
const { theme, toggleTheme } = useTheme();

return (
<Button
variant="ghost"
size="sm"
onClick={toggleTheme}
className="hover:bg-sidebar-accent h-8 w-8 p-0"
title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`}
>
{theme === 'dark' ? <SunIcon /> : <MoonIcon />}
</Button>
);
};
6 changes: 5 additions & 1 deletion src/renderer/components/sidebar/FooterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { GithubIcon } from '@/components/icons';
import { SettingsModal } from '@/components/shared/settings/SettingsModal';
import { Divider } from '@/components/shared/Divider';
import { SidebarFooter } from '@/components/ui/sidebar';
import { ThemeToggle } from '@/components/shared/ThemeToggle';

export function FooterBar() {
const [appVersion, setAppVersion] = useState<string>(undefined);
Expand All @@ -17,12 +18,15 @@ export function FooterBar() {
<SidebarFooter className="mt-auto">
<Divider />
<div className="flex items-center justify-between">
{/* Settings and icon on the left */}
{/* Settings and theme toggle on the left */}
<div className="flex items-center gap-2">
<SettingsModal />
<span className="shrink-0 text-[12px] leading-[1.2] font-medium tracking-[0px] whitespace-pre text-(--text-secondary) normal-case no-underline">
Settings
</span>
{/*<div className="ml-2">
<ThemeToggle />
</div>*/}
</div>

<div className="flex items-center gap-2">
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/components/ui/sonner.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useTheme } from 'next-themes';
import { useTheme } from '@/contexts/ThemeContext';
import { Toaster as Sonner } from 'sonner';

type ToasterProps = React.ComponentProps<typeof Sonner>;

const Toaster = ({ ...props }: ToasterProps) => {
const { theme = 'system' } = useTheme();
const { theme } = useTheme();

return (
<Sonner
Expand Down
66 changes: 66 additions & 0 deletions src/renderer/contexts/ThemeContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { createContext, useContext, useEffect, useState } from 'react';

type Theme = 'light' | 'dark';

interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export const useTheme = () => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};

interface ThemeProviderProps {
children: React.ReactNode;
defaultTheme?: Theme;
}

export const ThemeProvider: React.FC<ThemeProviderProps> = ({
children,
defaultTheme = 'dark',
}) => {
const [theme, setTheme] = useState<Theme>(defaultTheme);

useEffect(() => {
const savedTheme = localStorage.getItem('theme') as Theme;
if (savedTheme && (savedTheme === 'light' || savedTheme === 'dark')) {
setTheme(savedTheme);
}
}, []);

useEffect(() => {
const root = document.documentElement;

// Remove both classes first
root.classList.remove('light', 'dark');

// Add the current theme class
root.classList.add(theme);

// Save to localStorage
localStorage.setItem('theme', theme);

// Debug log
console.log('Theme changed to:', theme, 'Classes:', root.classList.toString());
}, [theme]);

const toggleTheme = () => {
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
};

const value = {
theme,
setTheme,
toggleTheme,
};

return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
};
9 changes: 2 additions & 7 deletions src/renderer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import '@/logging/console';
import { enableMapSet } from 'immer';

enableMapSet();

import '@/styles/tailwind.css';
enableMapSet(); // immer support for Map and Set
import { createRoot } from 'react-dom/client';
import { App } from '@/App';
import { RendererEventService } from '@/services/event/renderer-event-service';
import { useCollectionStore } from '@/state/collectionStore';
import winston from 'winston';

import('@/lib/monaco/config'); // lazy load monaco editor to improve startup time
import('@/lib/monaco/config.js'); // lazy load monaco editor to improve startup time

// set up store
const { initialize } = useCollectionStore.getState();
Expand All @@ -23,8 +20,6 @@ declare global {
}
}

document.getElementById('body')?.classList.add('dark');

const container = document.getElementById('root');
const root = createRoot(container);

Expand Down
11 changes: 4 additions & 7 deletions src/renderer/lib/monaco/MonacoEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { Editor, EditorProps } from '@monaco-editor/react';
import { useTheme } from '@/contexts/ThemeContext';

export default function MonacoEditor(props: EditorProps) {
return (
<Editor
theme="vs-dark" // TODO: apply theme from settings in the future
keepCurrentModel
{...props}
/>
);
const { theme } = useTheme();

return <Editor theme={theme === 'dark' ? 'vs-dark' : 'vs-light'} keepCurrentModel {...props} />;
}
78 changes: 0 additions & 78 deletions src/renderer/styles/global.css

This file was deleted.

Loading