Skip to content
23 changes: 23 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ToastContainer, toast, Slide } from 'react-toastify';
import clsx from 'clsx';

import TitleBar from './window/TitleBar';
import SettingsPanel from './components/panel/SettingsPanel';
import FolderTree from './components/panel/FolderTree';
import LibraryExportPanel from './components/panel/right/LibraryExportPanel';
import Resizer from './components/ui/Resizer';
Expand Down Expand Up @@ -130,6 +131,7 @@ function App() {
rightPanelWidth,
compactEditorPanelHeightOverride,
activeRightPanel,
isSettingsOpen,
setUI,
setRightPanel,
} = useUIStore(
Expand All @@ -144,6 +146,7 @@ function App() {
rightPanelWidth: state.rightPanelWidth,
compactEditorPanelHeightOverride: state.compactEditorPanelHeightOverride,
activeRightPanel: state.activeRightPanel,
isSettingsOpen: state.isSettingsOpen,
setUI: state.setUI,
setRightPanel: state.setRightPanel,
})),
Expand Down Expand Up @@ -907,6 +910,26 @@ function App() {
executeDelete={executeDelete}
handleSaveCollage={handleSaveCollage}
/>
{isSettingsOpen && appSettings && (
<div
className="fixed inset-0 z-60 flex flex-col bg-bg-primary overflow-hidden"
role="dialog"
aria-modal="true"
aria-label="Settings"
>
<div className="flex flex-1 min-h-0 flex-col px-4 py-6 sm:px-6 sm:py-8 md:py-10 lg:px-8">
<div className="mx-auto flex h-full min-h-0 w-full max-w-4xl flex-1 flex-col overflow-hidden">
<SettingsPanel
appSettings={appSettings}
onBack={() => setUI({ isSettingsOpen: false })}
onLibraryRefresh={handleLibraryRefresh}
onSettingsChange={handleSettingsChange}
rootPath={rootPath}
/>
</div>
</div>
</div>
)}
<ToastContainer
position="bottom-right"
autoClose={5000}
Expand Down
34 changes: 21 additions & 13 deletions src/components/panel/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1056,19 +1056,27 @@ export default function Editor({ onBackToLibrary, onContextMenu, transformWrappe
});

useEffect(() => {
const rootStyle = getComputedStyle(document.documentElement);
const bgPrimaryStr = rootStyle.getPropertyValue('--app-bg-primary') || 'rgb(24, 24, 24)';
const bgSecondaryStr = rootStyle.getPropertyValue('--app-bg-secondary') || 'rgb(35, 35, 35)';

wgpuStateRef.current = {
useWgpuRenderer: appSettings?.useWgpuRenderer,
isReady: selectedImage?.isReady ?? false,
hasRenderedFirstFrame,
isCropping,
uncroppedAdjustedPreviewUrl,
showOriginal,
bgPrimary: parseRgb(bgPrimaryStr),
bgSecondary: parseRgb(bgSecondaryStr),
let isCancelled = false;
const applyDocumentThemeToWgpuRef = () => {
if (isCancelled) return;
const rootStyle = getComputedStyle(document.documentElement);
const bgPrimaryStr = rootStyle.getPropertyValue('--app-bg-primary') || 'rgb(24, 24, 24)';
const bgSecondaryStr = rootStyle.getPropertyValue('--app-bg-secondary') || 'rgb(35, 35, 35)';

wgpuStateRef.current = {
useWgpuRenderer: appSettings?.useWgpuRenderer,
isReady: selectedImage?.isReady ?? false,
hasRenderedFirstFrame,
isCropping,
uncroppedAdjustedPreviewUrl,
showOriginal,
bgPrimary: parseRgb(bgPrimaryStr),
bgSecondary: parseRgb(bgSecondaryStr),
};
};
queueMicrotask(applyDocumentThemeToWgpuRef);
return () => {
isCancelled = true;
};
}, [
appSettings?.useWgpuRenderer,
Expand Down
2 changes: 1 addition & 1 deletion src/components/panel/SettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ export default function SettingsPanel({
onClick={onBack}
size="icon"
variant="ghost"
data-tooltip="Go to Home"
data-tooltip="Back"
>
<ArrowLeft />
</Button>
Expand Down
30 changes: 29 additions & 1 deletion src/components/panel/right/RightPanelSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { motion } from 'framer-motion';
import { SlidersHorizontal, Info, Crop, Layers, Paintbrush, SwatchBook, FileInput, type LucideIcon } from 'lucide-react';
import {
SlidersHorizontal,
Info,
Crop,
Layers,
Paintbrush,
SwatchBook,
FileInput,
Cog,
type LucideIcon,
} from 'lucide-react';
import { Panel } from '../../ui/AppProperties';

interface PanelOptions {
Expand All @@ -11,6 +21,7 @@ interface PanelOptions {
interface RightPanelSwitcherProps {
activePanel: Panel | null;
onPanelSelect(id: Panel): void;
onOpenAppSettings?(): void;
isInstantTransition: boolean;
layout?: 'horizontal' | 'vertical';
}
Expand All @@ -32,6 +43,7 @@ const panelGroups: Array<Array<PanelOptions>> = [
export default function RightPanelSwitcher({
activePanel,
onPanelSelect,
onOpenAppSettings,
isInstantTransition,
layout = 'vertical',
}: RightPanelSwitcherProps) {
Expand Down Expand Up @@ -69,6 +81,22 @@ export default function RightPanelSwitcher({
))}
</div>
))}
{!isHorizontal && onOpenAppSettings && (
<>
<div className="flex-1 min-h-2" aria-hidden />
<div className="shrink-0 flex flex-col gap-1 pt-1 border-t border-surface">
<button
type="button"
className="relative p-2 rounded-md transition-colors duration-200 text-text-secondary hover:bg-surface hover:text-text-primary"
onClick={onOpenAppSettings}
data-tooltip="App settings"
aria-label="App settings"
>
<Cog size={20} className="relative z-10" />
</button>
</div>
</>
)}
</div>
);
}
1 change: 1 addition & 0 deletions src/components/views/EditorView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ export default function EditorView({
<RightPanelSwitcher
activePanel={activeRightPanel}
onPanelSelect={handleRightPanelSelect}
onOpenAppSettings={() => setUI({ isSettingsOpen: true })}
isInstantTransition={isInstantTransition}
/>
</div>
Expand Down
10 changes: 9 additions & 1 deletion src/hooks/useKeyboardShortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,8 @@ export const useKeyboardShortcuts = ({
match: (e: KeyboardEvent) => e.code === 'Escape',
execute: (e: KeyboardEvent, s: any) => {
e.preventDefault();
if (s.editor.isStraightenActive) s.editor.setEditor({ isStraightenActive: false });
if (s.ui.isSettingsOpen) s.ui.setUI({ isSettingsOpen: false });
else if (s.editor.isStraightenActive) s.editor.setEditor({ isStraightenActive: false });
else if (s.ui.customEscapeHandler) s.ui.customEscapeHandler();
else if (s.editor.activeAiSubMaskId) s.editor.setEditor({ activeAiSubMaskId: null });
else if (s.editor.activeAiPatchContainerId) s.editor.setEditor({ activeAiPatchContainerId: null });
Expand Down Expand Up @@ -504,7 +505,14 @@ export const useKeyboardShortcuts = ({
const handleKeyDown = (event: KeyboardEvent) => {
const state = getStoreState();

if (state.ui.isSettingsOpen && event.code === 'Escape') {
event.preventDefault();
state.ui.setUI({ isSettingsOpen: false });
return;
}

const isModalOpen =
state.ui.isSettingsOpen ||
state.ui.isCreateFolderModalOpen ||
state.ui.isRenameFolderModalOpen ||
state.ui.isRenameFileModalOpen ||
Expand Down
2 changes: 2 additions & 0 deletions src/store/useUIStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ interface UIState {
renameTargetPaths: Array<string>;
isImportModalOpen: boolean;
isCopyPasteSettingsModalOpen: boolean;
isSettingsOpen: boolean;
importTargetFolder: string | null;
importSourcePaths: Array<string>;
folderActionTarget: string | null;
Expand Down Expand Up @@ -139,6 +140,7 @@ export const useUIStore = create<UIState>((set, get) => ({
renameTargetPaths: [],
isImportModalOpen: false,
isCopyPasteSettingsModalOpen: false,
isSettingsOpen: false,
importTargetFolder: null,
importSourcePaths: [],
folderActionTarget: null,
Expand Down
Loading