Summary
Editing a value inside a MySQL json column in the row inspector's JSON field editor makes the app
unresponsive. In one session macOS produced three reports about 2 minutes apart: a CPU report (90 s CPU
over 150 s) and two hang reports (135.74 s and 71.81 s unresponsive). All three samples show the same
main-thread cycle, and the app recovers once it unwinds.
The document is only ~1.6 KB (~40 lines pretty-printed), so this is not a size or parse-latency
issue — it is a layout/update feedback loop that never converges.
Version: 0.75.0 (131) · macOS 15.7.9 (24G830) · arm64 · Team ID D7HJ5TFYCU
Steps to reproduce
- Open a connection to a MySQL database with a table that has a
json column.
- Select a row, open the row inspector, use the JSON field editor.
- Type a character inside the JSON.
Result: the main thread spins; the window reports "not responding" for 1-2 minutes before recovering.
It reproduced on every attempt in that session.
Expected behavior
Typing in the JSON field editor updates the value without entering a layout loop. The editor should not
write observable state from within the SwiftUI update pass, and the displayed form of the value should
round-trip through the binding without re-parsing the document on every keystroke.
Main-thread stack (sanitized excerpts from the three reports)
1. layout/update → the embedded editor's representable update → scroll → bounds notification → hidden-state change
closure #5 in closure #1 in PlatformViewChild.updateValue()
static Update.ensure<A>(_:)
partial apply for closure #1 in perform #1 <A>(work:) in closure #5 in closure #1 in PlatformViewChild.updateValue()
ViewRendererHost.performExternalUpdate(_:)
closure #1 in closure #1 in closure #5 in closure #1 in PlatformViewChild.updateValue()
PlatformViewRepresentableAdaptor.updateViewProvider(_:context:)
??? (TablePro + 29326276)
??? (TablePro + 29325320)
??? (TablePro + 29313988)
??? (TablePro + 29327168)
??? (TablePro + 29334372)
??? (TablePro + 28995552)
-[NSView scrollPoint:]
-[NSClipView _scrollPoint:fromView:]
-[NSClipView _scrollTo:animateScroll:flashScrollerKnobs:]
-[NSScrollView scrollClipView:toPoint:]
-[NSClipView scrollToPoint:]
-[NSClipView _immediateScrollToPoint:]
-[NSView translateOriginToPoint:]
-[NSView _postBoundsChangeNotification]
-[NSNotificationCenter postNotificationName:object:userInfo:]
_CFXNotificationPost
_CFXRegistrationPost
___CFXRegistrationPost_block_invoke
__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__
??? (TablePro + 26618164) <- app observer on the clip view's bounds change
??? (TablePro + 30896648)
??? (TablePro + 30859636)
??? (TablePro + 30674000)
??? (TablePro + 30949460)
??? (TablePro + 30948724)
-[NSView _setHidden:setNeedsDisplay:]
-[NSView _recursiveGainedHiddenAncestorBySwitchingWindow:]
-[NSView didChangeValueForKey:]
2. A SwiftUI onChange handler writes state during the render pass (same sample)
ViewRendererHost.render(interval:updateDisplayList:targetTimestamp:)
static Update.dispatchActions()
Update.Action.callAsFunction()
partial apply for closure #2 in ValueActionDispatcher.updateValue()
_ValueActionModifier.sendAction(old:) <- SwiftUI onChange
??? (TablePro + 12177520)
??? (TablePro + 12174004)
??? (TablePro + 12173808)
Binding.wrappedValue.setter
LocationBox.set(_:transaction:)
FunctionalLocation.set(_:transaction:)
partial apply for closure #1 in Binding.init(get:set:)
??? (TablePro + 11549680)
??? (TablePro + 17285000)
static Published.subscript.modify
static Published.withMutation<A>(of:keyPath:storage:apply:)
closure #1 in static Published.subscript.setter
PublishedSubject.send(_:)
ObservableObjectPublisher.send()
ObservableObjectPublisher.Inner.send()
SubscriptionLifetime.Connection.receive(_:)
AttributeInvalidatingSubscriber<A>.receive(_:)
AttributeInvalidatingSubscriber.invalidateAttribute() (running)
3. The other hang report: the same representable update pass, inside text layout and frame adjustment
PlatformViewRepresentableAdaptor.updateViewProvider(_:context:)
??? (TablePro + 29326276)
??? (TablePro + 29325092)
??? (TablePro + 29338748)
??? (TablePro + 29313988)
??? (TablePro + 29338848)
??? (TablePro + 29000044)
??? (TablePro + 30893076)
??? (TablePro + 30893888)
??? (TablePro + 30698436)
??? (TablePro + 30694776)
??? (TablePro + 30859932)
-[NSView setFrame:]
-[NSView setFrameSize:]
The binary is stripped (the only defined symbol is __mh_execute_header), so these offsets cannot be
resolved to function names from the reports.
Suspected loop in the 0.75.0 source
TablePro/Views/RowInspector/FieldEditors/JsonEditorView.swift:35-64 — both onChange handlers
(propagateEdit(), syncFromBinding()) write context.value / displayText from inside the update
pass, which is what the _ValueActionModifier.sendAction excerpt above shows.
TablePro/Models/UI/MultiRowEditState.swift:304 — resolvePendingValue returns
JsonReindenter.normalize(value) for isJson fields, so the binding stores compact JSON while the
editor displays pretty JSON (reindent). The two sides are only ever compared equal after a full
re-parse of the document, in both onChange handlers, on every keystroke.
Packages/TableProEditor/Sources/TableProEditorKit/SourceEditor/SourceEditor.swift:154 —
updateNSViewController pushes text and the recorded scroll position back into the TextKit view on
every SwiftUI update (applyRepresentableText, and controller.scrollPosition = ..., which scrolls
for real: Controller/TextViewController.swift:189). The resulting clip-view bounds notification
re-runs gutter and floating-inset layout (Controller/TextViewController+Lifecycle.swift:94-116,
Controller/TextViewController+StyleViews.swift:82, Gutter/GutterView.swift:347), which invalidates
layout again, closing the cycle.
Summary
Editing a value inside a MySQL
jsoncolumn in the row inspector's JSON field editor makes the appunresponsive. In one session macOS produced three reports about 2 minutes apart: a CPU report (90 s CPU
over 150 s) and two hang reports (135.74 s and 71.81 s unresponsive). All three samples show the same
main-thread cycle, and the app recovers once it unwinds.
The document is only ~1.6 KB (~40 lines pretty-printed), so this is not a size or parse-latency
issue — it is a layout/update feedback loop that never converges.
Version: 0.75.0 (131) · macOS 15.7.9 (24G830) · arm64 · Team ID D7HJ5TFYCU
Steps to reproduce
jsoncolumn.Result: the main thread spins; the window reports "not responding" for 1-2 minutes before recovering.
It reproduced on every attempt in that session.
Expected behavior
Typing in the JSON field editor updates the value without entering a layout loop. The editor should not
write observable state from within the SwiftUI update pass, and the displayed form of the value should
round-trip through the binding without re-parsing the document on every keystroke.
Main-thread stack (sanitized excerpts from the three reports)
1. layout/update → the embedded editor's representable update → scroll → bounds notification → hidden-state change
2. A SwiftUI onChange handler writes state during the render pass (same sample)
3. The other hang report: the same representable update pass, inside text layout and frame adjustment
The binary is stripped (the only defined symbol is
__mh_execute_header), so these offsets cannot beresolved to function names from the reports.
Suspected loop in the 0.75.0 source
TablePro/Views/RowInspector/FieldEditors/JsonEditorView.swift:35-64— bothonChangehandlers(
propagateEdit(),syncFromBinding()) writecontext.value/displayTextfrom inside the updatepass, which is what the
_ValueActionModifier.sendActionexcerpt above shows.TablePro/Models/UI/MultiRowEditState.swift:304—resolvePendingValuereturnsJsonReindenter.normalize(value)forisJsonfields, so the binding stores compact JSON while theeditor displays pretty JSON (
reindent). The two sides are only ever compared equal after a fullre-parse of the document, in both
onChangehandlers, on every keystroke.Packages/TableProEditor/Sources/TableProEditorKit/SourceEditor/SourceEditor.swift:154—updateNSViewControllerpushes text and the recorded scroll position back into the TextKit view onevery SwiftUI update (
applyRepresentableText, andcontroller.scrollPosition = ..., which scrollsfor real:
Controller/TextViewController.swift:189). The resulting clip-view bounds notificationre-runs gutter and floating-inset layout (
Controller/TextViewController+Lifecycle.swift:94-116,Controller/TextViewController+StyleViews.swift:82,Gutter/GutterView.swift:347), which invalidateslayout again, closing the cycle.