Skip to content

Conversation

@Myestery
Copy link
Collaborator

@Myestery Myestery commented Oct 30, 2025

This pull request introduces several improvements to the node context menu in the graph selection toolbox, focusing on enhanced usability, accessibility, and maintainability. The most significant changes are the addition of a search/filter feature for menu options, improved handling and display of disabled and categorized menu items, and better viewport-aware positioning for popovers and submenus.

Menu Usability & Filtering:

  • Added a search input at the top of the node options menu, allowing users to filter menu items by label. Filtering contextually hides dividers and category labels, and resets/focuses the search field when the menu is opened or closed. (NodeOptions.vue, useMoreOptionsMenu.ts) [1] [2] [3] [4] [5]

Menu Item Display & Accessibility:

  • Improved menu item rendering to support category labels and visually distinguish disabled options. Disabled options are now styled and prevented from triggering actions when clicked. (MenuOptionItem.vue, SubmenuPopover.vue, useMoreOptionsMenu.ts) [1] [2] [3] [4] [5]
  • Extended the menu option type definitions to include category and disabled flags, and added a source field for distinguishing between hardcoded and dynamic options. (useMoreOptionsMenu.ts) [1] [2]

Popover & Submenu Positioning:

  • Refactored popover positioning logic to use a viewport-aware utility, preventing menus and submenus from overflowing the visible area. Submenus now dock to the bottom of the viewport if they would otherwise overflow. (NodeOptions.vue, SubmenuPopover.vue) [1] [2]

Codebase Maintenance:

  • Updated imports and removed unused code related to canvas interactions, and improved composable structure for menu options. (NodeOptions.vue, useMoreOptionsMenu.ts) [1] [2] [3] [4]

These changes collectively enhance the user experience for interacting with node options, making the menu more navigable, accessible, and robust.

Recording.2025-11-11.235411.mp4

@github-actions
Copy link

github-actions bot commented Oct 30, 2025

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 12/02/2025, 02:02:10 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Oct 30, 2025

🎭 Playwright Test Results

Some tests failed

⏰ Completed at: 12/02/2025, 02:12:06 AM UTC

📈 Summary

  • Total Tests: 494
  • Passed: 480 ✅
  • Failed: 3 ❌
  • Flaky: 2 ⚠️
  • Skipped: 9 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 471 / ❌ 3 / ⚠️ 2 / ⏭️ 9
  • chromium-2x: View Report • ✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • chromium-0.5x: View Report • ✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • mobile-chrome: View Report • ✅ 6 / ❌ 0 / ⚠️ 0 / ⏭️ 0

🎉 Click on the links above to view detailed test results for each browser configuration.

@github-actions
Copy link

github-actions bot commented Oct 30, 2025

Bundle Size Report

Summary

  • Raw size: 17 MB baseline 17 MB — 🔴 +3.98 kB
  • Gzip: 3.37 MB baseline 3.37 MB — 🔴 +1.57 kB
  • Brotli: 2.58 MB baseline 2.58 MB — 🔴 +930 B
  • Bundles: 97 current • 97 baseline • 36 added / 36 removed

Category Glance
Graph Workspace 🔴 +3.79 kB (960 kB) · App Entry Points 🔴 +191 B (3.18 MB) · Vendor & Third-Party ⚪ 0 B (8.56 MB) · Other ⚪ 0 B (3.84 MB) · Panels & Settings ⚪ 0 B (298 kB) · UI Components ⚪ 0 B (139 kB) · + 3 more

Per-category breakdown
App Entry Points — 3.18 MB (baseline 3.18 MB) • 🔴 +191 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-SrtR75w6.js (new) 2.95 MB 🔴 +2.95 MB 🔴 +616 kB 🔴 +468 kB
assets/index-BQcQwdpo.js (removed) 2.95 MB 🟢 -2.95 MB 🟢 -616 kB 🟢 -468 kB
assets/index-BXDzG5wY.js (new) 227 kB 🔴 +227 kB 🔴 +48.6 kB 🔴 +40 kB
assets/index-DSUyz4Qm.js (removed) 227 kB 🟢 -227 kB 🟢 -48.6 kB 🟢 -40 kB
assets/index-DyiqTMGx.js (removed) 345 B 🟢 -345 B 🟢 -243 B 🟢 -237 B
assets/index-W0tvnXBs.js (new) 345 B 🔴 +345 B 🔴 +244 B 🔴 +209 B

Status: 3 added / 3 removed

Graph Workspace — 960 kB (baseline 956 kB) • 🔴 +3.79 kB

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-DvXeqvAv.js (new) 960 kB 🔴 +960 kB 🔴 +186 kB 🔴 +142 kB
assets/GraphView-DeEAWQUg.js (removed) 956 kB 🟢 -956 kB 🟢 -185 kB 🟢 -141 kB

Status: 1 added / 1 removed

Views & Navigation — 6.54 kB (baseline 6.54 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-BLS31PJ8.js (removed) 6.54 kB 🟢 -6.54 kB 🟢 -2.14 kB 🟢 -1.89 kB
assets/UserSelectView-DWSjxQZW.js (new) 6.54 kB 🔴 +6.54 kB 🔴 +2.14 kB 🔴 +1.89 kB

Status: 1 added / 1 removed

Panels & Settings — 298 kB (baseline 298 kB) • ⚪ 0 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CreditsPanel-CjKGHQb5.js (new) 21.4 kB 🔴 +21.4 kB 🔴 +5.15 kB 🔴 +4.5 kB
assets/CreditsPanel-omeGa93g.js (removed) 21.4 kB 🟢 -21.4 kB 🟢 -5.14 kB 🟢 -4.49 kB
assets/KeybindingPanel-Co7dlTPP.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.42 kB 🔴 +3.02 kB
assets/KeybindingPanel-uev5TeJ0.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.42 kB 🟢 -3 kB
assets/ExtensionPanel-CP1IHi__.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.57 kB 🟢 -2.26 kB
assets/ExtensionPanel-JxRXN-5J.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.57 kB 🔴 +2.25 kB
assets/AboutPanel-BQX0pmW7.js (removed) 9.16 kB 🟢 -9.16 kB 🟢 -2.46 kB 🟢 -2.21 kB
assets/AboutPanel-Cz3qK056.js (new) 9.16 kB 🔴 +9.16 kB 🔴 +2.46 kB 🔴 +2.21 kB
assets/ServerConfigPanel-C321WfSx.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -1.83 kB 🟢 -1.63 kB
assets/ServerConfigPanel-CudeFVXa.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +1.83 kB 🔴 +1.63 kB
assets/UserPanel-C8DTfjUC.js (new) 6.23 kB 🔴 +6.23 kB 🔴 +1.71 kB 🔴 +1.52 kB
assets/UserPanel-Dh_zFsU7.js (removed) 6.23 kB 🟢 -6.23 kB 🟢 -1.71 kB 🟢 -1.51 kB
assets/settings-BhbWhsRg.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BXTtSH4O.js 33.3 kB 33.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C9Pzn-NG.js 25.2 kB 25.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CCy2fA_h.js 27.3 kB 27.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CQpqEFfl.js 26.6 kB 26.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DHcnxypw.js 21.7 kB 21.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DhFTK9fY.js 25.1 kB 25.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DlT4t_ui.js 25.9 kB 25.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DRgSrIdD.js 24.2 kB 24.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-tjkeqiZq.js 21.1 kB 21.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

UI Components — 139 kB (baseline 139 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/Load3D.vue_vue_type_script_setup_true_lang-DHeJyUET.js (new) 53.9 kB 🔴 +53.9 kB 🔴 +8.52 kB 🔴 +7.32 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-DTXMObcx.js (removed) 53.9 kB 🟢 -53.9 kB 🟢 -8.52 kB 🟢 -7.32 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-6WW91OW6.js (removed) 47 kB 🟢 -47 kB 🟢 -10.1 kB 🟢 -8.79 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-B7dFnPIX.js (new) 47 kB 🔴 +47 kB 🔴 +10.1 kB 🔴 +8.79 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-DIUbo-Rs.js (new) 12.9 kB 🔴 +12.9 kB 🔴 +3.37 kB 🔴 +2.99 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-sqkeLkOX.js (removed) 12.9 kB 🟢 -12.9 kB 🟢 -3.37 kB 🟢 -2.97 kB
assets/ComfyQueueButton-C_LCsKHf.js (new) 8.44 kB 🔴 +8.44 kB 🔴 +2.47 kB 🔴 +2.21 kB
assets/ComfyQueueButton-CT5p2u3t.js (removed) 8.44 kB 🟢 -8.44 kB 🟢 -2.47 kB 🟢 -2.21 kB
assets/MediaTitle.vue_vue_type_script_setup_true_lang-DKBuequ-.js (removed) 897 B 🟢 -897 B 🟢 -500 B 🟢 -436 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-x_AYQdtp.js (new) 897 B 🔴 +897 B 🔴 +503 B 🔴 +429 B
assets/LazyImage.vue_vue_type_script_setup_true_lang-C6H8lTKh.js 10.8 kB 10.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-C5LozO_F.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-B-LyNlD7.js 2.04 kB 2.04 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-Cj7vQINR.js 2 kB 2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 5 added / 5 removed

Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/keybindingService-CHWsidLF.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/keybindingService-QlvKhnC5.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.58 kB
assets/audioService-D7Rdx86O.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -960 B 🟢 -827 B
assets/audioService-fdZVpGmd.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +960 B 🔴 +821 B
assets/serverConfigStore-BQ9GXEkd.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

Utilities & Hooks — 2.94 kB (baseline 2.94 kB) • ⚪ 0 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-Bbiz8a_0.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -650 B 🟢 -548 B
assets/audioUtils-DX6JwntS.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +650 B 🔴 +548 B
assets/mathUtil-CTARWQ-l.js 1.07 kB 1.07 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeFilterUtil-CXKCRJ-m.js 460 B 460 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 1 added / 1 removed

Vendor & Third-Party — 8.56 MB (baseline 8.56 MB) • ⚪ 0 B

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-B_stLV7c.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-BawBdcAH.js 3.98 MB 3.98 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-DqE43B_H.js 1.96 MB 1.96 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-aR6ntw5X.js 1.37 MB 1.37 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-CogkskC9.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-DCnqNLx6.js 160 kB 160 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BZLod3g9.js 407 kB 407 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 3.84 MB (baseline 3.84 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WidgetRecordAudio-CC_tz0Vs.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.23 kB 🔴 +4.62 kB
assets/WidgetRecordAudio-CuylfOd6.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.23 kB 🟢 -4.62 kB
assets/AudioPreviewPlayer-Dc4LYAD8.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.4 kB 🟢 -3.04 kB
assets/AudioPreviewPlayer-Nd228ZZc.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.4 kB 🔴 +3.04 kB
assets/WidgetGalleria-BGlKc0Mz.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.44 kB 🔴 +1.3 kB
assets/WidgetGalleria-Dk52fsdt.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.44 kB 🟢 -1.3 kB
assets/WidgetColorPicker-BfuX2Cyh.js (removed) 3.41 kB 🟢 -3.41 kB 🟢 -1.38 kB 🟢 -1.23 kB
assets/WidgetColorPicker-DHF5HM45.js (new) 3.41 kB 🔴 +3.41 kB 🔴 +1.38 kB 🔴 +1.23 kB
assets/WidgetMarkdown-Bctv8lwm.js (removed) 3.1 kB 🟢 -3.1 kB 🟢 -1.28 kB 🟢 -1.13 kB
assets/WidgetMarkdown-CmSDa1cs.js (new) 3.1 kB 🔴 +3.1 kB 🔴 +1.28 kB 🔴 +1.12 kB
assets/WidgetAudioUI-BVS8JAOo.js (new) 2.82 kB 🔴 +2.82 kB 🔴 +1.12 kB 🔴 +1.04 kB
assets/WidgetAudioUI-BXNhccNi.js (removed) 2.82 kB 🟢 -2.82 kB 🟢 -1.12 kB 🟢 -1.01 kB
assets/WidgetTextarea-BTmssz01.js (new) 2.48 kB 🔴 +2.48 kB 🔴 +1.01 kB 🔴 +893 B
assets/WidgetTextarea-U3TJTiVI.js (removed) 2.48 kB 🟢 -2.48 kB 🟢 -1.01 kB 🟢 -884 B
assets/WidgetInputText-B0EfhCnx.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +916 B 🔴 +844 B
assets/WidgetInputText-BexPeE8G.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -916 B 🟢 -847 B
assets/MediaImageBottom-0g29eUb3.js (new) 1.57 kB 🔴 +1.57 kB 🔴 +739 B 🔴 +646 B
assets/MediaImageBottom-B9-cic1t.js (removed) 1.57 kB 🟢 -1.57 kB 🟢 -737 B 🟢 -639 B
assets/MediaAudioBottom-CykqvPWE.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -738 B 🟢 -654 B
assets/MediaAudioBottom-CzYuaym0.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +738 B 🔴 +654 B
assets/MediaVideoBottom-CxAH2ccP.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +736 B 🔴 +656 B
assets/MediaVideoBottom-teekWhUB.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -735 B 🟢 -650 B
assets/Media3DBottom-BN4TK3Zp.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -730 B 🟢 -646 B
assets/Media3DBottom-DDjtvbEt.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +728 B 🔴 +649 B
assets/Media3DTop-DGjlCDXM.js (new) 1.49 kB 🔴 +1.49 kB 🔴 +762 B 🔴 +648 B
assets/Media3DTop-m7_iYW7A.js (removed) 1.49 kB 🟢 -1.49 kB 🟢 -762 B 🟢 -651 B
assets/WidgetSelect-BmbVACAp.js (new) 655 B 🔴 +655 B 🔴 +343 B 🔴 +286 B
assets/WidgetSelect-DsI5tm7l.js (removed) 655 B 🟢 -655 B 🟢 -344 B 🟢 -285 B
assets/WidgetInputNumber-Bq7ENP_m.js (new) 595 B 🔴 +595 B 🔴 +324 B 🔴 +271 B
assets/WidgetInputNumber-BsIcME1W.js (removed) 595 B 🟢 -595 B 🟢 -327 B 🟢 -274 B
assets/Load3D-Bxgcj4_S.js (new) 424 B 🔴 +424 B 🔴 +265 B 🔴 +224 B
assets/Load3D-CYTns2GD.js (removed) 424 B 🟢 -424 B 🟢 -266 B 🟢 -225 B
assets/WidgetLegacy-B7ic1JQQ.js (removed) 364 B 🟢 -364 B 🟢 -234 B 🟢 -192 B
assets/WidgetLegacy-BOe7mw_K.js (new) 364 B 🔴 +364 B 🔴 +234 B 🔴 +193 B
assets/commands-_s-RvhJR.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BuUILW6P.js 13 kB 13 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BV4R6fLx.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BWp4HdfU.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CLwPdnT6.js 14.2 kB 14.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CWMchBmd.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DazTQhtc.js 12.9 kB 12.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DmWrOe93.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DwiH7Kr6.js 13.8 kB 13.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-mS3LCNPn.js 14.5 kB 14.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-5lOBdqcC.js 84.5 kB 84.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BOCuaVpE.js 73.4 kB 73.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-ClrEFGUz.js 72.4 kB 72.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Cw9RZWRY.js 89 B 89 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CyNU0iQX.js 99.3 kB 99.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-D7gwLxft.js 114 kB 114 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DC8o4BCt.js 86.8 kB 86.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DKiesCV4.js 94.3 kB 94.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Hq2q-OtB.js 83.6 kB 83.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-USAlAlnj.js 82 kB 82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-CSwk_z5a.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-DBacjcjH.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-Ct23Baus.js 2.76 kB 2.76 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-_Px5dSNW.js 306 kB 306 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-7z21KPoS.js 285 kB 285 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bw_Jitw_.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BWKZzBPK.js 346 kB 346 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CGbgH4Yl.js 320 kB 320 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CjjjdWkV.js 313 kB 313 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CVrNtxvj.js 288 kB 288 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DLRSA0IK.js 309 kB 309 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DQV2gnwA.js 372 kB 372 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-ofqLG5vz.js 310 kB 310 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-DE8yMz8M.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-DXoIcmUZ.js 2.21 kB 2.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-CPRhYPcc.js 1.58 kB 1.58 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 17 added / 17 removed

@Myestery Myestery marked this pull request as ready for review November 11, 2025 22:52
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Nov 11, 2025
@DrJKL DrJKL force-pushed the right-click-vue-node-contextmenu branch from 3d05c49 to 8e7026d Compare November 12, 2025 21:16
@Myestery Myestery force-pushed the right-click-vue-node-contextmenu branch from 8e7026d to 52b7b5c Compare November 15, 2025 00:57
- Add disabled property to MenuOption and SubMenuOption interfaces
- Apply semantic token styling for disabled items (text-node-icon-disabled)
- Prevent interactions on disabled menu and submenu items
- Use cursor-not-allowed and pointer-events-none for disabled state
- Pass through disabled flag from LiteGraph menu items to Vue components
Add translation key for the Extensions section label in the context menu.
This allows the Extensions category header to be properly localized.
Add support for non-clickable category labels in the context menu.
Category items are displayed as uppercase labels with secondary text color
and proper translation support via contextMenu namespace.

This enables grouping menu items under section headers like "Extensions".
Add comprehensive menu ordering system to ensure consistent context menu layout:
- Define MENU_ORDER constant with 5 logical sections
- Implement automatic section-based divider insertion
- Separate core items from extension items
- Add blacklist for unwanted duplicate items (Colors, Shapes, Title, Mode, etc.)
- Preserve Vue hardcoded options over LiteGraph items during deduplication
- Add source tracking (litegraph vs vue) for precedence handling

Menu structure:
1. Basic operations (Rename, Copy, Duplicate)
2. Node actions (Run Branch, Pin, Bypass, Mute)
3. Structure operations (Convert to Subgraph, Frame selection, Minimize Node)
4. Node properties (Node Info, Color)
5. Node-specific operations (Image operations)
6. Extensions section (non-core items)
7. Delete (always at bottom)

The system ensures proper ordering regardless of whether items come from
LiteGraph or Vue hardcoded menus, with Extensions clearly separated.
Reorder menu sections to follow the specification:
1. Basic operations: Rename, Copy, Duplicate
2. Node actions: Run Branch, Pin, Bypass, Mute (for groups)
3. Structure operations: Convert to Subgraph, Frame selection, Minimize Node
4. Node properties: Node Info, Color (Shape removed)
5. Node-specific operations: Image operations only

Changes:
- Add explicit dividers after each section
- Remove Shape, Alignment, and Adjust Size options
- Merge LiteGraph and Vue options for single node selection
- Add source tracking (vue) for precedence during deduplication
- Add comprehensive logging for debugging menu construction

For single node selection, both LiteGraph and Vue options are merged
with Vue options taking precedence, then structured via buildStructuredMenu.
Add real-time search filtering to the node context menu:
- Search input with icon at the top of the menu
- Case-insensitive filtering by menu item label
- Auto-focus on menu open
- Clear search on Escape key or menu close
- Smart divider handling (removes unnecessary dividers during search)
- Hides Extensions category label when searching

The search provides instant feedback as users type, making it easier
to find specific menu items in long context menus.
Move Resize and Clone menu items from fallback section to Section 3
(Structure operations) so they appear alongside Convert to Subgraph,
Frame selection, and Minimize Node.

Updated section boundaries:
- Section 3 now spans indices 9-15 (was 9-13)
- Section 4 now spans indices 16-17 (was 14-15)

This ensures Resize and Clone appear under the same divider group
as other structure-related operations.
When right-clicking near the bottom of the viewport, the context menu now docks to the bottom edge instead of floating off-screen, ensuring it remains accessible and scrollable.
Replaces simple substring matching with Fuse.js fuzzy search for better
user experience. Adds 300ms debouncing to reduce processing overhead.
Includes debug logging for troubleshooting.
@DrJKL DrJKL added the claude-review Add to trigger a PR code review from Claude Code label Nov 21, 2025
@github-actions github-actions bot removed the claude-review Add to trigger a PR code review from Claude Code label Nov 21, 2025
@DrJKL DrJKL self-assigned this Nov 21, 2025
Copy link
Contributor

Choose a reason for hiding this comment

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

How much of this is necessary because we're not using a good existing component?

Copy link
Contributor

Choose a reason for hiding this comment

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

The menu isn't keyboard navigable?
That's a pretty big accessibility concern.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 21, 2025

📝 Walkthrough

Walkthrough

Replaces the Popover-based node options UI with a PrimeVue ContextMenu, adds a LiteGraph→Vue context-menu converter and legacy compatibility wrappers, widens LiteGraph menu typings to allow null entries, moves color‑picker positioning to inline DOM, privatizes selection‑toolbox signals, and removes submenu positioning composable.

Changes

Cohort / File(s) Summary
Removed menu component layer
src/components/graph/selectionToolbox/MenuOptionItem.vue, src/components/graph/selectionToolbox/NodeOptions.vue
Deleted legacy MenuOptionItem and Popover-based NodeOptions, including templates, logic, submenu handling, and exposed APIs (toggle, hide, isOpen).
New LiteGraph‑Vue menu bridge
src/composables/graph/contextMenuConverter.ts
Added conversion utilities: convertContextMenuToOptions and buildStructuredMenu to normalize, dedupe, categorize, and convert LiteGraph context menus (including dynamic submenus) into MenuOption structures.
Enhanced menu options composition
src/composables/graph/useMoreOptionsMenu.ts
Extended MenuOption/SubMenuOption shapes (added disabled, source, isColorPicker, type: 'category'), merged LiteGraph options with Vue options, and integrated buildStructuredMenu for ordering and sections.
Node menu adjustments
src/composables/graph/useNodeMenuOptions.ts
Marked Color submenu with isColorPicker: true and standardized bypass icon to a constant.
Legacy compatibility layer
src/composables/useContextMenuTranslation.ts
Wrapped getCanvasMenuOptions and getNodeMenuOptions to collect and append legacy monkey‑patched menu items via legacyMenuCompat; registered wrapper for node menu.
Localization additions
src/locales/en/main.json
Added translation keys: g.noResults ("No Results") and contextMenu.Extensions ("Extensions").
Component API exposure
src/components/input/SearchBox.vue
Exposed internal focusInput via defineExpose.
Context menu type widening
src/lib/litegraph/src/LGraphCanvas.ts
Broadened getExtraMenuOptions and getCanvasMenuOptions signatures to accept/return `(IContextMenuValue
Compatibility framework updates
src/lib/litegraph/src/contextMenuCompat.ts
Updated ContextMenuValueProvider, registerWrapper, install, and extractLegacyItems signatures and internals to support null-typed arrays and generic method keys.
New context menu UI replacement
src/components/graph/NodeContextMenu.vue
Added Vue 3 component rendering PrimeVue ContextMenu driven by menuOptions store, supports native submenus and a color‑picker submenu; exposes toggle, show, hide, and isOpen.
Selection toolbox integration
src/components/graph/SelectionToolbox.vue, src/components/graph/SelectionToolbox.test.ts
Imported and rendered NodeContextMenu in selection toolbox; added test stub for node-context-menu.
Removed NodeOptions reference
src/components/graph/GraphCanvas.vue
Removed import/usage of the old NodeOptions component from GraphCanvas template.
Color picker positioning modernization
src/components/graph/selectionToolbox/ColorPickerMenu.vue
Replaced Popover with a fixed positioned div, added showToRight(target) and toggle(target), outside-click handling with ignore selectors, and disabled-state handling for submenu items.
Selection toolbox signal privatization
src/composables/canvas/useSelectionToolboxPosition.ts
Converted previously exported refs (moreOptionsOpen, forceCloseMoreOptionsSignal, restoreMoreOptionsSignal, moreOptionsRestorePending) to module-private constants.
Removed submenu positioning composable
src/composables/graph/useSubmenuPositioning.ts
Deleted the entire useSubmenuPositioning composable (toggleSubmenu, createPositionedTarget, synthetic event creation, cleanup, hideAllSubmenus, etc.).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Canvas as LGraphCanvas
    participant Converter as contextMenuConverter
    participant Store as MenuOptionsStore
    participant CMC as NodeContextMenu
    participant ColorPicker as ColorPickerMenu

    User->>Canvas: Right-click node
    Canvas->>Canvas: getNodeMenuOptions()
    Canvas->>Converter: convertContextMenuToOptions(litegraphItems)
    Converter->>Converter: normalize, dedupe, capture dynamic submenus
    Converter->>Store: buildStructuredMenu(merged options)
    Store-->>CMC: MenuOption[]
    Canvas->>CMC: toggle(event)
    CMC->>CMC: convert MenuOption[] → PrimeVue menuItems
    CMC->>User: show context menu

    User->>CMC: Click color option (isColorPicker)
    CMC->>ColorPicker: toggle(target)
    ColorPicker->>ColorPicker: compute position, show
    User->>ColorPicker: select color
    ColorPicker->>CMC: handleColorSelect()
    CMC->>CMC: execute action, hide menus
Loading

Possibly related PRs

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch right-click-vue-node-contextmenu

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

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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/graph/selectionToolbox/MenuOptionItem.vue (1)

1-47: Category row & disabled styling look good; badge i18n keying may need tightening

  • The new option.type === 'category' branch with pointer-events-none and t(\contextMenu.${option.label}`)` is consistent with how other context-menu labels are translated and correctly non-interactive.
  • The dynamic class array for the main row cleanly distinguishes disabled vs enabled states and matches the new disabled behavior elsewhere.

One small i18n concern: badge values are translated via t(option.badge), where option.badge is typically 'new' or 'deprecated', but the corresponding strings live under the contextMenu namespace (contextMenu.new / contextMenu.deprecated) in main.json. Unless you also define root-level new/deprecated keys in the messages, this will likely show the raw key instead of a localized string.

You can tighten this by scoping badges to the same namespace:

-      :value="t(option.badge)"
+      :value="t(`contextMenu.${option.badge}`)"

This keeps badge translations aligned with the rest of the context-menu labels.

♻️ Duplicate comments (2)
src/components/graph/selectionToolbox/NodeOptions.vue (2)

130-189: Filtering & divider reconstruction logic looks sound, including divider/category adjacency

The fuzzy‑search pipeline (searchableMenuOptionsuseFusefilteredMenuOptions) correctly:

  • Excludes divider/category items from search targets,
  • Matches by label, then rebuilds the menu in original order,
  • Avoids leading/trailing dividers and collapses sequences so you never introduce multiple dividers between hits.

Since categories are skipped entirely when query is non‑empty, “divider + category + item” sequences in the original structured menu end up as at most a single divider before the first matching item, which answers the earlier question about adjacent dividers/categories.


226-253: Direct cssText append plus individual property sets

repositionPopover updates overlayEl.style.cssText and then sets top/bottom individually. This works but can be brittle (order/precedence) and makes it harder to reason about final styles; also, multiple style writes may cause additional layout/repaint work in some browsers. Consider computing a single style object and applying via Object.assign(overlayEl.style, style) instead.

🧹 Nitpick comments (7)
src/components/graph/selectionToolbox/SubmenuPopover.vue (1)

89-125: Consider centralizing viewport-aware positioning with the shared utility

repositionSubmenu manually measures the overlay and docks it to the bottom of the viewport when it would overflow:

const rect = overlayEl.getBoundingClientRect()
const menuHeight = overlayEl.offsetHeight || overlayEl.scrollHeight
const viewportHeight = window.innerHeight
const menuBottom = rect.top + menuHeight
const wouldOverflow = menuBottom > viewportHeight
// ...
overlayEl.style.position = 'fixed'
overlayEl.style.bottom = '0px'
overlayEl.style.top = ''

This is very similar in intent to the new calculateMenuPosition helper you added for the main menu. To avoid drift between primary and submenu behavior (and duplicate reflow/measurement logic), it’s worth considering:

  • Reusing calculateMenuPosition here by feeding it the submenu’s trigger rect and overlay element, or
  • Extracting the “dock to bottom” portion into a shared internal helper used by both.

Not urgent, but it would simplify future tweaks to overflow behavior.

src/components/graph/selectionToolbox/NodeOptions.vue (2)

76-81: Consider avoiding unused imports from @vueuse/core

breakpointsTailwind, useBreakpoints, and useRafFn are used, but if debouncedRef is the only part needed for search, keep an eye that all imported symbols remain used after future refactors to avoid dead code bloat.


404-437: Search reset on show/hide is consistent, consider preserving query across brief hides

Currently searchQuery is cleared on both onPopoverShow and onPopoverHide, so any transient hide (e.g. drag‑related auto‑close/restore) will always lose the query. If users often reopen the menu immediately, you might consider only clearing on true manual close, similar to how wasOpenBeforeHide is tracked.

src/composables/graph/contextMenuConverter.ts (4)

23-72: Label‑driven classification is brittle under renames/localization

Both CORE_MENU_ITEMS and MENU_ORDER depend on the human‑readable label string (e.g. 'Run Branch', 'Open Image') to classify and order items. This works now but is fragile if:

  • Labels are localized, or
  • LiteGraph/Vue options are renamed.

A more robust approach is to carry a stable identifier (e.g. id: 'run-branch') on MenuOption and base CORE_MENU_ITEMS / MENU_ORDER on those ids rather than translated labels.


136-202: removeDuplicateMenuOptions preserves ordering and Vue precedence

Grouping by label and choosing a source === 'vue' candidate when duplicates exist achieves the “Vue overrides LiteGraph” goal while:

  • Keeping dividers/categories and unlabeled items in original order, and
  • Avoiding label collisions from extension items without labels.

The two‑pass approach with itemsWithoutLabel is a bit dense; a brief comment explaining the ordering intent would help future maintainers.


453-522: Dynamic submenu capture via LiteGraph.ContextMenu override is reasonable but invasive

Temporarily replacing LiteGraph.ContextMenu to intercept dynamic submenu construction, then restoring it in a finally block, is a pragmatic way to introspect items without rendering DOM. The mock ContextMenu returns a minimal { close, root } object, which should satisfy most callbacks.

Given this is a global mutation, it’s important that:

  • No other concurrent code depends on LiteGraph.ContextMenu while a dynamic submenu is being captured, and
  • The target LiteGraph version doesn’t rely on special prototype behavior of ContextMenu that your function replacement would miss.

If LiteGraph ever gains an official hook for submenu generation, migrating to that would reduce risk.


605-610: Static analysis XSS warning on innerHTML is a false positive in this context

stripHtmlTags assigns to temp.innerHTML on a detached element and only reads textContent/innerText, never inserting the element into the live DOM. That means scripts or event handlers in html will not execute, so this usage is safe against XSS.

To avoid future confusion and linter noise, consider:

  • Adding a comment explaining that the element is never attached to the DOM, or
  • Swapping to DOMParser for HTML → text extraction if you want to eliminate innerHTML entirely.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14d94da and 03c8cf8.

📒 Files selected for processing (9)
  • src/components/graph/selectionToolbox/MenuOptionItem.vue (3 hunks)
  • src/components/graph/selectionToolbox/NodeOptions.vue (9 hunks)
  • src/components/graph/selectionToolbox/SubmenuPopover.vue (4 hunks)
  • src/composables/graph/contextMenuConverter.ts (1 hunks)
  • src/composables/graph/useMoreOptionsMenu.ts (5 hunks)
  • src/composables/graph/useNodeMenuOptions.ts (1 hunks)
  • src/composables/graph/useViewportAwareMenuPositioning.ts (1 hunks)
  • src/composables/useContextMenuTranslation.ts (2 hunks)
  • src/locales/en/main.json (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/composables/graph/useMoreOptionsMenu.ts (5)
src/renderer/core/canvas/canvasStore.ts (1)
  • useCanvasStore (24-149)
src/composables/graph/useImageMenuOptions.ts (1)
  • useImageMenuOptions (11-108)
src/composables/graph/useNodeMenuOptions.ts (1)
  • useNodeMenuOptions (12-128)
src/composables/graph/useGroupMenuOptions.ts (1)
  • useGroupMenuOptions (16-196)
src/composables/graph/contextMenuConverter.ts (2)
  • convertContextMenuToOptions (358-447)
  • buildStructuredMenu (253-349)
src/composables/graph/contextMenuConverter.ts (2)
src/composables/graph/useMoreOptionsMenu.ts (2)
  • MenuOption (18-29)
  • SubMenuOption (31-37)
src/lib/litegraph/src/litegraph.ts (1)
  • LiteGraph (17-17)
src/composables/useContextMenuTranslation.ts (2)
src/lib/litegraph/src/contextMenuCompat.ts (1)
  • legacyMenuCompat (148-148)
src/lib/litegraph/src/interfaces.ts (1)
  • IContextMenuValue (405-426)
🪛 ast-grep (0.40.0)
src/composables/graph/contextMenuConverter.ts

[warning] 607-607: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: temp.innerHTML = html
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html

(unsafe-html-content-assignment)


[warning] 607-607: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: temp.innerHTML = html
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html

(dom-content-modification)

⏰ 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: test
  • GitHub Check: collect
  • GitHub Check: setup
  • GitHub Check: lint-and-format
🔇 Additional comments (17)
src/locales/en/main.json (1)

127-128: New translation keys look consistent and safe

g.noResults and contextMenu.Extensions/contextMenu.deprecated fit existing naming and usage patterns; JSON structure is valid and compatible with current i18n usage.

Also applies to: 431-433

src/composables/graph/useNodeMenuOptions.ts (1)

92-105: Bypass option icon change is isolated and correct

Only the icon for the bypass option changes; labels, shortcut (Ctrl+B), and action logic remain intact. Safe UI-level adjustment.

src/components/graph/selectionToolbox/MenuOptionItem.vue (1)

70-75: Defensive disabled guard in handleClick is appropriate

Short-circuiting handleClick when props.option.disabled is true matches the new disabled styling and protects against any future changes that might remove pointer-events-none from the row.

src/components/graph/selectionToolbox/SubmenuPopover.vue (1)

31-46: Submenu disabled styling and click-guard are consistent and correct

The updated class composition for subOption rows plus the subOption.disabled branch gives you:

  • Visual disabled state (cursor-not-allowed, muted text).
  • Non-interactive behavior (pointer-events-none).
  • A defensive guard in handleSubmenuClick to prevent accidental emissions.

This keeps submenu behavior aligned with the top‑level menu items.

Also applies to: 132-137

src/composables/useContextMenuTranslation.ts (1)

61-63: Node menu legacy/extension wiring matches the canvas pattern

Installing legacyMenuCompat for getNodeMenuOptions, then wrapping it to:

  • Append items from the new app.collectNodeMenuItems(node) API, and
  • Append legacy monkey‑patched items from legacyMenuCompat.extractLegacyItems('getNodeMenuOptions', ...),

and finally registering the wrapper/original pair with legacyMenuCompat.registerWrapper mirrors the existing canvas‑menu integration and keeps all sources flowing through a single, composable hook point.

The sequencing (install → capture original → wrap → register) looks correct and should preserve legacy behavior while enabling extensions.

Also applies to: 79-88, 94-101

src/components/graph/selectionToolbox/NodeOptions.vue (3)

111-117: Good separation of mobile vs desktop search focus

Using useBreakpoints(breakpointsTailwind) and isMobileViewport to skip auto‑focus on small viewports nicely avoids pulling up the keyboard on mobile when the menu opens.


266-284: Guard against detached trigger element is a good addition

The if (!el || !el.isConnected) return false check in openPopover prevents trying to open against a stale DOM target during drag/move or unmount scenarios, which should avoid subtle positioning errors and exceptions.


321-349: Target‑switch behavior while open is well‑handled

The toggle logic that switches the popover to a new targetEl without closing, bumps the options version, clears search, repositions, and re‑focuses the input (desktop only) gives a smooth experience when quickly right‑clicking different nodes.

src/composables/graph/contextMenuConverter.ts (3)

78-122: Duplicate detection is conservative but acceptable

normalizeLabel plus equivalents (color/colors, pin/unpin, delete/remove, clone/duplicate) gives a reasonable duplicate filter while avoiding over‑eager merging. The logic short‑circuits on direct normalized match and only then checks equivalence groups, which is easy to reason about and should keep unrelated items (e.g. “Delete Selection”) distinct.


253-349: Structured core/extensions menu looks correct

buildStructuredMenu:

  • Dedups first, then splits into core vs extension vs Delete,
  • Orders core items using MENU_ORDER and inserts a single divider between sections,
  • Adds an “Extensions” category label with a divider before it,
  • Forces Delete/Remove (no submenu) to the bottom with a preceding divider.

This matches the described UX and should be stable as long as core labels remain consistent with those produced by Vue/LiteGraph.


527-599: Submenu conversion correctly propagates disabled state and strips HTML

convertSubmenuToOptions:

  • Skips null separators,
  • Handles both string items (calling options.callback when provided) and object items via their callback,
  • Uses stripHtmlTags so labels from HTML‑styled content don’t leak markup into the Vue menu,
  • Propagates disabled to SubMenuOption.

This is a solid bridge from LiteGraph submenu definitions to Vue submenus.

src/composables/graph/useMoreOptionsMenu.ts (6)

18-37: Type extensions align with converter/renderer changes

Adding type?: 'divider' | 'category', disabled?: boolean, and source?: 'litegraph' | 'vue' to MenuOption, plus disabled?: boolean on SubMenuOption, matches what contextMenuConverter and the menu components expect and should keep the types coherent across the pipeline.


85-96: markAsVueOptions correctly tags only actionable items

The helper skips divider and category entries and clones other options with source: 'vue'. This pairs cleanly with removeDuplicateMenuOptions, which prefers Vue items over LiteGraph when de‑duplicating by label.


102-117: Using useCanvasStore for LiteGraph integration is appropriate

Pulling canvasStore.canvas here to access getNodeMenuOptions keeps the LiteGraph dependency localized to this composable, instead of leaking it into UI components. That keeps NodeOptions.vue focused on rendering rather than data sourcing.


161-178: Single‑node LiteGraph menu merge path is sensible

The block that, for a single node and no group context, calls canvasStore.canvas.getNodeMenuOptions(node) and runs convertContextMenuToOptions(rawItems, node, false) gives you raw LiteGraph options without structuring, which you then merge and structure once. The try/catch with logging prevents a bad extension from breaking the whole menu.


251-262: Merge + structuring flow achieves Vue‑over‑LiteGraph precedence

Merging with const merged = [...litegraphOptions, ...markedVueOptions] and then calling buildStructuredMenu(merged) ensures:

  • LiteGraph contributes all its items,
  • Vue items override LiteGraph ones with the same label (via source: 'vue'),
  • The final menu is always ordered/sectioned consistently, regardless of which side supplied a given option.

The fallback path (no LiteGraph options) that structures only Vue options keeps behavior consistent across selection types.


265-276: Filtering menuOptionsWithSubmenu by hasSubmenu && submenu is straightforward

The computed menuOptionsWithSubmenu remains aligned with the updated MenuOption shape and will naturally pick up submenus coming from either Vue or LiteGraph sources.

Comment on lines 143 to +248
// Section 1: Basic selection operations (Rename, Copy, Duplicate)
options.push(...getBasicSelectionOptions())
const basicOps = getBasicSelectionOptions()
options.push(...basicOps)
options.push({ type: 'divider' })

// Section 2: Node Info & Size Adjustment
if (nodeDef.value) {
options.push(getNodeInfoOption(showNodeHelp))
// Section 2: Node actions (Run Branch, Pin, Bypass, Mute)
if (hasOutputNodesSelected.value) {
const runBranch = getRunBranchOption()
options.push(runBranch)
}

if (groupContext) {
options.push(getFitGroupToNodesOption(groupContext))
} else {
options.push(getAdjustSizeOption())
if (!groupContext) {
const pin = getPinOption(states, bump)
const bypass = getBypassOption(states, bump)
options.push(pin)
options.push(bypass)
}

// Section 3: Collapse/Shape/Color
if (groupContext) {
// Group context: Shape, Color, Divider
options.push(getGroupShapeOptions(groupContext, bump))
options.push(getGroupColorOptions(groupContext, bump))
options.push({ type: 'divider' })
} else {
// Node context: Expand/Minimize, Shape, Color, Divider
options.push(...getNodeVisualOptions(states, bump))
options.push({ type: 'divider' })
const groupModes = getGroupModeOptions(groupContext, bump)
options.push(...groupModes)
}

// Section 4: Image operations (if image node)
if (hasImageNode.value && selectedNodes.value.length > 0) {
options.push(...getImageMenuOptions(selectedNodes.value[0]))
}

// Section 5: Subgraph operations
options.push(...getSubgraphOptions(hasSubgraphsSelected))

// Section 6: Multiple nodes operations
if (hasMultipleNodes.value) {
options.push(...getMultipleNodesOptions())
}

// Section 7: Divider
options.push({ type: 'divider' })

// Section 8: Pin/Unpin (non-group only)
if (!groupContext) {
options.push(getPinOption(states, bump))
}

// Section 9: Alignment (if multiple nodes)
// Section 3: Structure operations (Convert to Subgraph, Frame selection, Minimize Node)
const subgraphOps = getSubgraphOptions(hasSubgraphsSelected)
options.push(...subgraphOps)
if (hasMultipleNodes.value) {
options.push(...getAlignmentOptions())
const multiOps = getMultipleNodesOptions()
options.push(...multiOps)
}

// Section 10: Mode operations
if (groupContext) {
// Group mode operations
options.push(...getGroupModeOptions(groupContext, bump))
const fitGroup = getFitGroupToNodesOption(groupContext)
options.push(fitGroup)
} else {
// Bypass option for nodes
options.push(getBypassOption(states, bump))
// Add minimize/expand option only
const visualOptions = getNodeVisualOptions(states, bump)
if (visualOptions.length > 0) {
options.push(visualOptions[0]) // Minimize/Expand
}
}
options.push({ type: 'divider' })

// Section 11: Run Branch (if output nodes)
if (hasOutputNodesSelected.value) {
options.push(getRunBranchOption())
// Section 4: Node properties (Node Info, Color)
if (nodeDef.value) {
const nodeInfo = getNodeInfoOption(showNodeHelp)
options.push(nodeInfo)
}
if (groupContext) {
const groupColor = getGroupColorOptions(groupContext, bump)
options.push(groupColor)
} else {
// Add shape and color options
const visualOptions = getNodeVisualOptions(states, bump)
if (visualOptions.length > 1) {
options.push(visualOptions[1]) // Shape (index 1)
}
if (visualOptions.length > 2) {
options.push(visualOptions[2]) // Color (index 2)
}
}

// Section 12: Final divider and Delete
options.push({ type: 'divider' })
options.push(getDeleteOption())

return options
// Section 5: Node-specific options (image operations)
if (hasImageNode.value && selectedNodes.value.length > 0) {
const imageOps = getImageMenuOptions(selectedNodes.value[0])
options.push(...imageOps)
options.push({ type: 'divider' })
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Vue sectioning is clear; watch for label mismatches with structuring rules

The manual sectioning (basic selection, node actions, structure, properties, image options) is neatly organized with explicit dividers. However, since buildStructuredMenu later reorders items based on label, a few things are worth double‑checking:

  • The labels produced here (via t('contextMenu.*')) must match the raw labels in CORE_MENU_ITEMS / MENU_ORDER (e.g. 'Open Image', 'Copy Image').
  • In particular, useImageMenuOptions uses t('contextMenu.Open in Mask Editor') while CORE_MENU_ITEMS contains 'Open in MaskEditor' (no space). If the rendered label includes the space, it won’t be treated as a core item and will fall under Extensions instead of the intended section.

Consider standardizing labels (or, better, switching to stable ids as noted in the converter comment) to ensure structuring behaves as designed.


I'll verify the label mismatch concerns by examining the relevant code files.Now let me search for the specific files and functions mentioned in the review:


🏁 Script executed:

#!/bin/bash
# Find the files and functions mentioned in the review
echo "=== Searching for useImageMenuOptions ==="
rg -n "useImageMenuOptions|getImageMenuOptions" --type ts

echo -e "\n=== Searching for CORE_MENU_ITEMS and MENU_ORDER ==="
rg -n "CORE_MENU_ITEMS|MENU_ORDER" --type ts

echo -e "\n=== Searching for buildStructuredMenu ==="
rg -n "buildStructuredMenu" --type ts

echo -e "\n=== Searching for 'Open in Mask' or 'Open in MaskEditor' ==="
rg -n "Open in M" --type ts

Length of output: 2508


Now let me examine the actual content of these key files:


🏁 Script executed:

#!/bin/bash
# Examine the relevant sections of contextMenuConverter.ts
echo "=== CORE_MENU_ITEMS definition (contextMenuConverter.ts around line 23) ==="
sed -n '23,80p' git/src/composables/graph/contextMenuConverter.ts

echo -e "\n=== MENU_ORDER definition (contextMenuConverter.ts around line 207) ==="
sed -n '207,250p' git/src/composables/graph/contextMenuConverter.ts

echo -e "\n=== buildStructuredMenu function (contextMenuConverter.ts around line 253) ==="
sed -n '253,300p' git/src/composables/graph/contextMenuConverter.ts

echo -e "\n=== useImageMenuOptions.ts around line 84 ==="
sed -n '75,95p' git/src/composables/graph/useImageMenuOptions.ts

Length of output: 646


Let me re-run with the repository cloned:


🏁 Script executed:

#!/bin/bash
# Examine the relevant sections of contextMenuConverter.ts
echo "=== CORE_MENU_ITEMS definition (contextMenuConverter.ts around line 23) ==="
sed -n '23,80p' src/composables/graph/contextMenuConverter.ts

echo -e "\n=== MENU_ORDER definition (contextMenuConverter.ts around line 207) ==="
sed -n '207,250p' src/composables/graph/contextMenuConverter.ts

echo -e "\n=== buildStructuredMenu function (contextMenuConverter.ts around line 253) ==="
sed -n '253,310p' src/composables/graph/contextMenuConverter.ts

echo -e "\n=== useImageMenuOptions.ts around line 84 ==="
sed -n '75,95p' src/composables/graph/useImageMenuOptions.ts

echo -e "\n=== Check how translation keys are used in useImageMenuOptions ==="
sed -n '70,110p' src/composables/graph/useImageMenuOptions.ts

Length of output: 5978


Fix label mismatch in "Open in Mask Editor" option

The translation key t('contextMenu.Open in Mask Editor') at useImageMenuOptions.ts:84 produces a label with a space, but CORE_MENU_ITEMS and MENU_ORDER in contextMenuConverter.ts define it as 'Open in MaskEditor' without a space. Since buildStructuredMenu matches labels against these constants using exact string comparison, this option will be incorrectly categorized as an Extension instead of a Core item and placed in the wrong menu section.

Align the label by either:

  • Changing the translation key to 'contextMenu.Open in MaskEditor' (no space), or
  • Updating CORE_MENU_ITEMS and MENU_ORDER to 'Open in Mask Editor' (with space)

Comment on lines 26 to 80
export function calculateMenuPosition(
options: MenuPositionOptions
): MenuPositionStyle {
const {
triggerRect,
menuElement,
isTriggeredByToolbox,
marginY = 8
} = options

// Calculate horizontal position (same as before)
const left = isTriggeredByToolbox
? triggerRect.left + triggerRect.width / 2
: triggerRect.right - triggerRect.width / 4

// Calculate initial top position
const initialTop = isTriggeredByToolbox
? triggerRect.bottom + marginY
: triggerRect.top - marginY - 6

// Get menu dimensions
const menuHeight = menuElement.offsetHeight || menuElement.scrollHeight
const viewportHeight = window.innerHeight

// Calculate available space below the trigger point
const spaceBelow = viewportHeight - initialTop

// Check if menu would overflow viewport bottom
const wouldOverflow = menuHeight > spaceBelow

const baseStyle: MenuPositionStyle = {
position: 'fixed',
left: `${left}px`,
transform: 'translate(-50%, 0)'
}

if (triggerRect.top < 0) {
// Dock to top of viewport if node is above
return {
...baseStyle,
top: '0px'
}
} else if (wouldOverflow) {
// Dock to bottom of viewport
return {
...baseStyle,
bottom: '0px'
}
} else {
// Position below trigger as normal
return {
...baseStyle,
top: `${initialTop}px`
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clamp menus that would render partially above the viewport

The vertical logic handles “node above viewport” via triggerRect.top < 0 and bottom overflow via wouldOverflow, but there’s an edge case when the trigger is near the top for non-toolbox menus:

const initialTop = isTriggeredByToolbox
  ? triggerRect.bottom + marginY
  : triggerRect.top - marginY - 6

For small positive triggerRect.top, initialTop can become negative even though triggerRect.top >= 0, so the menu can be positioned partly off-screen at the top.

You can clamp based on initialTop instead to keep the menu fully visible:

-  if (triggerRect.top < 0) {
+  if (initialTop < 0) {
     // Dock to top of viewport if node is above
     return {
       ...baseStyle,
       top: '0px'
     }
   } else if (wouldOverflow) {

This still covers the “node is above the viewport” case while also fixing near‑top triggers.

🤖 Prompt for AI Agents
In src/composables/graph/useViewportAwareMenuPositioning.ts around lines 26-80,
the menu can still be positioned partly off-screen near the top because
initialTop can be negative even when triggerRect.top >= 0; replace the current
triggerRect.top check with logic that clamps initialTop: compute initialTop as
before, set clampedTop = Math.max(0, initialTop), use clampedTop for
spaceBelow/wouldOverflow calculations and return top: `${clampedTop}px` when not
docking to bottom, and treat initialTop < 0 (or clampedTop === 0) as the “dock
to top” case so menus never render above the viewport.

@Myestery Myestery requested a review from viva-jinyi as a code owner November 22, 2025 03:04
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: 1

♻️ Duplicate comments (1)
src/components/graph/selectionToolbox/NodeOptions.vue (1)

27-55: Search/filter behavior looks solid; reconsider Escape handling with Popover

The search pipeline (searchQuery → debounced Fuse over searchableMenuOptionsfilteredMenuOptions) correctly:

  • hides dividers and categories while filtering,
  • preserves divider structure only where matched items remain, and
  • exposes an empty state only when no actionable items match.

One thing to double‑check: with @keydown.escape="clearSearch" on SearchBox and :close-on-escape="true" on <Popover>, pressing Escape will clear the query and still close the menu, so the clear is effectively redundant and you can’t get “Escape clears first, closes on second press”. If the intended UX is “Escape clears while there’s text, otherwise closes the menu”, you’ll need to either:

  • accept the KeyboardEvent in clearSearch and call event.stopPropagation() when searchQuery is non‑empty, or
  • use .stop in the template and conditionally re‑emit only when the query is already empty.

Also applies to: 81-184, 399-402, 403-416

🧹 Nitpick comments (2)
src/lib/litegraph/src/LGraphCanvas.ts (1)

711-715: Null‑aware menu typings look correct; ensure implementers handle null entries

Updating getExtraMenuOptions and getCanvasMenuOptions to use (IContextMenuValue | null)[] matches the actual usage of null as dividers in menu arrays and lines up with the compat layer. Just make sure any custom getExtraMenuOptions implementations and downstream consumers treat entries as nullable (skip or filter them) rather than assuming every item is a real option.

Also applies to: 8012-8048

src/components/graph/selectionToolbox/NodeOptions.vue (1)

190-257: Avoid appending to style.cssText every frame in repositionPopover

repositionPopover currently updates the overlay via:

overlayEl.style.cssText += `; left: ${style.left}; position: ${style.position}; transform: ${style.transform};`

and runs on every RAF tick while open. This continually grows the inline style string and does unnecessary work; it’s safer and cheaper to assign properties directly, e.g.:

overlayEl.style.position = style.position
overlayEl.style.left = style.left
overlayEl.style.transform = style.transform

keeping the existing explicit top/bottom assignments as‑is. That avoids unbounded cssText growth and makes future overrides easier to reason about.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03c8cf8 and 17ab44e.

📒 Files selected for processing (6)
  • src/components/graph/selectionToolbox/MenuOptionItem.vue (3 hunks)
  • src/components/graph/selectionToolbox/NodeOptions.vue (9 hunks)
  • src/components/input/SearchBox.vue (1 hunks)
  • src/composables/useContextMenuTranslation.ts (3 hunks)
  • src/lib/litegraph/src/LGraphCanvas.ts (2 hunks)
  • src/lib/litegraph/src/contextMenuCompat.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/graph/selectionToolbox/MenuOptionItem.vue
🧰 Additional context used
🧬 Code graph analysis (3)
src/lib/litegraph/src/LGraphCanvas.ts (1)
src/lib/litegraph/src/interfaces.ts (1)
  • IContextMenuValue (405-426)
src/lib/litegraph/src/contextMenuCompat.ts (2)
src/lib/litegraph/src/interfaces.ts (1)
  • IContextMenuValue (405-426)
src/lib/litegraph/src/LGraphCanvas.ts (1)
  • LGraphCanvas (251-8596)
src/composables/useContextMenuTranslation.ts (3)
src/lib/litegraph/src/interfaces.ts (1)
  • IContextMenuValue (405-426)
src/lib/litegraph/src/LGraphCanvas.ts (3)
  • getCanvasMenuOptions (8012-8048)
  • LGraphCanvas (251-8596)
  • item (3492-3508)
src/lib/litegraph/src/contextMenuCompat.ts (1)
  • legacyMenuCompat (164-164)
⏰ 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: setup
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (6)
src/components/input/SearchBox.vue (1)

65-70: Exposing focusInput for parent control is appropriate here

focusInput safely guards the InputText ref and exposing it via defineExpose gives callers (e.g. NodeOptions.vue) a clean way to focus the field without leaking internals.

Also applies to: 94-96

src/composables/useContextMenuTranslation.ts (1)

25-28: Node/canvas menu wrappers correctly merge extension and legacy items

The updated wrappers for getCanvasMenuOptions/getNodeMenuOptions cleanly:

  • call the original implementation,
  • append new extension items (collectCanvasMenuItems / collectNodeMenuItems),
  • then append legacy monkey‑patched items via legacyMenuCompat.extractLegacyItems(...),
    all under a unified (IContextMenuValue | null)[] return type. This preserves legacy behavior while enabling the new extension API and keeps translateMenus’ expectations intact.

Also applies to: 64-66, 73-91, 97-102

src/lib/litegraph/src/contextMenuCompat.ts (4)

10-12: Type signature correctly updated to support nullable menu items.

The change from IContextMenuValue[] to (IContextMenuValue | null)[] aligns with the broader API updates in LGraphCanvas that now support nullable menu items.


65-99: Installation logic correctly tracks monkey-patching with deprecation warnings.

The method properly:

  • Stores the original method
  • Uses a property setter to detect monkey-patching
  • Issues deprecation warnings with extension identification
  • Maintains a set to avoid duplicate warnings

Note: The same type safety concerns from lines 48-55 apply to the casting at lines 72-75.


48-55: Type safety is lost through double casting; no signature validation exists.

The pattern as unknown as ContextMenuValueProvider (lines 50, 54) completely bypasses TypeScript's type checking. While this may be necessary for the compatibility layer to work with Map storage, there is no runtime signature validation. When methods are retrieved and invoked at line 143 (methodToCall.apply(context, args)), there are only identity checks (e.g., currentMethod === registeredWrapper) but no verification that the method actually matches the ContextMenuValueProvider signature. If a caller registers a method with an incompatible signature, the code will fail at runtime without error handling.

Add signature validation in registerWrapper to verify that the provided method returns the expected type, or add a try-catch around the .apply() invocation at line 143 to gracefully handle signature mismatches.


112-161: No issues found—null entries are safely handled downstream.

The extractLegacyItems call sites in useContextMenuTranslation.ts push returned items (including nulls) into the res array. Later iteration safely accesses properties using optional chaining: if (item?.content) { item.content = ... }. This pattern correctly handles null values without errors.

Comment on lines 148 to 152
if (patchedItems.length > originalItems.length) {
return patchedItems.slice(originalItems.length) as IContextMenuValue[]
return patchedItems.slice(
originalItems.length
) as (IContextMenuValue | null)[]
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Length-based comparison for detecting added items is fragile.

The logic at line 148 assumes monkey patches only append items to the end of the array. If a legacy patch:

  • Removes items: originalItems.length could be >= patchedItems.length, returning empty array
  • Reorders items: The slice would return the wrong items
  • Replaces items: The diff count would be wrong

This could cause legacy menu items to be missed or incorrectly extracted.

Consider one of these approaches:

  1. Set-based diffing: Compare items by identity/reference to find genuinely added items
  2. Documentation: Clearly document that this compatibility layer only supports patches that append items
  3. Validation: Add a warning when patchedItems.length < originalItems.length to detect removals

Example of set-based diffing:

-      if (patchedItems.length > originalItems.length) {
-        return patchedItems.slice(
-          originalItems.length
-        ) as (IContextMenuValue | null)[]
-      }
+      // Find items in patchedItems that aren't in originalItems by reference
+      const originalSet = new Set(originalItems)
+      const addedItems = patchedItems.filter(item => !originalSet.has(item))
+      return addedItems
🤖 Prompt for AI Agents
In src/lib/litegraph/src/contextMenuCompat.ts around lines 148-152 the current
length-based check to detect added menu items is fragile (it assumes patches
only append items); replace this with a set-based diff: build a Set of
originalItems (by reference or a stable identifier) and return items from
patchedItems that are not in that Set to reliably detect additions regardless of
reorders or replacements; also add validation/logging to warn (or handle) when
patchedItems.length < originalItems.length to detect removals and avoid silent
failures, and document the behavior in a short comment above the function.

@Myestery Myestery requested a review from a team as a code owner December 1, 2025 21:16
- Create NodeContextMenu.vue using pure PrimeVue ContextMenu
- Remove custom positioning logic, let PrimeVue handle it
- Delete NodeOptions.vue and MenuOptionItem.vue
- Mount NodeContextMenu in SelectionToolbox
- Update test stubs
- Rename SubmenuPopover.vue to ColorPickerMenu.vue
- Add isColorPicker prop to MenuOption interface
- Use isColorPicker flag instead of checking submenu colors
- Fix onClickOutside immediate close with ignore option
- Delete useSubmenuPositioning.ts (no longer used)
- Delete useViewportAwareMenuPositioning.ts (no longer used)
- Remove unused exports from useSelectionToolboxPosition.ts
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: 1

🧹 Nitpick comments (5)
src/composables/canvas/useSelectionToolboxPosition.ts (1)

23-23: Consider updating comment to reflect internal scope.

The comment describes these as "Shared signals" but they're now module-private. Consider rewording to "Internal signals" or "Module-scoped signals" for clarity.

-// Shared signals for auxiliary UI (e.g., MoreOptions) to coordinate hide/restore
+// Internal signals for auxiliary UI (e.g., MoreOptions) to coordinate hide/restore
src/components/graph/NodeContextMenu.vue (2)

4-18: Add keyboard accessibility to the custom menu item anchor.

The custom anchor element lacks keyboard accessibility attributes. Users navigating via keyboard won't be able to interact with color submenu items properly.

Consider adding role="menuitem" and keyboard event handling:

       <a
         v-bind="props.action"
         class="flex items-center gap-2 px-3 py-1.5"
+        role="menuitem"
         @click="item.isColorSubmenu ? showColorPopover($event) : undefined"
+        @keydown.enter="item.isColorSubmenu ? showColorPopover($event) : undefined"
       >

126-136: Type assertion as MouseEvent may be unsafe.

The toggle function accepts a generic Event but casts it to MouseEvent when calling show(). If toggle is called with a keyboard event (e.g., Enter key), the cast would be incorrect and could cause issues with contextMenu.value?.show(event) if it relies on mouse-specific properties.

Consider handling both event types:

 function toggle(
   event: Event,
   _element?: HTMLElement,
   _clickedFromToolbox?: boolean
 ) {
   if (isOpen.value) {
     hide()
   } else {
-    show(event as MouseEvent)
+    if (event instanceof MouseEvent) {
+      show(event)
+    } else {
+      // For keyboard events, show at a default position or use the target element
+      const target = event.target as HTMLElement
+      const rect = target.getBoundingClientRect()
+      const syntheticEvent = new MouseEvent('contextmenu', {
+        clientX: rect.left,
+        clientY: rect.bottom
+      })
+      show(syntheticEvent)
+    }
   }
 }
src/components/graph/selectionToolbox/ColorPickerMenu.vue (1)

84-95: Consider viewport boundary checking for the positioned menu.

The showToRight function positions the menu to the right of the target element but doesn't check if the menu would overflow the viewport. Per the PR objectives, viewport-aware positioning was introduced to prevent overflow.

Consider adding viewport boundary checks:

 const showToRight = (target: HTMLElement) => {
   const rect = target.getBoundingClientRect()
+  const menuWidth = 200 // Approximate menu width, or measure after render
+  const viewportWidth = window.innerWidth
+  
+  let left = rect.right + 4
+  // If menu would overflow right edge, position to the left of target
+  if (left + menuWidth > viewportWidth) {
+    left = rect.left - menuWidth - 4
+  }
+  
   position.value = {
     top: rect.top,
-    left: rect.right + 4
+    left
   }
   isVisible.value = true
   justOpened = true
   setTimeout(() => {
     justOpened = false
   }, 0)
 }
src/composables/graph/useMoreOptionsMenu.ts (1)

183-250: Section assembly is coherent; consider avoiding duplicate getNodeVisualOptions calls

The restructuring into explicit sections (basic selection, node/group actions, structure, properties, image) reads clearly and composes well with buildStructuredMenu. Guards for groupContext, hasMultipleNodes, hasImageNode, and hasOutputNodesSelected keep the menu context‑sensitive.

One small cleanup: getNodeVisualOptions(states, bump) is called twice when !groupContext (once for the Minimize/Expand entry and once for Shape/Color). You can compute it once and reuse to avoid duplicated work and keep the mapping between indices and semantics in one place:

-    if (groupContext) {
-      const fitGroup = getFitGroupToNodesOption(groupContext)
-      options.push(fitGroup)
-    } else {
-      // Add minimize/expand option only
-      const visualOptions = getNodeVisualOptions(states, bump)
-      if (visualOptions.length > 0) {
-        options.push(visualOptions[0]) // Minimize/Expand
-      }
-    }
+    let visualOptions: MenuOption[] = []
+    if (groupContext) {
+      const fitGroup = getFitGroupToNodesOption(groupContext)
+      options.push(fitGroup)
+    } else {
+      // Add minimize/expand option only
+      visualOptions = getNodeVisualOptions(states, bump)
+      if (visualOptions.length > 0) {
+        options.push(visualOptions[0]) // Minimize/Expand
+      }
+    }
@@
-    if (groupContext) {
+    if (groupContext) {
       const groupColor = getGroupColorOptions(groupContext, bump)
       options.push(groupColor)
     } else {
       // Add shape and color options
-      const visualOptions = getNodeVisualOptions(states, bump)
       if (visualOptions.length > 1) {
         options.push(visualOptions[1]) // Shape (index 1)
       }
       if (visualOptions.length > 2) {
         options.push(visualOptions[2]) // Color (index 2)
       }
     }

Purely an ergonomics/readability improvement; behavior stays the same.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16f3e43 and 06d3ea5.

📒 Files selected for processing (11)
  • src/components/graph/GraphCanvas.vue (0 hunks)
  • src/components/graph/NodeContextMenu.vue (1 hunks)
  • src/components/graph/SelectionToolbox.test.ts (1 hunks)
  • src/components/graph/SelectionToolbox.vue (2 hunks)
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue (4 hunks)
  • src/components/graph/selectionToolbox/MenuOptionItem.vue (0 hunks)
  • src/components/graph/selectionToolbox/NodeOptions.vue (0 hunks)
  • src/composables/canvas/useSelectionToolboxPosition.ts (1 hunks)
  • src/composables/graph/useMoreOptionsMenu.ts (5 hunks)
  • src/composables/graph/useNodeMenuOptions.ts (2 hunks)
  • src/composables/graph/useSubmenuPositioning.ts (0 hunks)
💤 Files with no reviewable changes (4)
  • src/components/graph/GraphCanvas.vue
  • src/composables/graph/useSubmenuPositioning.ts
  • src/components/graph/selectionToolbox/NodeOptions.vue
  • src/components/graph/selectionToolbox/MenuOptionItem.vue
🧰 Additional context used
📓 Path-based instructions (21)
**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/components/graph/SelectionToolbox.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
  • src/composables/graph/useNodeMenuOptions.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Use es-toolkit for utility functions

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/composables/graph/useNodeMenuOptions.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use TypeScript for type safety

**/*.{ts,tsx}: Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/composables/graph/useNodeMenuOptions.ts
**/*.{ts,tsx,js,vue}

📄 CodeRabbit inference engine (.cursorrules)

Implement proper error handling in components and services

**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in .prettierrc
Organize imports by sorting and grouping by plugin, and run pnpm format before committing

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/components/graph/SelectionToolbox.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
  • src/composables/graph/useNodeMenuOptions.ts
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/components/graph/SelectionToolbox.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
  • src/composables/graph/useNodeMenuOptions.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/composables/graph/useNodeMenuOptions.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use camelCase for variable and setting names in TypeScript/Vue files

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/components/graph/SelectionToolbox.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
  • src/composables/graph/useNodeMenuOptions.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,vue}: Use const settingStore = useSettingStore() and settingStore.get('Comfy.SomeSetting') to retrieve settings in TypeScript/Vue files
Use await settingStore.set('Comfy.SomeSetting', newValue) to update settings in TypeScript/Vue files
Check server capabilities using api.serverSupportsFeature('feature_name') before using enhanced features
Use api.getServerFeature('config_name', defaultValue) to retrieve server feature configuration

Enforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/components/graph/SelectionToolbox.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
  • src/composables/graph/useNodeMenuOptions.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
Use defaultsByInstallVersion property for gradual feature rollout based on version in settings configuration

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/composables/graph/useNodeMenuOptions.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/components/graph/SelectionToolbox.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
  • src/composables/graph/useNodeMenuOptions.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/components/graph/SelectionToolbox.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
  • src/composables/graph/useNodeMenuOptions.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/components/graph/SelectionToolbox.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
  • src/composables/graph/useNodeMenuOptions.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/components/graph/SelectionToolbox.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
  • src/composables/graph/useNodeMenuOptions.ts
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/SelectionToolbox.vue
  • src/components/graph/NodeContextMenu.vue
**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Unit and component tests should be located in tests-ui/ or co-located with components as src/components/**/*.{test,spec}.ts; E2E tests should be in browser_tests/

Files:

  • src/components/graph/SelectionToolbox.test.ts
**/*.vue

📄 CodeRabbit inference engine (.cursorrules)

**/*.vue: Use setup() function for component logic in Vue 3 Composition API
Utilize ref and reactive for reactive state in Vue 3
Implement computed properties with computed() function
Use watch and watchEffect for side effects in Vue 3
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection in Vue 3
Use Vue 3.5 style of default prop declaration with defineProps()
Organize Vue components in <script> <style> order
Use Tailwind CSS for styling Vue components
Implement responsive design with Tailwind CSS
Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components in Vue 3
Follow Vue 3 style guide and naming conventions
Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)

Never use :class="[]" to merge class names - always use import { cn } from '@/utils/tailwindUtil' for class merging in Vue templates

**/*.vue: Use TypeScript with Vue 3 Single File Components (.vue files)
Name Vue components in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/SelectionToolbox.vue
  • src/components/graph/NodeContextMenu.vue
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/SelectionToolbox.vue
  • src/components/graph/NodeContextMenu.vue
**/*.{vue,html}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use dark: or dark-theme: Tailwind variants - instead use semantic values from style.css theme, e.g. bg-node-component-surface

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/SelectionToolbox.vue
  • src/components/graph/NodeContextMenu.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/SelectionToolbox.vue
  • src/components/graph/NodeContextMenu.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/SelectionToolbox.vue
  • src/components/graph/NodeContextMenu.vue
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/canvas/useSelectionToolboxPosition.ts
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/composables/graph/useNodeMenuOptions.ts
🧠 Learnings (25)
📓 Common learnings
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue TabMenu component with Tabs without panels
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Dropdown component with Select
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Sidebar component with Drawer
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InlineMessage component with Message
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Calendar component with DatePicker
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Steps component with Stepper without panels
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup

Applied to files:

  • src/components/graph/SelectionToolbox.test.ts
  • src/composables/graph/useMoreOptionsMenu.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in tests

Applied to files:

  • src/components/graph/SelectionToolbox.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features

Applied to files:

  • src/components/graph/SelectionToolbox.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{js,ts,jsx,tsx} : When adding features, always write vitest unit tests using cursor rules in @.cursor

Applied to files:

  • src/components/graph/SelectionToolbox.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : When writing tests for subgraph-related code, always import from the barrel export at `@/lib/litegraph/src/litegraph` to avoid circular dependency issues

Applied to files:

  • src/components/graph/SelectionToolbox.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilities

Applied to files:

  • src/components/graph/SelectionToolbox.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Dropdown component with Select

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Sidebar component with Drawer

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Calendar component with DatePicker

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InlineMessage component with Message

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Chips component with AutoComplete with multiple enabled

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:14.779Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:14.779Z
Learning: Applies to **/*.{ts,tsx,vue} : Use `const settingStore = useSettingStore()` and `settingStore.get('Comfy.SomeSetting')` to retrieve settings in TypeScript/Vue files

Applied to files:

  • src/components/graph/SelectionToolbox.vue
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/*.{vue,ts,tsx} : Follow Vue 3 composition API style guide

Applied to files:

  • src/composables/graph/useMoreOptionsMenu.ts
  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/composables/graph/useMoreOptionsMenu.ts
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.{vue,ts,tsx} : Leverage VueUse functions for performance-enhancing utilities

Applied to files:

  • src/composables/graph/useMoreOptionsMenu.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles

Applied to files:

  • src/composables/graph/useMoreOptionsMenu.ts
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Utilize ref and reactive for reactive state in Vue 3

Applied to files:

  • src/composables/graph/useMoreOptionsMenu.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)

Applied to files:

  • src/components/graph/NodeContextMenu.vue
🧬 Code graph analysis (1)
src/composables/graph/useMoreOptionsMenu.ts (5)
src/renderer/core/canvas/canvasStore.ts (1)
  • useCanvasStore (24-152)
src/composables/graph/useImageMenuOptions.ts (1)
  • useImageMenuOptions (11-108)
src/composables/graph/useNodeMenuOptions.ts (1)
  • useNodeMenuOptions (12-129)
src/composables/graph/useGroupMenuOptions.ts (1)
  • useGroupMenuOptions (16-196)
src/composables/graph/contextMenuConverter.ts (2)
  • convertContextMenuToOptions (358-447)
  • buildStructuredMenu (253-349)
🪛 ESLint
src/components/graph/selectionToolbox/ColorPickerMenu.vue

[error] 51-51: Unable to resolve path to module '@comfyorg/tailwind-utils'.

(import-x/no-unresolved)


[error] 52-52: Unable to resolve path to module '@vueuse/core'.

(import-x/no-unresolved)

src/components/graph/SelectionToolbox.vue

[error] 72-72: Unable to resolve path to module './NodeContextMenu.vue'.

(import-x/no-unresolved)

src/composables/graph/useMoreOptionsMenu.ts

[error] 5-5: Unable to resolve path to module '@/renderer/core/canvas/canvasStore'.

(import-x/no-unresolved)


[error] 6-6: Unable to resolve path to module '@/utils/litegraphUtil'.

(import-x/no-unresolved)


[error] 11-11: Unable to resolve path to module './contextMenuConverter'.

(import-x/no-unresolved)

src/components/graph/NodeContextMenu.vue

[error] 1-1: Resolve error: EACCES: permission denied, open '/saEQzionsM'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at processBodyStatement (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:9:31)
at Program (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:100:21)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/namespace)


[error] 1-1: Resolve error: EACCES: permission denied, open '/bOzMFyGRnd'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-unresolved.js:31:34)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/no-unresolved)


[error] 1-1: Resolve error: EACCES: permission denied, open '/socnuAABhC'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at checkDefault (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/default.js:22:39)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/default)


[error] 1-1: Resolve error: EACCES: permission denied, open '/IUYggMyLaY'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at importType (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/import-type.js:126:63)
at checkImportForRelativePackage (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:15:38)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:59:40
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/no-relative-packages)


[error] 1-1: Resolve error: EACCES: permission denied, open '/ZppeldVgLu'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-useless-path-segments.js:58:34
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/no-useless-path-segments)


[error] 32-32: Unable to resolve path to module 'primevue/contextmenu'.

(import-x/no-unresolved)


[error] 34-34: Unable to resolve path to module 'vue'.

(import-x/no-unresolved)


[error] 39-39: Unable to resolve path to module '@/composables/graph/useMoreOptionsMenu'.

(import-x/no-unresolved)


[error] 45-45: Unable to resolve path to module './selectionToolbox/ColorPickerMenu.vue'.

(import-x/no-unresolved)

⏰ 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: setup
  • GitHub Check: test
  • GitHub Check: lint-and-format
  • GitHub Check: collect

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: 1

♻️ Duplicate comments (1)
src/components/graph/selectionToolbox/ColorPickerMenu.vue (1)

24-27: Remove duplicate hover class.

Line 26 contains hover:bg-secondary-background-hover which is already applied on line 20, making it redundant.

Apply this diff:

           subOption.disabled
             ? 'cursor-not-allowed pointer-events-none text-node-icon-disabled'
-            : 'hover:bg-secondary-background-hover'
+            : ''

Note: The dark-theme: variant usage on line 26 was already flagged in a previous review.

🧹 Nitpick comments (1)
src/components/graph/selectionToolbox/ColorPickerMenu.vue (1)

109-120: Consider more maintainable ignore selectors.

The ignore selectors rely on hardcoded PrimeVue internal class names (.p-contextmenu, .p-contextmenu-item-link). If PrimeVue changes these class names in a future version, the click-outside behavior will break.

Consider using data attributes or template refs for more maintainable ignore logic:

// Option 1: Pass parent context menu ref as prop
onClickOutside(
  popoverRef,
  () => {
    if (justOpened) {
      justOpened = false
      return
    }
    hide()
  },
  { ignore: [props.parentMenuRef] }
)

// Option 2: Use data attribute selectors
{ ignore: ['[data-context-menu]', '[data-context-menu-item]'] }

Then add the corresponding data attributes to the parent ContextMenu component's template.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06d3ea5 and dad2e8b.

📒 Files selected for processing (1)
  • src/components/graph/selectionToolbox/ColorPickerMenu.vue (4 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
**/*.vue

📄 CodeRabbit inference engine (.cursorrules)

**/*.vue: Use setup() function for component logic in Vue 3 Composition API
Utilize ref and reactive for reactive state in Vue 3
Implement computed properties with computed() function
Use watch and watchEffect for side effects in Vue 3
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection in Vue 3
Use Vue 3.5 style of default prop declaration with defineProps()
Organize Vue components in <script> <style> order
Use Tailwind CSS for styling Vue components
Implement responsive design with Tailwind CSS
Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components in Vue 3
Follow Vue 3 style guide and naming conventions
Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)

Never use :class="[]" to merge class names - always use import { cn } from '@/utils/tailwindUtil' for class merging in Vue templates

**/*.vue: Use TypeScript with Vue 3 Single File Components (.vue files)
Name Vue components in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
**/*.{ts,tsx,js,vue}

📄 CodeRabbit inference engine (.cursorrules)

Implement proper error handling in components and services

**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in .prettierrc
Organize imports by sorting and grouping by plugin, and run pnpm format before committing

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use camelCase for variable and setting names in TypeScript/Vue files

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
**/*.{vue,html}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use dark: or dark-theme: Tailwind variants - instead use semantic values from style.css theme, e.g. bg-node-component-surface

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,vue}: Use const settingStore = useSettingStore() and settingStore.get('Comfy.SomeSetting') to retrieve settings in TypeScript/Vue files
Use await settingStore.set('Comfy.SomeSetting', newValue) to update settings in TypeScript/Vue files
Check server capabilities using api.serverSupportsFeature('feature_name') before using enhanced features
Use api.getServerFeature('config_name', defaultValue) to retrieve server feature configuration

Enforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Dropdown component with Select
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue TabMenu component with Tabs without panels
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Sidebar component with Drawer
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Calendar component with DatePicker
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InlineMessage component with Message
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Chips component with AutoComplete with multiple enabled
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Dropdown component with Select

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Sidebar component with Drawer

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Calendar component with DatePicker

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InlineMessage component with Message

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Chips component with AutoComplete with multiple enabled

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:14.779Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:14.779Z
Learning: Applies to **/*.{vue,html} : Never use `dark:` or `dark-theme:` Tailwind variants - instead use semantic values from `style.css` theme, e.g. `bg-node-component-surface`

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:14.779Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:14.779Z
Learning: Applies to **/*.vue : Never use `:class="[]"` to merge class names - always use `import { cn } from '@/utils/tailwindUtil'` for class merging in Vue templates

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,css} : Use the correct tokens from style.css in the design system package

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use Tailwind CSS for styling

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Implement responsive design with Tailwind CSS

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners

Applied to files:

  • src/components/graph/selectionToolbox/ColorPickerMenu.vue
🪛 ESLint
src/components/graph/selectionToolbox/ColorPickerMenu.vue

[error] 51-51: Unable to resolve path to module '@comfyorg/tailwind-utils'.

(import-x/no-unresolved)


[error] 52-52: Unable to resolve path to module '@vueuse/core'.

(import-x/no-unresolved)

⏰ 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: setup
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (1)
src/components/graph/selectionToolbox/ColorPickerMenu.vue (1)

129-131: LGTM!

The early return for disabled submenu options correctly prevents actions from being triggered on disabled items.

Comment on lines +84 to 95
const showToRight = (target: HTMLElement) => {
const rect = target.getBoundingClientRect()
position.value = {
top: rect.top,
left: rect.right + 4
}
isVisible.value = true
justOpened = true
setTimeout(() => {
justOpened = false
}, 0)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add viewport boundary checks to prevent overflow.

The positioning logic places the menu to the right of the target without checking viewport boundaries. The menu could overflow off the right edge or bottom of the viewport, degrading user experience.

Consider adding boundary detection:

 const showToRight = (target: HTMLElement) => {
   const rect = target.getBoundingClientRect()
+  const menuWidth = 200 // approximate or measure actual width
+  const menuHeight = popoverRef.value?.offsetHeight || 0
+  
+  let left = rect.right + 4
+  let top = rect.top
+  
+  // Check right boundary
+  if (left + menuWidth > window.innerWidth) {
+    left = rect.left - menuWidth - 4
+  }
+  
+  // Check bottom boundary
+  if (top + menuHeight > window.innerHeight) {
+    top = window.innerHeight - menuHeight - 8
+  }
+  
   position.value = {
-    top: rect.top,
-    left: rect.right + 4
+    top: Math.max(8, top),
+    left: Math.max(8, left)
   }
   isVisible.value = true
   justOpened = true
   setTimeout(() => {
     justOpened = false
   }, 0)
 }

Alternatively, consider using a positioning utility library or VueUse's useFloating composable.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/components/graph/selectionToolbox/ColorPickerMenu.vue around lines 84–95,
the showToRight function positions the menu to the right of the target without
checking viewport boundaries; update it to compute the menu element size
(offsetWidth/offsetHeight or getBoundingClientRect on the menu ref) and then
clamp or flip the calculated left/top so the menu stays within window.innerWidth
and window.innerHeight with a small margin (e.g., 8px); if there isn't enough
space to the right, position to the left of the target (rect.left - menuWidth -
margin), and if the bottom would overflow, adjust top to rect.bottom -
menuHeight or clamp to maximum allowed top; ensure measurement happens on a
visible element (temporarily set isVisible if needed) or compute off-DOM, then
set position.value and isVisible.value as before.

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: 1

🧹 Nitpick comments (1)
src/components/graph/NodeContextMenu.vue (1)

117-120: Clarify the purpose of bump() call.

Line 118 calls bump() before showing the context menu. While this likely refreshes the menu options, consider adding a comment to explain its purpose for future maintainers.

 function show(event: MouseEvent) {
+  // Refresh menu options before displaying
   bump()
   contextMenu.value?.show(event)
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dad2e8b and 01cca71.

📒 Files selected for processing (1)
  • src/components/graph/NodeContextMenu.vue (1 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
**/*.vue

📄 CodeRabbit inference engine (.cursorrules)

**/*.vue: Use setup() function for component logic in Vue 3 Composition API
Utilize ref and reactive for reactive state in Vue 3
Implement computed properties with computed() function
Use watch and watchEffect for side effects in Vue 3
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection in Vue 3
Use Vue 3.5 style of default prop declaration with defineProps()
Organize Vue components in <script> <style> order
Use Tailwind CSS for styling Vue components
Implement responsive design with Tailwind CSS
Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components in Vue 3
Follow Vue 3 style guide and naming conventions
Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)

Never use :class="[]" to merge class names - always use import { cn } from '@/utils/tailwindUtil' for class merging in Vue templates

**/*.vue: Use TypeScript with Vue 3 Single File Components (.vue files)
Name Vue components in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/graph/NodeContextMenu.vue
**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json

Files:

  • src/components/graph/NodeContextMenu.vue
**/*.{ts,tsx,js,vue}

📄 CodeRabbit inference engine (.cursorrules)

Implement proper error handling in components and services

**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in .prettierrc
Organize imports by sorting and grouping by plugin, and run pnpm format before committing

Files:

  • src/components/graph/NodeContextMenu.vue
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/components/graph/NodeContextMenu.vue
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/components/graph/NodeContextMenu.vue
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use camelCase for variable and setting names in TypeScript/Vue files

Files:

  • src/components/graph/NodeContextMenu.vue
**/*.{vue,html}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use dark: or dark-theme: Tailwind variants - instead use semantic values from style.css theme, e.g. bg-node-component-surface

Files:

  • src/components/graph/NodeContextMenu.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,vue}: Use const settingStore = useSettingStore() and settingStore.get('Comfy.SomeSetting') to retrieve settings in TypeScript/Vue files
Use await settingStore.set('Comfy.SomeSetting', newValue) to update settings in TypeScript/Vue files
Check server capabilities using api.serverSupportsFeature('feature_name') before using enhanced features
Use api.getServerFeature('config_name', defaultValue) to retrieve server feature configuration

Enforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates

Files:

  • src/components/graph/NodeContextMenu.vue
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

Files:

  • src/components/graph/NodeContextMenu.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/graph/NodeContextMenu.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/graph/NodeContextMenu.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/graph/NodeContextMenu.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Files:

  • src/components/graph/NodeContextMenu.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/graph/NodeContextMenu.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/graph/NodeContextMenu.vue
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Dropdown component with Select
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue TabMenu component with Tabs without panels
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Sidebar component with Drawer
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Calendar component with DatePicker
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InlineMessage component with Message
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Steps component with Stepper without panels
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue TabMenu component with Tabs without panels

Applied to files:

  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Sidebar component with Drawer

Applied to files:

  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover

Applied to files:

  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Dropdown component with Select

Applied to files:

  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)

Applied to files:

  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.vue : Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively

Applied to files:

  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch

Applied to files:

  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)

Applied to files:

  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:48:23.088Z
Learning: Applies to **/*.vue : Name Vue components in PascalCase (e.g., `MenuHamburger.vue`)

Applied to files:

  • src/components/graph/NodeContextMenu.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InlineMessage component with Message

Applied to files:

  • src/components/graph/NodeContextMenu.vue
🪛 ESLint
src/components/graph/NodeContextMenu.vue

[error] 1-1: Resolve error: EACCES: permission denied, open '/SBDDjiZKMN'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at processBodyStatement (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:9:31)
at Program (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:100:21)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/namespace)


[error] 1-1: Resolve error: EACCES: permission denied, open '/cFsKQTujGL'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-unresolved.js:31:34)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/no-unresolved)


[error] 1-1: Resolve error: EACCES: permission denied, open '/eRcSMJPorO'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at checkDefault (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/default.js:22:39)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/default)


[error] 1-1: Resolve error: EACCES: permission denied, open '/huRjmINgMY'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at importType (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/import-type.js:126:63)
at checkImportForRelativePackage (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:15:38)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:59:40
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/no-relative-packages)


[error] 1-1: Resolve error: EACCES: permission denied, open '/qsAqdrUujK'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/get-tsconfig@4.10.1/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-import-resolver-typescript@4.4.4_eslint-plugin-import-x@4.16.1_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/eslint-import-context@0.1.9_unrs-resolver@1.11.1/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-useless-path-segments.js:58:34
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint+utils@8.44.0_eslint@9.35.0_jiti@2.5.1__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2189:43)
at /home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1773:17
at Array.map ()
at Linter._verifyWithFlatConfigArrayAndProcessor (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1768:30)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2275:16)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/eslint@9.35.0_jiti@2.5.1/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)

(import-x/no-useless-path-segments)


[error] 35-35: Unable to resolve path to module 'primevue/contextmenu'.

(import-x/no-unresolved)


[error] 37-37: Unable to resolve path to module 'vue'.

(import-x/no-unresolved)


[error] 42-42: Unable to resolve path to module '@/composables/graph/useMoreOptionsMenu'.

(import-x/no-unresolved)


[error] 48-48: Unable to resolve path to module './selectionToolbox/ColorPickerMenu.vue'.

(import-x/no-unresolved)

⏰ 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: deploy-and-comment
  • GitHub Check: test
  • GitHub Check: setup
  • GitHub Check: lint-and-format
  • GitHub Check: collect
🔇 Additional comments (5)
src/components/graph/NodeContextMenu.vue (5)

50-54: LGTM!

The ExtendedMenuItem interface properly extends PrimeVue's MenuItem with custom properties needed for color submenu handling and shortcuts.


73-109: LGTM!

The conversion logic correctly transforms MenuOption structures into PrimeVue-compatible MenuItem objects, handling dividers, color submenus, native submenus, and action items appropriately.


123-139: LGTM!

The hide and toggle functions correctly manage both the context menu and color picker states. The unused parameters in toggle are appropriately prefixed with underscores to indicate they're present for API compatibility.


143-148: Event handling prevents default behavior for color picker.

Lines 144-145 stop event propagation and prevent default behavior to enable the custom ColorPickerMenu. This is necessary for the functionality but may affect accessibility features.

Ensure this custom handling doesn't break keyboard navigation or screen reader announcements. Consider adding keyboard event handling (Enter/Space keys) to activate the color picker.


4-21: Custom click handler may interfere with keyboard navigation.

Line 7 intercepts clicks on color submenu items with a custom handler. While this enables the custom ColorPickerMenu, it may bypass PrimeVue's native keyboard navigation and ARIA attributes for submenus.

Verify that keyboard users can navigate to and activate the color submenu using standard arrow keys and Enter/Space. Test with a screen reader to ensure the submenu is properly announced.

Comment on lines +161 to +164
onMounted(() => {
isOpen.value = false
registerNodeOptionsInstance({ toggle, hide, isOpen })
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, locate the NodeContextMenu.vue file
fd -t f "NodeContextMenu.vue" | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 110


🏁 Script executed:

#!/bin/bash
# Search for NodeContextMenu instantiation patterns
rg -n "NodeContextMenu" --type=vue --type=ts -C 2 | head -100

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Find and examine registerNodeOptionsInstance to understand the pattern
rg -n "registerNodeOptionsInstance" --type=ts --type=vue --type=js -B 3 -A 5

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Read the NodeContextMenu.vue file directly to inspect the relevant lines
cat -n src/components/graph/NodeContextMenu.vue | head -180 | tail -130

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3853


🏁 Script executed:

#!/bin/bash
# Search for registerNodeOptionsInstance without file type restrictions
rg -n "registerNodeOptionsInstance" -B 3 -A 5

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2345


🏁 Script executed:

#!/bin/bash
# Search for NodeContextMenu usage patterns
rg -n "NodeContextMenu" -B 2 -A 2

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2984


🏁 Script executed:

#!/bin/bash
# Examine the useMoreOptionsMenu.ts to understand the singleton pattern
cat -n src/composables/graph/useMoreOptionsMenu.ts | head -100

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3267


🏁 Script executed:

#!/bin/bash
# Check for nodeOptionsInstance variable declaration and usage
rg -n "nodeOptionsInstance" -B 2 -A 2

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1179


Remove redundant initialization in onMounted.

Line 162 sets isOpen.value = false, but the ref is already initialized to false on line 58, making this line redundant and unnecessary.

The registration pattern in line 163 is intentional—nodeOptionsInstance is a module-level singleton designed to maintain a single registered instance for the toggleNodeOptions() function. This is by design and working as intended, with proper cleanup on unmount (line 167).

 onMounted(() => {
-  isOpen.value = false
   registerNodeOptionsInstance({ toggle, hide, isOpen })
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onMounted(() => {
isOpen.value = false
registerNodeOptionsInstance({ toggle, hide, isOpen })
})
onMounted(() => {
registerNodeOptionsInstance({ toggle, hide, isOpen })
})
🤖 Prompt for AI Agents
In src/components/graph/NodeContextMenu.vue around lines 161 to 164, remove the
redundant initialization isOpen.value = false inside onMounted since the ref is
already initialized to false at line 58; leave the registerNodeOptionsInstance({
toggle, hide, isOpen }) call intact to preserve the module-level singleton
registration and its existing unmount cleanup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants