| import { | |
| X, | |
| Settings, | |
| Keyboard, | |
| Monitor, | |
| Shield, | |
| HardDrive, | |
| Info, | |
| } from "lucide-react"; | |
| import { useAppStore } from "../store"; | |
| import { useState } from "react"; | |
| type Tab = | |
| | "general" | |
| | "appearance" | |
| | "shortcuts" | |
| | "storage" | |
| | "privacy" | |
| | "about"; | |
| export const SettingsPanel = () => { | |
| const { isSettingsOpen, setIsSettingsOpen } = useAppStore(); | |
| const [activeTab, setActiveTab] = useState<Tab>("general"); | |
| const tabs: { id: Tab; label: string; icon: React.ReactNode }[] = [ | |
| { id: "general", label: "General", icon: <Settings size={16} /> }, | |
| { id: "appearance", label: "Appearance", icon: <Monitor size={16} /> }, | |
| { | |
| id: "shortcuts", | |
| label: "Keyboard Shortcuts", | |
| icon: <Keyboard size={16} />, | |
| }, | |
| { id: "storage", label: "Storage & Data", icon: <HardDrive size={16} /> }, | |
| { id: "privacy", label: "Privacy & Security", icon: <Shield size={16} /> }, | |
| { id: "about", label: "About", icon: <Info size={16} /> }, | |
| ]; | |
| return ( | |
| <div | |
| className={`absolute right-0 top-0 h-full w-[600px] z-40 bg-panel-bg shadow-2xl border-l border-panel-border flex flex-col transform transition-transform duration-500 ease-[cubic-bezier(0.19,1,0.22,1)] ${isSettingsOpen ? "translate-x-0" : "translate-x-full"}`} | |
| > | |
| <div className="h-14 border-b border-white/5 flex items-center justify-between px-6 bg-black/20 shrink-0"> | |
| <h2 className="text-ui-primary font-medium flex items-center gap-2"> | |
| <Settings size={16} className="text-ui-secondary" /> Settings | |
| </h2> | |
| <button | |
| onClick={() => setIsSettingsOpen(false)} | |
| className="text-ui-secondary hover:text-ui-primary p-1 rounded-md hover:bg-white/5 transition-colors" | |
| > | |
| <X size={18} /> | |
| </button> | |
| </div> | |
| <div className="flex flex-1 overflow-hidden"> | |
| {/* Sidebar */} | |
| <div className="w-[200px] bg-black/10 border-r border-white/5 p-4 flex flex-col gap-1 overflow-y-auto custom-scrollbar shrink-0"> | |
| {tabs.map((tab) => ( | |
| <button | |
| key={tab.id} | |
| onClick={() => setActiveTab(tab.id)} | |
| className={`flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors text-left ${activeTab === tab.id ? "bg-accent-blue text-white font-medium" : "text-ui-secondary hover:text-ui-primary hover:bg-white/5"}`} | |
| > | |
| {tab.icon} | |
| {tab.label} | |
| </button> | |
| ))} | |
| </div> | |
| {/* Content */} | |
| <div className="flex-1 p-8 overflow-y-auto custom-scrollbar text-ui-primary text-sm"> | |
| <div | |
| key={activeTab} | |
| className="transition-all duration-200" | |
| > | |
| {activeTab === "general" && ( | |
| <div className="space-y-8"> | |
| <section> | |
| <h3 className="text-base font-medium mb-4 text-white"> | |
| Startup & Behavior | |
| </h3> | |
| <div className="space-y-4"> | |
| <label className="flex items-center justify-between group cursor-pointer"> | |
| <div> | |
| <div className="font-medium"> | |
| Open last board on startup | |
| </div> | |
| <div className="text-ui-secondary text-xs mt-0.5"> | |
| Automatically load the most recently used board | |
| </div> | |
| </div> | |
| <div className="w-10 h-6 bg-accent-blue rounded-full relative transition-colors"> | |
| <div className="absolute right-1 top-1 w-4 h-4 bg-white rounded-full shadow" /> | |
| </div> | |
| </label> | |
| <label className="flex items-center justify-between group cursor-pointer"> | |
| <div> | |
| <div className="font-medium"> | |
| Hardware Acceleration | |
| </div> | |
| <div className="text-ui-secondary text-xs mt-0.5"> | |
| Use GPU to render canvas (requires restart) | |
| </div> | |
| </div> | |
| <div className="w-10 h-6 bg-accent-blue rounded-full relative transition-colors"> | |
| <div className="absolute right-1 top-1 w-4 h-4 bg-white rounded-full shadow" /> | |
| </div> | |
| </label> | |
| </div> | |
| </section> | |
| </div> | |
| )} | |
| {activeTab === "appearance" && ( | |
| <div className="space-y-8"> | |
| <section> | |
| <h3 className="text-base font-medium mb-4 text-white"> | |
| Theme | |
| </h3> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div className="border border-accent-blue bg-black/20 p-4 rounded-lg cursor-pointer"> | |
| <div className="w-full h-20 bg-[#1C1C1E] rounded mb-3 border border-panel-border overflow-hidden flex"> | |
| <div className="w-1/3 bg-[#2A2A2E] border-r border-[#3A3A3E]" /> | |
| </div> | |
| <div className="text-center font-medium"> | |
| Dark (Default) | |
| </div> | |
| </div> | |
| <div className="border border-white/10 bg-black/20 p-4 rounded-lg cursor-pointer opacity-50 hover:opacity-100 transition-opacity"> | |
| <div className="w-full h-20 bg-gray-100 rounded mb-3 border border-gray-300 overflow-hidden flex"> | |
| <div className="w-1/3 bg-gray-200 border-r border-gray-300" /> | |
| </div> | |
| <div className="text-center font-medium">Light</div> | |
| </div> | |
| </div> | |
| </section> | |
| <section> | |
| <h3 className="text-base font-medium mb-4 text-white"> | |
| Canvas | |
| </h3> | |
| <div className="space-y-4"> | |
| <label className="flex items-center justify-between group cursor-pointer"> | |
| <div> | |
| <div className="font-medium">Show grid</div> | |
| <div className="text-ui-secondary text-xs mt-0.5"> | |
| Display a subtle dot grid on the canvas | |
| </div> | |
| </div> | |
| <div className="w-10 h-6 bg-white/10 rounded-full relative transition-colors"> | |
| <div className="absolute left-1 top-1 w-4 h-4 bg-ui-secondary rounded-full shadow" /> | |
| </div> | |
| </label> | |
| </div> | |
| </section> | |
| </div> | |
| )} | |
| {activeTab === "shortcuts" && ( | |
| <div className="space-y-6"> | |
| <section> | |
| <h3 className="text-base font-medium mb-4 text-white"> | |
| Global Shortcuts | |
| </h3> | |
| <div className="space-y-2"> | |
| {[ | |
| { action: "Toggle Browser Panel", key: "B" }, | |
| { action: "Toggle Library Panel", key: "L" }, | |
| { action: "Open Settings", key: "Ctrl + ," }, | |
| { action: "Pan Canvas", key: "Space + Drag" }, | |
| { action: "Zoom Canvas", key: "Mouse Scroll" }, | |
| { action: "Undo", key: "Ctrl + Z" }, | |
| { action: "Redo", key: "Ctrl + Shift + Z" }, | |
| ].map((kb) => ( | |
| <div | |
| key={kb.action} | |
| className="flex items-center justify-between py-2 border-b border-white/5" | |
| > | |
| <span className="text-ui-secondary">{kb.action}</span> | |
| <span className="px-2 py-1 bg-black/30 border border-white/10 rounded text-xs font-mono text-ui-primary"> | |
| {kb.key} | |
| </span> | |
| </div> | |
| ))} | |
| </div> | |
| </section> | |
| <div className="text-xs text-ui-secondary bg-accent-blue/10 text-accent-blue p-3 rounded-lg flex items-center gap-2"> | |
| <Info size={14} /> Custom shortcuts are planned for a future | |
| update. | |
| </div> | |
| </div> | |
| )} | |
| {activeTab === "storage" && ( | |
| <div className="space-y-8"> | |
| <section> | |
| <h3 className="text-base font-medium mb-4 text-white"> | |
| Local Library | |
| </h3> | |
| <div className="space-y-4"> | |
| <div className="bg-black/20 p-4 rounded-lg border border-white/5"> | |
| <div className="flex justify-between items-center mb-2"> | |
| <div className="font-medium text-white"> | |
| Image Storage Mode | |
| </div> | |
| <span className="text-xs font-medium text-accent-blue bg-accent-blue/10 px-2 py-0.5 rounded"> | |
| Embedded | |
| </span> | |
| </div> | |
| <p className="text-ui-secondary text-xs"> | |
| Images are copied into the .refstudio save archive. | |
| Makes boards easier to share but increases file size. | |
| </p> | |
| </div> | |
| <button className="w-full py-2 bg-white/5 hover:bg-white/10 rounded-lg border border-white/10 transition-colors text-sm font-medium"> | |
| Clear Image Cache | |
| </button> | |
| </div> | |
| </section> | |
| <section> | |
| <h3 className="text-base font-medium mb-4 text-white text-accent-amber"> | |
| Danger Zone | |
| </h3> | |
| <div className="border border-accent-amber/30 rounded-lg p-4 bg-accent-amber/5"> | |
| <div className="mb-3"> | |
| <div className="font-medium text-accent-amber"> | |
| Reset All Application Data | |
| </div> | |
| <div className="text-ui-secondary text-xs mt-1"> | |
| This will permanently delete all local boards, | |
| settings, and library tags. This action cannot be | |
| undone. | |
| </div> | |
| </div> | |
| <button | |
| className="bg-accent-amber text-black font-semibold px-4 py-2 rounded text-xs hover:bg-accent-amber/90 transition-colors" | |
| onClick={() => { | |
| if ( | |
| confirm( | |
| "Are you sure you want to delete all data? This cannot be undone.", | |
| ) | |
| ) { | |
| localStorage.clear(); | |
| window.location.reload(); | |
| } | |
| }} | |
| > | |
| Factory Reset | |
| </button> | |
| </div> | |
| </section> | |
| </div> | |
| )} | |
| {activeTab === "privacy" && ( | |
| <div className="space-y-8"> | |
| <section> | |
| <h3 className="text-base font-medium mb-4 text-white"> | |
| Privacy Preferences | |
| </h3> | |
| <p className="text-ui-secondary text-xs leading-relaxed mb-6"> | |
| Refstudio is designed to be local-first. We believe your | |
| references belong to you. We do not track your usage, | |
| collect telemetry, or analyze your boards. | |
| </p> | |
| <div className="space-y-4"> | |
| <label className="flex items-center justify-between group cursor-pointer"> | |
| <div> | |
| <div className="font-medium"> | |
| Check for updates automatically | |
| </div> | |
| <div className="text-ui-secondary text-xs mt-0.5"> | |
| Pings our server once a day to check for new | |
| versions | |
| </div> | |
| </div> | |
| <div className="w-10 h-6 bg-accent-blue rounded-full relative transition-colors"> | |
| <div className="absolute right-1 top-1 w-4 h-4 bg-white rounded-full shadow" /> | |
| </div> | |
| </label> | |
| <label className="flex items-center justify-between group cursor-pointer opacity-50"> | |
| <div> | |
| <div className="font-medium"> | |
| Share anonymous crash reports | |
| </div> | |
| <div className="text-ui-secondary text-xs mt-0.5"> | |
| Help us fix bugs by sending stack traces when the | |
| app crashes | |
| </div> | |
| </div> | |
| <div className="w-10 h-6 bg-white/10 rounded-full relative transition-colors"> | |
| <div className="absolute left-1 top-1 w-4 h-4 bg-ui-secondary rounded-full shadow" /> | |
| </div> | |
| </label> | |
| </div> | |
| </section> | |
| </div> | |
| )} | |
| {activeTab === "about" && ( | |
| <div className="space-y-8 flex flex-col items-center text-center mt-12"> | |
| <div className="w-20 h-20 bg-gradient-to-br from-[#1C1C1E] to-[#2A2A2E] border border-[#3A3A3E] rounded-2xl flex items-center justify-center shadow-2xl mb-4"> | |
| <Monitor size={32} className="text-accent-blue" /> | |
| </div> | |
| <div> | |
| <h1 className="text-2xl font-semibold text-white"> | |
| Refstudio | |
| </h1> | |
| <p className="text-ui-secondary mt-1">Version 1.0.0-beta</p> | |
| </div> | |
| <div className="max-w-xs text-ui-secondary text-sm leading-relaxed"> | |
| The zero-friction reference board for visual artists. | |
| Local-first, private by default, and uncompromisingly fast. | |
| </div> | |
| <div className="flex gap-4 mt-4"> | |
| <a | |
| href="#" | |
| className="text-accent-blue hover:underline text-sm font-medium" | |
| > | |
| Website | |
| </a> | |
| <a | |
| href="#" | |
| className="text-accent-blue hover:underline text-sm font-medium" | |
| > | |
| Release Notes | |
| </a> | |
| <a | |
| href="#" | |
| className="text-accent-blue hover:underline text-sm font-medium" | |
| > | |
| License | |
| </a> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |