Skip to content

fix: resolve reported data grid, editor, and filter UX issues#1773

Open
datlechin wants to merge 2 commits into
mainfrom
fix/ux-batch-datagrid-editor-filters
Open

fix: resolve reported data grid, editor, and filter UX issues#1773
datlechin wants to merge 2 commits into
mainfrom
fix/ux-batch-datagrid-editor-filters

Conversation

@datlechin

Copy link
Copy Markdown
Member

Fixes a batch of UX issues reported from real first-use feedback. Investigated with parallel codebase tracing across the data grid, SQL editor, filter bar, and the bottom toolbar.

Root cause: one bug behind three "stopped working mid-session" symptoms

The dead Columns / + Add buttons, the dead mouse in the SQL editor, and the general "unusable after a while" feeling were all the same bug. The SQL autocomplete popup (SuggestionController in the vendored CodeEditSourceEditor) is a borderless non-activating panel at pop-up-menu level, added as a child window ordered above the main window. Its positioning only flipped the panel above the cursor when it would run off the screen bottom, never the editor's bottom. With the cursor on a lower line and the schema loaded, the panel drew straight down over the status bar and lower editor lines. Being non-activating, clicking it never changed first responder, so it never auto-closed and just ate every click until Esc.

Fixes

Editor / autocomplete

  • Constrain the completion panel to the editor's bounds (flip above the cursor against the editor bottom, not just the screen), so it never overlaps the status bar or sibling chrome.
  • Dismiss the panel on any click outside it, matching native menu/popover behavior.
  • Fix a retain cycle in FindPanelHostingView that leaked an Escape key monitor every time a tab was closed with the Find bar open.
  • Defensive cancelHover() on the jump-to-definition failure path.

Data grid

  • Dragging a column edge to resize no longer triggers a sort (the header mouseDown now bails to pure resize when the click starts in the resize zone).
  • Hidden columns stay hidden after a resize or reorder. The width/order persistence path was replacing the whole layout struct and wiping the hidden-column set; it now merges geometry only.
  • Column widths no longer snap back when the grid reloads, sorts, or paginates right after a resize. Resizes update an in-memory cache immediately; the disk write stays debounced.

Filters

  • SQL errors from filters now appear in a selectable, copyable inline banner above the results instead of a small alert window. The opt-in "Fix with AI" prompt is preserved.
  • Autocomplete no longer pops up on empty space; it only appears while typing a word.
  • Pressing Escape to close the autocomplete no longer also closes the filter bar.

Not changed (by design)

Backspace deleting two characters is CodeEditSourceEditor's smart-edit behavior (delete a full indent unit in leading whitespace, or both halves of an auto-inserted bracket pair), the same as Xcode. Left as-is.

Tests

  • ColumnLayoutStateTests - geometry updates preserve the hidden-column set.
  • FileColumnLayoutPersisterTests - cacheLayout is in-memory only and is not written to disk.
  • FilterValueTextFieldTests - whitespace suppresses completion; Escape grace keeps the filter bar open.

The gesture/positioning fixes (header resize zone, panel geometry) are AppKit-window-bound and verified by hand rather than unit tested.

Notes

  • The autocomplete and Find-panel changes are in the vendored LocalPackages/CodeEditSourceEditor. They lint clean under that package's own config.

https://claude.ai/code/session_0198faM6VCrViRU4XwRoS1DC

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ed9dc8f87

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

onDismiss: { tab.display.activeResultSet?.errorMessage = nil }
onDismiss: {
tab.display.activeResultSet?.errorMessage = nil
tab.execution.errorMessage = nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear execution errors through tab state

When the banner is showing tab.execution.errorMessage, this dismiss handler tries to assign through the QueryTab value captured by resultsSection(tab:). Unlike activeResultSet (a reference type), execution is a stored struct property on an immutable function parameter, so this breaks the build instead of clearing the error; route the dismissal through tabManager.mutate(tabId:) or another mutable tab-state path.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 73bd3840c3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

func fixErrorWithAI(error: String) {
guard let query = lastQueryErrorSQL else { return }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the tab's failed SQL for AI fixes

This reads a coordinator-global lastQueryErrorSQL, which is overwritten by the next failing query in any tab. Because the banner is stored per tab via tab.execution.errorMessage, a user can fail a query in tab A, fail a different query in tab B, then return to tab A and click Fix with AI; the AI receives tab B's SQL paired with tab A's error. Store or pass the failed SQL with the tab/result that owns the banner instead of using a single coordinator field.

Useful? React with 👍 / 👎.

private func executionErrorBanner(tab: QueryTab) -> some View {
if let error = tab.execution.errorMessage,
tab.display.activeResultSet?.errorMessage == nil,
tab.display.resultSets.count <= 1 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Show execution errors when results are pinned

When a tab has a pinned result (or otherwise has more than one result set), this condition suppresses the new execution-level banner. The single-statement error path only sets tab.execution.errorMessage and leaves the existing display.resultSets in place, so after a failed run with pinned results the UI keeps showing the old results and no error banner, while the modal sheet was removed in this commit. The result-set count should not hide execution errors that are not represented by an active result set.

Useful? React with 👍 / 👎.

Comment on lines +103 to +105
let liveWidths = currentColumnWidths()
guard !liveWidths.isEmpty else { return saved }
return (saved ?? ColumnLayoutState()).mergingWidths(liveWidths)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip live-width merging after schema changes

This merges current NSTableView widths even on updates where the result schema changed. In applyStructuralUpdate, rebuildColumnMetadataCache(from:) has already swapped identitySchema to the new columns before this runs, while the table still contains slot-based columns from the previous result (dataColumn-0, etc.), so old widths are attributed to unrelated new column names by position. After resizing a column and running a different SELECT, the new result can inherit stale widths instead of its stored/default layout; only merge live widths when the schema is unchanged, or capture them before replacing the schema.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant