Skip to content

Conversation

@aaryan610
Copy link
Member

@aaryan610 aaryan610 commented Dec 22, 2025

Description

This PR refactors table extension's column and row drag handle plugins-

  1. Now, before adding new react renderers, old renderers are destroyed ensuring unnecessary event listeners are removed.
  2. Event listeners are now tracked using refs ensuring safe removal on destroy/unmount.

Type of Change

  • Code refactoring

Note

Refactors table row/column drag handles and plugins to reliably clean up window event listeners and React renderers for better performance and stability.

  • Editor Table Drag Handles (packages/editor/src/core/extensions/table/plugins/drag-handles/*/drag-handle.tsx)
    • Track window mouseup/mousemove handlers via refs; remove on finish/unmount; prevent concurrent drags; add defensive error handling.
    • No UX change; dropdown interactions preserved with Floating UI.
  • Plugins (*/drag-handles/*/plugin.ts)
    • Maintain arrays of ReactRenderer instances; destroy old renderers before recreating and on plugin destroy.
    • Preserve mapped decorations when structure unchanged; add null-safe decorations access in props.

Written by Cursor Bugbot for commit e285ccc. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • Bug Fixes

    • Prevents lingering window event listeners during table row/column drags and ensures handlers are removed on drag finish or component unmount.
  • Performance

    • Preserves and reuses drag-handle renderer instances when possible, reducing unnecessary recreation and memory churn.
  • Stability

    • Adds safer cleanup and error handling to plugin teardown and drag interactions, improving editor robustness and preventing orphaned UI artifacts.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds listener-tracking and cleanup to column and row drag-handle components and introduces renderer lifecycle management (track, destroy, reuse) plus a plugin-level destroy hook for column and row drag-handle plugins.

Changes

Cohort / File(s) Summary
Column drag handle listener cleanup
packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
Adds activeListenersRef (useRef) to store mouseup/mousemove handlers; removes existing listeners before attaching new ones; adds unmount cleanup effect; clears refs after finish.
Row drag handle listener cleanup
packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
Adds persistent activeListenersRef for mouse handlers; removes lingering window listeners on unmount; clears refs when drag finishes; ensures listeners are removed before attaching new ones.
Column plugin renderer lifecycle
packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts
Adds renderers?: ReactRenderer[] to plugin state; destroys old renderers before rebuilding, reuses when possible, creates new renderers per column and stores them; adjusts decorations access and adds plugin destroy() hook with error handling.
Row plugin renderer lifecycle
packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts
Adds renderers?: ReactRenderer[] to plugin state; destroys previous renderers before creating new per-row renderers, reuses when decorations unchanged; adds plugin destroy() hook and safer decorations access.
Formatting-only import split
apps/web/core/components/workspace/confirm-workspace-member-remove.tsx
Splits a combined import into two separate import lines; no behavioral change.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect renderer lifecycle and state-preservation logic in both plugin.ts files for correct destroy/reuse semantics and error handling.
  • Verify plugin destroy() hooks are invoked by the host plugin system and do not leak resources.
  • Validate window event listener attach/detach logic and unmount cleanup in both drag-handle.tsx files to avoid double removals or stale refs.
  • Quick check that the formatting-only change does not affect exports or bundling.

Poem

🐰 I nibble refs and tidy strings of code,
I hop through handlers on the cleanup road.
Renderers counted, listeners set free—
A sprightly table, lean as can be! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '[WIKI-740] refactor: editor table performance' directly corresponds to the PR's main objective of refactoring table drag-handle plugins for performance improvements through better cleanup.
Description check ✅ Passed The description covers the key changes (renderer cleanup and event listener tracking) and includes the required 'Type of Change' section with 'Code refactoring' checked, though 'Test Scenarios' and 'References' sections are missing.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/editor-table-performance

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b4df11 and e285ccc.

📒 Files selected for processing (1)
  • apps/web/core/components/workspace/confirm-workspace-member-remove.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/web/core/components/workspace/confirm-workspace-member-remove.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: check:types
  • GitHub Check: check:lint
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Cursor Bugbot

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@makeplane
Copy link

makeplane bot commented Dec 22, 2025

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves performance and memory management in the table editor's drag handle plugins by implementing proper cleanup of React renderers and event listeners. The changes ensure that old renderers are destroyed before creating new ones and that event listeners attached to the window object are properly tracked and removed.

Key Changes:

  • Added renderer tracking in plugin state to enable cleanup of old renderers before creating new ones
  • Implemented a destroy hook in both row and column plugins to clean up renderers when plugins are destroyed
  • Added useRef-based tracking of window event listeners in drag handle components for cleanup on unmount

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts Adds renderer tracking to plugin state, implements cleanup before creating new renderers, and adds destroy hook for plugin teardown
packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx Adds ref-based tracking of window event listeners and cleanup effect on component unmount
packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts Adds renderer tracking to plugin state, implements cleanup before creating new renderers, and adds destroy hook for plugin teardown
packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx Adds ref-based tracking of window event listeners and cleanup effect on component unmount

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts (1)

109-121: Consider extracting shared cleanup logic.

Both column and row plugins have identical destroy() implementations. Since this is a refactor focused on performance and maintainability, consider extracting this cleanup logic into a shared utility function.

🔎 Example shared utility
// In a shared utils file
export function destroyRenderers(renderers: ReactRenderer[] | undefined): void {
  renderers?.forEach((renderer) => {
    try {
      renderer.destroy();
    } catch (error) {
      console.error("Error destroying renderer:", error);
    }
  });
}

Then in both plugins:

 destroy() {
   const state =
     editor.state &&
     (TABLE_COLUMN_DRAG_HANDLE_PLUGIN_KEY.getState(editor.state) as TableColumnDragHandlePluginState | undefined);
-  state?.renderers?.forEach((renderer: ReactRenderer) => {
-    try {
-      renderer.destroy();
-    } catch (error) {
-      console.error("Error destroying renderer:", error);
-    }
-  });
+  destroyRenderers(state?.renderers);
 },
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57bbf3c and d394951.

📒 Files selected for processing (4)
  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Use const type parameters for more precise literal inference in TypeScript 5.0+
Use the satisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicit is return types in filter/check functions
Use NoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing in switch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacy experimentalDecorators
Use using declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Use with { type: "json" } for import attributes; avoid deprecated assert syntax (TypeScript 5.3/5.8+)
Use import type explicitly when importing types to ensure they are erased during compilation, respecting verbatimModuleSyntax flag
Use .ts, .mts, .cts extensions in import type statements (TypeScript 5.2+)
Use import type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize new Set methods like union, intersection, etc., when available (TypeScript 5.5+)
Use Object.groupBy / Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
Use Promise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted, toSpliced, with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields via super in classes (TypeScript 5....

Files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, run pnpm fix:format

Files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts
🧠 Learnings (3)
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.843Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
📚 Learning: 2025-10-01T15:30:17.605Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7888
File: packages/propel/src/avatar/avatar.stories.tsx:2-3
Timestamp: 2025-10-01T15:30:17.605Z
Learning: In the makeplane/plane repository, avoid suggesting inline type imports (e.g., `import { Avatar, type TAvatarSize }`) due to bundler compatibility issues. Keep type imports and value imports as separate statements.

Applied to files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
🧬 Code graph analysis (2)
packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts (1)
packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts (1)
  • decorations (104-107)
packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts (1)
packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts (1)
  • decorations (104-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Agent
  • GitHub Check: CodeQL analysis (javascript-typescript)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Build packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (8)
packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx (2)

52-70: Good cleanup pattern for window event listeners.

The implementation correctly captures activeListenersRef.current at effect setup time (line 60), which is the right approach for cleanup functions. This ensures the cleanup removes the exact handlers that were registered, even if activeListenersRef.current is modified before unmount.


192-201: Solid defensive implementation.

Storing handler references before attaching listeners (lines 194-196), combined with the try-catch wrapper, provides robust error handling. If an exception occurs during listener setup, handleFinish() is called to clean up markers and state.

packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx (2)

52-70: Consistent cleanup implementation across drag handles.

The cleanup pattern correctly mirrors the row drag handle implementation. Capturing the ref value at setup time ensures reliable cleanup regardless of subsequent state changes.


192-201: LGTM!

The try-catch with fallback to handleFinish() ensures markers are cleaned up even if listener attachment fails unexpectedly.

packages/editor/src/core/extensions/table/plugins/drag-handles/row/plugin.ts (2)

67-74: LGTM!

Properly destroys old renderers before creating new ones, preventing memory leaks. The try-catch ensures a single failing renderer doesn't prevent others from being cleaned up.


109-121: Remove incorrect claim about getState() throwing.

The first concern about editor.state being undefined is valid and properly handled by the code. However, ProseMirror's PluginKey.getState() returns PluginState | undefined and does not throw exceptions—it safely returns undefined if the plugin key has no state. The code's defensive pattern with optional chaining and try-catch wrapping renderer cleanup is appropriate and safe.

packages/editor/src/core/extensions/table/plugins/drag-handles/column/plugin.ts (2)

21-22: Renderer preservation looks correct.

When decorations aren't stale, the existing renderers are preserved (line 63), avoiding unnecessary destruction and recreation. This is a good optimization for performance.

Also applies to: 57-65


67-99: Proper renderer lifecycle management.

The implementation correctly:

  1. Destroys old renderers before creating new ones (lines 68-74)
  2. Creates and tracks new renderers (lines 78, 91)
  3. Returns the new renderers array in state (line 99)

This ensures no orphaned ReactRenderer instances are left behind.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

tableWidth?: number;
tableNodePos?: number;
// track renderers for cleanup
renderers?: ReactRenderer[];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renderers not destroyed when navigating away from table

When a user navigates away from a table (so findTable returns undefined), the apply function returns {} without destroying the renderers tracked in prev.renderers. This causes a memory leak because the ReactRenderer instances and their associated event listeners are never cleaned up. The PR adds renderer tracking for cleanup but misses this code path where prev has renderers that need destruction before returning an empty state.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx (1)

58-70: Simplify cleanup by directly accessing the ref.

The local variable capture of activeListenersRef.current is unnecessary. Since refs maintain the same object reference and the properties are mutated rather than replaced, you can directly access activeListenersRef.current in the cleanup function.

🔎 Proposed simplification
  // Cleanup window event listeners on unmount
  useEffect(() => {
-    const listenersRef = activeListenersRef.current;
    return () => {
      // Remove any lingering window event listeners when component unmounts
-      if (listenersRef.mouseup) {
-        window.removeEventListener("mouseup", listenersRef.mouseup);
+      if (activeListenersRef.current.mouseup) {
+        window.removeEventListener("mouseup", activeListenersRef.current.mouseup);
      }
-      if (listenersRef.mousemove) {
-        window.removeEventListener("mousemove", listenersRef.mousemove);
+      if (activeListenersRef.current.mousemove) {
+        window.removeEventListener("mousemove", activeListenersRef.current.mousemove);
      }
    };
  }, []);
packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx (1)

58-70: Simplify cleanup by directly accessing the ref.

The local variable capture is unnecessary—you can directly access activeListenersRef.current in the cleanup function.

🔎 Proposed simplification
  // Cleanup window event listeners on unmount
  useEffect(() => {
-    const listenersRef = activeListenersRef.current;
    return () => {
      // Remove any lingering window event listeners when component unmounts
-      if (listenersRef.mouseup) {
-        window.removeEventListener("mouseup", listenersRef.mouseup);
+      if (activeListenersRef.current.mouseup) {
+        window.removeEventListener("mouseup", activeListenersRef.current.mouseup);
      }
-      if (listenersRef.mousemove) {
-        window.removeEventListener("mousemove", listenersRef.mousemove);
+      if (activeListenersRef.current.mousemove) {
+        window.removeEventListener("mousemove", activeListenersRef.current.mousemove);
      }
    };
  }, []);
🧹 Nitpick comments (1)
packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx (1)

48-258: Consider extracting shared drag logic.

The column and row drag-handle components share nearly identical listener tracking and cleanup logic. Consider refactoring the common patterns into a shared custom hook (e.g., useDragListeners) or utility to reduce duplication and improve maintainability.

💡 Example approach

Create a shared hook:

function useDragListeners() {
  const activeListenersRef = useRef<{
    mouseup?: (e: MouseEvent) => void;
    mousemove?: (e: MouseEvent) => void;
  }>({});

  useEffect(() => {
    return () => {
      if (activeListenersRef.current.mouseup) {
        window.removeEventListener("mouseup", activeListenersRef.current.mouseup);
      }
      if (activeListenersRef.current.mousemove) {
        window.removeEventListener("mousemove", activeListenersRef.current.mousemove);
      }
    };
  }, []);

  const attachListeners = (mouseup: (e: MouseEvent) => void, mousemove: (e: MouseEvent) => void) => {
    // Remove old listeners
    if (activeListenersRef.current.mouseup) {
      window.removeEventListener("mouseup", activeListenersRef.current.mouseup);
    }
    if (activeListenersRef.current.mousemove) {
      window.removeEventListener("mousemove", activeListenersRef.current.mousemove);
    }
    
    // Store and attach new listeners
    activeListenersRef.current.mouseup = mouseup;
    activeListenersRef.current.mousemove = mousemove;
    window.addEventListener("mouseup", mouseup);
    window.addEventListener("mousemove", mousemove);
  };

  const clearListeners = () => {
    if (activeListenersRef.current.mouseup) {
      window.removeEventListener("mouseup", activeListenersRef.current.mouseup);
    }
    if (activeListenersRef.current.mousemove) {
      window.removeEventListener("mousemove", activeListenersRef.current.mousemove);
    }
    activeListenersRef.current.mouseup = undefined;
    activeListenersRef.current.mousemove = undefined;
  };

  return { attachListeners, clearListeners };
}

Then use it in both components to eliminate duplication.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d394951 and 2b4df11.

📒 Files selected for processing (2)
  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Use const type parameters for more precise literal inference in TypeScript 5.0+
Use the satisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicit is return types in filter/check functions
Use NoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing in switch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacy experimentalDecorators
Use using declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Use with { type: "json" } for import attributes; avoid deprecated assert syntax (TypeScript 5.3/5.8+)
Use import type explicitly when importing types to ensure they are erased during compilation, respecting verbatimModuleSyntax flag
Use .ts, .mts, .cts extensions in import type statements (TypeScript 5.2+)
Use import type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize new Set methods like union, intersection, etc., when available (TypeScript 5.5+)
Use Object.groupBy / Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
Use Promise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted, toSpliced, with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields via super in classes (TypeScript 5....

Files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, run pnpm fix:format

Files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
🧠 Learnings (3)
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.843Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
📚 Learning: 2025-10-01T15:30:17.605Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7888
File: packages/propel/src/avatar/avatar.stories.tsx:2-3
Timestamp: 2025-10-01T15:30:17.605Z
Learning: In the makeplane/plane repository, avoid suggesting inline type imports (e.g., `import { Avatar, type TAvatarSize }`) due to bundler compatibility issues. Keep type imports and value imports as separate statements.

Applied to files:

  • packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx
  • packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Build packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
packages/editor/src/core/extensions/table/plugins/drag-handles/row/drag-handle.tsx (4)

52-56: LGTM!

The activeListenersRef properly tracks window event listeners for cleanup, addressing the memory leak concern from previous reviews.


116-125: Memory leak properly addressed!

The pre-cleanup logic correctly removes existing listeners before adding new ones, preventing multiple simultaneous drag operations and addressing the memory leak concern from previous reviews.


166-168: LGTM!

Clearing the ref after removing listeners ensures no stale references remain for the next drag operation.


204-206: LGTM!

Storing listener references before attaching them enables proper cleanup in all scenarios (rapid clicks, errors, unmount).

packages/editor/src/core/extensions/table/plugins/drag-handles/column/drag-handle.tsx (2)

52-56: LGTM!

The activeListenersRef properly tracks window event listeners, mirroring the cleanup strategy in the row drag-handle.


116-125: Memory leak properly addressed!

The cleanup strategy is correct:

  1. Remove old listeners before starting a new drag (lines 116-125)
  2. Clear refs after finishing a drag (lines 166-168)
  3. Store refs before attaching listeners (lines 204-206)

This prevents multiple simultaneous drags and ensures proper cleanup in all scenarios.

Also applies to: 166-168, 204-206

@pushya22 pushya22 merged commit 373e640 into preview Dec 23, 2025
10 checks passed
@pushya22 pushya22 deleted the refactor/editor-table-performance branch December 23, 2025 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants