refactor(settings): migrate from dialog to full-screen page layout

Complete migration of settings from modal dialog to dedicated full-screen
page, improving UX and providing more space for configuration options.

Changes:
- Remove SettingsDialog component (legacy modal-based interface)
- Add SettingsPage component with full-screen layout using FullScreenPanel
- Refactor App.tsx routing to support dedicated settings page
  * Add settings route handler
  * Update navigation logic from dialog-based to page-based
  * Integrate with existing app switcher and provider management
- Update ImportExportSection to work with new page layout
  * Improve spacing and layout for better readability
  * Enhanced error handling and user feedback
  * Better integration with page-level actions
- Enhance useSettings hook to support page-based workflow
  * Add navigation state management
  * Improve settings persistence logic
  * Better error boundary handling

Benefits:
- More intuitive navigation with dedicated settings page
- Better use of screen space for complex configurations
- Improved accessibility with clearer visual hierarchy
- Consistent with modern desktop application patterns
- Easier to extend with new settings sections

This change is part of the larger UI refactoring initiative to modernize
the application interface and improve user experience.
This commit is contained in:
YoVinchen
2025-11-21 09:28:11 +08:00
parent d802b7bf61
commit 764ba81ea6
5 changed files with 655 additions and 471 deletions

View File

@@ -33,7 +33,10 @@ export interface UseSettingsResult {
browseAppConfigDir: () => Promise<void>;
resetDirectory: (app: AppId) => Promise<void>;
resetAppConfigDir: () => Promise<void>;
saveSettings: () => Promise<SaveResult | null>;
saveSettings: (
overrides?: Partial<SettingsFormState>,
options?: { silent?: boolean },
) => Promise<SaveResult | null>;
resetSettings: () => void;
acknowledgeRestart: () => void;
}
@@ -115,24 +118,29 @@ export function useSettings(): UseSettingsResult {
]);
// 保存设置
const saveSettings = useCallback(async (): Promise<SaveResult | null> => {
if (!settings) return null;
const saveSettings = useCallback(
async (
overrides?: Partial<SettingsFormState>,
options?: { silent?: boolean },
): Promise<SaveResult | null> => {
const mergedSettings = settings ? { ...settings, ...overrides } : null;
if (!mergedSettings) return null;
try {
const sanitizedAppDir = sanitizeDir(appConfigDir);
const sanitizedClaudeDir = sanitizeDir(settings.claudeConfigDir);
const sanitizedCodexDir = sanitizeDir(settings.codexConfigDir);
const sanitizedGeminiDir = sanitizeDir(settings.geminiConfigDir);
const sanitizedClaudeDir = sanitizeDir(mergedSettings.claudeConfigDir);
const sanitizedCodexDir = sanitizeDir(mergedSettings.codexConfigDir);
const sanitizedGeminiDir = sanitizeDir(mergedSettings.geminiConfigDir);
const previousAppDir = initialAppConfigDir;
const previousClaudeDir = sanitizeDir(data?.claudeConfigDir);
const previousCodexDir = sanitizeDir(data?.codexConfigDir);
const previousGeminiDir = sanitizeDir(data?.geminiConfigDir);
const payload: Settings = {
...settings,
...mergedSettings,
claudeConfigDir: sanitizedClaudeDir,
codexConfigDir: sanitizedCodexDir,
geminiConfigDir: sanitizedGeminiDir,
language: settings.language,
language: mergedSettings.language,
};
await saveMutation.mutateAsync(payload);
@@ -191,9 +199,23 @@ export function useSettings(): UseSettingsResult {
const appDirChanged = sanitizedAppDir !== (previousAppDir ?? undefined);
setRequiresRestart(appDirChanged);
if (!options?.silent) {
toast.success(
t("notifications.settingsSaved", {
defaultValue: "设置已保存",
}),
);
}
return { requiresRestart: appDirChanged };
} catch (error) {
console.error("[useSettings] Failed to save settings", error);
toast.error(
t("notifications.settingsSaveFailed", {
defaultValue: "保存设置失败: {{error}}",
error: (error as Error)?.message ?? String(error),
}),
);
throw error;
}
}, [