From 8fa00334bb3932e800752719fc0b467f6274147f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Wed, 29 Apr 2026 11:03:11 +0700 Subject: [PATCH 1/4] refactor(datagrid): drop reloadVersion / lastIdentity counter bridge (Phase D) Cell edits, row inserts and removals now drive NSTableView through Delta dispatch directly, so the SwiftUI counter-driven reload path is redundant. Removes: - lastReapplyVersion (already dead) - lastReloadVersion field on TableViewCoordinator - versionChanged branch in reloadAndSyncSelection (delta path already covers this) - reloadVersion field from DataGridIdentity composition - consumeChangedRowIndices on AnyChangeManager / DataChangeManager / StructureChangeManager - changedRowIndices accounting on PendingChanges and StructureChangeManager (14 insert calls and the consume/clear sites) To keep the modified-cell yellow marker correct without the SwiftUI rerun, commitCellEdit and TableViewCoordinator.applyDelta(.cellChanged / .cellsChanged) now call rebuildVisualStateCache before the controller dispatch. This was previously implicit via the versionChanged branch's second reload. Net -259 LOC. Build green, smoke-tested cell edit/undo, add-row, table switch. --- .claude/scheduled_tasks.lock | 1 + .../ChangeTracking/AnyChangeManager.swift | 5 -- .../ChangeTracking/DataChangeManager.swift | 6 -- .../Core/ChangeTracking/PendingChanges.swift | 26 ------- .../StructureChangeManager.swift | 17 ---- .../Views/Results/DataGridCoordinator.swift | 4 +- TablePro/Views/Results/DataGridView.swift | 21 +---- .../Extensions/DataGridView+CellCommit.swift | 9 +-- .../AnyChangeManagerTests.swift | 9 --- .../DataChangeManagerExtendedTests.swift | 15 ---- .../DataChangeManagerTests.swift | 75 ------------------ .../ChangeTracking/PendingChangesTests.swift | 77 ------------------- .../Views/Results/DataGridIdentityTests.swift | 7 -- 13 files changed, 7 insertions(+), 265 deletions(-) create mode 100644 .claude/scheduled_tasks.lock diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 000000000..062453ce7 --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"3b48a3d2-8229-4b5e-bf47-30eab596d1ab","pid":18791,"procStart":"Wed Apr 29 01:03:47 2026","acquiredAt":1777434861839} \ No newline at end of file diff --git a/TablePro/Core/ChangeTracking/AnyChangeManager.swift b/TablePro/Core/ChangeTracking/AnyChangeManager.swift index cfa0c1ac3..200cde5e2 100644 --- a/TablePro/Core/ChangeTracking/AnyChangeManager.swift +++ b/TablePro/Core/ChangeTracking/AnyChangeManager.swift @@ -19,7 +19,6 @@ protocol ChangeManaging: AnyObject { ) func undoRowDeletion(rowIndex: Int) func undoRowInsertion(rowIndex: Int) - func consumeChangedRowIndices() -> Set } @Observable @@ -63,10 +62,6 @@ final class AnyChangeManager { wrapped.undoRowInsertion(rowIndex: rowIndex) } - func consumeChangedRowIndices() -> Set { - wrapped.consumeChangedRowIndices() - } - init(_ manager: any ChangeManaging) { self.wrapped = manager } diff --git a/TablePro/Core/ChangeTracking/DataChangeManager.swift b/TablePro/Core/ChangeTracking/DataChangeManager.swift index c0d12c513..5587136a8 100644 --- a/TablePro/Core/ChangeTracking/DataChangeManager.swift +++ b/TablePro/Core/ChangeTracking/DataChangeManager.swift @@ -78,12 +78,6 @@ final class DataChangeManager: ChangeManaging { undoManager.setActionName(actionName) } - // MARK: - Helper Methods - - func consumeChangedRowIndices() -> Set { - pending.consumeChangedRowIndices() - } - // MARK: - Configuration func clearChanges() { diff --git a/TablePro/Core/ChangeTracking/PendingChanges.swift b/TablePro/Core/ChangeTracking/PendingChanges.swift index 286c25b3c..c1fd6f9b3 100644 --- a/TablePro/Core/ChangeTracking/PendingChanges.swift +++ b/TablePro/Core/ChangeTracking/PendingChanges.swift @@ -17,7 +17,6 @@ struct PendingChanges: Equatable { private(set) var insertedRowIndices: Set = [] private(set) var modifiedCells: [Int: Set] = [:] private(set) var insertedRowData: [Int: [String?]] = [:] - private(set) var changedRowIndices: Set = [] private var changeIndex: [RowChangeKey: Int] = [:] @@ -77,7 +76,6 @@ struct PendingChanges: Equatable { if let insertIdx = changeIndex[RowChangeKey(rowIndex: rowIndex, type: .insert)] { updateInsertedCell(at: insertIdx, columnIndex: columnIndex, columnName: columnName, newValue: newValue) - changedRowIndices.insert(rowIndex) return true } @@ -93,7 +91,6 @@ struct PendingChanges: Equatable { changeIndex[updateKey] = changes.count - 1 modifiedCells[rowIndex, default: []].insert(columnIndex) } - changedRowIndices.insert(rowIndex) return true } @@ -103,7 +100,6 @@ struct PendingChanges: Equatable { modifiedCells.removeValue(forKey: rowIndex) appendChange(RowChange(rowIndex: rowIndex, type: .delete, originalRow: originalRow)) deletedRowIndices.insert(rowIndex) - changedRowIndices.insert(rowIndex) } mutating func recordRowInsertion(rowIndex: Int, values: [String?]) { @@ -114,7 +110,6 @@ struct PendingChanges: Equatable { insertedRowData[rowIndex] = values appendChange(RowChange(rowIndex: rowIndex, type: .insert, cellChanges: [])) insertedRowIndices.insert(rowIndex) - changedRowIndices.insert(rowIndex) } // MARK: - Mutate (cancelling pending edits) @@ -123,7 +118,6 @@ struct PendingChanges: Equatable { guard deletedRowIndices.contains(rowIndex) else { return false } removeChange(rowIndex: rowIndex, type: .delete) deletedRowIndices.remove(rowIndex) - changedRowIndices.insert(rowIndex) return true } @@ -135,7 +129,6 @@ struct PendingChanges: Equatable { insertedRowData.removeValue(forKey: rowIndex) shiftRowIndicesDown(at: rowIndex) - changedRowIndices.insert(rowIndex) return true } @@ -159,7 +152,6 @@ struct PendingChanges: Equatable { removeChange(rowIndex: rowIndex, type: .insert) insertedRowIndices.remove(rowIndex) insertedRowData.removeValue(forKey: rowIndex) - changedRowIndices.insert(rowIndex) } let sortedRemoved = validRows.sorted() @@ -188,7 +180,6 @@ struct PendingChanges: Equatable { modifiedCells.removeValue(forKey: rowIndex) appendChange(RowChange(rowIndex: rowIndex, type: .delete, originalRow: originalRow)) deletedRowIndices.insert(rowIndex) - changedRowIndices.insert(rowIndex) } /// Re-apply a cell edit during undo replay (skips undo registration). @@ -213,7 +204,6 @@ struct PendingChanges: Equatable { if let insertIdx = changeIndex[RowChangeKey(rowIndex: rowIndex, type: .insert)] { updateInsertedCell(at: insertIdx, columnIndex: columnIndex, columnName: columnName, newValue: newValue) - changedRowIndices.insert(rowIndex) return } @@ -229,7 +219,6 @@ struct PendingChanges: Equatable { changeIndex[updateKey] = changes.count - 1 modifiedCells[rowIndex, default: []].insert(columnIndex) } - changedRowIndices.insert(rowIndex) } /// Replace an inserted row's cell value during undo replay (no shift, no undo). @@ -241,7 +230,6 @@ struct PendingChanges: Equatable { ) { guard let insertIdx = changeIndex[RowChangeKey(rowIndex: rowIndex, type: .insert)] else { return } updateInsertedCell(at: insertIdx, columnIndex: columnIndex, columnName: columnName, newValue: newValue) - changedRowIndices.insert(rowIndex) } /// Restore a cell's value during undo replay when an existing change matches. @@ -274,7 +262,6 @@ struct PendingChanges: Equatable { newValue: previousValue ) } - changedRowIndices.insert(rowIndex) } /// Insert a synthetic .insert RowChange for undo replay (e.g., after redoing a deletion's undo). @@ -291,7 +278,6 @@ struct PendingChanges: Equatable { if let savedValues { insertedRowData[rowIndex] = savedValues } - changedRowIndices.insert(rowIndex) } /// Insert a batch of rows (for undo replay of a batch deletion's undo). @@ -314,7 +300,6 @@ struct PendingChanges: Equatable { changes.append(RowChange(rowIndex: rowIndex, type: .insert, cellChanges: cellChanges)) insertedRowIndices.insert(rowIndex) insertedRowData[rowIndex] = values - changedRowIndices.insert(rowIndex) } rebuildChangeIndex() } @@ -338,23 +323,14 @@ struct PendingChanges: Equatable { insertedRowIndices.removeAll() modifiedCells.removeAll() insertedRowData.removeAll() - changedRowIndices.removeAll() } - mutating func consumeChangedRowIndices() -> Set { - let indices = changedRowIndices - changedRowIndices.removeAll() - return indices - } - - /// Replace internal state from a serialized snapshot. mutating func restore(from snapshot: TabChangeSnapshot) { changes = snapshot.changes deletedRowIndices = snapshot.deletedRowIndices insertedRowIndices = snapshot.insertedRowIndices modifiedCells = snapshot.modifiedCells insertedRowData = snapshot.insertedRowData - changedRowIndices = [] rebuildChangeIndex() } @@ -471,7 +447,6 @@ struct PendingChanges: Equatable { if changes[updateIdx].cellChanges.isEmpty { removeChangeAt(updateIdx) } - changedRowIndices.insert(rowIndex) return true } @@ -494,7 +469,6 @@ struct PendingChanges: Equatable { } modifiedCells = newModifiedCells - changedRowIndices = Set(changedRowIndices.map { $0 >= insertionPoint ? $0 + 1 : $0 }) rebuildChangeIndex() } diff --git a/TablePro/Core/SchemaTracking/StructureChangeManager.swift b/TablePro/Core/SchemaTracking/StructureChangeManager.swift index ba968bc5e..65a61d1d7 100644 --- a/TablePro/Core/SchemaTracking/StructureChangeManager.swift +++ b/TablePro/Core/SchemaTracking/StructureChangeManager.swift @@ -18,9 +18,6 @@ final class StructureChangeManager: ChangeManaging { var hasChanges: Bool { !pendingChanges.isEmpty } var reloadVersion: Int = 0 - // Track which rows changed since last reload for granular updates - private(set) var changedRowIndices: Set = [] - // Current state (loaded from database) private(set) var currentColumns: [EditableColumnDefinition] = [] private(set) var currentIndexes: [EditableIndexDefinition] = [] @@ -48,13 +45,6 @@ final class StructureChangeManager: ChangeManaging { var canUndo: Bool { undoManager.canUndo } var canRedo: Bool { undoManager.canRedo } - /// Consume and clear changed row indices (for granular table reloads) - func consumeChangedRowIndices() -> Set { - let indices = changedRowIndices - changedRowIndices.removeAll() - return indices - } - // MARK: - Load Schema func loadSchema( @@ -261,7 +251,6 @@ final class StructureChangeManager: ChangeManaging { pendingChanges[key] = .deleteColumn(column) trackChangeKey(key) if let rowIndex = workingColumns.firstIndex(where: { $0.id == id }) { - changedRowIndices.insert(rowIndex) } } else { let rowIndex = workingColumns.firstIndex(where: { $0.id == id }) @@ -273,7 +262,6 @@ final class StructureChangeManager: ChangeManaging { } if let rowIndex { for i in rowIndex.. var lastIdentity: DataGridIdentity? - var lastReloadVersion: Int = 0 - var lastReapplyVersion: Int = -1 private(set) var cachedRowCount: Int = 0 private(set) var cachedColumnCount: Int = 0 private(set) var enumOrSetColumns: Set = [] @@ -340,6 +338,7 @@ final class TableViewCoordinator: NSObject, NSTableViewDelegate, NSTableViewData guard row >= 0, row < tableView.numberOfRows else { return } guard tableColumn >= 0, tableColumn < tableView.numberOfColumns else { return } invalidateDisplayCache(forDisplayRow: row, column: column) + rebuildVisualStateCache() tableView.reloadData( forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: tableColumn) @@ -359,6 +358,7 @@ final class TableViewCoordinator: NSObject, NSTableViewDelegate, NSTableViewData invalidateDisplayCache(forDisplayRow: position.row, column: position.column) } guard !rowSet.isEmpty, !colSet.isEmpty else { return } + rebuildVisualStateCache() tableView.reloadData(forRowIndexes: rowSet, columnIndexes: colSet) case .rowsInserted(let indices): guard !indices.isEmpty else { return } diff --git a/TablePro/Views/Results/DataGridView.swift b/TablePro/Views/Results/DataGridView.swift index ef07c1329..4b0c877b1 100644 --- a/TablePro/Views/Results/DataGridView.swift +++ b/TablePro/Views/Results/DataGridView.swift @@ -23,7 +23,6 @@ struct RowVisualState { } struct DataGridIdentity: Equatable { - let reloadVersion: Int let schemaVersion: Int let metadataVersion: Int let paginationVersion: Int @@ -35,9 +34,8 @@ struct DataGridIdentity: Equatable { let primaryKeyColumns: [String] let hiddenColumns: Set - init(reloadVersion: Int, schemaVersion: Int, metadataVersion: Int, paginationVersion: Int, + init(schemaVersion: Int, metadataVersion: Int, paginationVersion: Int, rowCount: Int, columnCount: Int, isEditable: Bool, configuration: DataGridConfiguration) { - self.reloadVersion = reloadVersion self.schemaVersion = schemaVersion self.metadataVersion = metadataVersion self.paginationVersion = paginationVersion @@ -233,7 +231,6 @@ struct DataGridView: NSViewRepresentable { let columnCount = latestRows.columns.count let currentIdentity = DataGridIdentity( - reloadVersion: changeManager.reloadVersion, schemaVersion: schemaVersion, metadataVersion: metadataVersion, paginationVersion: paginationVersion, @@ -262,7 +259,6 @@ struct DataGridView: NSViewRepresentable { tableView.usesAlternatingRowBackgroundColors = settings.showAlternateRows } - let versionChanged = coordinator.lastReloadVersion != changeManager.reloadVersion let metadataChanged = previousIdentity.map { $0.metadataVersion != metadataVersion } ?? false let oldRowCount = coordinator.cachedRowCount let oldColumnCount = coordinator.cachedColumnCount @@ -328,7 +324,6 @@ struct DataGridView: NSViewRepresentable { coordinator: coordinator, tableRows: latestRows, needsFullReload: needsFullReload, - versionChanged: versionChanged, metadataChanged: metadataChanged, paginationChanged: paginationChanged ) @@ -502,7 +497,6 @@ struct DataGridView: NSViewRepresentable { coordinator: TableViewCoordinator, tableRows: TableRows, needsFullReload: Bool, - versionChanged: Bool, metadataChanged: Bool = false, paginationChanged: Bool = false ) { @@ -527,21 +521,8 @@ struct DataGridView: NSViewRepresentable { tableView.reloadData(forRowIndexes: visibleRows, columnIndexes: fkColumnIndices) } } - } else if versionChanged { - let changedRows = changeManager.consumeChangedRowIndices() - if changedRows.count > 500 { - tableView.reloadData() - } else if !changedRows.isEmpty { - let rowIndexSet = IndexSet(changedRows) - let columnIndexSet = IndexSet(integersIn: 0.. 0 { tableView.scrollRowToVisible(0) } diff --git a/TablePro/Views/Results/Extensions/DataGridView+CellCommit.swift b/TablePro/Views/Results/Extensions/DataGridView+CellCommit.swift index 7860afda3..559eac04d 100644 --- a/TablePro/Views/Results/Extensions/DataGridView+CellCommit.swift +++ b/TablePro/Views/Results/Extensions/DataGridView+CellCommit.swift @@ -39,15 +39,12 @@ extension TableViewCoordinator { } delegate?.dataGridDidEditCell(row: row, column: columnIndex, newValue: newValue) invalidateDisplayCache() + rebuildVisualStateCache() + let tableColumnIndex = DataGridView.tableColumnIndex(for: columnIndex) if storageRow != nil, case .cellChanged = delta { - let displayDelta: Delta = .cellChanged( - row: row, - column: DataGridView.tableColumnIndex(for: columnIndex) - ) - tableRowsController.apply(displayDelta) + tableRowsController.apply(.cellChanged(row: row, column: tableColumnIndex)) } else { - let tableColumnIndex = DataGridView.tableColumnIndex(for: columnIndex) tableView.reloadData( forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: tableColumnIndex) diff --git a/TableProTests/Core/ChangeTracking/AnyChangeManagerTests.swift b/TableProTests/Core/ChangeTracking/AnyChangeManagerTests.swift index 3a221296a..72e8eceb0 100644 --- a/TableProTests/Core/ChangeTracking/AnyChangeManagerTests.swift +++ b/TableProTests/Core/ChangeTracking/AnyChangeManagerTests.swift @@ -92,15 +92,6 @@ struct AnyChangeManagerTests { #expect(wrapper.isRowDeleted(100) == false) } - @Test("StructureChangeManager wrapper: consumeChangedRowIndices returns empty set") - func structureManagerConsumeChangedRowIndicesEmpty() { - let structureManager = StructureChangeManager() - let wrapper = AnyChangeManager(structureManager) - - let indices = wrapper.consumeChangedRowIndices() - #expect(indices.isEmpty) - } - @Test("StructureChangeManager wrapper: hasChanges forwards correctly when false") func structureManagerHasChangesForwardsFalse() { let structureManager = StructureChangeManager() diff --git a/TableProTests/Core/ChangeTracking/DataChangeManagerExtendedTests.swift b/TableProTests/Core/ChangeTracking/DataChangeManagerExtendedTests.swift index 585d1b3a9..6230c9e75 100644 --- a/TableProTests/Core/ChangeTracking/DataChangeManagerExtendedTests.swift +++ b/TableProTests/Core/ChangeTracking/DataChangeManagerExtendedTests.swift @@ -671,21 +671,6 @@ struct DataChangeManagerExtendedTests { #expect(manager.changes.count == 1) } - @Test("changedRowIndices includes all operation types") - func changedRowIndicesIncludesAllOperationTypes() { - let manager = makeManager() - manager.recordCellChange( - rowIndex: 0, columnIndex: 1, columnName: "name", - oldValue: "Alice", newValue: "Bob" - ) - manager.recordRowDeletion(rowIndex: 1, originalRow: ["2", "Charlie", "c@test.com"]) - manager.recordRowInsertion(rowIndex: 2, values: ["x", "y", "z"]) - let changed = manager.consumeChangedRowIndices() - #expect(changed.contains(0)) - #expect(changed.contains(1)) - #expect(changed.contains(2)) - } - @Test("configureForTable with triggerReload false does not increment reloadVersion") func configureForTableNoTriggerReload() { let manager = DataChangeManager() diff --git a/TableProTests/Core/ChangeTracking/DataChangeManagerTests.swift b/TableProTests/Core/ChangeTracking/DataChangeManagerTests.swift index 86b8d3328..aa70a39f4 100644 --- a/TableProTests/Core/ChangeTracking/DataChangeManagerTests.swift +++ b/TableProTests/Core/ChangeTracking/DataChangeManagerTests.swift @@ -237,26 +237,6 @@ struct DataChangeManagerTests { #expect(manager.changes[1].rowIndex == 1) } - @Test("changedRowIndices contains the changed row") - func changedRowIndicesTracksChanges() async { - let manager = DataChangeManager() - manager.configureForTable( - tableName: "users", - columns: ["id", "name"], - primaryKeyColumns: ["id"] - ) - - manager.recordCellChange( - rowIndex: 5, - columnIndex: 1, - columnName: "name", - oldValue: "Alice", - newValue: "Bob" - ) - - #expect(manager.consumeChangedRowIndices().contains(5)) - } - // MARK: - Row Deletion Tests @Test("Record row deletion makes hasChanges true") @@ -338,61 +318,6 @@ struct DataChangeManagerTests { #expect(manager.hasChanges) } - // MARK: - consumeChangedRowIndices Tests - - @Test("consumeChangedRowIndices returns the set of changed indices") - func consumeReturnsChangedIndices() async { - let manager = DataChangeManager() - manager.configureForTable( - tableName: "users", - columns: ["id", "name"], - primaryKeyColumns: ["id"] - ) - - manager.recordCellChange( - rowIndex: 0, - columnIndex: 1, - columnName: "name", - oldValue: "Alice", - newValue: "Bob" - ) - manager.recordCellChange( - rowIndex: 2, - columnIndex: 1, - columnName: "name", - oldValue: "Charlie", - newValue: "Dave" - ) - - let consumed = manager.consumeChangedRowIndices() - - #expect(consumed.contains(0)) - #expect(consumed.contains(2)) - #expect(consumed.count == 2) - } - - @Test("consumeChangedRowIndices clears indices after consuming") - func consumeClearsIndices() async { - let manager = DataChangeManager() - manager.configureForTable( - tableName: "users", - columns: ["id", "name"], - primaryKeyColumns: ["id"] - ) - - manager.recordCellChange( - rowIndex: 0, - columnIndex: 1, - columnName: "name", - oldValue: "Alice", - newValue: "Bob" - ) - - _ = manager.consumeChangedRowIndices() - - #expect(manager.consumeChangedRowIndices().isEmpty) - } - // MARK: - clearChanges Tests @Test("clearChanges removes all changes") diff --git a/TableProTests/Core/ChangeTracking/PendingChangesTests.swift b/TableProTests/Core/ChangeTracking/PendingChangesTests.swift index b1f2cc2a8..ddcfba70f 100644 --- a/TableProTests/Core/ChangeTracking/PendingChangesTests.swift +++ b/TableProTests/Core/ChangeTracking/PendingChangesTests.swift @@ -220,71 +220,6 @@ struct PendingChangesSnapshotTests { } } -@Suite("PendingChanges - changedRowIndices tracking") -struct PendingChangesChangedRowIndicesTests { - @Test("revertUpdateCell records the row as changed") - func revertUpdateCellMarksChanged() { - var pending = PendingChanges() - pending.recordCellChange( - rowIndex: 4, columnIndex: 1, columnName: "name", - oldValue: "A", newValue: "B" - ) - _ = pending.consumeChangedRowIndices() - - pending.revertUpdateCell( - rowIndex: 4, columnIndex: 1, columnName: "name", previousValue: "A" - ) - #expect(pending.consumeChangedRowIndices().contains(4)) - } - - @Test("undoRowDeletion records the row as changed") - func undoRowDeletionMarksChanged() { - var pending = PendingChanges() - pending.recordRowDeletion(rowIndex: 7, originalRow: ["a"]) - _ = pending.consumeChangedRowIndices() - - _ = pending.undoRowDeletion(rowIndex: 7) - #expect(pending.consumeChangedRowIndices().contains(7)) - } - - @Test("undoRowInsertion records the row as changed") - func undoRowInsertionMarksChanged() { - var pending = PendingChanges() - pending.recordRowInsertion(rowIndex: 2, values: ["x"]) - _ = pending.consumeChangedRowIndices() - - _ = pending.undoRowInsertion(rowIndex: 2) - #expect(pending.consumeChangedRowIndices().contains(2)) - } - - @Test("reapplyRowDeletion records the row as changed") - func reapplyRowDeletionMarksChanged() { - var pending = PendingChanges() - _ = pending.consumeChangedRowIndices() - pending.reapplyRowDeletion(rowIndex: 3, originalRow: ["a"]) - #expect(pending.consumeChangedRowIndices().contains(3)) - } - - @Test("reapplyCellChange records the row as changed") - func reapplyCellChangeMarksChanged() { - var pending = PendingChanges() - _ = pending.consumeChangedRowIndices() - pending.reapplyCellChange( - rowIndex: 5, columnIndex: 1, columnName: "name", - originalDBValue: nil, newValue: "X", originalRow: nil - ) - #expect(pending.consumeChangedRowIndices().contains(5)) - } - - @Test("reinsertRow records the row as changed") - func reinsertRowMarksChanged() { - var pending = PendingChanges() - _ = pending.consumeChangedRowIndices() - pending.reinsertRow(rowIndex: 1, columns: ["a"], savedValues: ["v"]) - #expect(pending.consumeChangedRowIndices().contains(1)) - } -} - @Suite("PendingChanges - clear and consume") struct PendingChangesLifecycleTests { @Test("Clear empties all internal state") @@ -303,16 +238,4 @@ struct PendingChangesLifecycleTests { #expect(!pending.isCellModified(rowIndex: 0, columnIndex: 1)) } - @Test("Consuming changedRowIndices empties the set") - func consumeChangedRows() { - var pending = PendingChanges() - pending.recordCellChange( - rowIndex: 3, columnIndex: 1, columnName: "name", - oldValue: "a", newValue: "b" - ) - let first = pending.consumeChangedRowIndices() - #expect(first == [3]) - let second = pending.consumeChangedRowIndices() - #expect(second.isEmpty) - } } diff --git a/TableProTests/Views/Results/DataGridIdentityTests.swift b/TableProTests/Views/Results/DataGridIdentityTests.swift index d57044fdb..3d6da157a 100644 --- a/TableProTests/Views/Results/DataGridIdentityTests.swift +++ b/TableProTests/Views/Results/DataGridIdentityTests.swift @@ -12,7 +12,6 @@ import Testing @Suite("DataGridIdentity") struct DataGridIdentityTests { private func makeIdentity( - reloadVersion: Int = 1, schemaVersion: Int = 2, metadataVersion: Int = 3, paginationVersion: Int = 0, @@ -30,7 +29,6 @@ struct DataGridIdentityTests { config.primaryKeyColumns = primaryKeyColumns config.hiddenColumns = hiddenColumns return DataGridIdentity( - reloadVersion: reloadVersion, schemaVersion: schemaVersion, metadataVersion: metadataVersion, paginationVersion: paginationVersion, @@ -46,11 +44,6 @@ struct DataGridIdentityTests { #expect(makeIdentity() == makeIdentity()) } - @Test("Different reloadVersion produces unequal identities") - func differentReloadVersion() { - #expect(makeIdentity(reloadVersion: 1) != makeIdentity(reloadVersion: 2)) - } - @Test("Different schemaVersion produces unequal identities") func differentSchemaVersion() { #expect(makeIdentity(schemaVersion: 2) != makeIdentity(schemaVersion: 3)) From 0bd7e81bb94d2ed8a05ea990c40f453ef4a9363e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Wed, 29 Apr 2026 11:03:42 +0700 Subject: [PATCH 2/4] chore: ignore .claude/ runtime files --- .claude/scheduled_tasks.lock | 1 - .gitignore | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 .claude/scheduled_tasks.lock diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock deleted file mode 100644 index 062453ce7..000000000 --- a/.claude/scheduled_tasks.lock +++ /dev/null @@ -1 +0,0 @@ -{"sessionId":"3b48a3d2-8229-4b5e-bf47-30eab596d1ab","pid":18791,"procStart":"Wed Apr 29 01:03:47 2026","acquiredAt":1777434861839} \ No newline at end of file diff --git a/.gitignore b/.gitignore index af9f0762c..b8caba61c 100644 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,4 @@ Libs/*.a Libs/.downloaded Libs/dylibs/ Libs/ios/ +.claude/ From c9803d0c4fb1f1991de17aedde859934c4be4022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Wed, 29 Apr 2026 11:07:02 +0700 Subject: [PATCH 3/4] Revert "chore: ignore .claude/ runtime files" .gitignore line --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index b8caba61c..af9f0762c 100644 --- a/.gitignore +++ b/.gitignore @@ -144,4 +144,3 @@ Libs/*.a Libs/.downloaded Libs/dylibs/ Libs/ios/ -.claude/ From 5992c40788da5bfef2df008292543bc7ab90e78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Wed, 29 Apr 2026 11:11:24 +0700 Subject: [PATCH 4/4] fix(structure): drop empty if-let / for blocks left by Phase D delete sweep The bulk sed deletion of changedRowIndices.insert(...) lines in StructureChangeManager.deleteColumn / deleteIndex / deleteForeignKey left six empty blocks behind: three 'if let rowIndex = workingX.firstIndex(...) { }' blocks where the binding is now unused, and three 'if let rowIndex { for i in rowIndex..