fix(datagrid): scroll long text values in the row inspector - #2373
Merged
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
datlechin
force-pushed
the
fix/inspector-long-text-scroll
branch
from
August 21, 2026 21:05
ea0d147 to
ccc2e40
Compare
datlechin
force-pushed
the
fix/inspector-long-text-scroll
branch
from
August 22, 2026 05:12
ccc2e40 to
0e32aad
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported in chat: "string field in inspector detail pane cannot scrollable when have large string in that field".
What was wrong
Three mechanisms, all in the row-details inspector (right panel, Details tab).
1. The control has no scroll view. SwiftUI's
TextFieldis anNSTextFieldon macOS at everyaxissetting. Dumping theNSViewtree underNSHostingViewforTextField(text:, axis: .vertical).lineLimit(3...6)holding a 400-line value gives:There is no
NSScrollViewand noNSScrolleranywhere in that subtree, solineLimitclips and there is nothing to scroll. Focusing the field installs a field editor 12,800pt tall inside the 109.5pt frame.TextEditorproducesAppKitScrollView->PlatformTextViewwith a liveNSScrollerinstead, and a synthesized wheel event moves it from 0 to 6473.5.2. Read-only removed the last way out.
MultiLineEditorViewandSingleLineEditorViewapplied.disabled(context.isReadOnly), which setsisEnabled = falseon theNSTextField, so a read-only value could not be selected, caret-scrolled or copied.FieldDetailViewhid the entire field menu on the same flag, so there was no Copy Value either. A large value on a query result with no writable table was unreachable by any route.3. The routing asked the column type, not the value.
ColumnType.isLongTextis six exact string comparisons (TEXT, TINYTEXT, MEDIUMTEXT, LONGTEXT, CLOB, NTEXT), so a 40 KB value inVARCHAR(10000),NVARCHAR(MAX),NCLOB,CITEXTor ClickHouse'sNullable(String)got a one-line field. Adding more names cannot fix it:ColumnTypeClassifier.classifycomputesstripWrappers(rawTypeName)for the lookup and then returnsfactory(rawTypeName)with the unstripped name, soNullable(String)is stored literally.What changed
TextValueEditor, a sharedNSViewRepresentableoverNSTextViewin anNSScrollView. Read-only isisEditable = falsewithisSelectable = true, never.disabled.MultiLineEditorViewrenders it insideResizableEditorContainerwith a persisted height underrowInspectorTextFieldHeight, the same shape the JSON field already uses, plus an Open in Window button.FieldEditorResolverpicks the multi-line editor from the value as well as the type: a line break, or more than 80 characters, which is two lines' worth (between 32 and 46 subheadline characters fit one line at the inspector's minimum width).SingleLineEditorViewrenders a read-only value as selectable text rather than a disabled field.FieldMenuContentsplitscanMutatefrom the copy actions, so a read-only field keeps Copy Value and loses Set NULL, Set DEFAULT, Set EMPTY and SQL Functions, which were previously all-or-nothing.TextViewerWindowControllerfor the pop-out, on a newValueViewerWindowControllerextracted from the JSON and PHP controllers, which were near-identical copies of the same window bookkeeping.StartupCommandsEditorandAIRulesEditor, two more hand-rolled copies of the sameNSTextView.scrollableTextView()representable, now useTextValueEditor. Net 101 lines removed across the four.Why not
TextEditorMeasured, not assumed.
TextEditoron macOS 14 leaves AppKit's substitutions on and SwiftUI has no API to reach them. Reading theNSTextViewit produces:.autocorrectionDisabled(true)clears spelling correction alone. A value editor built on it would save a typed straight quote as U+201C and a typed--as U+2014, silently rewriting the stored value..disabled()also leaves the underlyingNSTextVieweditable=trueand first-responder eligible, so there is no clean read-only-but-selectable mode. This is whyStartupCommandsEditorandAIRulesEditoralready hand-rolled the representable, andTextValueEditoris now the one copy.Two choices worth flagging
The editor is picked from the stored value, once per field, not from the live draft. Recomputing it as you type would swap the view type the moment a draft crossed 80 characters, and SwiftUI rebuilds the child on a
switchbranch change, so focus and the caret would go with it. The cost is that pasting a very large value into a field that resolved short keeps the text field for that editing session; it stays selectable, copyable and caret-scrollable, and reading the row back after Save gives it the text view.The pop-out is a window, not an Expand in Sidebar. JSON and PHP have both. The inspector's minimum width is 270pt, and every reference client pushes a large value to a bigger surface rather than a narrow side pane, DataGrip explicitly so. The sidebar-expand state is also a stored column index that a table switch can leave pointing at an unrelated column, and this change does not add a third copy of it.
SourceEditorwas not reused.JsonEditorViewgets scrolling for free fromCodeEditSourceEditor, but that is a code editor: tree-sitter parsing, a gutter and code-shaped key handling, for prose in a 270pt pane. The app's own precedent for plain text is theNSTextViewrepresentable thatStartupCommandsEditorandAIRulesEditoralready had.Why nothing is truncated
The cost of a text view is driven by the longest paragraph, not the total size, so a display cap was considered and rejected. Measured at the inspector's width, layout plus display:
TextField(axis: .vertical)NSTextViewTextKit 1 with
allowsNonContiguousLayoutis not a rescue: it costs 277 ms at 1 MB and 1,420 ms at 5 MB just to set the string, and the same per keystroke. Wrapping one enormous paragraph is a platform limit, so the change shows the whole value and is faster than what shipped at every size rather than hiding the limit behind a custom truncation notice.Before / After
Same query, same steps, both real builds, captured from
InspectorLongTextFieldUITestsdriving the app:SELECT hex(zeroblob(999));against the sample database, one row selected, Details tab open. The value is 1,998 characters and the result set has no writable table, so this is the read-only path the report is about.Before (
dd7b252cf): one greyed line ending in an ellipsis. It is aTextFieldunder.disabled(true), so it takes no first responder: the remaining 1,950 characters cannot be scrolled to, selected or copied, and the field menu was hidden on the same flag so there was no Copy Value either.After: the value wraps in a scrolling text view, in normal text colour rather than the disabled grey, with the drag handle that resizes it and remembers the height, and the pop-out button that opens it in its own window.
The same difference is machine-checkable, which is what the new UI test asserts. Running it against the base build fails with:
26 is the query editor. On the base build the 1,998 character value is in no text view at all; on this branch the assertion passes.
Verified
verify.sh build: PASS.verify.sh testoverFieldEditorResolverTests,ResizableFieldMetricsTests,FieldEditorContextPolicyTests,TextValueEditorDefaultsTests,MultiRowEditStateTests,MultiRowEditStateJsonTests,TableViewCoordinatorPopoverDismissalTests,ColumnTypeTests: 177 cases, 177 passed.verify.sh uitest InspectorLongTextFieldUITests: a 4,000 character value from the sample database must land in a text view, which scrolls, rather than a text field, which clips. The element type is the only thing that separates the fix from the bug, since both report the same accessibility value.swiftlint --strictoverTablePro,TableProTestsandTableProUITests: 0 violations.A review pass over the finished diff found six things, all fixed here and covered by the numbers above. The one that mattered was mine: the new empty-state placeholder reported a stored
''asNULL, which a database client must never do, so it now returns nothing where the stored value really is the empty string. The rest:allowsUndowas left off the text view, so Command Z walked past the field and reached the app's row-edit undo; the always-on hover menu sat on top of the type picker's own chevron; Tab wrote a tab character into the value instead of moving to the next field; the popped-out Text window ignored Escape while the JSON and PHP windows close on it; and a sentence promisingCmd+Finside a field was wrong, because that key is bound toperformFind:, whichNSTextViewdoes not implement, so the sentence is gone.New unit coverage: the value-driven routing rules including
NCLOB,nvarchar(max)andNullable(String), the threshold boundary, JSON and picker columns keeping their own editors, the read-only menu policy, the placeholder never echoing a stored value, and every automatic substitution being off on the text view.