listless

A simple list app for Apple platforms
Log | Files | Refs | README | LICENSE

commit 79287d36d80f9fc71e7e950d8cc439b034eaf7bc
parent 5373c36ceee1b853e64962aa2f0ebb0031352296
Author: Michael Camilleri <[email protected]>
Date:   Sun,  8 Feb 2026 14:14:45 +0900

Use swift-format to format code

Co-Authored-By: Claude 4.5 Sonnet <[email protected]>

Diffstat:
A.swift-format | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MListless/Models/TaskItem.swift | 2+-
MListless/Models/TaskStore.swift | 2+-
MListless/Sync/PersistenceController.swift | 6++++--
MListless/Views/TaskListView.swift | 93++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
MListless/Views/TaskRowView.swift | 21+++++++++++----------
MListlessMac/Views/ClickableTextField.swift | 38+++++++++++++++++++++++++++-----------
MListlessMac/Views/HoverCursorModifier.swift | 2+-
MListlessMac/Views/PlatformScrollIndicatorsModifier.swift | 3++-
MListlessMac/Views/PlatformTextFieldWidthModifier.swift | 3++-
MListlessMac/Views/TaskListView+Toolbar.swift | 3++-
MListlessMac/Views/TaskRowDragGesture.swift | 11++++++-----
MListlessiOS/ColorExtensions.swift | 2+-
MListlessiOS/Views/TaskRowDragGesture.swift | 11++++++-----
14 files changed, 196 insertions(+), 76 deletions(-)

diff --git a/.swift-format b/.swift-format @@ -0,0 +1,75 @@ +{ + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "indentConditionalCompilationBlocks" : true, + "indentSwitchCaseLabels" : false, + "indentation" : { + "spaces" : 4 + }, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : false, + "lineBreakBeforeEachGenericRequirement" : false, + "lineBreakBetweenDeclarationAttributes" : false, + "lineLength" : 100, + "maximumBlankLines" : 1, + "multiElementCollectionTrailingCommas" : true, + "noAssignmentInExpressions" : { + "allowedFunctions" : [ + "XCTAssertNoThrow" + ] + }, + "prioritizeKeepingFunctionOutputTogether" : false, + "reflowMultilineStringLiterals" : "never", + "respectsExistingLineBreaks" : true, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLiteralForEmptyCollectionInit" : false, + "AlwaysUseLowerCamelCase" : true, + "AmbiguousTrailingClosureOverload" : true, + "AvoidRetroactiveConformances" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : false, + "NeverUseForceTry" : false, + "NeverUseImplicitlyUnwrappedOptionals" : false, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyLinesOpeningClosingBraces" : false, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoPlaygroundLiterals" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : false, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "TypeNamesShouldBeCapitalized" : true, + "UseEarlyExits" : false, + "UseExplicitNilCheckInConditions" : true, + "UseLetInEveryBoundCaseVariable" : true, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : true, + "UseSynthesizedInitializer" : true, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : false + }, + "spacesAroundRangeFormationOperators" : false, + "spacesBeforeEndOfLineComments" : 2, + "tabWidth" : 8, + "version" : 1 +} diff --git a/Listless/Models/TaskItem.swift b/Listless/Models/TaskItem.swift @@ -1,5 +1,5 @@ -import Foundation import CoreData +import Foundation @objc(TaskItem) public class TaskItem: NSManagedObject, Identifiable { diff --git a/Listless/Models/TaskStore.swift b/Listless/Models/TaskStore.swift @@ -1,5 +1,5 @@ -import Foundation import CoreData +import Foundation import Observation @MainActor diff --git a/Listless/Sync/PersistenceController.swift b/Listless/Sync/PersistenceController.swift @@ -27,7 +27,8 @@ final class PersistenceController { ) description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) - description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) + description.setOption( + true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) } container.loadPersistentStores { storeDescription, error in @@ -37,7 +38,8 @@ final class PersistenceController { } container.viewContext.automaticallyMergesChangesFromParent = true - container.viewContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType) + container.viewContext.mergePolicy = NSMergePolicy( + merge: .mergeByPropertyObjectTrumpMergePolicyType) performDataMigrationIfNeeded() } diff --git a/Listless/Views/TaskListView.swift b/Listless/Views/TaskListView.swift @@ -61,31 +61,37 @@ struct TaskListView: View { Color.clear .frame(maxHeight: .infinity) .layoutPriority(1) - .dropDestination(for: String.self, action: { _, _ in false }, isTargeted: { isTargeted in - if isTargeted { - updateVisualOrder(insertBefore: task.id) - } - }) + .dropDestination( + for: String.self, action: { _, _ in false }, + isTargeted: { isTargeted in + if isTargeted { + updateVisualOrder(insertBefore: task.id) + } + }) // Middle 2/3 - insert based on direction Color.clear .frame(maxHeight: .infinity) .layoutPriority(4) - .dropDestination(for: String.self, action: { _, _ in false }, isTargeted: { isTargeted in - if isTargeted { - updateVisualOrderSmart(relativeTo: task.id) - } - }) + .dropDestination( + for: String.self, action: { _, _ in false }, + isTargeted: { isTargeted in + if isTargeted { + updateVisualOrderSmart(relativeTo: task.id) + } + }) // Bottom 1/6 - insert AFTER Color.clear .frame(maxHeight: .infinity) .layoutPriority(1) - .dropDestination(for: String.self, action: { _, _ in false }, isTargeted: { isTargeted in - if isTargeted { - updateVisualOrder(insertAfter: task.id) - } - }) + .dropDestination( + for: String.self, action: { _, _ in false }, + isTargeted: { isTargeted in + if isTargeted { + updateVisualOrder(insertAfter: task.id) + } + }) } } } @@ -95,11 +101,13 @@ struct TaskListView: View { if !activeTasks.isEmpty && draggedTaskID != nil { Color.clear .frame(height: 44) - .dropDestination(for: String.self, action: { _, _ in false }, isTargeted: { isTargeted in - if isTargeted { - updateVisualOrder(insertAtEnd: true) - } - }) + .dropDestination( + for: String.self, action: { _, _ in false }, + isTargeted: { isTargeted in + if isTargeted { + updateVisualOrder(insertAtEnd: true) + } + }) } ForEach(completedTasks) { task in @@ -235,7 +243,9 @@ struct TaskListView: View { } private func handleFocusChange(from oldValue: FocusField?, to newValue: FocusField?) { - print("🟣 handleFocusChange() from: \(String(describing: oldValue)) to: \(String(describing: newValue))") + print( + "🟣 handleFocusChange() from: \(String(describing: oldValue)) to: \(String(describing: newValue))" + ) let oldID = taskID(from: oldValue) let newID = taskID(from: newValue) @@ -248,7 +258,7 @@ struct TaskListView: View { } private func taskID(from field: FocusField?) -> UUID? { - guard case let .task(id) = field else { return nil } + guard case .task(let id) = field else { return nil } return id } @@ -274,7 +284,6 @@ struct TaskListView: View { managedObjectContext.undoManager?.enableUndoRegistration() } - private func updateTitle(_ task: TaskItem, _ title: String) { guard task.title != title else { return } store.updateWithoutSaving(taskID: task.id, title: title) @@ -362,7 +371,9 @@ struct TaskListView: View { private func toggleSelectedTask() -> KeyPress.Result { guard focusedField == .scrollView else { return .ignored } guard let currentID = selectedTaskID else { return .handled } - guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else { return .handled } + guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else { + return .handled + } toggleCompletion(task) return .handled } @@ -370,7 +381,9 @@ struct TaskListView: View { private func focusSelectedTask() -> KeyPress.Result { guard focusedField == .scrollView else { return .ignored } guard let currentID = selectedTaskID else { return .handled } - guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else { return .handled } + guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else { + return .handled + } guard !task.isCompleted else { return .handled } startEditing(currentID) return .handled @@ -395,7 +408,6 @@ struct TaskListView: View { return .handled } - // MARK: - Focus Management private func focusTextField(_ taskID: UUID) { @@ -410,14 +422,17 @@ struct TaskListView: View { } private func endEditing(_ taskID: UUID, shouldCreateNewTask: Bool) { - print("🟢 endEditing() called for task \(taskID), shouldCreateNewTask: \(shouldCreateNewTask)") + print( + "🟢 endEditing() called for task \(taskID), shouldCreateNewTask: \(shouldCreateNewTask)") // Save any pending changes store.save() // Check conditions BEFORE deleting the task let wasLastActiveTask = isLastActiveTask(taskID) let willBeDeleted = shouldDeleteIfEmpty(taskID: taskID) - print("🟢 endEditing() wasLastActiveTask: \(wasLastActiveTask), willBeDeleted: \(willBeDeleted)") + print( + "🟢 endEditing() wasLastActiveTask: \(wasLastActiveTask), willBeDeleted: \(willBeDeleted)" + ) if willBeDeleted { print("🟢 endEditing() deleting task - focus will be repaired automatically by onChange") @@ -453,7 +468,8 @@ struct TaskListView: View { private func updateVisualOrder(insertBefore targetID: UUID) { guard let draggedID = draggedTaskID, - let order = visualOrder else { return } + let order = visualOrder + else { return } var newOrder = order.filter { $0 != draggedID } if let targetIndex = newOrder.firstIndex(of: targetID) { @@ -469,7 +485,8 @@ struct TaskListView: View { private func updateVisualOrder(insertAfter targetID: UUID) { guard let draggedID = draggedTaskID, - let order = visualOrder else { return } + let order = visualOrder + else { return } var newOrder = order.filter { $0 != draggedID } if let targetIndex = newOrder.firstIndex(of: targetID) { @@ -485,11 +502,13 @@ struct TaskListView: View { private func updateVisualOrderSmart(relativeTo targetID: UUID) { guard let draggedID = draggedTaskID, - let order = visualOrder else { return } + let order = visualOrder + else { return } // Determine if dragged item is currently above or below target guard let draggedIndex = order.firstIndex(of: draggedID), - let targetIndex = order.firstIndex(of: targetID) else { return } + let targetIndex = order.firstIndex(of: targetID) + else { return } if draggedIndex < targetIndex { // Dragging from above → insert after target @@ -502,7 +521,8 @@ struct TaskListView: View { private func updateVisualOrder(insertAtEnd: Bool) { guard let draggedID = draggedTaskID, - let order = visualOrder else { return } + let order = visualOrder + else { return } var newOrder = order.filter { $0 != draggedID } newOrder.append(draggedID) @@ -516,9 +536,10 @@ struct TaskListView: View { private func handleDrop(items: [String]) -> Bool { guard let droppedUUIDString = items.first, - let droppedUUID = UUID(uuidString: droppedUUIDString), - let order = visualOrder, - let finalIndex = order.firstIndex(of: droppedUUID) else { + let droppedUUID = UUID(uuidString: droppedUUIDString), + let order = visualOrder, + let finalIndex = order.firstIndex(of: droppedUUID) + else { draggedTaskID = nil visualOrder = nil return false diff --git a/Listless/Views/TaskRowView.swift b/Listless/Views/TaskRowView.swift @@ -34,9 +34,9 @@ struct TaskRowView: View { let progress = Double(index) / Double(totalTasks - 1) // Define color stops based on the gradient image - let topColor = Color(hue: 0.98, saturation: 0.85, brightness: 1.0) // Coral/red - let midColor = Color(hue: 0.88, saturation: 0.75, brightness: 0.95) // Pink/magenta - let bottomColor = Color(hue: 0.72, saturation: 0.65, brightness: 0.85) // Purple/blue + let topColor = Color(hue: 0.98, saturation: 0.85, brightness: 1.0) // Coral/red + let midColor = Color(hue: 0.88, saturation: 0.75, brightness: 0.95) // Pink/magenta + let bottomColor = Color(hue: 0.72, saturation: 0.65, brightness: 0.85) // Purple/blue // Interpolate between colors if progress < 0.5 { @@ -123,7 +123,8 @@ struct TaskRowView: View { ) .focused($focusedField, equals: .task(taskID)) .frame(maxWidth: .infinity, alignment: .leading) - .accessibilityIdentifier(isCurrentlyEditing ? "task-textfield" : "task-text-\(task.title)") + .accessibilityIdentifier( + isCurrentlyEditing ? "task-textfield" : "task-text-\(task.title)") } .padding(.vertical, 8) .padding(.horizontal, 16) @@ -209,19 +210,19 @@ struct TaskRowView: View { let text = isEditing ? editingTitle : task.title guard !text.isEmpty else { return } #if os(macOS) - let pasteboard = NSPasteboard.general - pasteboard.clearContents() - pasteboard.setString(text, forType: .string) + let pasteboard = NSPasteboard.general + pasteboard.clearContents() + pasteboard.setString(text, forType: .string) #else - UIPasteboard.general.string = text + UIPasteboard.general.string = text #endif } private func pasteFromPasteboard() { #if os(macOS) - guard let string = NSPasteboard.general.string(forType: .string) else { return } + guard let string = NSPasteboard.general.string(forType: .string) else { return } #else - guard let string = UIPasteboard.general.string else { return } + guard let string = UIPasteboard.general.string else { return } #endif if isEditing { editingTitle = string diff --git a/ListlessMac/Views/ClickableTextField.swift b/ListlessMac/Views/ClickableTextField.swift @@ -1,5 +1,5 @@ -import SwiftUI import AppKit +import SwiftUI /// Custom NSTextField that notifies when clicked (becomes first responder) class ClickableNSTextField: NSTextField { @@ -33,7 +33,7 @@ struct ClickableTextField: NSViewRepresentable { textField.cell?.wraps = true textField.cell?.isScrollable = false textField.isSelectable = true // Shows I-beam cursor on hover - textField.isEditable = true // Always editable, becomes first responder on click + textField.isEditable = true // Always editable, becomes first responder on click // Notify when field is clicked (becomes first responder) textField.onBecomeFirstResponder = { @@ -62,19 +62,24 @@ struct ClickableTextField: NSViewRepresentable { textField.isSelectable = !isCompleted } - func sizeThatFits(_ proposal: ProposedViewSize, nsView: ClickableNSTextField, context: Context) -> CGSize? { + func sizeThatFits(_ proposal: ProposedViewSize, nsView: ClickableNSTextField, context: Context) + -> CGSize? + { let maxWidth = proposal.width ?? 300 let isEditing = nsView.currentEditor() != nil // Always calculate height based on maxWidth to preserve multiline wrapping - let height = calculateHeight(for: text, width: maxWidth, font: nsView.font ?? .systemFont(ofSize: NSFont.systemFontSize)) + let height = calculateHeight( + for: text, width: maxWidth, + font: nsView.font ?? .systemFont(ofSize: NSFont.systemFontSize)) if isEditing { // When editing, take full width return CGSize(width: maxWidth, height: max(height, 22)) } else { // When not editing, size width to content but maintain multiline height - let width = calculateWidth(for: text, font: nsView.font ?? .systemFont(ofSize: NSFont.systemFontSize)) + let width = calculateWidth( + for: text, font: nsView.font ?? .systemFont(ofSize: NSFont.systemFontSize)) return CGSize(width: min(width, maxWidth), height: max(height, 22)) } } @@ -107,7 +112,8 @@ struct ClickableTextField: NSViewRepresentable { ) let textStorage = NSTextStorage(attributedString: attributedString) let layoutManager = NSLayoutManager() - let textContainer = NSTextContainer(size: CGSize(width: width, height: .greatestFiniteMagnitude)) + let textContainer = NSTextContainer( + size: CGSize(width: width, height: .greatestFiniteMagnitude)) textContainer.lineFragmentPadding = 0 layoutManager.addTextContainer(textContainer) @@ -124,7 +130,10 @@ struct ClickableTextField: NSViewRepresentable { let onEditingChanged: (Bool, _ shouldCreateNewTask: Bool) -> Void var editEndReason: EditEndReason = .focusLost - init(text: Binding<String>, onEditingChanged: @escaping (Bool, _ shouldCreateNewTask: Bool) -> Void) { + init( + text: Binding<String>, + onEditingChanged: @escaping (Bool, _ shouldCreateNewTask: Bool) -> Void + ) { _text = text self.onEditingChanged = onEditingChanged } @@ -134,11 +143,14 @@ struct ClickableTextField: NSViewRepresentable { let displayText = text.isEmpty ? "New task" : text let attributes: [NSAttributedString.Key: Any] = [ .font: NSFont.systemFont(ofSize: NSFont.systemFontSize), - .foregroundColor: text.isEmpty ? NSColor.secondaryLabelColor : (isCompleted ? NSColor.secondaryLabelColor : NSColor.labelColor), + .foregroundColor: text.isEmpty + ? NSColor.secondaryLabelColor + : (isCompleted ? NSColor.secondaryLabelColor : NSColor.labelColor), .strikethroughStyle: isCompleted ? NSUnderlineStyle.single.rawValue : 0, - .strikethroughColor: NSColor.secondaryLabelColor + .strikethroughColor: NSColor.secondaryLabelColor, ] - textField.attributedStringValue = NSAttributedString(string: displayText, attributes: attributes) + textField.attributedStringValue = NSAttributedString( + string: displayText, attributes: attributes) } func handleBecomeFirstResponder() { @@ -158,7 +170,11 @@ struct ClickableTextField: NSViewRepresentable { text = textField.stringValue } - func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { + func control( + _ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector + ) + -> Bool + { print("🟡 ClickableTextField.doCommandBy selector: \(commandSelector)") if commandSelector == #selector(NSResponder.insertNewline(_:)) { // Return key pressed diff --git a/ListlessMac/Views/HoverCursorModifier.swift b/ListlessMac/Views/HoverCursorModifier.swift @@ -1,5 +1,5 @@ -import SwiftUI import AppKit +import SwiftUI struct TextHoverModifier: ViewModifier { let isCompleted: Bool diff --git a/ListlessMac/Views/PlatformScrollIndicatorsModifier.swift b/ListlessMac/Views/PlatformScrollIndicatorsModifier.swift @@ -18,7 +18,8 @@ private struct PlatformScrollIndicatorsModifier: ViewModifier { content .background( GeometryReader { contentProxy in - Color.clear.preference(key: ContentHeightKey.self, value: contentProxy.size.height) + Color.clear.preference( + key: ContentHeightKey.self, value: contentProxy.size.height) } ) .scrollIndicators(effectiveContentHeight > proxy.size.height ? .visible : .hidden) diff --git a/ListlessMac/Views/PlatformTextFieldWidthModifier.swift b/ListlessMac/Views/PlatformTextFieldWidthModifier.swift @@ -32,7 +32,8 @@ private struct MacTextFieldWidthModifier: ViewModifier { .fixedSize() .background( GeometryReader { proxy in - Color.clear.preference(key: TextFieldWidthPreferenceKey.self, value: proxy.size.width) + Color.clear.preference( + key: TextFieldWidthPreferenceKey.self, value: proxy.size.width) } ) .hidden() diff --git a/ListlessMac/Views/TaskListView+Toolbar.swift b/ListlessMac/Views/TaskListView+Toolbar.swift @@ -20,7 +20,8 @@ extension TaskListView { Button { if let currentID = selectedTaskID, - let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) { + let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) + { deleteTask(task) } } label: { diff --git a/ListlessMac/Views/TaskRowDragGesture.swift b/ListlessMac/Views/TaskRowDragGesture.swift @@ -7,11 +7,12 @@ extension View { taskID: UUID, onDragStart: @escaping () -> Void ) -> some View { - self.modifier(TaskRowDragGesture( - isActive: isActive, - taskID: taskID, - onDragStart: onDragStart - )) + self.modifier( + TaskRowDragGesture( + isActive: isActive, + taskID: taskID, + onDragStart: onDragStart + )) } } diff --git a/ListlessiOS/ColorExtensions.swift b/ListlessiOS/ColorExtensions.swift @@ -1,5 +1,5 @@ -import UIKit import SwiftUI +import UIKit typealias PlatformColor = UIColor diff --git a/ListlessiOS/Views/TaskRowDragGesture.swift b/ListlessiOS/Views/TaskRowDragGesture.swift @@ -7,11 +7,12 @@ extension View { taskID: UUID, onDragStart: @escaping () -> Void ) -> some View { - self.modifier(TaskRowDragGesture( - isActive: isActive, - taskID: taskID, - onDragStart: onDragStart - )) + self.modifier( + TaskRowDragGesture( + isActive: isActive, + taskID: taskID, + onDragStart: onDragStart + )) } }