Frontmatter
| id | 8809 |
| title | Fix SharedCanvas destroy lifecycle to ensure graph cleanup |
| state | Closed |
| labels | bugairegression |
| assignees | tobiu |
| createdAt | Jan 19, 2026, 1:25 PM |
| updatedAt | Jan 19, 2026, 1:28 PM |
| githubUrl | https://github.com/neomjs/neo/issues/8809 |
| author | tobiu |
| commentsCount | 1 |
| parentIssue | null |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| contentTrust | |
| projected | |
| quarantined | 0 |
| signals | [] |
| blockedBy | [] |
| blocking | [] |
| closedAt | Jan 19, 2026, 1:28 PM |
Fix SharedCanvas destroy lifecycle to ensure graph cleanup

tobiu
Jan 19, 2026, 1:28 PM
Input from Gemini 3 Pro:
✦ Implemented
destroy()override inPortal.view.shared.Canvasto explicitly callrenderer.clearGraph(). This ensures the worker graph is cleaned up even ifoffscreenRegisteredfails to flip to false during destruction (e.g. rapid view replacement).
The
Portal.view.shared.Canvasbase class relies onafterSetOffscreenRegisteredto callclearGraphon the worker renderer. However,offscreenRegisteredis only set tofalseinNeo.component.Canvaswhenmountedchanges tofalse. When a component is destroyed directly (e.g. via route change),mountedis not explicitly set tofalsebefore destruction, sooffscreenRegisteredremainstrue. This leaves the SharedWorker graph in a stale state (activeanimationIdbut detached context). When the component is recreated,waitForCanvassees the staleanimationIdand assumes the loop is running, preventing a restart.Fix: Override
destroy()inPortal.view.shared.Canvasto explicitly callthis.renderer.clearGraph(). This ensures cleanup regardless of the mount state.