From 27246d70f2a3f070da85a96dc1d1b5736de9398d Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 15 Feb 2026 20:25:58 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20rewrite=20settings=20dialog=20with=20ta?= =?UTF-8?q?bbed=20panel=20=E2=80=94=20appearance,=20boards,=20shortcuts,?= =?UTF-8?q?=20about?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/settings/SettingsDialog.tsx | 267 ++++++++++++++++++--- 1 file changed, 233 insertions(+), 34 deletions(-) diff --git a/src/components/settings/SettingsDialog.tsx b/src/components/settings/SettingsDialog.tsx index 7cd2de3..2b4ebb3 100644 --- a/src/components/settings/SettingsDialog.tsx +++ b/src/components/settings/SettingsDialog.tsx @@ -1,4 +1,7 @@ -import { Sun, Moon, Monitor } from "lucide-react"; +import { useState } from "react"; +import { + Sun, Moon, Monitor, RotateCcw, +} from "lucide-react"; import { Dialog, DialogContent, @@ -10,12 +13,15 @@ import { Separator } from "@/components/ui/separator"; import { Button } from "@/components/ui/button"; import { useAppStore } from "@/stores/app-store"; import type { AppSettings } from "@/types/settings"; +import type { ColumnWidth } from "@/types/board"; interface SettingsDialogProps { open: boolean; onOpenChange: (open: boolean) => void; } +type Tab = "appearance" | "boards" | "shortcuts" | "about"; + const THEME_OPTIONS: { value: AppSettings["theme"]; label: string; @@ -26,13 +32,69 @@ const THEME_OPTIONS: { { value: "system", label: "System", icon: Monitor }, ]; +const ACCENT_PRESETS: { hue: string; label: string }[] = [ + { hue: "160", label: "Teal" }, + { hue: "240", label: "Blue" }, + { hue: "300", label: "Purple" }, + { hue: "350", label: "Pink" }, + { hue: "25", label: "Red" }, + { hue: "55", label: "Orange" }, + { hue: "85", label: "Yellow" }, + { hue: "130", label: "Lime" }, + { hue: "200", label: "Cyan" }, + { hue: "0", label: "Slate" }, +]; + +const DENSITY_OPTIONS: { + value: AppSettings["density"]; + label: string; +}[] = [ + { value: "compact", label: "Compact" }, + { value: "comfortable", label: "Comfortable" }, + { value: "spacious", label: "Spacious" }, +]; + +const WIDTH_OPTIONS: { value: ColumnWidth; label: string }[] = [ + { value: "narrow", label: "Narrow" }, + { value: "standard", label: "Standard" }, + { value: "wide", label: "Wide" }, +]; + +const SHORTCUTS: { key: string; description: string; category: string }[] = [ + { key: "Ctrl+K", description: "Open command palette", category: "Navigation" }, + { key: "Ctrl+Z", description: "Undo", category: "Board" }, + { key: "Ctrl+Shift+Z", description: "Redo", category: "Board" }, + { key: "?", description: "Keyboard shortcuts", category: "Navigation" }, + { key: "Escape", description: "Close modal / cancel", category: "Navigation" }, +]; + +const TABS: { value: Tab; label: string }[] = [ + { value: "appearance", label: "Appearance" }, + { value: "boards", label: "Boards" }, + { value: "shortcuts", label: "Shortcuts" }, + { value: "about", label: "About" }, +]; + +function SectionLabel({ children }: { children: React.ReactNode }) { + return ( + + ); +} + export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) { - const theme = useAppStore((s) => s.settings.theme); + const [tab, setTab] = useState("appearance"); + const settings = useAppStore((s) => s.settings); const setTheme = useAppStore((s) => s.setTheme); + const setAccentColor = useAppStore((s) => s.setAccentColor); + const setUiZoom = useAppStore((s) => s.setUiZoom); + const setDensity = useAppStore((s) => s.setDensity); + const setDefaultColumnWidth = useAppStore((s) => s.setDefaultColumnWidth); return ( - + Settings @@ -42,43 +104,180 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) { -
- {/* Theme section */} -
- -
- {THEME_OPTIONS.map(({ value, label, icon: Icon }) => ( - + {/* Tab bar */} +
+ {TABS.map((t) => ( + + ))} +
+ + {/* Tab content */} +
+ {tab === "appearance" && ( + <> + {/* Theme */} +
+ Theme +
+ {THEME_OPTIONS.map(({ value, label, icon: Icon }) => ( + + ))} +
+
+ + + + {/* UI Zoom */} +
+
+ UI Zoom +
+ + {Math.round(settings.uiZoom * 100)}% + + {settings.uiZoom !== 1 && ( + + )} +
+
+ setUiZoom(parseFloat(e.target.value))} + className="w-full accent-pylon-accent" + /> +
+ 75% + 100% + 150% +
+
+ + + + {/* Accent Color */} +
+ Accent Color +
+ {ACCENT_PRESETS.map(({ hue, label }) => { + const isAchromatic = hue === "0"; + const bg = isAchromatic + ? "oklch(50% 0 0)" + : `oklch(55% 0.12 ${hue})`; + return ( +
+
+ + + + {/* Density */} +
+ Density +
+ {DENSITY_OPTIONS.map(({ value, label }) => ( + + ))} +
+
+ + )} + + {tab === "boards" && ( +
+ Default Column Width +
+ {WIDTH_OPTIONS.map(({ value, label }) => ( + + ))} +
+
+ )} + + {tab === "shortcuts" && ( +
+ {SHORTCUTS.map(({ key, description }) => ( +
+ {description} + + {key} + +
))}
-
+ )} - - - {/* About section */} -
- -
-

OpenPylon v0.1.0

+ {tab === "about" && ( +
+

OpenPylon

- Local-first Kanban board + v0.1.0 · Local-first Kanban board +

+

+ Built with Tauri, React, and TypeScript.

-
+ )}