Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- iCloud sync no longer silently substitutes empty defaults when an SSH config, SSL config, jump-host list, or driver-specific field stored in a synced record fails to decode. A device on an older app build that pulled a record written by a newer build would previously end up with a connection missing its SSH/SSL config, then push that empty config back to iCloud and overwrite the authoritative copy. Decode failures now skip the record entirely and log which field failed; the cloud copy stays intact until the device is updated.
- iCloud sync of app settings (general, appearance, editor, data grid, history, tabs, keyboard, AI) no longer silently does nothing when a category's payload fails to decode. Each of the eight category branches previously wrapped the decode in `try?`, so a record written by a newer schema version would fall through with no log, no error, and no UI signal: the user would think their settings synced when they hadn't. Decode failures now skip the category and log which one failed and why.
- Keychain reads no longer collapse a cancelled Touch ID prompt, a failed biometric auth, or any unknown OSStatus into "not found". The `KeychainResult` enum now distinguishes `.userCancelled`, `.authFailed`, and `.error(OSStatus)` from `.notFound`, and the read paths in connection passwords, SSH profile secrets, AI provider keys, and the license key log each case with its own message. Previously a cancelled prompt looked identical to a missing entry, so the caller would treat the password as gone and silently re-save with an empty string on the next write, producing duplicate keychain entries or a connection saved with a blank password.
- Terminal PTY writes retry on `EINTR` instead of treating any non-positive return as "we're done". A signal mid-write previously truncated the input the user typed; the loop would exit silently and the keystrokes were partially sent. The new path retries on `EINTR`, logs the byte position and errno on any other non-recoverable failure, and reports a return value of zero distinctly so the cause is visible in Console.
- Result-grid cells on rows marked for deletion keep their dropdown / date / JSON / blob chevron visible at reduced opacity instead of hiding it, so the cell type is still legible while clearly inactive. Click on the dimmed chevron is a no-op; FK arrow navigation is unchanged. Matches the macOS HIG "disabled appearance" guideline.
- Foreign key navigation from a table with unsaved edits opens the referenced table in a new window tab to preserve the edit buffer. Closing that new tab no longer wipes the original tab's data grid. Previously the new tab's teardown broadcast a connection-scoped event that other coordinators on the same connection received, causing them to release their cell data.
- Tables sidebar refreshes automatically after a successful SQL import; the refresh notification now fires after the success sheet's dismissal animation, so the main window is key when the observer runs (#1114)
Expand Down
23 changes: 18 additions & 5 deletions TablePro/Core/Terminal/TerminalProcessManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import os

@MainActor
final class TerminalProcessManager {
private static let logger = Logger(subsystem: "com.TablePro", category: "TerminalProcessManager")
nonisolated private static let logger = Logger(subsystem: "com.TablePro", category: "TerminalProcessManager")

private let fdLock = NSLock()
nonisolated(unsafe) private var _ptyFD: Int32 = -1
Expand Down Expand Up @@ -90,15 +90,28 @@ final class TerminalProcessManager {
guard !data.isEmpty else { return }
let fd = fdLock.withLock { _ptyFD }
guard fd >= 0 else { return }
let total = data.count
data.withUnsafeBytes { buffer in
guard let ptr = buffer.baseAddress else { return }
var remaining = data.count
var remaining = total
var offset = 0
while remaining > 0 {
let written = Darwin.write(fd, ptr.advanced(by: offset), remaining)
if written <= 0 { break }
offset += written
remaining -= written
if written > 0 {
offset += written
remaining -= written
continue
}
if written == 0 {
Self.logger.error("PTY write returned 0; aborting after \(offset) of \(total) bytes")
return
}
let err = errno
if err == EINTR {
continue
}
Self.logger.error("PTY write failed errno=\(err) after \(offset) of \(total) bytes")
return
}
}
}
Expand Down
Loading