← All repositories

listless

A simple list app for Apple platforms

git clone https://code.inqk.net/listless.git


commit 7d831d8ddffddb726ec25e3065572a83884564f4
parent 22d14a88e25a2f69ffc59fda81c7c5b4d8532fa7
Author: Michael Camilleri <[email protected]>
Date:   Tue, 17 Feb 2026 16:10:47 +0900

Fix various bugs

Co-Authored-By: Codex GPT 5.3 <[email protected]>

Diffstat

MListless.xcodeproj/project.pbxproj | 6++++++
MListless/Models/TaskStore.swift | 4++--
MListless/Sync/PersistenceController.swift | 3+--
AListless/Views/AccentColor.swift | 29+++++++++++++++++++++++++++++
MListless/Views/TaskListView.swift | 26++------------------------
MListlessMac/Views/TaskRowView.swift | 30+++---------------------------
MListlessiOS/Views/TaskRowView.swift | 26+-------------------------

7 files changed, 44 insertions(+), 80 deletions(-)

diff --git a/Listless.xcodeproj/project.pbxproj b/Listless.xcodeproj/project.pbxproj

@@ -11,12 +11,14 @@
 		0ACA67F6578EFF181EE5C9A7 /* TaskItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBB8A3BEB346267B30B4675F /* TaskItem.swift */; };
 		15B71073767FB4766A6BA2BE /* HoverCursorModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D41D6E0CED14D79F31C45062 /* HoverCursorModifier.swift */; };
 		1AA328A921EF8A7FDD03119A /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75B048B19C5219862BBED2E7 /* TestHelpers.swift */; };
+		1E3857004DDD888BB4A6ED50 /* AccentColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0630FFEEFF7662A414E9A8 /* AccentColor.swift */; };
 		269B93D5543770B464DFB37A /* TaskStoreOrderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D3A2DDCE24E54ABCCFBBD4C /* TaskStoreOrderingTests.swift */; };
 		3ABE52A15C2059D8D5570528 /* TaskStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3DEE364304587D280C5672 /* TaskStore.swift */; };
 		3D1F551A03B97ECF4E3DC8B0 /* TaskRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E06485DBE35B60868E14202A /* TaskRowView.swift */; };
 		3FCEFB586D9A9085A201AE7D /* TaskListView+PullToCreate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BF73E1B8FE337B76D3E757 /* TaskListView+PullToCreate.swift */; };
 		42E4CDE1D17463554CC4F41F /* TaskListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537A913AC421BAEF60D26D9C /* TaskListView.swift */; };
 		5B60B409CE4BA668DB30A65D /* Listless.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = C093494053E6C348F245D4EC /* Listless.xcdatamodeld */; };
+		5CFFBC8A17E065640857540A /* AccentColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0630FFEEFF7662A414E9A8 /* AccentColor.swift */; };
 		614FCCA450EC0BFFD8B40640 /* ListlessMacApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA467DF2E59BDBE6EEF6A7D /* ListlessMacApp.swift */; };
 		6C252050E62AED3A0A684EBF /* KeyboardNavigationModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7B37EF6A2389656D105FF8 /* KeyboardNavigationModifier.swift */; };
 		731477635D3F4DFF1F78D673 /* AppColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1FE1858BC9E8915A091D33 /* AppColors.swift */; };
@@ -80,6 +82,7 @@
 		74255E6B6C40899E9B17D927 /* .gitkeep */ = {isa = PBXFileReference; path = .gitkeep; sourceTree = "<group>"; };
 		75B048B19C5219862BBED2E7 /* TestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestHelpers.swift; sourceTree = "<group>"; };
 		7C73E9D4C42CCABBF0F33543 /* Listless.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Listless.entitlements; sourceTree = "<group>"; };
+		7F0630FFEEFF7662A414E9A8 /* AccentColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentColor.swift; sourceTree = "<group>"; };
 		81880970CCFC2CB00B65047E /* PlatformTextFieldWidthModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformTextFieldWidthModifier.swift; sourceTree = "<group>"; };
 		82A3509AD32A54434BCC8017 /* HoverCursorModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoverCursorModifier.swift; sourceTree = "<group>"; };
 		9262207DAC21619BD9EDEE15 /* Listless.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Listless.entitlements; sourceTree = "<group>"; };
@@ -114,6 +117,7 @@
 			isa = PBXGroup;
 			children = (
 				D123BB181208FC825777B0A7 /* .gitkeep */,
+				7F0630FFEEFF7662A414E9A8 /* AccentColor.swift */,
 				0E7B37EF6A2389656D105FF8 /* KeyboardNavigationModifier.swift */,
 				537A913AC421BAEF60D26D9C /* TaskListView.swift */,
 			);
@@ -365,6 +369,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				5CFFBC8A17E065640857540A /* AccentColor.swift in Sources */,
 				83378A0575640417ED41FC25 /* ClickableTextField.swift in Sources */,
 				15B71073767FB4766A6BA2BE /* HoverCursorModifier.swift in Sources */,
 				D8EFF49E9156083D675D47F0 /* KeyboardNavigationModifier.swift in Sources */,
@@ -388,6 +393,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				1E3857004DDD888BB4A6ED50 /* AccentColor.swift in Sources */,
 				731477635D3F4DFF1F78D673 /* AppColors.swift in Sources */,
 				FEC96DAA2BF8BB5D6D8504EB /* HoverCursorModifier.swift in Sources */,
 				6C252050E62AED3A0A684EBF /* KeyboardNavigationModifier.swift in Sources */,

diff --git a/Listless/Models/TaskStore.swift b/Listless/Models/TaskStore.swift

@@ -25,9 +25,9 @@ final class TaskStore {
             let activeTasks = allTasks.filter { !$0.isCompleted }
                 .sorted { $0.sortOrder < $1.sortOrder }
 
-            // Completed tasks sorted by updatedAt (most recently completed last)
+            // Completed tasks sorted by updatedAt (most recently completed first)
             let completedTasks = allTasks.filter { $0.isCompleted }
-                .sorted { $0.updatedAt < $1.updatedAt }
+                .sorted { $0.updatedAt > $1.updatedAt }
 
             return activeTasks + completedTasks
         } catch {

diff --git a/Listless/Sync/PersistenceController.swift b/Listless/Sync/PersistenceController.swift

@@ -51,8 +51,7 @@ final class PersistenceController {
             do {
                 try context.save()
             } catch {
-                let nsError = error as NSError
-                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
+                print("Failed to save context: \(error.localizedDescription)")
             }
         }
     }

diff --git a/Listless/Views/AccentColor.swift b/Listless/Views/AccentColor.swift

@@ -0,0 +1,29 @@
+import SwiftUI
+
+func taskColor(forIndex index: Int, total: Int) -> Color {
+    guard total > 1 else { return Color(hue: 0.98, saturation: 0.85, brightness: 1.0) }
+
+    // Gradient matches gradient.png: coral/red → pink/magenta → purple/blue
+    let progress = Double(index) / Double(total - 1)
+    let top    = (h: 0.98, s: 0.85, b: 1.00)
+    let mid    = (h: 0.88, s: 0.75, b: 0.95)
+    let bottom = (h: 0.72, s: 0.65, b: 0.85)
+
+    if progress < 0.5 {
+        return interpolateHSB(from: top, to: mid, progress: progress * 2.0)
+    } else {
+        return interpolateHSB(from: mid, to: bottom, progress: (progress - 0.5) * 2.0)
+    }
+}
+
+private func interpolateHSB(
+    from: (h: Double, s: Double, b: Double),
+    to: (h: Double, s: Double, b: Double),
+    progress: Double
+) -> Color {
+    Color(
+        hue: from.h + (to.h - from.h) * progress,
+        saturation: from.s + (to.s - from.s) * progress,
+        brightness: from.b + (to.b - from.b) * progress
+    )
+}

diff --git a/Listless/Views/TaskListView.swift b/Listless/Views/TaskListView.swift

@@ -202,7 +202,7 @@ struct TaskListView: View {
                 HStack(spacing: 0) {
                     Rectangle()
                         .fill(
-                            accentColor(
+                            taskColor(
                                 forIndex: visualOrder?.firstIndex(of: state.taskID) ?? 0,
                                 total: displayActiveTasks.count
                             )
@@ -506,7 +506,7 @@ struct TaskListView: View {
         }
 
         guard let currentID = selectedTaskID else {
-            selectedTaskID = completedTasks.first?.id ?? activeTasks.first?.id
+            selectedTaskID = activeTasks.first?.id ?? completedTasks.first?.id
             return .handled
         }
 
@@ -771,27 +771,5 @@ struct TaskListView: View {
         visualOrder = nil
     }
 
-    private func accentColor(forIndex index: Int, total: Int) -> Color {
-        guard total > 1 else { return Color(hue: 0.98, saturation: 0.85, brightness: 1.0) }
-        let progress = Double(index) / Double(total - 1)
-        let top    = (h: 0.98, s: 0.85, b: 1.00)
-        let mid    = (h: 0.88, s: 0.75, b: 0.95)
-        let bottom = (h: 0.72, s: 0.65, b: 0.85)
-        if progress < 0.5 {
-            let t = progress * 2.0
-            return Color(
-                hue: top.h + (mid.h - top.h) * t,
-                saturation: top.s + (mid.s - top.s) * t,
-                brightness: top.b + (mid.b - top.b) * t
-            )
-        } else {
-            let t = (progress - 0.5) * 2.0
-            return Color(
-                hue: mid.h + (bottom.h - mid.h) * t,
-                saturation: mid.s + (bottom.s - mid.s) * t,
-                brightness: mid.b + (bottom.b - mid.b) * t
-            )
-        }
-    }
     #endif
 }

diff --git a/ListlessMac/Views/TaskRowView.swift b/ListlessMac/Views/TaskRowView.swift

@@ -29,31 +29,7 @@ struct TaskRowView: View {
 
     private func computeAccentColor() -> Color {
         guard !task.isCompleted else { return .clear }
-        guard totalTasks > 1 else { return Color(hue: 0.98, saturation: 0.85, brightness: 1.0) }
-
-        // Gradient matches gradient.png: coral/red → pink/magenta → purple/blue
-        let progress = Double(index) / Double(totalTasks - 1)
-        let top    = (h: 0.98, s: 0.85, b: 1.00)
-        let mid    = (h: 0.88, s: 0.75, b: 0.95)
-        let bottom = (h: 0.72, s: 0.65, b: 0.85)
-
-        if progress < 0.5 {
-            return interpolateHSB(from: top, to: mid, progress: progress * 2.0)
-        } else {
-            return interpolateHSB(from: mid, to: bottom, progress: (progress - 0.5) * 2.0)
-        }
-    }
-
-    private func interpolateHSB(
-        from: (h: Double, s: Double, b: Double),
-        to: (h: Double, s: Double, b: Double),
-        progress: Double
-    ) -> Color {
-        Color(
-            hue: from.h + (to.h - from.h) * progress,
-            saturation: from.s + (to.s - from.s) * progress,
-            brightness: from.b + (to.b - from.b) * progress
-        )
+        return taskColor(forIndex: index, total: totalTasks)
     }
 
     init(
@@ -213,7 +189,7 @@ struct TaskRowView: View {
     }
 
     private func copyToPasteboard() {
-        let text = isEditing ? editingTitle : task.title
+        let text = isCurrentlyEditing ? editingTitle : task.title
         guard !text.isEmpty else { return }
         let pasteboard = NSPasteboard.general
         pasteboard.clearContents()
@@ -222,7 +198,7 @@ struct TaskRowView: View {
 
     private func pasteFromPasteboard() {
         guard let string = NSPasteboard.general.string(forType: .string) else { return }
-        if isEditing {
+        if isCurrentlyEditing {
             editingTitle = string
         }
         onTitleChange(task, string)

diff --git a/ListlessiOS/Views/TaskRowView.swift b/ListlessiOS/Views/TaskRowView.swift

@@ -164,31 +164,7 @@ struct TaskRowView: View {
 
     private func computeAccentColor() -> Color {
         guard !task.isCompleted else { return .clear }
-        guard totalTasks > 1 else { return Color(hue: 0.98, saturation: 0.85, brightness: 1.0) }
-
-        // Gradient: coral/red → pink/magenta → purple/blue (matches macOS)
-        let progress = Double(index) / Double(totalTasks - 1)
-        let top    = (h: 0.98, s: 0.85, b: 1.00)
-        let mid    = (h: 0.88, s: 0.75, b: 0.95)
-        let bottom = (h: 0.72, s: 0.65, b: 0.85)
-
-        if progress < 0.5 {
-            return interpolateHSB(from: top, to: mid, progress: progress * 2.0)
-        } else {
-            return interpolateHSB(from: mid, to: bottom, progress: (progress - 0.5) * 2.0)
-        }
-    }
-
-    private func interpolateHSB(
-        from: (h: Double, s: Double, b: Double),
-        to: (h: Double, s: Double, b: Double),
-        progress: Double
-    ) -> Color {
-        Color(
-            hue: from.h + (to.h - from.h) * progress,
-            saturation: from.s + (to.s - from.s) * progress,
-            brightness: from.b + (to.b - from.b) * progress
-        )
+        return taskColor(forIndex: index, total: totalTasks)
     }
 
     @ViewBuilder