diff --git a/CHANGELOG.md b/CHANGELOG.md index c4b9aaa95..51b82c347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - AI Chat: Gemini provider now round-trips `thoughtSignature` on function calls, fixing the second-round 400 error after a tool runs. - AI Chat: GitHub Copilot tool registration was failing with "Expected string" schema validation errors. Optional fields now register with `type: "string"` instead of `type: ["string", "null"]` and are excluded from `required`, which Copilot's LSP validator accepts. - MySQL/MariaDB: `BIT(N)` columns now display as decimal numbers (`0`, `1`, `255`) instead of raw bytes that showed up as control characters like `^A` in the data grid. (#1272) +- Structure tab: Refresh and ⌘R now show external schema changes immediately. Previously a column renamed from another session stayed stale on the Columns and DDL sub-tabs until the user switched tabs, and the ClickHouse Parts sub-tab ignored Refresh entirely. (#1281) - ClickHouse, BigQuery, CloudflareD1, LibSQL, Etcd, and DynamoDB: long-running queries no longer fail at 30 seconds when Settings > Query timeout is set higher. The HTTP transport now uses the configured query timeout plus a 30-second grace, so the server's `max_execution_time` (or equivalent) fires before the client gives up. Setting "No limit" raises the transport ceiling to 1 hour. (#1267) - AI Chat: DeepSeek V4 thinking content (`reasoning_content`) is now captured during streaming and passed back in subsequent turns, fixing 400 errors when using deepseek-v4-pro or deepseek-v4-flash. - MongoDB: the connection form now shows a Username field. It was hidden for databases where authentication is optional, so connections to auth-enabled servers saved with no credentials and every query failed with "requires authentication" even though the connection looked healthy. diff --git a/TablePro/Views/Results/DataGridUpdateSnapshot.swift b/TablePro/Views/Results/DataGridUpdateSnapshot.swift index 128bdac9e..9540a68c8 100644 --- a/TablePro/Views/Results/DataGridUpdateSnapshot.swift +++ b/TablePro/Views/Results/DataGridUpdateSnapshot.swift @@ -18,4 +18,5 @@ struct DataGridUpdateSnapshot: Equatable { let hasMoveDelegate: Bool let rowHeight: CGFloat let alternatingRows: Bool + let reloadVersion: Int } diff --git a/TablePro/Views/Results/DataGridView.swift b/TablePro/Views/Results/DataGridView.swift index 721d66244..0665159be 100644 --- a/TablePro/Views/Results/DataGridView.swift +++ b/TablePro/Views/Results/DataGridView.swift @@ -165,10 +165,12 @@ struct DataGridView: NSViewRepresentable { isEditable: isEditable, hasMoveDelegate: delegate != nil, rowHeight: rowHeight, - alternatingRows: alternatingRows + alternatingRows: alternatingRows, + reloadVersion: changeManager.reloadVersion ) if snapshot != coordinator.lastUpdateSnapshot { + let contentChanged = snapshot.reloadVersion != coordinator.lastUpdateSnapshot?.reloadVersion applyStructuralUpdate( tableView: tableView, coordinator: coordinator, @@ -177,7 +179,8 @@ struct DataGridView: NSViewRepresentable { columnCount: columnCount, rowHeight: rowHeight, alternatingRows: alternatingRows, - hasMoveDelegate: snapshot.hasMoveDelegate + hasMoveDelegate: snapshot.hasMoveDelegate, + contentChanged: contentChanged ) coordinator.lastUpdateSnapshot = snapshot } @@ -194,7 +197,8 @@ struct DataGridView: NSViewRepresentable { columnCount: Int, rowHeight: CGFloat, alternatingRows: Bool, - hasMoveDelegate: Bool + hasMoveDelegate: Bool, + contentChanged: Bool ) { if let rowNumCol = tableView.tableColumns.first(where: { $0.identifier == ColumnIdentitySchema.rowNumberIdentifier }) { let shouldHide = !configuration.showRowNumbers @@ -231,7 +235,10 @@ struct DataGridView: NSViewRepresentable { let structureChanged = oldRowCount != rowDisplayCount || oldColumnCount != columnCount let schemaChanged = coordinator.rebuildColumnMetadataCache(from: latestRows) - let needsFullReload = structureChanged || schemaChanged + let needsFullReload = structureChanged || schemaChanged || contentChanged + if contentChanged { + coordinator.invalidateDisplayCache() + } if oldRowCount == 0, rowDisplayCount > 0, rowHeight > 0 { let visibleRows = Int(tableView.visibleRect.height / rowHeight) + 5 diff --git a/TablePro/Views/Structure/ClickHousePartsView.swift b/TablePro/Views/Structure/ClickHousePartsView.swift index f4031e2d6..5920a0e47 100644 --- a/TablePro/Views/Structure/ClickHousePartsView.swift +++ b/TablePro/Views/Structure/ClickHousePartsView.swift @@ -51,6 +51,9 @@ struct ClickHousePartsView: View { } } .task { await loadParts() } + .onReceive(AppCommands.shared.refreshData) { _ in + Task { await loadParts() } + } } private var partsToolbar: some View { diff --git a/TablePro/Views/Structure/TableStructureView+DataLoading.swift b/TablePro/Views/Structure/TableStructureView+DataLoading.swift index 5867fa817..028ced7ce 100644 --- a/TablePro/Views/Structure/TableStructureView+DataLoading.swift +++ b/TablePro/Views/Structure/TableStructureView+DataLoading.swift @@ -167,6 +167,7 @@ extension TableStructureView { } private func reloadAllTabs() async { + loadedTabs.removeAll() await loadColumns() await fetchTabData(.indexes) if connection.type.supportsForeignKeys {