@@ -6,7 +6,7 @@ Detailed guide for understanding and extending React components in Loopi.
66
77```
88App (src/app.tsx)
9- ├── Router → Dashboard | AutomationBuilder | Credentials
9+ ├── Router → Dashboard | AutomationBuilder | Settings
1010│
1111├── Dashboard (src/components/Dashboard.tsx)
1212│ ├── Tabs: "Your Automations" | "Examples"
@@ -19,34 +19,40 @@ App (src/app.tsx)
1919│ └── Lists 7 example automations
2020│ └── "Load Example" button for each
2121│
22- └── AutomationBuilder (src/components/AutomationBuilder.tsx)
23- ├── BuilderHeader
24- │ ├── Title & Description
25- │ ├── Run/Stop buttons
26- │ └── Settings dialog
27- │
28- ├── BuilderCanvas (ReactFlow)
29- │ ├── AutomationNode (visual node)
30- │ ├── AddStepPopup (step type picker)
31- │ └── Edge connections
32- │
33- └── NodeDetails (right sidebar)
34- ├── NodeHeader (title + delete)
35- │
36- ├── StepEditor (routes to specific step editor)
37- │ ├── ClickStep (src/components/.../stepTypes/ClickStep.tsx)
38- │ ├── TypeStep
39- │ ├── ExtractStep
40- │ ├── ApiCallStep
41- │ ├── NavigateStep
42- │ ├── SetVariableStep
43- │ ├── ModifyVariableStep
44- │ └── ... other steps
45- │
46- └── ConditionEditor (if conditional node)
47- ├── Element existence conditions
48- ├── Value comparison conditions
49- └── Post-processing options
22+ ├── AutomationBuilder (src/components/AutomationBuilder.tsx)
23+ │ ├── BuilderHeader
24+ │ │ ├── Title & Description
25+ │ │ ├── Run/Stop buttons
26+ │ │ └── Settings dialog
27+ │ │
28+ │ ├── BuilderCanvas (ReactFlow)
29+ │ │ ├── AutomationNode (visual node)
30+ │ │ ├── AddStepPopup (step type picker)
31+ │ │ └── Edge connections
32+ │ │
33+ │ └── NodeDetails (right sidebar)
34+ │ ├── NodeHeader (title + delete)
35+ │ │
36+ │ ├── StepEditor (routes to specific step editor)
37+ │ │ ├── ClickStep (src/components/.../stepTypes/ClickStep.tsx)
38+ │ │ ├── TypeStep
39+ │ │ ├── ExtractStep
40+ │ │ ├── ApiCallStep
41+ │ │ ├── NavigateStep
42+ │ │ ├── SetVariableStep
43+ │ │ ├── ModifyVariableStep
44+ │ │ └── ... other steps
45+ │ │
46+ │ └── ConditionEditor (if conditional node)
47+ │ ├── Element existence conditions
48+ │ ├── Value comparison conditions
49+ │ └── Post-processing options
50+ │
51+ └── Settings (src/components/Settings.tsx)
52+ ├── Appearance (Theme selector: Light, Dark, System)
53+ ├── Downloads (Download path configuration with folder picker)
54+ ├── Notifications (Enable/Disable toggle)
55+ └── About (App info and GitHub link)
5056```
5157
5258### Key Components
@@ -422,6 +428,13 @@ Routes IPC messages to appropriate services.
422428- ` loopi:loadExample ` → TreeStore.loadExample()
423429- ` loopi:deleteTree ` → TreeStore.deleteAutomation()
424430
431+ ** Settings Handlers:**
432+ - ` loopi:loadSettings ` → SettingsStore.loadSettings() - Retrieves saved app settings
433+ - ` loopi:saveSettings ` → SettingsStore.saveSettings() - Persists settings and re-setup download handler
434+
435+ ** File Dialog Handlers:**
436+ - ` dialog:selectFolder ` → electron.dialog.showOpenDialog() - Opens native folder picker
437+
425438** Type Definitions (src/types/globals.d.ts):**
426439``` typescript
427440interface ElectronAPI {
@@ -432,6 +445,11 @@ interface ElectronAPI {
432445 loadExample: (fileName : string ) => Promise <StoredAutomation >;
433446 delete: (automationId : string ) => Promise <boolean >;
434447 };
448+ settings: {
449+ load: () => Promise <AppSettings >;
450+ save: (settings : AppSettings ) => Promise <void >;
451+ };
452+ selectFolder: () => Promise <string | null >;
435453}
436454
437455declare global {
@@ -441,6 +459,159 @@ declare global {
441459}
442460```
443461
462+ ### Settings Component (src/components/Settings.tsx)
463+
464+ App-wide preferences and configuration interface.
465+
466+ ** Purpose:**
467+ Allows users to customize theme, manage download location, and configure notifications. All settings persist to disk via Electron storage and apply immediately without a save button.
468+
469+ ** Features:**
470+ - ** Theme Selection** : Light, Dark, or System preference
471+ - Stores selection in ` ~/.config/[AppName]/settings.json `
472+ - Applies theme by toggling ` dark ` class on document root
473+ - Respects system preference when "System" mode selected
474+ - No page flicker: theme loads synchronously from storage before render
475+
476+ - ** Download Path Configuration** : Custom download location
477+ - Folder picker via native file dialog
478+ - Stores full path in settings
479+ - Auto-creates directory if doesn't exist
480+ - Used by DownloadManager when processing downloads
481+
482+ - ** Notifications Toggle** : Enable/disable app notifications
483+ - Stores preference in settings
484+ - Can be extended for toast notification control
485+
486+ - ** About Section** : App info and links
487+ - GitHub repository link
488+ - App version and description
489+
490+ ** Implementation:**
491+ ``` typescript
492+ export function Settings() {
493+ const [settings, setSettings] = useState <AppSettings >({
494+ theme: " light" ,
495+ enableNotifications: true ,
496+ });
497+
498+ // Load settings on mount
499+ useEffect (() => {
500+ window .electronAPI .settings .load ().then (setSettings );
501+ }, []);
502+
503+ // Auto-save on change
504+ useEffect (() => {
505+ window .electronAPI .settings .save (settings );
506+ }, [settings ]);
507+
508+ // Theme application
509+ useEffect (() => {
510+ if (settings .theme === " system" ) {
511+ const prefersDark = window .matchMedia (" (prefers-color-scheme: dark)" ).matches ;
512+ document .documentElement .classList .toggle (" dark" , prefersDark );
513+ } else {
514+ document .documentElement .classList .toggle (" dark" , settings .theme === " dark" );
515+ }
516+ }, [settings .theme ]);
517+
518+ const handleSelectFolder = async () => {
519+ const path = await window .electronAPI .selectFolder ();
520+ if (path ) {
521+ setSettings (prev => ({ ... prev , downloadPath: path }));
522+ }
523+ };
524+
525+ return (
526+ < div className = " p-6 space-y-6" >
527+ {/* Theme Section */ }
528+ < div className = " space-y-2" >
529+ <Label >Theme < / Label >
530+ < Select value = {settings.theme } onValueChange = {... }>
531+ <SelectTrigger >
532+ < SelectValue / >
533+ < / SelectTrigger >
534+ <SelectContent >
535+ < SelectItem value = " light" > Light < / SelectItem >
536+ < SelectItem value = " dark" > Dark < / SelectItem >
537+ < SelectItem value = " system" > System < / SelectItem >
538+ < / SelectContent >
539+ < / Select >
540+ < / div >
541+
542+ {/* Download Path Section */ }
543+ < div className = " space-y-2" >
544+ <Label >Download Location < / Label >
545+ < div className = " flex gap-2" >
546+ < Input
547+ value = {settings.downloadPath || " " }
548+ readOnly
549+ className = " text-xs"
550+ / >
551+ < Button onClick = {handleSelectFolder } variant = " outline" >
552+ Browse
553+ < / Button >
554+ < / div >
555+ < / div >
556+
557+ {/* Notifications Section */ }
558+ < div className = " flex items-center justify-between" >
559+ <Label >Enable Notifications < / Label >
560+ < Switch
561+ checked = {settings.enableNotifications }
562+ onCheckedChange = {... }
563+ / >
564+ < / div >
565+ < / div >
566+ );
567+ }
568+ ```
569+
570+ ** Type Definition (src/types/globals.d.ts):**
571+ ``` typescript
572+ interface AppSettings {
573+ theme: " light" | " dark" | " system" ;
574+ enableNotifications: boolean ;
575+ downloadPath? : string ;
576+ }
577+
578+ interface ElectronAPI {
579+ settings: {
580+ load: () => Promise <AppSettings >;
581+ save: (settings : AppSettings ) => Promise <void >;
582+ };
583+ selectFolder: () => Promise <string | null >;
584+ // ... other APIs
585+ }
586+ ```
587+
588+ ** Backend Integration:**
589+ - Loads from ` SettingsStore ` (src/main/settingsStore.ts)
590+ - Saves to disk via IPC handler ` loopi:saveSettings `
591+ - Load handler: ` loopi:loadSettings `
592+ - Folder picker handler: ` dialog:selectFolder `
593+ - Download handler re-setup after settings save
594+
595+ ** Dark Theme System:**
596+ The dark theme uses CSS class toggling and Tailwind's dark mode:
597+ ``` css
598+ .dark .react-flow {
599+ background : #1f2937 ;
600+ }
601+
602+ .dark .react-flow__controls {
603+ background : #111827 ;
604+ color : #f3f4f6 ;
605+ }
606+ ```
607+
608+ Tailwind classes automatically respect ` .dark ` class:
609+ ``` jsx
610+ < div className= " bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100" >
611+ Content
612+ < / div>
613+ ```
614+
444615### UI Component Patterns
445616
446617#### Form Fields
0 commit comments