Fix Angular 21 change detection for simulation time, import-csv, connect-database, and ravel-select-horizontal-dim#639
Conversation
Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.10 to 7.5.11. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](isaacs/node-tar@v7.5.10...v7.5.11) --- updated-dependencies: - dependency-name: tar dependency-version: 7.5.11 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
…_PAINT handler - RESTService/addon.cc: Make destroyFrame synchronous on Windows/macOS like renderFrame. The child window is created on the JS thread (via synchronous renderFrame), so destroyFrame must also run on the JS thread to avoid cross-thread SendMessageA calls in ~WindowInformation() that block the minsky thread while holding minskyCmdMutex, causing a deadlock when the JS thread tries to acquire minskyCmdMutex. - model/windowInformation.cc: Add ValidateRect in windowProc WM_PAINT handler when GWLP_USERDATA is 0 to prevent infinite WM_PAINT loop when window is being destroyed. - model/windowInformation.cc: Add SWP_NOACTIVATE flag to SetWindowPos in blit() to prevent unnecessary window activation messages on every paint, reducing message cascade issues with screen sharing tools like Zoom. Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
…SetWindowPos from blit - RESTService/addon.cc: drawNativeWindows() now swaps nativeWindowsToRedraw under minskyCmdMutex then releases the lock before calling draw(), matching the existing macOSXDrawNativeWindows() pattern. Previously holding minskyCmdMutex across the entire draw() call could deadlock: GDI/Cairo operations inside draw() can need the JS thread's message pump while the JS thread is blocked trying to acquire minskyCmdMutex for a synchronous command. - model/windowInformation.cc: Removed SetWindowPos(HWND_TOP,...) from blit(). Calling it on every WM_PAINT fired WM_WINDOWPOSCHANGED messages that could cascade into further WM_PAINT messages and triggered screen-sharing hooks (e.g. Zoom) on every paint, keeping the JS thread's message pump busy and preventing it from returning to the libuv event loop to service minsky thread BlockingCall resolutions. - RESTService/addon.cc: Corrected the comment on synchronous destroyFrame dispatch: the real reason is that Windows requires a window to be destroyed on the same thread that created it, not (as previously stated) that minskyCmdMutex blocks windowProc. - model/windowInformation.cc: Expanded the ValidateRect else-branch comment to clarify when GWLP_USERDATA is zero (WindowInformation already torn down). Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
…ispatch per review feedback - Revert drawNativeWindows() back to holding minskyCmdMutex during draw() as intended: the mutex protects the model from being modified while a view is being redrawn, and releasing it before draw() would break that invariant. - Revert destroyFrame from the synchronous dispatch list: destroyFrame only posts WM_CLOSE to the window and does not fix the underlying deadlock. Only renderFrame needs to be dispatched synchronously on the JS thread. The only change retained in this PR is the removal of SetWindowPos from blit() in windowInformation.cc, which prevents cascaded WM_WINDOWPOSCHANGED/ WM_PAINT messages that kept the JS thread's message pump busy indefinitely. Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
…lockup-issue Fix Windows canvas lockup: remove SetWindowPos from blit()
PlotWidgetSuper inherits from both ItemT<PlotWidget> (via Item) and
RenderNativeWindow (via EventInterface). Both chains define moveTo()
and position(), but classdesc resolves to EventInterface's empty
implementations rather than Item's working ones.
This makes PlotWidget::moveTo() silently no-op via the REST API, and
position() always returns {0,0} — preventing programmatic positioning
of plot widgets.
Add explicit overrides in PlotWidget that delegate to Item::moveTo()
and Item::x()/y(), following the existing disambiguation pattern used
for width() and height().
Fixes tickets:#1918
…nz/minsky into primesoftnz-fix/plotwidget-moveto-position
Bumps [handlebars](https://github.com/handlebars-lang/handlebars.js) from 4.7.8 to 4.7.9. - [Release notes](https://github.com/handlebars-lang/handlebars.js/releases) - [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/v4.7.9/release-notes.md) - [Commits](handlebars-lang/handlebars.js@v4.7.8...v4.7.9) --- updated-dependencies: - dependency-name: handlebars dependency-version: 4.7.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 1.1.13. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](juliangruber/brace-expansion@v1.1.12...v1.1.13) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.3.3 to 1.4.0. - [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md) - [Commits](digitalbazaar/forge@v1.3.3...v1.4.0) --- updated-dependencies: - dependency-name: node-forge dependency-version: 1.4.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
slope/intercepts, rather than the linear interpolation that the standard linear regression operator does.
…d tests Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/6762f0ad-cf41-452b-b6b4-3fc692881ff2 Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
…rmancecoder/minsky into feature-bulk-linear-regression
…-regression Added a "bulk" Linear regression operator that returns a tensor of
…-database, and ravel-select-horizontal-dim components Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/f1045e9e-5e27-4c75-9055-d178ab7ae366 Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
|
@copilot Please rebase this PR to develop branch, not master |
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughConverted simulation time state from plain string properties to Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
gui-js/libs/ui-components/src/lib/header/header.component.ts (1)
81-89: Collapse the two simulation-time subscriptions into one CD trigger.Line 81-89 currently runs
detectChanges()twice per tick (t$thendeltaT$). A combined subscription reduces redundant change detection work.Proposed refactor
-import { Subject, takeUntil } from 'rxjs'; +import { combineLatest, Subject, takeUntil } from 'rxjs'; @@ - this.commService.t$.pipe(takeUntil(this.destroy$)).subscribe((t) => { - this.t = t; - this.changeDetectorRef.detectChanges(); - }); - - this.commService.deltaT$.pipe(takeUntil(this.destroy$)).subscribe((deltaT) => { - this.deltaT = deltaT; - this.changeDetectorRef.detectChanges(); - }); + combineLatest([this.commService.t$, this.commService.deltaT$]) + .pipe(takeUntil(this.destroy$)) + .subscribe(([t, deltaT]) => { + this.t = t; + this.deltaT = deltaT; + this.changeDetectorRef.detectChanges(); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@gui-js/libs/ui-components/src/lib/header/header.component.ts` around lines 81 - 89, The two subscriptions to this.commService.t$ and this.commService.deltaT$ cause detectChanges() to run twice; replace them with a single combined subscription (e.g., combineLatest([this.commService.t$, this.commService.deltaT$]) or an equivalent operator) that pipes takeUntil(this.destroy$), assigns both this.t and this.deltaT inside the subscription callback, and then calls this.changeDetectorRef.detectChanges() once; update references to the existing destroy$, t, deltaT, commService observables and changeDetectorRef so behavior remains identical but with a single CD trigger.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@gui-js/libs/core/src/lib/services/communication/communication.service.ts`:
- Around line 285-291: The run-until check is using the previous observable
value (Number(this.t$.value)) which can overshoot; change the condition to use
the current loop variable t (or Number(t) if t might be a string) when comparing
against runUntilTime so showPlayButton$.next(true) fires immediately; leave the
subsequent updates to this.t$.next(t.toFixed(2)) and
this.deltaT$.next(deltaT.toFixed(2)) as-is.
---
Nitpick comments:
In `@gui-js/libs/ui-components/src/lib/header/header.component.ts`:
- Around line 81-89: The two subscriptions to this.commService.t$ and
this.commService.deltaT$ cause detectChanges() to run twice; replace them with a
single combined subscription (e.g., combineLatest([this.commService.t$,
this.commService.deltaT$]) or an equivalent operator) that pipes
takeUntil(this.destroy$), assigns both this.t and this.deltaT inside the
subscription callback, and then calls this.changeDetectorRef.detectChanges()
once; update references to the existing destroy$, t, deltaT, commService
observables and changeDetectorRef so behavior remains identical but with a
single CD trigger.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f56f932c-c6dc-4f34-a964-a458fc0f0226
📒 Files selected for processing (6)
gui-js/libs/core/src/lib/services/communication/communication.service.tsgui-js/libs/ui-components/src/lib/connect-database/connect-database.component.tsgui-js/libs/ui-components/src/lib/header/header.component.htmlgui-js/libs/ui-components/src/lib/header/header.component.tsgui-js/libs/ui-components/src/lib/import-csv/import-csv.component.tsgui-js/libs/ui-components/src/lib/ravel-select-horizontal-dim/ravel-select-horizontal-dim.component.ts
gui-js/libs/core/src/lib/services/communication/communication.service.ts
Outdated
Show resolved
Hide resolved
…-change-detection # Conflicts: # Doxyversion # doc/version.tex # gui-js/libs/shared/src/lib/constants/version.ts # gui-js/package-lock.json # gui-js/package.json # minskyVersion.h Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Addresses remaining Angular 21 change-detection gaps where UI state wasn’t updating after async operations (simulation time in header, CSV import tab switching, DB table list, and ravel horizontal-dim column list).
Changes:
- Converted
CommunicationServicesimulation time fields from plain strings toBehaviorSubject<string>(t$,deltaT$) and updated the simulation-time update logic to publish via.next(). - Updated Header component/template to subscribe to
t$/deltaT$, store localt/deltaT, and manually trigger change detection. - Added
detectChanges()calls after async UI-state updates in Import CSV, Connect Database, and Ravel Select Horizontal Dim.
Reviewed changes
Copilot reviewed 35 out of 37 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| gui-js/libs/core/src/lib/services/communication/communication.service.ts | Publishes simulation time via BehaviorSubject instead of mutating string fields. |
| gui-js/libs/ui-components/src/lib/header/header.component.ts | Subscribes to simulation time streams and triggers manual change detection. |
| gui-js/libs/ui-components/src/lib/header/header.component.html | Binds to component t / deltaT instead of service fields. |
| gui-js/libs/ui-components/src/lib/import-csv/import-csv.component.ts | Forces CD after async load so selectedTabIndex update renders. |
| gui-js/libs/ui-components/src/lib/connect-database/connect-database.component.ts | Forces CD after async table-name load so dropdown populates. |
| gui-js/libs/ui-components/src/lib/ravel-select-horizontal-dim/ravel-select-horizontal-dim.component.ts | Forces CD after async numerical column-name load so list populates. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
gui-js/libs/core/src/lib/services/communication/communication.service.ts
Outdated
Show resolved
Hide resolved
gui-js/libs/ui-components/src/lib/connect-database/connect-database.component.ts
Show resolved
Hide resolved
Done in b7b8f63. The |
developbranch (merged develop into branch)tanddeltaTtoBehaviorSubject<string>t$anddeltaT$, calldetectChanges()commService.t/deltaTthis.cdr.detectChanges()at end ofload()methodthis.cdRef.detectChanges()at end ofgetTables()developvia PR Fix dialog rendering in 8 components after Angular 20→21 migration #637Original prompt
Problem
After the Angular 20→21 migration, four components still have change detection issues where data doesn't update in the UI after async operations complete:
1. Header Component - Simulation Time Not Updating
File:
gui-js/libs/ui-components/src/lib/header/header.component.html(lines 118-121)The template displays
tanddeltaTvalues directly fromcommService:File:
gui-js/libs/core/src/lib/services/communication/communication.service.ts(lines 284-293)The service updates these values but doesn't trigger change detection:
Root Cause: In Angular 21, the header component needs to manually trigger change detection after
commService.tandcommService.deltaTare updated. The component already hasChangeDetectorRefinjected and uses it for other observables, but not for the simulation time values.Solution: Subscribe to simulation time updates in the header component and call
changeDetectorRef.detectChanges()when they change. ConverttanddeltaTto BehaviorSubjects in the service.2. Import CSV Component - Tab Not Switching After File Load
File:
gui-js/libs/ui-components/src/lib/import-csv/import-csv.component.ts(lines 318-347)The
load()method updatesselectedTabIndexbut doesn't trigger change detection:Root Cause: After all the async operations complete,
selectedTabIndexis set but Angular 21 doesn't detect the change. The component hasChangeDetectorRefinjected but doesn't call it after the async load completes.Solution: Call
this.cdr.detectChanges()at the end of theload()method afterselectedTabIndexis updated.3. Connect Database Component - Table Selector Not Populating
File:
gui-js/libs/ui-components/src/lib/connect-database/connect-database.component.ts(lines 44-47, 60-76)The
getTables()method updatesthis.tablesarray but doesn't trigger change detection:Template:
gui-js/libs/ui-components/src/lib/connect-database/connect-database.html(lines 13-18)Root Cause: After
getTables()completes, thetablesarray is populated but Angular 21 doesn't detect that the array reference changed. The component hasChangeDetectorRefinjected but doesn't use it.Solution: Call
this.cdRef.detectChanges()at the end ofgetTables()method.4. Ravel Select Horizontal Dim Component - Numerical Columns Not Populating
File:
gui-js/libs/ui-components/src/lib/ravel-select-horizontal-dim/ravel-select-horizontal-dim.component.ts(lines 36-38)The
ngOnInit()method populatesdataColsbut doesn't trigger change detection:Template:
gui-js/libs/ui-components/src/lib/ravel-select-horizontal-dim/ravel-select-horizontal-dim.html(lines 5-13)