fix(editor): memory leak caused by missing unsubscription from autoUpdate (#13205)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved resource cleanup for floating UI elements and popups to
prevent potential memory leaks and ensure proper disposal when
components are removed or updated.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Cats Juice
2025-07-15 10:27:48 +08:00
committed by GitHub
parent 7cff8091e4
commit 36bd8f645a
4 changed files with 17 additions and 3 deletions
@@ -85,6 +85,8 @@ export class MenuSubMenu extends MenuFocusable {
.catch(err => console.error(err));
});
this.menu.openSubMenu(menu);
// in case that the menu is not closed, but the component is removed,
this.disposables.add(unsub);
}
protected override render(): unknown {
@@ -116,6 +116,7 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
`;
private _cleanup: (() => void) | null = null;
private _autoUpdateCleanup: (() => void) | null = null;
private _prevTool: ToolOptionWithType | null = null;
@@ -128,6 +129,11 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
return [TemplateCard1[theme], TemplateCard2[theme], TemplateCard3[theme]];
}
override connectedCallback() {
super.connectedCallback();
this.disposables.add(() => this._autoUpdateCleanup?.());
}
private _closePanel() {
if (this._openedPanel) {
this._openedPanel.remove();
@@ -175,8 +181,8 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
requestAnimationFrame(() => {
const arrowEl = panel.renderRoot.querySelector('.arrow') as HTMLElement;
autoUpdate(this, panel, () => {
this._autoUpdateCleanup?.();
this._autoUpdateCleanup = autoUpdate(this, panel, () => {
computePosition(this, panel, {
placement: 'top',
middleware: [offset(20), arrow({ element: arrowEl }), shift()],
@@ -160,6 +160,7 @@ export class ChatCopyMore extends WithDisposable(LitElement) {
mainAxis: 0,
crossAxis: -100,
});
this.disposables.add(() => this._morePopper?.dispose());
}
}
}
@@ -54,6 +54,8 @@ export class EdgelessCopilotWidget extends WidgetComponent<RootBlockModel> {
private _selectionModelRect!: DOMRect;
private _autoUpdateCleanup: (() => void) | null = null;
groups: AIItemGroupConfig[] = [];
get gfx() {
@@ -145,7 +147,8 @@ export class EdgelessCopilotWidget extends WidgetComponent<RootBlockModel> {
const originMaxHeight = window.getComputedStyle(panel).maxHeight;
autoUpdate(referenceElement, panel, () => {
this._autoUpdateCleanup?.();
this._autoUpdateCleanup = autoUpdate(referenceElement, panel, () => {
computePosition(referenceElement, panel, {
placement: 'bottom-start',
middleware: [
@@ -267,6 +270,8 @@ export class EdgelessCopilotWidget extends WidgetComponent<RootBlockModel> {
this._copilotPanel = null;
})
);
this._disposables.add(() => this._autoUpdateCleanup?.());
}
determineInsertionBounds(width = 800, height = 95) {