@@ -82,11 +82,22 @@ struct ContentView: View {
8282 @State private var isHoveringHistoryText = false
8383 @State private var isHoveringHistoryPath = false
8484 @State private var isHoveringHistoryArrow = false
85- @State private var colorScheme : ColorScheme = . light // Add state for color scheme
8685 @State private var isHoveringThemeToggle = false // Add state for theme toggle hover
8786 let timer = Timer . publish ( every: 1 , on: . main, in: . common) . autoconnect ( )
8887 let entryHeight : CGFloat = 40
8988
89+ @AppStorage ( " colorScheme " ) private var colorSchemeString : String = " auto "
90+ @Environment ( \. colorScheme) private var systemColorScheme
91+ @State private var showingThemePopover = false
92+ @State private var refreshID = UUID ( )
93+ private var currentColorScheme : ColorScheme {
94+ switch colorSchemeString {
95+ case " light " : return . light
96+ case " dark " : return . dark
97+ default : return systemColorScheme
98+ }
99+ }
100+
90101 let availableFonts = NSFontManager . shared. availableFontFamilies
91102 let standardFonts = [ " Lato-Regular " , " Arial " , " .AppleSystemUIFont " , " Times New Roman " ]
92103 let fontSizes : [ CGFloat ] = [ 16 , 18 , 20 , 22 , 24 , 26 ]
@@ -149,13 +160,6 @@ struct ContentView: View {
149160 Here's my journal entry:
150161 """
151162
152- // Initialize with saved theme preference if available
153- init ( ) {
154- // Load saved color scheme preference
155- let savedScheme = UserDefaults . standard. string ( forKey: " colorScheme " ) ?? " light "
156- _colorScheme = State ( initialValue: savedScheme == " dark " ? . dark : . light)
157- }
158-
159163 // Modify getDocumentsDirectory to use cached value
160164 private func getDocumentsDirectory( ) -> URL {
161165 return documentsDirectory
@@ -351,9 +355,9 @@ struct ContentView: View {
351355
352356 var timerColor : Color {
353357 if timerIsRunning {
354- return isHoveringTimer ? ( colorScheme == . light ? . black : . white) : . gray. opacity ( 0.8 )
358+ return isHoveringTimer ? ( currentColorScheme == . light ? . black : . white) : . gray. opacity ( 0.8 )
355359 } else {
356- return isHoveringTimer ? ( colorScheme == . light ? . black : . white) : ( colorScheme == . light ? . gray : . gray. opacity ( 0.8 ) )
360+ return isHoveringTimer ? ( currentColorScheme == . light ? . black : . white) : ( currentColorScheme == . light ? . gray : . gray. opacity ( 0.8 ) )
357361 }
358362 }
359363
@@ -372,25 +376,24 @@ struct ContentView: View {
372376 return fontSize / 2
373377 }
374378
375- // Add a color utility computed property
376379 var popoverBackgroundColor : Color {
377- return colorScheme == . light ? Color ( NSColor . controlBackgroundColor) : Color ( NSColor . darkGray)
380+ return currentColorScheme == . light ? Color ( NSColor . controlBackgroundColor) : Color ( NSColor . darkGray)
378381 }
379382
380383 var popoverTextColor : Color {
381- return colorScheme == . light ? Color . primary : Color . white
384+ return currentColorScheme == . light ? Color . primary : Color . white
382385 }
383386
384387 var body : some View {
385- let buttonBackground = colorScheme == . light ? Color . white : Color . black
388+ let buttonBackground = currentColorScheme == . light ? Color . white : Color . black
386389 let navHeight : CGFloat = 68
387- let textColor = colorScheme == . light ? Color . gray : Color . gray. opacity ( 0.8 )
388- let textHoverColor = colorScheme == . light ? Color . black : Color . white
390+ let textColor = currentColorScheme == . light ? Color . gray : Color . gray. opacity ( 0.8 )
391+ let textHoverColor = currentColorScheme == . light ? Color . black : Color . white
389392
390393 HStack ( spacing: 0 ) {
391394 // Main content
392395 ZStack {
393- Color ( colorScheme == . light ? . white : . black)
396+ Color ( currentColorScheme == . light ? . white : . black)
394397 . ignoresSafeArea ( )
395398
396399 TextEditor ( text: Binding (
@@ -404,17 +407,16 @@ struct ContentView: View {
404407 }
405408 }
406409 ) )
407- . background ( Color ( colorScheme == . light ? . white : . black) )
408410 . font ( . custom( selectedFont, size: fontSize) )
409- . foregroundColor ( colorScheme == . light ? Color ( red: 0.20 , green: 0.20 , blue: 0.20 ) : Color ( red: 0.9 , green: 0.9 , blue: 0.9 ) )
411+ . foregroundColor ( currentColorScheme == . light ? Color ( red: 0.20 , green: 0.20 , blue: 0.20 ) : Color ( red: 0.9 , green: 0.9 , blue: 0.9 ) )
410412 . scrollContentBackground ( . hidden)
411413 . scrollIndicators ( . never)
412414 . lineSpacing ( lineHeight)
413415 . frame ( maxWidth: 650 )
414- . id ( " \( selectedFont) - \( fontSize) - \( colorScheme ) " )
416+ . id ( " \( selectedFont) - \( fontSize) - \( currentColorScheme ) " )
415417 . padding ( . bottom, bottomNavOpacity > 0 ? navHeight : 0 )
416418 . ignoresSafeArea ( )
417- . colorScheme ( colorScheme )
419+ . colorScheme ( currentColorScheme )
418420 . onAppear {
419421 placeholderText = placeholderOptions. randomElement ( ) ?? " \n \n Begin writing "
420422 // Removed findSubview code which was causing errors
@@ -424,7 +426,7 @@ struct ContentView: View {
424426 if text. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty {
425427 Text ( placeholderText)
426428 . font ( . custom( selectedFont, size: fontSize) )
427- . foregroundColor ( colorScheme == . light ? . gray. opacity ( 0.5 ) : . gray. opacity ( 0.6 ) )
429+ . foregroundColor ( currentColorScheme == . light ? . gray. opacity ( 0.5 ) : . gray. opacity ( 0.6 ) )
428430 // .padding(.top, 8)
429431 // .padding(.leading, 8)
430432 . allowsHitTesting ( false )
@@ -630,7 +632,6 @@ struct ContentView: View {
630632 . frame ( width: 250 )
631633 . padding ( . horizontal, 12 )
632634 . padding ( . vertical, 8 )
633- . background ( popoverBackgroundColor)
634635 . cornerRadius ( 8 )
635636 . shadow ( color: Color . black. opacity ( 0.1 ) , radius: 4 , y: 2 )
636637 } else if text. count < 350 {
@@ -640,7 +641,6 @@ struct ContentView: View {
640641 . frame ( width: 250 )
641642 . padding ( . horizontal, 12 )
642643 . padding ( . vertical, 8 )
643- . background ( popoverBackgroundColor)
644644 . cornerRadius ( 8 )
645645 . shadow ( color: Color . black. opacity ( 0.1 ) , radius: 4 , y: 2 )
646646 } else {
@@ -672,7 +672,6 @@ struct ContentView: View {
672672 . foregroundColor ( popoverTextColor)
673673 }
674674 . frame ( width: 120 )
675- . background ( popoverBackgroundColor)
676675 . cornerRadius ( 8 )
677676 . shadow ( color: Color . black. opacity ( 0.1 ) , radius: 4 , y: 2 )
678677 }
@@ -724,12 +723,13 @@ struct ContentView: View {
724723
725724 // Theme toggle button
726725 Button ( action: {
727- colorScheme = colorScheme == . light ? . dark : . light
728- // Save preference
729- UserDefaults . standard. set ( colorScheme == . light ? " light " : " dark " , forKey: " colorScheme " )
726+ showingThemePopover = true
730727 } ) {
731- Image ( systemName: colorScheme == . light ? " moon.fill " : " sun.max.fill " )
728+ let theme = AppColorScheme ( rawValue: colorSchemeString) ?? . auto
729+ Image ( systemName: theme. systemImage)
730+ . frame ( width: 18 ) // If you remove this, there will be flickering in the width of the bar.
732731 . foregroundColor ( isHoveringThemeToggle ? textHoverColor : textColor)
732+ . animation ( . easeInOut( duration: 0.3 ) , value: colorSchemeString)
733733 }
734734 . buttonStyle ( . plain)
735735 . onHover { hovering in
@@ -741,6 +741,38 @@ struct ContentView: View {
741741 NSCursor . pop ( )
742742 }
743743 }
744+ . popover ( isPresented: $showingThemePopover, attachmentAnchor: . point( UnitPoint ( x: 0.5 , y: 0 ) ) , arrowEdge: . top) {
745+ VStack ( spacing: 0 ) {
746+ ForEach ( [ " light " , " dark " , " auto " ] , id: \. self) { themeValue in
747+ let theme = AppColorScheme ( rawValue: themeValue) ?? . auto
748+ Button ( action: {
749+ withAnimation ( . easeInOut( duration: 0.3 ) ) {
750+ colorSchemeString = themeValue
751+ }
752+ showingThemePopover = false
753+ } ) {
754+ HStack {
755+ Image ( systemName: theme. systemImage)
756+ . frame ( width: 20 )
757+ Text ( theme. displayName)
758+ . frame ( maxWidth: . infinity, alignment: . leading)
759+ }
760+ . padding ( . horizontal, 12 )
761+ . padding ( . vertical, 8 )
762+ . cornerRadius ( 4 )
763+ }
764+ . buttonStyle ( . plain)
765+ . foregroundColor ( popoverTextColor)
766+
767+ if themeValue != " auto " {
768+ Divider ( )
769+ }
770+ }
771+ }
772+ . background ( popoverBackgroundColor)
773+ . cornerRadius ( 8 )
774+ . shadow ( color: Color . black. opacity ( 0.1 ) , radius: 4 , y: 2 )
775+ }
744776
745777 Text ( " • " )
746778 . foregroundColor ( . gray)
@@ -772,7 +804,6 @@ struct ContentView: View {
772804 }
773805 }
774806 . padding ( )
775- . background ( Color ( colorScheme == . light ? . white : . black) )
776807 . opacity ( bottomNavOpacity)
777808 . onHover { hovering in
778809 isHoveringBottomNav = hovering
@@ -861,8 +892,8 @@ struct ContentView: View {
861892 Image ( systemName: " arrow.down.circle " )
862893 . font ( . system( size: 11 ) )
863894 . foregroundColor ( hoveredExportId == entry. id ?
864- ( colorScheme == . light ? . black : . white) :
865- ( colorScheme == . light ? . gray : . gray. opacity ( 0.8 ) ) )
895+ ( currentColorScheme == . light ? . black : . white) :
896+ ( currentColorScheme == . light ? . gray : . gray. opacity ( 0.8 ) ) )
866897 }
867898 . buttonStyle ( . plain)
868899 . help ( " Export entry as PDF " )
@@ -934,12 +965,12 @@ struct ContentView: View {
934965 . scrollIndicators ( . never)
935966 }
936967 . frame ( width: 200 )
937- . background ( Color ( colorScheme == . light ? . white : NSColor . black) )
938968 }
939969 }
940970 . frame ( minWidth: 1100 , minHeight: 600 )
941971 . animation ( . easeInOut( duration: 0.2 ) , value: showingSidebar)
942- . preferredColorScheme ( colorScheme)
972+ . animation ( . easeInOut( duration: 0.3 ) , value: currentColorScheme) // MARK : this sets the light/dark mode transition time
973+ . preferredColorScheme ( currentColorScheme)
943974 . onAppear {
944975 showingSidebar = false // Hide sidebar by default
945976 loadExistingEntries ( )
@@ -1305,4 +1336,4 @@ extension NSView {
13051336
13061337#Preview {
13071338 ContentView ( )
1308- }
1339+ }
0 commit comments