@@ -67,23 +67,17 @@ final class ThemeModel: ObservableObject {
67
67
}
68
68
}
69
69
70
+ @Published var presentingDetails : Bool = false
71
+
72
+ @Published var detailsTheme : Theme ?
73
+
70
74
/// The selected appearance in the sidebar.
71
75
/// - **0**: dark mode themes
72
76
/// - **1**: light mode themes
73
77
@Published var selectedAppearance : Int = 0
74
78
75
- /// The selected tab in the main section.
76
- /// - **0**: Preview
77
- /// - **1**: Editor
78
- /// - **2**: Terminal
79
- @Published var selectedTab : Int = 1
80
-
81
79
/// An array of loaded ``Theme``.
82
- @Published var themes : [ Theme ] = [ ] {
83
- didSet {
84
- saveThemes ( )
85
- }
86
- }
80
+ @Published var themes : [ Theme ] = [ ]
87
81
88
82
/// The currently selected ``Theme``.
89
83
@Published var selectedTheme : Theme ? {
@@ -244,23 +238,6 @@ final class ThemeModel: ObservableObject {
244
238
}
245
239
}
246
240
247
- /// Removes all overrides of the given theme in
248
- /// `~/Library/Application Support/CodeEdit/settings.json`
249
- ///
250
- /// After removing overrides, themes are reloaded
251
- /// from `~/Library/Application Support/CodeEdit/Themes`. See ``loadThemes()``
252
- /// for more information.
253
- ///
254
- /// - Parameter theme: The theme to reset
255
- func reset( _ theme: Theme ) {
256
- Settings . shared. preferences. theme. overrides [ theme. name] = [ : ]
257
- do {
258
- try self . loadThemes ( )
259
- } catch {
260
- print ( error)
261
- }
262
- }
263
-
264
241
/// Removes the given theme from `–/Library/Application Support/CodeEdit/themes`
265
242
///
266
243
/// After removing the theme, themes are reloaded
@@ -285,51 +262,6 @@ final class ThemeModel: ObservableObject {
285
262
}
286
263
}
287
264
288
- /// Saves changes on theme properties to `overrides`
289
- /// in `~/Library/Application Support/CodeEdit/settings.json`.
290
- private func saveThemes( ) {
291
- let url = themesURL
292
- themes. forEach { theme in
293
- do {
294
- // load the original theme from `~/Library/Application Support/CodeEdit/Themes/`
295
- let originalUrl = url. appendingPathComponent ( theme. name) . appendingPathExtension ( " cetheme " )
296
- let originalData = try Data ( contentsOf: originalUrl)
297
- let originalTheme = try JSONDecoder ( ) . decode ( Theme . self, from: originalData)
298
-
299
- // get properties of the current theme as well as the original
300
- guard let terminalColors = try theme. terminal. allProperties ( ) as? [ String : Theme . Attributes ] ,
301
- let editorColors = try theme. editor. allProperties ( ) as? [ String : Theme . Attributes ] ,
302
- let oTermColors = try originalTheme. terminal. allProperties ( ) as? [ String : Theme . Attributes ] ,
303
- let oEditColors = try originalTheme. editor. allProperties ( ) as? [ String : Theme . Attributes ]
304
- else {
305
- // TODO: Throw a proper error
306
- throw NSError ( ) // swiftlint:disable:this discouraged_direct_init
307
- }
308
-
309
- // compare the properties and if there are differences, save to overrides
310
- // in `settings.json
311
- var newAttr : [ String : [ String : Theme . Attributes ] ] = [ " terminal " : [ : ] , " editor " : [ : ] ]
312
- terminalColors. forEach { ( key, value) in
313
- if value != oTermColors [ key] {
314
- newAttr [ " terminal " ] ? [ key] = value
315
- }
316
- }
317
-
318
- editorColors. forEach { ( key, value) in
319
- if value != oEditColors [ key] {
320
- newAttr [ " editor " ] ? [ key] = value
321
- }
322
- }
323
- DispatchQueue . main. async {
324
- Settings . shared. preferences. theme. overrides [ theme. name] = newAttr
325
- }
326
-
327
- } catch {
328
- print ( error)
329
- }
330
- }
331
- }
332
-
333
265
func importTheme( ) {
334
266
let openPanel = NSOpenPanel ( )
335
267
let allowedTypes = [ UTType ( filenameExtension: " cetheme " ) !]
@@ -349,26 +281,82 @@ final class ThemeModel: ObservableObject {
349
281
}
350
282
}
351
283
284
+ func rename( to newName: String , theme: Theme ) {
285
+ do {
286
+ guard let oldURL = theme. fileURL else {
287
+ throw NSError (
288
+ domain: " ThemeModel " ,
289
+ code: 1 ,
290
+ userInfo: [ NSLocalizedDescriptionKey: " Theme file URL not found " ]
291
+ )
292
+ }
293
+
294
+ var iterator = 1
295
+ var finalName = newName
296
+ var finalURL = themesURL. appendingPathComponent ( finalName) . appendingPathExtension ( " cetheme " )
297
+
298
+ // Check for existing display names in themes
299
+ while themes. contains ( where: { theme != $0 && $0. displayName == finalName } ) {
300
+ finalName = " \( newName) \( iterator) "
301
+ finalURL = themesURL. appendingPathComponent ( finalName) . appendingPathExtension ( " cetheme " )
302
+ iterator += 1
303
+ }
304
+
305
+ try filemanager. moveItem ( at: oldURL, to: finalURL)
306
+
307
+ try self . loadThemes ( )
308
+
309
+ if let index = themes. firstIndex ( where: { $0. fileURL == finalURL } ) {
310
+ themes [ index] . displayName = finalName
311
+ themes [ index] . fileURL = finalURL
312
+ themes [ index] . name = finalName. lowercased ( ) . replacingOccurrences ( of: " " , with: " - " )
313
+ }
314
+
315
+ } catch {
316
+ print ( " Error renaming theme: \( error. localizedDescription) " )
317
+ }
318
+ }
319
+
352
320
func duplicate( _ url: URL ) {
353
321
do {
354
322
// Construct the destination file URL
355
323
var destinationFileURL = self . themesURL. appendingPathComponent ( url. lastPathComponent)
356
324
325
+ // Extract the base filename and extension
326
+ let fileExtension = destinationFileURL. pathExtension
327
+
328
+ var fileName = destinationFileURL. deletingPathExtension ( ) . lastPathComponent
329
+ var newFileName = fileName
330
+
357
331
// Check if the file already exists
358
332
var iterator = 1
333
+
334
+ let isBundled = url. absoluteString. hasPrefix ( bundledThemesURL? . absoluteString ?? " " )
335
+ let isImporting =
336
+ !url. absoluteString. hasPrefix ( bundledThemesURL? . absoluteString ?? " " )
337
+ && !url. absoluteString. hasPrefix ( themesURL. absoluteString)
338
+
339
+ if isBundled {
340
+ newFileName = " \( fileName) \( iterator) "
341
+ destinationFileURL = self . themesURL
342
+ . appendingPathComponent ( newFileName)
343
+ . appendingPathExtension ( fileExtension)
344
+ }
345
+
359
346
while FileManager . default. fileExists ( atPath: destinationFileURL. path) {
360
- // Extract the base filename and extension
361
- let fileExtension = destinationFileURL. pathExtension
362
- var fileName = destinationFileURL. deletingPathExtension ( ) . lastPathComponent
347
+ fileName = destinationFileURL. deletingPathExtension ( ) . lastPathComponent
363
348
364
349
// Remove any existing iterator
365
350
if let range = fileName. range ( of: " \\ d+$ " , options: . regularExpression) {
366
351
fileName = String ( fileName [ ..< range. lowerBound] )
367
352
}
368
353
369
354
// Generate a new filename with an iterator
370
- let newFileName = " \( fileName) \( iterator) "
371
- destinationFileURL = self . themesURL. appendingPathComponent ( newFileName) . appendingPathExtension ( fileExtension)
355
+ newFileName = " \( fileName) \( iterator) "
356
+ destinationFileURL = self . themesURL
357
+ . appendingPathComponent ( newFileName)
358
+ . appendingPathExtension ( fileExtension)
359
+
372
360
iterator += 1
373
361
}
374
362
@@ -377,13 +365,31 @@ final class ThemeModel: ObservableObject {
377
365
378
366
try self . loadThemes ( )
379
367
380
- if var newTheme = self . themes. first ( where: { $0. fileURL == destinationFileURL } ) {
381
- newTheme. name = newTheme. fileURL? . lastPathComponent ?? " "
382
- self . selectedTheme = newTheme
368
+ if var index = self . themes. firstIndex ( where: { $0. fileURL == destinationFileURL } ) {
369
+ self . themes [ index] . displayName = newFileName
370
+ self . themes [ index] . name = newFileName. lowercased ( ) . replacingOccurrences ( of: " " , with: " - " )
371
+ if isImporting != true {
372
+ self . themes [ index] . author = NSFullUserName ( )
373
+ }
374
+ self . selectedTheme = self . themes [ index]
375
+ self . detailsTheme = self . themes [ index]
383
376
}
384
377
} catch {
385
378
print ( " Error adding theme: \( error. localizedDescription) " )
386
379
}
387
380
}
388
381
382
+ /// Save theme to file
383
+ func save( _ theme: Theme ) {
384
+ do {
385
+ if let fileURL = theme. fileURL {
386
+ let data = try JSONEncoder ( ) . encode ( theme)
387
+ let json = try JSONSerialization . jsonObject ( with: data)
388
+ let prettyJSON = try JSONSerialization . data ( withJSONObject: json, options: [ . prettyPrinted] )
389
+ try prettyJSON. write ( to: fileURL, options: . atomic)
390
+ }
391
+ } catch {
392
+ print ( " Error saving theme: \( error. localizedDescription) " )
393
+ }
394
+ }
389
395
}
0 commit comments