@@ -6,54 +6,55 @@ import MASShortcut
66extension Defaults {
77 static func encoded( ) -> String ? {
88 guard let version = Bundle . main. infoDictionary ? [ " CFBundleVersion " ] as? String else { return nil }
9-
9+
1010 var shortcuts = [ String: Shortcut] ( )
1111 for action in WindowAction . active {
1212 if let masShortcut = ShortcutStore . shortcut ( for: action) {
1313 shortcuts [ action. name] = Shortcut ( masShortcut: masShortcut)
1414 }
1515 }
1616 for defaultsKey in TodoManager . defaultsKeys {
17- let fallback = defaultsKey == TodoManager . toggleDefaultsKey
18- ? MASShortcut ( keyCode: kVK_ANSI_B, modifierFlags: [ . control, . option] )
19- : MASShortcut ( keyCode: kVK_ANSI_N, modifierFlags: [ . control, . option] )
20- if let shortcut = ShortcutStore . shortcut ( forKey: defaultsKey, fallback: fallback) {
21- shortcuts [ defaultsKey] = Shortcut ( masShortcut: shortcut)
22- }
17+ guard let masShortcut = ShortcutStore . shortcut ( forKey: defaultsKey) else { continue }
18+ shortcuts [ defaultsKey] = Shortcut ( masShortcut: masShortcut)
2319 }
24-
20+
2521 var codableDefaults = [ String: CodableDefault] ( )
2622 for exportableDefault in Defaults . array {
2723 codableDefaults [ exportableDefault. key] = exportableDefault. toCodable ( )
2824 }
29-
25+
3026 let config = Config ( bundleId: " com.knollsoft.Rectangle " ,
3127 version: version,
3228 shortcuts: shortcuts,
3329 defaults: codableDefaults)
34-
30+
3531 let encoder = JSONEncoder ( )
3632 encoder. outputFormatting = . prettyPrinted
3733 if #available( macOS 10 . 13 , * ) {
3834 encoder. outputFormatting. update ( with: . sortedKeys)
3935 }
40- return ( try ? encoder. encode ( config) ) . flatMap { String ( data: $0, encoding: . utf8) }
36+ if let encodedJson = try ? encoder. encode ( config) {
37+ if let jsonString = String ( data: encodedJson, encoding: . utf8) {
38+ return jsonString
39+ }
40+ }
41+ return nil
4142 }
42-
43+
4344 static func convert( jsonString: String ) -> Config ? {
4445 guard let jsonData = jsonString. data ( using: . utf8) else { return nil }
4546 let decoder = JSONDecoder ( )
4647 return try ? decoder. decode ( Config . self, from: jsonData)
4748 }
48-
49+
4950 static func load( fileUrl: URL ) {
5051 // Size cap: legitimate configs are ~tens of KB; refuse anything that
5152 // looks abusive (defense against OOM via a giant config file).
5253 if let attrs = try ? FileManager . default. attributesOfItem ( atPath: fileUrl. path) ,
5354 let size = attrs [ . size] as? NSNumber , size. intValue > 1_048_576 {
5455 return
5556 }
56-
57+
5758 guard let jsonString = try ? String ( contentsOf: fileUrl, encoding: . utf8) ,
5859 let config = convert ( jsonString: jsonString) else { return }
5960
@@ -62,7 +63,7 @@ extension Defaults {
6263 availableDefault. load ( from: codedDefault)
6364 }
6465 }
65-
66+
6667 for action in WindowAction . active {
6768 let importedShortcut = config. shortcuts [ action. name] ?? action. aliasName. flatMap { config. shortcuts [ $0] }
6869 if let shortcut = importedShortcut? . toMASSHortcut ( ) {
@@ -74,19 +75,19 @@ extension Defaults {
7475 ShortcutStore . setShortcut ( shortcut, forKey: defaultsKey)
7576 }
7677 }
77-
78+
7879 Notification . Name. configImported. post ( )
7980 Notification . Name. shortcutsChanged. post ( )
8081 }
81-
82+
8283 static func loadFromSupportDir( ) {
8384 if let rectangleSupportURL = getSupportDir ( ) ?
8485 . appendingPathComponent ( " Rectangle " , isDirectory: true ) {
85-
86+
8687 let configURL = rectangleSupportURL. appendingPathComponent ( " RectangleConfig.json " )
87- let fm = FileManager . default
88-
89- if ( try ? configURL . checkResourceIsReachable ( ) ) == true {
88+
89+ let exists = try ? configURL . checkResourceIsReachable ( )
90+ if exists == true {
9091 // Defense-in-depth: any process running as this user can drop
9192 // a RectangleConfig.json in Application Support and have it
9293 // silently applied on next launch, overwriting shortcuts and
@@ -95,8 +96,9 @@ extension Defaults {
9596 // We also refuse symlinks (could redirect reads elsewhere) and
9697 // any file with world-write permission (suggests tampering).
9798 let path = configURL. path
99+ let fm = FileManager . default
98100 var isSafe = true
99-
101+
100102 if let attrs = try ? fm. attributesOfItem ( atPath: path) {
101103 if ( attrs [ . type] as? FileAttributeType ) == . typeSymbolicLink {
102104 isSafe = false
@@ -106,7 +108,7 @@ extension Defaults {
106108 isSafe = false
107109 }
108110 }
109-
111+
110112 guard isSafe else {
111113 AlertUtil . oneButtonAlert (
112114 question: " Refused to load RectangleConfig.json " ,
@@ -115,7 +117,7 @@ extension Defaults {
115117 try ? fm. removeItem ( at: configURL)
116118 return
117119 }
118-
120+
119121 let response = AlertUtil . twoButtonAlert (
120122 question: " Apply Rectangle configuration? " ,
121123 text: " A configuration file was found at \( path) . Applying it will overwrite your current Rectangle shortcuts and preferences. Apply now? " ,
@@ -126,11 +128,11 @@ extension Defaults {
126128 try ? fm. removeItem ( at: configURL)
127129 return
128130 }
129-
131+
130132 load ( fileUrl: configURL)
131133 do {
132134 let newFilename = " RectangleConfig \( timestamp ( ) ) .json "
133-
135+
134136 try fm. moveItem ( atPath: configURL. path, toPath: rectangleSupportURL. appendingPathComponent ( newFilename) . path)
135137 } catch {
136138 do {
@@ -142,11 +144,11 @@ extension Defaults {
142144 }
143145 }
144146 }
145-
147+
146148 private static func getSupportDir( ) -> URL ? {
147149 FileManager . default. urls ( for: . applicationSupportDirectory, in: . userDomainMask) . first
148150 }
149-
151+
150152 private static func timestamp( ) -> String {
151153 let date = Date ( )
152154 let formatter = DateFormatter ( )
0 commit comments